@@ -90,10 +90,48 @@ library P256 {
90
90
) private view returns (bool valid , bool supported ) {
91
91
if (! _isProperSignature (r, s) || ! isValidPublicKey (qx, qy)) {
92
92
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
93
110
}
111
+ }
94
112
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
+ }
97
135
}
98
136
99
137
/**
0 commit comments