-
Notifications
You must be signed in to change notification settings - Fork 12.1k
Fix issue with detection of RIP7212 precompile #5620
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
c0632e0
7edf9f6
b3d2219
3f58714
88fd7ad
65b1a53
a2e2d65
174db70
670b858
5e7f44b
2627809
a0da59a
a2a0bf2
dcac7dd
7478900
111a0a4
f640dca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'openzeppelin-solidity': patch | ||
--- | ||
|
||
`P256`: Fix precompile detection, avoiding revert in `verifyNative`, and reducing cost of `verify` when the signature is invalid. | ||
Amxx marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -90,10 +90,42 @@ 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 ( | ||
_rip7212( | ||
0x4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4d, | ||
0xa73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac, | ||
0x36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d60, | ||
0x4aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff3, | ||
0x7618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e | ||
) | ||
Amxx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) { | ||
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 signature are indistinguishable from the absence of the precompile. | ||
Amxx marked this conversation as resolved.
Show resolved
Hide resolved
Amxx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* 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") { | ||
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) | ||
ernestognw marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let success := staticcall(gas(), 0x100, ptr, 0xa0, 0x00, 0x20) | ||
isValid := and(success, and(eq(returndatasize(), 0x20), eq(mload(0), 1))) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a little paranoid on the gas estimation part. // The `invalid` upon `staticcall` failure is solely for gas estimation.
// When given sufficient gas, the precompile will not revert.
if iszero(staticcall(gas(), 0x100, m, 0xa0, 0x00, 0x00)) { invalid() }
// The precompile will only return `uint256(1)` if and only if the signature is valid.
// As per latest spec, verified against the op-geth implementation:
// See: https://github.com/ethereum-optimism/op-geth/blob/02dfe8692a3c606dbabd83c1ced2037aab9753d7/core/vm/contracts.go#L1342
// Otherwise, it will always return no data.
isValid := iszero(iszero(returndatasize())) More conservative (in case the spec and implementations are somehow edited again): // The `invalid` upon `staticcall` failure is solely for gas estimation.
// When given sufficient gas, the precompile will not revert.
mstore(0x00, 0) // Zeroize the slot for the returndata.
if iszero(staticcall(gas(), 0x100, m, 0xa0, 0x00, 0x20)) { invalid() }
// The precompile will only return `uint256(1)` if and only if the signature is valid.
// As per latest spec, verified against the op-geth implementation:
// See: https://github.com/ethereum-optimism/op-geth/blob/02dfe8692a3c606dbabd83c1ced2037aab9753d7/core/vm/contracts.go#L1342
// Otherwise, it will always return no data.
isValid := mload(0x00)
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to avoid the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also, aren't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we worry about the precompile returning something that is neither 0 nor 1 ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we don't need to worry about it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like @Vectorized's idea since the library is designed to work with RIP-7212 so it should be safe to assume that all implementations would be compliant (i.e. returning empty bytes on failed verification). We can add a note so that developers can just override the function and manually check the returndata, but I'm afraid the consequences of missing such note would be pretty bad. |
||
} | ||
|
||
/** | ||
|
Uh oh!
There was an error while loading. Please reload this page.