Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/mighty-melons-cheer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': patch
---

`P256`: Fix precompile detection, avoid incorrect reverts in `verifyNative` and reduce cost of `verify` when the signature is invalid.
42 changes: 40 additions & 2 deletions contracts/utils/cryptography/P256.sol
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,48 @@ library P256 {
) private view returns (bool valid, bool supported) {
if (!_isProperSignature(r, s) || !isValidPublicKey(qx, qy)) {
return (false, true); // signature is invalid, and its not because the precompile is missing
} else if (_rip7212(h, r, s, qx, qy)) {
return (true, true); // precompile is present, signature is valid
} else if (
// Given precompiles have no bytecode (i.e. `address(0x100).code.length == 0`), we use
// a valid signature with small `r` and `s` values to check if the precompile is present. Taken from
// https://github.com/C2SP/wycheproof/blob/4672ff74d68766e7785c2cac4c597effccef2c5c/testvectors/ecdsa_secp256r1_sha256_p1363_test.json#L1181-L1204
_rip7212(
0xbb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca605023, // sha256("123400")
0x0000000000000000000000000000000000000000000000000000000000000005,
0x0000000000000000000000000000000000000000000000000000000000000001,
0x6627cec4f0731ea23fc2931f90ebe5b7572f597d20df08fc2b31ee8ef16b1572,
0x6170ed77d8d0a14fc5c9c3c4c9be7f0d3ee18f709bb275eaf2073e258fe694a5
)
) {
return (false, true); // precompile is present, signature is invalid
} else {
return (false, false); // precompile is absent
}
}

(bool success, bytes memory returndata) = address(0x100).staticcall(abi.encode(h, r, s, qx, qy));
return (success && returndata.length == 0x20) ? (abi.decode(returndata, (bool)), true) : (false, false);
/**
* @dev Low level helper for {_tryVerifyNative}. Calls the precompile and checks if there is a return value.
*
* NOTE: According to RIP-7212, invalid signatures are indistinguishable from the absence of the precompile.
* Getting the success boolean, copying the returndata to memory, and loading it as a boolean, is not strictly
* necessary, but it protects against non-standard implementations that would return 0 (false) for
* invalid signatures.
*/
function _rip7212(bytes32 h, bytes32 r, bytes32 s, bytes32 qx, bytes32 qy) private view returns (bool isValid) {
assembly ("memory-safe") {
mstore(0x00, 0)
let ptr := mload(0x40)
mstore(ptr, h)
mstore(add(ptr, 0x20), r)
mstore(add(ptr, 0x40), s)
mstore(add(ptr, 0x60), qx)
mstore(add(ptr, 0x80), qy)
if iszero(staticcall(gas(), 0x100, ptr, 0xa0, 0x00, 0x20)) {
invalid()
}
isValid := mload(0x00)
}
}

/**
Expand Down
1 change: 1 addition & 0 deletions hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ module.exports = {
// we rely on the `code-size` compiler warning, that will cause a compilation error.
allowUnlimitedContractSize: true,
initialBaseFeePerGas: argv.coverage ? 0 : undefined,
enableRip7212: true,
},
},
exposed: {
Expand Down
Loading
Loading