-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
71 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,10 +8,41 @@ pragma solidity 0.8.21; | |
* @custom:security-contact [email protected] | ||
**/ | ||
library P256 { | ||
/// Address of the RIP-7212 precompile | ||
address public constant PRECOMPILE = address(0x100); | ||
|
||
/// Address of the fallback P256Verifier contract | ||
address public constant VERIFIER = | ||
0xc2b78104907F722DABAc4C69f826a522B2754De4; | ||
|
||
/// P256 curve order n/2 for malleability check | ||
uint256 constant P256_N_DIV_2 = | ||
57896044605178124381348723474703786764998477612067880171211129530534256022184; | ||
|
||
/** | ||
* @dev Verifies a P256 signature. Costs ~3k gas for a valid signature on a | ||
* on a chain with RIP-7212, ~300k otherwise. Treats malleable (s > n/2) | ||
* signatures as invalid. | ||
*/ | ||
function verifySignature( | ||
bytes32 message_hash, | ||
uint256 r, | ||
uint256 s, | ||
uint256 x, | ||
uint256 y | ||
) internal view returns (bool) { | ||
// check for signature malleability | ||
if (s > P256_N_DIV_2) { | ||
return false; | ||
} | ||
|
||
return verifySignatureAllowMalleability(message_hash, r, s, x, y); | ||
} | ||
|
||
/** | ||
* @dev Verifies a P256 signature. Treats malleable (s > n/2) signatures as | ||
* valid, matching the behavior specified by NIST and RIP-7212 exactly. | ||
*/ | ||
function verifySignatureAllowMalleability( | ||
bytes32 message_hash, | ||
uint256 r, | ||
|
@@ -23,36 +54,19 @@ library P256 { | |
|
||
(bool success, bytes memory ret) = PRECOMPILE.staticcall(args); | ||
if (success && ret.length > 0) { | ||
// RIP-7212 precompile returns 1 if signature is valid | ||
// and nothing if signature is invalid, so those fall back to | ||
// more expensive Solidity implementation. | ||
// RIP-7212 precompile returns 1 if signature is valid. | ||
return abi.decode(ret, (uint256)) == 1; | ||
} | ||
|
||
// RIP-7212 is flawed in that it returns no data for an invalid | ||
// signature. This means that "invalid signature" and "missing | ||
// precompile" are not distguishable: both fall back to the more | ||
// expensive Solidity implementation. | ||
(bool fallbackSuccess, bytes memory fallbackRet) = VERIFIER.staticcall( | ||
args | ||
); | ||
assert(fallbackSuccess); // never reverts, always returns 0 or 1 | ||
|
||
return abi.decode(fallbackRet, (uint256)) == 1; | ||
} | ||
|
||
/// P256 curve order n/2 for malleability check | ||
uint256 constant P256_N_DIV_2 = | ||
57896044605178124381348723474703786764998477612067880171211129530534256022184; | ||
|
||
function verifySignature( | ||
bytes32 message_hash, | ||
uint256 r, | ||
uint256 s, | ||
uint256 x, | ||
uint256 y | ||
) internal view returns (bool) { | ||
// check for signature malleability | ||
if (s > P256_N_DIV_2) { | ||
return false; | ||
} | ||
|
||
return verifySignatureAllowMalleability(message_hash, r, s, x, y); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,9 @@ import "./P256.sol"; | |
* @custom:security-contact [email protected] | ||
*/ | ||
library WebAuthn { | ||
/// Checks whether prefix occurs in the beginning of str. | ||
/** | ||
* @dev Checks whether prefix occurs in the beginning of str. | ||
*/ | ||
function startsWith( | ||
string memory prefix, | ||
string memory str | ||
|
@@ -33,14 +35,20 @@ library WebAuthn { | |
return true; | ||
} | ||
|
||
bytes1 private constant AUTH_DATA_FLAGS_UP = 0x01; // Bit 0 | ||
bytes1 private constant AUTH_DATA_FLAGS_UV = 0x04; // Bit 2 | ||
bytes1 private constant AUTH_DATA_FLAGS_BE = 0x08; // Bit 3 | ||
bytes1 private constant AUTH_DATA_FLAGS_BS = 0x10; // Bit 4 | ||
/// Bit mask: authData bit 0, indicating user presence | ||
bytes1 private constant AUTH_DATA_FLAGS_UP = 0x01; | ||
/// Bit mask: authData bit 2, indicating user verification | ||
bytes1 private constant AUTH_DATA_FLAGS_UV = 0x04; | ||
/// Bit mask: authData bit 3, indicating backup eligibility | ||
bytes1 private constant AUTH_DATA_FLAGS_BE = 0x08; | ||
/// Bit mask: authData bit 4, indicating backup state | ||
bytes1 private constant AUTH_DATA_FLAGS_BS = 0x10; | ||
|
||
/// Verifies the authFlags in authenticatorData. Numbers in inline comment | ||
/// correspond to the same numbered bullets in | ||
/// https://www.w3.org/TR/webauthn-2/#sctn-verifying-assertion. | ||
/** | ||
* @dev Verifies the authFlags in authenticatorData. Numbers in inline | ||
* comment correspond to the same numbered bullets in the WebAuthn spec: | ||
* https://www.w3.org/TR/webauthn-3/#sctn-authenticator-data | ||
*/ | ||
function checkAuthFlags( | ||
bytes1 flags, | ||
bool requireUserVerification | ||
|
@@ -72,8 +80,8 @@ library WebAuthn { | |
} | ||
|
||
/** | ||
* Verifies a Webauthn P256 signature (Authentication Assertion) as described | ||
* in https://www.w3.org/TR/webauthn-2/#sctn-verifying-assertion. We do not | ||
* @dev Verifies a Webauthn P256 signature (Authentication Assertion). See | ||
* https://www.w3.org/TR/webauthn-2/#sctn-verifying-assertion. We do not | ||
* verify all the steps as described in the specification, only ones relevant | ||
* to our context. Please carefully read through this list before usage. | ||
* Specifically, we do verify the following: | ||
|