Skip to content

Commit aa29301

Browse files
Amxxarr00ernestognw
authored
Fix issue with detection of RIP7212 precompile (#5620)
Co-authored-by: Arr00 <13561405+arr00@users.noreply.github.com> Co-authored-by: Ernesto García <ernestognw@gmail.com>
1 parent 450b833 commit aa29301

File tree

7 files changed

+249
-164
lines changed

7 files changed

+249
-164
lines changed

Diff for: .changeset/mighty-melons-cheer.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': patch
3+
---
4+
5+
`P256`: Adjust precompile detection in `verifyNative` to consider empty `returndata` on invalid verification. Previously, invalid signatures would've reverted with a `MissingPrecompile` error in chains with RIP-7212 support.

Diff for: contracts/utils/cryptography/P256.sol

+40-2
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,48 @@ library P256 {
9090
) private view returns (bool valid, bool supported) {
9191
if (!_isProperSignature(r, s) || !isValidPublicKey(qx, qy)) {
9292
return (false, true); // signature is invalid, and its not because the precompile is missing
93+
} else if (_rip7212(h, r, s, qx, qy)) {
94+
return (true, true); // precompile is present, signature is valid
95+
} else if (
96+
// Given precompiles have no bytecode (i.e. `address(0x100).code.length == 0`), we use
97+
// a valid signature with small `r` and `s` values to check if the precompile is present. Taken from
98+
// https://github.com/C2SP/wycheproof/blob/4672ff74d68766e7785c2cac4c597effccef2c5c/testvectors/ecdsa_secp256r1_sha256_p1363_test.json#L1173-L1204
99+
_rip7212(
100+
0xbb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca605023, // sha256("123400")
101+
0x0000000000000000000000000000000000000000000000000000000000000005,
102+
0x0000000000000000000000000000000000000000000000000000000000000001,
103+
0xa71af64de5126a4a4e02b7922d66ce9415ce88a4c9d25514d91082c8725ac957,
104+
0x5d47723c8fbe580bb369fec9c2665d8e30a435b9932645482e7c9f11e872296b
105+
)
106+
) {
107+
return (false, true); // precompile is present, signature is invalid
108+
} else {
109+
return (false, false); // precompile is absent
93110
}
111+
}
94112

95-
(bool success, bytes memory returndata) = address(0x100).staticcall(abi.encode(h, r, s, qx, qy));
96-
return (success && returndata.length == 0x20) ? (abi.decode(returndata, (bool)), true) : (false, false);
113+
/**
114+
* @dev Low level helper for {_tryVerifyNative}. Calls the precompile and checks if there is a return value.
115+
*/
116+
function _rip7212(bytes32 h, bytes32 r, bytes32 s, bytes32 qx, bytes32 qy) private view returns (bool isValid) {
117+
assembly ("memory-safe") {
118+
// Use the free memory pointer without updating it at the end of the function
119+
let ptr := mload(0x40)
120+
mstore(ptr, h)
121+
mstore(add(ptr, 0x20), r)
122+
mstore(add(ptr, 0x40), s)
123+
mstore(add(ptr, 0x60), qx)
124+
mstore(add(ptr, 0x80), qy)
125+
// RIP-7212 precompiles return empty bytes when an invalid signature is passed, making it impossible
126+
// to distinguish the presence of the precompile. Custom precompile implementations may decide to
127+
// return `bytes32(0)` (i.e. false) without developers noticing, so we decide to evaluate the return value
128+
// without expanding memory using scratch space.
129+
mstore(0x00, 0) // zero out scratch space in case the precompile doesn't return anything
130+
if iszero(staticcall(gas(), 0x100, ptr, 0xa0, 0x00, 0x20)) {
131+
invalid()
132+
}
133+
isValid := mload(0x00)
134+
}
97135
}
98136

99137
/**

Diff for: hardhat.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ module.exports = {
102102
// we rely on the `code-size` compiler warning, that will cause a compilation error.
103103
allowUnlimitedContractSize: true,
104104
initialBaseFeePerGas: argv.coverage ? 0 : undefined,
105+
enableRip7212: true,
105106
},
106107
},
107108
exposed: {

0 commit comments

Comments
 (0)