2024 10 v5.1
2024 10 v5.1
OpenZeppelin
Contracts
Release v5.1 Audit
October 3, 2024
Table of Contents
Table of Contents __________________________________________________________________ 2
Summary _________________________________________________________________________ 4
Scope ____________________________________________________________________________ 5
Phase 1 5
Phase 2 6
Recommendations _______________________________________________________________ 36
Protection Against Postquantum Adversaries in RSA Signatures 36
Conclusion ______________________________________________________________________ 38
Phase 1
The scope of this audit was limited to the changes introduced in some contracts that were
previously audited for the v5.0.0 release.
contracts
├── access
│ ├── extensions
│ │ └── AccessControlEnumerable.sol
│ └── manager
│ └── AccessManager.sol
├── governance
│ ├── Governor.sol
│ ├── extensions
│ │ └── GovernorCountingSimple.sol
│ └── utils
│ └── Votes.sol
├── metatx
│ └── ERC2771Forwarder.sol
├── proxy
│ ├── Clones.sol
│ ├── ERC1967
│ │ └── ERC1967Utils.sol
│ └── transparent
│ └── TransparentUpgradeableProxy.sol
├── token
│ ├── ERC1155
│ │ ├── ERC1155.sol
│ │ └── extensions
│ │ └── ERC1155Supply.sol
│ ├── ERC20
│ │ └── utils
│ │ └── SafeERC20.sol
│ ├── ERC721
│ │ ├── ERC721.sol
│ │ └── extensions
│ │ └── ERC721Enumerable.sol
│ └── common
│ └── ERC2981.sol
Phase 2
In scope were the following files:
contracts
├── finance
│ └── VestingWalletCliff.sol
├── governance
│ └── extensions
│ └── GovernorCountingFractional.sol
├── token
│ ├── ERC20
│ │ └── extensions
│ │ ├── draft-ERC20TemporaryApproval.sol
│ │ └── ERC1363.sol
│ ├── ERC721
│ │ └── utils
│ │ └── ERC721Utils.sol
│ └── ERC1155
│ └── utils
│ └── ERC1155Utils.sol
└── utils
├── Errors.sol
├── Packing.sol
├── Panic.sol
├── ReentrancyGuardTransient.sol
├── SlotDerivation.sol
├── cryptography
│ ├── Hashes.sol
│ ├── P256.sol
│ └── RSA.sol
└── structs
├── CircularBuffer.sol
Overview - Phase 1
Version 5.1 of the OpenZeppelin Contracts library introduces minor changes to previously
existing contracts. The following modifications were made across the majority of the in-scope
contracts:
• Improved documentation: Many contracts have had their docstrings reworked. They
have either been improved or have been adapted to describe the new functionalities.
• Improved handling of common errors: Common errors have now been isolated into a
separate Panic or Errors library.
• Improved return parameters: Many functions have been changed to have their return
parameters named.
For a complete list of specific changes made in individual files, one can refer to the changelog
from version 5.0.0 to the latest one.
During the course of the audit, some behaviors were observed in the system that were
considered worth mentioning to the community.
Once the delay period has passed (if it is applicable for a given role and caller), the
execute function within this contract is responsible for carrying out these operations. It
achieves this by invoking the target contract’s function through a low-level call, with the
option to send value along with the call.
Overview - Phase 2
Version 5.1 of the OpenZeppelin Contracts library introduces several new features, including
the following:
While traditional voting mechanisms allow an account to participate in a voting process only
once, the GovernorCountingFractional contract allows for rolling voting, whereby one
account can vote for a proposal with a portion of its total weight and then subsequently vote
again with the remaining portion of its weight.
Code Refactor
In the v5.1 release, the checkOnERC721Received function which is used to verify if a
recipient contract implements the IERC721Receiver-onERC721Received hook, has been
moved to a new ERC721Utils library. Similarly, the checkOnERC1155Received and
checkOnERC1155BatchReceived functions have been moved to the ERC1155Utils
library.
A comprehensive list of changes made in the v5.1 release can be found in the changelog.
In addition, the complexity grows because while a library must cover a wide range of potential
use cases, the responsibility for secure implementation often lies with the developers who
integrate it into their projects. A library's security risks can multiply depending on how well
developers understand and utilize its contracts. This necessitates extra care to ensure that all
potential threats, both direct and indirect, are either identified and addressed, or documented
so that the developers are aware of the security risks.
However, when calling execute on a scheduled operation, the call is performed using
msg.value as the attached value. This msg.value can be anything and is not meant to be
a part of operationId . As a result, there can be different execution outcomes for the same
operationId if the target contract implements a logic that depends on msg.value . To
some extent, the same argument can be made for the provided gas in the execute function,
given that the target might have custom logic based on that as well.
Consider adding minimum committed minValue and minGas parameters to the schedule
function and check those in the execute function implementation. Alternatively, consider
making them exact values instead of minimal ones.
Both gas and value were left out of the operationId considering that the operation
can only be executed by its original scheduler. For this reason, the proposer has full
control of how the proposal is executed and can evaluate the execution requirements of
the target at that moment.
The team considers that specifying exact gas and value allows to DoS an operation
by manipulating the target requirements. Similarly, specifying minimum values does not
eliminate the possibility of different execution outcomes.
For these reasons, and considering that this change would be breaking, we have
decided not to include gas and value parameters as part of an operation.
The issue occurs when the proof array only contains the Merkle root, and both the
proofFlags and leaves arrays are empty. Despite the absence of a complete proof, the
function still computes the Merkle root and considers the proof as valid. This can lead to a
scenario where a user can bypass proper validation by submitting an incomplete proof that the
function accepts as valid and returns true . A secret gist has been created with PoC
demonstrating the potential problem.
Update: Resolved in pull request #5144 at commit c304b67 and in pull request #5142 at
commit bcd4beb. The OpenZeppelin Contracts team stated:
Consider cleaning the upper bytes of the address and bool type key before hashing these
values.
Low Severity
L-01 Royalty Calculation May Result in Zero
Token Transfers Between Buyer and Seller of NFT
- Phase 1
In ERC2981.sol , the _setTokenRoyalty function checks and reverts if feeNumerator
is greater than _feeDenominator() to ensure that royaltyAmount is always less than
the salePrice . However, the function accepts feeNumerator to be equal to
_feeDenominator() . In the royaltyInfo function, if feeNumerator is equal to
_feeDenominator() , the royaltyAmount becomes equal to salePrice , which could
translate to the total price of the NFT sale being paid as royalty and no transfer of ERC-20
tokens between the buyer and the seller.
It is essential to note that most NFT marketplaces that support royalty payments have distinct
addresses, one for transfer of sale price, and another for paying royalty. In general, the royalty
is paid to the creator of the NFT and the sale price is transferred from the buyer to the seller (or
owner) of the NFT. The ERC states that royalty should be a percentage of the sale price.
Therefore, the royalty being equal to 100% of the sale price is a valid scenario. However, a
Consider documenting the aforementioned behavior so that the protocols integrating with this
contract are aware of potential zero-amount transfers.
Update: Resolved in pull request #5173. The OpenZeppelin Contracts team stated:
The team agreed with the risks of paying the royaltyAmount using an ERC-20 token
that reverts on 0-value transfers. We are documenting this issue with a note for
integrators to consider.
Consider thoroughly documenting all functions (and their parameters) that are part of any
contract's public API. Functions implementing sensitive functionality, even if not public, should
be clearly documented as well. When writing docstrings, consider following the Ethereum
Natural Specification Format (NatSpec).
While balancing the trade-offs between providing flexible library code and the risk of side
effects like memory manipulation, consider adding this edge case as a warning in the
documentation.
• This docstrings within the _encode function in Base64.sol states that, if padding is
absent, the data.length is rounded up and then multiplied by 4. However, in the code
implementation, data.length is first multiplied by 4 and then rounded up.
Consider updating the aforementioned instances of misleading docstrings for improved code
clarity and readability.
MerkleTree.sol has the pragma directive pragma solidity ^0.8.0; and imports the
file Panic.sol , which has a different pragma directive pragma solidity ^0.8.20; . In
addition, Hashes.sol is the only other file in the entire codebase that uses pragma
solidity ^0.8.0; directive.
Update: Resolved in pull request #5198. The OpenZeppelin Contracts team stated:
We are increasing the pragma version to 0.8.20. This is consistent with the rest of the
library.
Consider starting the vesting period after the cliff has ended.
Update: Acknowledged, not resolved. The understanding of the cliff point in time is subjective
and the OpenZeppelin Contracts team prefers that the cliff timestamp marks the beginning of
the vesting. The OpenZeppelin Contracts team stated:
In any case, this difference of interpretation (< vs <=) only changes the outcome for one
particular second. This is insignificant over the common duration of cliffs (months) and
vestings (years). For this difference to even be visible, you would need a block to be
produced at the exact second the cliff ends (there is an 8% chance that this block even
exists, considering 12s between blocks), and you would have to release the asset in that
exact block. Even if that were to happen, re-submitting the same transaction in the next
block would "resolve" things.
Here we value lower gas cost (of < over <=) and, more importantly, consistency with
existing vesting wallets.
The root element is removed by reading the root and the last elements of the heap. If the
rootNode is not the last element of the array, the value of the root is replaced with the value
of the last element, and the respective indexes and lookups are exchanged. Once copied to
The current implementations of the insert , pop , and replace functionalities are difficult
to follow due to the necessary reading and writing of the indexes and lookups .
Additionally, the _swap function along with the _siftUp or _siftDown functions costs
more gas to swap the indexes and lookups of two nodes as compared to simply
swapping the values .
To reduce the complexity of the codebase, consider simplifying the heap structure by using a
linear uint256 array that stores the values of each node. The array is created such that the
root is always at index 0 (i.e., the first element of the array). For each node at index i , the
value of the left child is found at index i * 2 + 1 and the right child is at i * 2 + 2 .
Update: Resolved in pull request #5190 at commit 71fb803 and in pull request #5215 at
commit 2843690. The OpenZeppelin Contracts team stated:
As suggested, the Heap is updated to remove the dependency on index and lookup
since it is redundant to store them. This way, we simplify the implementation and
improve readability while keeping the core array-based implementation that will leverage
cheaper adjacent storage accesses after the Verkle EVM upgrade.
The above is equivalent to computing the third root of the padded message MM
M modulo nn
n,
which is feasible for e=3e=3
e = 3, when the structure of MM
M is partially known as in the case of the
PKCS#1 v1.5 padding scheme and the solution SS
S is smaller than the modulus nn
n. Specifically,
1. Choose an arbitrary mm
m the attacker wants to forge a signature for.
2. Compute the hash h(m)h(m)
h(m).
3. Compute the padded message MM
M according to the PKCS#1 v.1.5 padding scheme:
M=(0x00∥0x01∥0xFF…
M = (0x00∥0x01∥0xFF … 0xFF∥0x00∥ASN.1 structure∥h(m))
4. 0xFF∥0x00∥ASN.
Break the solution SS S into a known part Δ\Delta
Δ, composed of the padding prefix up to (and
1excluding)
structure∥h(m))M
the hash h(m)h(m)
h(m) and an unknown part xx ∣x∣=∣h(m)∣|
x, of size ∣x∣ = ∣h(m)∣, that we want
=
to solve for: S S=(0x00∥0x01∥0xFF…
= (0x00∥0x01∥0xFF … 0xFF∥0x00∥ASN.1 x| structure∥x) =
(\texttt{0x00}\|
∣x∣ 0xFF∥0x00∥ASN. =|
Δ∥x = 2 Δ + x
\texttt{0x01}\| 1 structure∥x)=Δ∥x=2∣x∣Δ+xS h(m)|
5. Apply Coppersmith's method to find a root of the following polynomial: P(x)=(2∣x∣Δ+x)e−M≡0
P (x) =
\texttt{0xFF} =
(2 ∣x∣
Δ + x) − M ≡ 0 mod n This is equivalent to solving for xx
e mod
x the equation
\ldots\texttt{0xFF}
(\texttt{0x00}\|
∣x∣
(2∣x∣Δ+x)e=M
(2 = M mod n, where e=3e=3 nP(x)=(2^{|
\| Δ + x)\texttt{0x01}\| e = 3.
e
6. mod
If a solution xx
x is found, then SS=Δ∥xS=\Delta\|
= Δ∥x is a valid forged signature produced x|} without
\texttt{0x00}\| \texttt{0xFF}
n(2^{| \Delta
\text{ASN.
knowledge the private keyxdd
of\ldots\texttt{0xFF}
d that would be validated correctly since S e
= (2∣x∣ Δ +
Se=(2∣x∣Δ+x)e=M
x|} +
mod
1x)e = M mod \| n. Stop.
\Delta x)^e-
7. structure}
If a solution is not found, repeat from step (1.) with a new message mm
\texttt{0x00}\| m. nS^e=(2^{|
M
+
\| x|}
\text{ASN. \equiv
x)^e
h(m)) and 1easy fix would be to enforce the use of large public exponents,
An immediate \Delta such as
= 0
+ Therefore, as a
6553765537
65537. This wouldstructure}
make the attack less feasible, but still theoretically possible.
M \quad
x)^e
long-term solution,\|consider switching to the EMSA-PSS padding scheme. The latter
\bmod =\bmod
effectively mitigates x)the attack due to its randomized padding which makes the n structure of the
n M
padded message M MM= unpredictable. In particular, it is not possible to break MMM into a known
\Delta\| \bmod
and unknown part.
x n
Update: Resolved =
in pull request #5234 at commit c9243c4. The OpenZeppelin Contracts team
prefers to keep the2^{|
implementation flexible by verifying any exponent with a valid signature. A
warning was addedx|}
to the documentation that exponent 65537 is recommended by the NIST
\Deltaraise security concerns.
and that lower exponents
+
x
It is important to note that approach (1.) would leave the _jAdd function with this particular
bug, but would resolve the issue in the context of this implementation. This is the case
Update: Resolved in pull request #5218 at commit 427d074. The OpenZeppelin Contracts
team chose to implement the first approach.
Consider documenting this reasoning in IGovernor.sol so that integrators are aware of this
limitation.
Update: Resolved in pull request #5175. Proper documentation has been added to the
IGovernor.sol interface.
Consider documenting the precedence of the role over the schedule to correctly reflect this
scenario.
Consider documenting explicitly that the padding is ignored while encoding the Base64URL
so that integrating projects are aware and able to modify the decoding functionality wherever
necessary.
Update: Resolved in pull request #5176. The OpenZeppelin Contracts team stated:
The use of @inheritdoc annotation is also inconsistent between certain contracts. For
instance, in AccessManager.sol , the annotation is a single-line comment, whereas in
Governor.sol , the annotation is in a multi-line comment /** ... */ format.
To improve code readability, consider using a consistent standard for inheriting documentation
throughout the codebase.
This inconsistency has been discussed in issue 3502. Particularly, this comment details
the limitations of using @inheritdoc for extending documentation. For these cases,
we leverage our documentation engine and use the @dev See {...} syntax to point
users in the right direction while also writing additional documentation. We acknowledge
the inconsistency between single-line comments and /** ... */ comments and will
consider making them consistent in the future.
To improve code readability, consider using the same return style across all of a contract's
functions.
Update: Resolved in pull request #5178 and in pull request #5177. The OpenZeppelin
Contracts team stated:
Previously, the team agreed to name the returned values when there is more than one.
We are documenting this clearly in our guidelines and adding missing names where
needed according to this policy.
Consider directly using the previously retrieved storage pointer instead of calling
_unsafeAccess again.
Consider reverting with distinct errors for the case where m == 0 and when the
staticcall fails due to an out-of-gas error. The above also applies to the analogous
modExp function for fixed-length arguments, though this is less likely to occur.
Consider adding a warning to the code so that integrators are aware of this scenario.
The referenced functions are currently private so they are not available for
developers to use. We consider the current documentation to be satisfactory since it
already describes that non-reverting calls are assumed to be successful in the
corresponding entry points (i.e., safeTransfer , safeTransferFrom , and
forceApprove ). We will consider documenting this if those functions ever become
internal .
• assembly ("memory-safe")
• /// @solidity memory-safe-assembly
Both types are used in the codebase, leading to inconsistencies. For example, the
SafeERC20 contract uses the former, whereas the ERC2771Forwarder contract uses the
latter. Moreover, according to Solidity documentation, the latter is likely to be deprecated.
Consider removing the unused import statement to improve code clarity and readability.
To improve the project's overall legibility, consider standardizing ordering throughout the
codebase as recommended by the Solidity Style Guide (Order of Functions).
A negative aspect of reordering functions is that it may change the compiler output,
leading to unexpected results (e.g., suddenly hitting stack-too-deep errors). Also,
changing the order is difficult to review and audit as it produces a big diff that may not
be substantial. For these reasons, we decided not to make changes to function
ordering.
To improve code readability, consider fixing the above along with any other instances of
typographical errors in the codebase.
• assembly ("memory-safe")
• /// @solidity memory-safe-assembly
Both types are being used in the codebase, leading to inconsistencies. For example, the
Packing contract uses the former, whereas the Panic contract uses the latter. Moreover,
according to Solidity documentation, the latter is likely to be deprecated.
Update: Resolved in pull request #5172. The OpenZeppelin Contracts team stated:
Issue N-09 was resolved by migrating the memory-safe declarations to the newer
assembly ("memory-safe") syntax.
To improve the project's overall legibility, consider standardizing ordering throughout the
codebase as recommended by the Solidity Style Guide (Order of Functions).
We generally adhere to the Solidity style guide on function ordering. However, in the
presented cases, changing the order of the functions makes it difficult to review and
audit parts of the contracts. For this reason, we decided not to change the function
ordering in these cases.
Consider adding named parameters to mappings in order to improve the readability and
maintainability of the codebase.
We agree that named parameters in mappings improve readability when the mapping
key specifies a name. In the case of the mapping's value , its name is already that of
the mapping itself (e.g., _proposalVotes ). For this reason, we are updating
GovernorCountingFractional to name the _proposalVotes parameter, but we
decided not to name its value as we have consistently not done so across the library.
Update: Resolved in pull request #5194. The OpenZeppelin Contracts team stated:
The indicated typographical errors have been addressed as per the recommendations.
As the OpenZeppelin contracts library is released under the MIT license, consider clarifying
that inspired and copied code under GPL v3 can be released under MIT.
The team recognizes the potential licensing conflicts and we are clarifying that the code
that inspired our implementation is under a GPL v3 license.
Consider applying the above suggestions for a clearer documentation that helps reason about
the codebase.
Update: Resolved in pull request #5206 at commit 0a0d44d and in pull request #5229 at
commit 9c986c5. The OpenZeppelin Contracts team stated:
Consider removing any redundant code to improve the readability and maintainability of the
codebase.
To avoid confusion and improve the readability of the codebase, consider using a consistent
notation.
Consider changing the function interface by passing both points' coordinates explicitly.
Alternatively, consider documenting why this interface is required as is.
Using JPoint for both points would be significantly more gas-expensive in the context
of how _jAdd is used within other functions, particularly _jMultShamir . The current
design minimizes unnecessary memory operations. The _jAdd function is primarily
called from contexts where one point is already in JPoint format (often a running
calculation result), while the other is a new point being added, for which we have direct
coordinate access (such as in _jAddPoint ).
Note that _jAdd using mixed input (point in memory + coordinates on the stack) is
necessary to circumvent stack-too-deep errors. The mloads for point 1 are done at the
very last moment. Doing the mload only once and caching the value on the stack would
possibly save gas, but it also blocks compilation without via-ir.
Consider enforcing this requirement in the code so that the signatures produced under moduli
of size less than 2048 are not supported. This is in accordance with the minimum key sizes
recommended by NIST throughout the year 2030.
Update: Resolved in pull request #5206. The OpenZeppelin Contracts team stated:
We acknowledge the risks of using a modulus of less than 2048 bits according to the
documentation so we are enforcing it.
While balancing the trade-offs between providing flexible library code and the risk of side
effects like memory manipulation, consider adding the aforementioned edge case as a warning
in the documentation.
Consider checking the inputs explicitly to reduce the attack surface of the codebase.
Update: Resolved in pull request #5214. A check was added to the setup function to ensure
that the buffer size is not zero. In the push function, the panic of modulus by zero has been
kept as it is.
To improve code clarity and readability, consider initializing the usedWeight variable as well.
As a mitigation, the RFC8017 standard proposes the EMSA-PSS padding scheme. This
scheme is probabilistic, meaning that the same message can have different paddings each
time it is signed. The added randomness effectively mitigates padding oracle attacks. In
addition to having oracle access to the verification algorithm, the Bleichenbacher attack on
signatures leverages the following weaknesses:
1. Faulty implementations that only check the first bytes of the decrypted signature ( 0x00 ,
0x02 , and the 0xFF padding) and omit to check the rest of the formatting. In particular,
they fail to check if the hash data matches the hash of the supplied message.
2. Use of small values for the public exponent e=3e=3
e = 3. Although the attack is theoretically
possible for large values of ee
e (e.g., 6553765537
65537), it is much less efficient in that case.
3. Malleability of the RSA scheme, i.e., small changes to the ciphertext or signature result in
predictable changes in the plaintext.
Point (1.) is not applicable to the RSA.sol implementation due to the thorough check being
performed on the full decrypted signature. On the other hand, points (2.) and (3.) are
applicable. As a result of the mitigation of point (1.), the padding oracle attack in its original
form is deemed as inapplicable to the specific implementation in RSA.sol .
The above being said, the EMSA-PSS padding scheme enforces a full check of the format of
the decrypted signature (point 1.). It also binds the hash, the randomness, and the padding
together, which makes any manipulation of the signature easily detectable. Moreover, it fixes
the inherent malleability property of RSA (point 3.), making the scheme non-malleable.
Therefore, in the interest of long-term security, consider switching to the EMSA-PSS padding
scheme.
Indeed, EMSA-PSS looks like a better, more modern scheme than EMSA-PKCS1-V1_5
which we currently support. We will consider adding support for EMSA-PSS in future
releases to improve the coverage of our library. That being said, it is going to take time
For consistency, consider either adding this statement to all the contracts or removing it from
the MerkleTree library.
These release notes have been added to previous versions since we found them to be
relevant in some cases. While the addition of the release notes has not been automated,
we decided to leave this note and make it more consistent in future documentation
updates.
• The MerkleTree library refers to the height of the binary tree both as "depth" [1, 2, 3]
and "levels" [4, 5, 6, 7]. Consider sticking to one word for consistency.
• In the Heap library documentation, node i is referred to as father for the nodes at
index 2*i+1 and 2*i+2 . Consider referring to i as the parent node for a more neutral
wording.
• In the pop function of Heap , the first node of the data array is saved in the rootNode
variable, while the actual root node is saved as rootData . Consider renaming
rootNode firstDataNode , rootData rootNode , and lastNode to
lastDataNode for a more descriptive name.
Update: Resolved in pull request #5215. The third point was resolved with L-07.
The analysis along path 1 identifies $7$ distinct valid weak private keys dd
d that would trigger the
bug: d ∈ {1, 2, 3, 2−1 , 3−1 , 2 3−1 , 3 2−1 }. Assuming that dd
d∈{1, d is chosen uniformly at random
2, n−1n-1
among n − 1 values, where nn
n is a 256256
256 bit prime, the probability to choose any one of the
weak3, 7
keys is negligible: 72256−1≈0\frac{7}
2256 −1 ≈ 0.
2−1, {2^{256}-1}\approx
Along3−1,
path 2, at most 20482048
0 valid weak private keys dd
2048 d are estimated that would trigger the bug.
While2significantly
3−1, larger than the path 1 case (77
7), the size of this set is still negligible w.r.t. the
3 2−1} 256
size of the whole space 22562^{256}
2 . Specifically, the probability of randomly drawing one of the weak
d 2048
keys is at most: 20482256−1≈0\frac{2048}
2256 −1 ≈ 0.
\in {2^{256}-1}\approx
\{1,2,3,2^{-1},
This analysis is in0 accordance with Zellic's security reduction argument which states:
3^{-1},
If an attacker A\mathcal{A}
2~3^{-1}, A chooses a (weak) public key and "manages" to compute σ σ=(r,s,h)
= (r, s, h)
3~2^{-1}\}
that \sigma
passes verification, then he'll also be able to efficiently compute the corresponding
(weak) private key dd d from the public key, which will invalidate the hardness=of ECDLP
assumption. (r,s,h)
In summary, the reported bug looks highly unlikely to be triggered by chance. It also seems
that the possibilities to exploit it maliciously are very limited and do not have critical
consequences. That being said, it should be noted that if there are other attack scenarios apart
from the ones listed above, they would require further investigation. In addition, the above
analysis assumes that the bug can only be triggered from the verifySolidity function as
an entry point. Last but not least, the bug clearly identifies an error in the computation,
regardless of any security implications. Therefore, it is recommended to change the _jAdd
implementation to cover the case where a point is added to itself, as outlined in the Zellic
write-up.
Recommendations
Protection Against Postquantum Adversaries in
RSA Signatures
In view of the fact that blockchain data is public and persists forever, attention can be drawn to
the possibility of adding perfect forward secrecy (PFS) to RSA signatures. This would be done
to prevent the recovery of the private key of a signature from the public key at a point in the
future when computational capabilities are more advanced (e.g., in a post-quantum setting).
PFS is typically associated with key exchange protocols (e.g., DH/ECDH) where session data is
encrypted with a temporary (ephemeral) session key which is discarded at the end of the
Adding PFS to signatures is uncommon and, to the best of our knowledge, no signature
schemes exist with this property. Indeed, such functionality would invalidate one of the core
properties of digital signatures, namely non-repudiation, which ensures that a signer is not able
to deny signing a piece of data after the fact. On the other hand, the relatively recent
emergence of blockchains presents new use cases that may, to some extent, justify the
suggested modification.
Specifically, the fact that blockchain data is public and persists forever may allow post-
quantum adversaries to recover the private key of a signature from the public key using
quantum computers. With PFS, the negative consequences from the latter will be prevented by
having an analogous notion of temporary private keys for signing that would be valid for a long,
but fixed, period of time (say, 10-20 years).
We suspect that adding PFS to digital signatures may require some non-trivial changes to the
signing and verification process. Yet, the changes would probably be more on the signer's
side, while the verifier would just need to discard signatures produced under invalid (i.e.,
expired) keys.
OpenZeppelin Contracts v5.1 introduced the GovernorCountingFractional contract, which allows delegates to fractionally split their voting weight into Against, For, and Abstain votes or cast their entire voting weight into one of the three options. This mechanism, called fractional voting, also supports rolling voting where a voter can split their vote into multiple operations. The vote is considered fractional if the support parameter is 255 and the params parameter has 48 bytes .
The RSA and P256 libraries in OpenZeppelin Contracts are inspired by repositories licensed under GPL version 3. Since the OpenZeppelin library is released under the MIT license, there could be conflicts because GPL code is typically required to remain under GPL terms when redistributed. OpenZeppelin has acknowledged this issue and is clarifying that the inspired code is indeed under GPL v3 to address potential licensing conflicts .
In the v5.1 release, OpenZeppelin improved code readability by addressing numerous typographical errors across the codebase, as highlighted in the release notes. They resolved issues such as incorrect spellings and inconsistent use of uppercase in hexadecimal representations. These changes were implemented via several pull requests to keep the codebase clean and professional .
OpenZeppelin addresses documentation discrepancies by revising and updating the documentation to match the implementation. For example, in VestingWalletCliff, the constructor's documentation was misaligned with its implementation, which uses Ownable instead of Ownable2Step. OpenZeppelin resolved these inconsistencies through documentation updates and pull requests, ensuring clarity and reducing misleading information .
OpenZeppelin revised cryptographic utilities like RSA and P256 to address potential licensing conflicts and improve technical accuracy. They clarified licensing issues by acknowledging GPL v3 in inspired code, corrected documentation to reflect hard-coded OIDs, and resolved redundant code and base inconsistencies to improve cryptographic function implementations .
The AccessManager schedule function omits gas and value parameters from operationId due to concerns that including them could lead to a Denial of Service (DoS) attack by manipulating target requirements. Additionally, specifying minimum values does not eliminate varied execution outcomes, and including these would introduce breaking changes. Therefore, OpenZeppelin decided against integrating these parameters in the operationId composition .
Although the OpenZeppelin Contracts v5.1 acknowledged the Solidity style guide suggestion for function ordering, they decided not to change the ordering in specific cases. Typically, public and external functions followed by internal and then private functions should be the order. However, for audit and review efficiency, they retained some non-standard ordering, which they believe aids in maintaining the contracts as they are .
The AccessManager contract uses the Address.functionCallWithValue function to perform external calls. If the call fails, the execute function reverts, ensuring that failed operations do not proceed. However, this mechanism allows the caller to retry the same operation without rescheduling, as long as it hasn't expired. This can lead to wastage of gas if the retries also fail .
The draft-ERC20TemporaryApproval contract extends ERC-20 tokens by allowing the owner to approve an allowance which is only valid for the current transaction. This is achieved using transient storage, enabling a temporary approval mechanism that reverts back after the transaction .
The MerkleProof.multiProofVerify function can validate an empty set's inclusion in a Merkle tree root. This poses significant integration risks, as integrators accepting user arrays may incorrectly assume that a valid proof was presented. An incomplete proof that merely contains the Merkle root can pass the verification, possibly allowing users to bypass proper validation and introduce security vulnerabilities .