Skip to content

Commit e35ffc1

Browse files
committed
N-03: fix documentation
1 parent 7178382 commit e35ffc1

File tree

3 files changed

+71
-55
lines changed

3 files changed

+71
-55
lines changed

src/P256.sol

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,41 @@ pragma solidity 0.8.21;
88
* @custom:security-contact [email protected]
99
**/
1010
library P256 {
11+
/// Address of the RIP-7212 precompile
1112
address public constant PRECOMPILE = address(0x100);
13+
14+
/// Address of the fallback P256Verifier contract
1215
address public constant VERIFIER =
1316
0xc2b78104907F722DABAc4C69f826a522B2754De4;
1417

18+
/// P256 curve order n/2 for malleability check
19+
uint256 constant P256_N_DIV_2 =
20+
57896044605178124381348723474703786764998477612067880171211129530534256022184;
21+
22+
/**
23+
* @dev Verifies a P256 signature. Costs ~3k gas for a valid signature on a
24+
* on a chain with RIP-7212, ~300k otherwise. Treats malleable (s > n/2)
25+
* signatures as invalid.
26+
*/
27+
function verifySignature(
28+
bytes32 message_hash,
29+
uint256 r,
30+
uint256 s,
31+
uint256 x,
32+
uint256 y
33+
) internal view returns (bool) {
34+
// check for signature malleability
35+
if (s > P256_N_DIV_2) {
36+
return false;
37+
}
38+
39+
return verifySignatureAllowMalleability(message_hash, r, s, x, y);
40+
}
41+
42+
/**
43+
* @dev Verifies a P256 signature. Treats malleable (s > n/2) signatures as
44+
* valid, matching the behavior specified by NIST and RIP-7212 exactly.
45+
*/
1546
function verifySignatureAllowMalleability(
1647
bytes32 message_hash,
1748
uint256 r,
@@ -23,36 +54,19 @@ library P256 {
2354

2455
(bool success, bytes memory ret) = PRECOMPILE.staticcall(args);
2556
if (success && ret.length > 0) {
26-
// RIP-7212 precompile returns 1 if signature is valid
27-
// and nothing if signature is invalid, so those fall back to
28-
// more expensive Solidity implementation.
57+
// RIP-7212 precompile returns 1 if signature is valid.
2958
return abi.decode(ret, (uint256)) == 1;
3059
}
3160

61+
// RIP-7212 is flawed in that it returns no data for an invalid
62+
// signature. This means that "invalid signature" and "missing
63+
// precompile" are not distguishable: both fall back to the more
64+
// expensive Solidity implementation.
3265
(bool fallbackSuccess, bytes memory fallbackRet) = VERIFIER.staticcall(
3366
args
3467
);
3568
assert(fallbackSuccess); // never reverts, always returns 0 or 1
3669

3770
return abi.decode(fallbackRet, (uint256)) == 1;
3871
}
39-
40-
/// P256 curve order n/2 for malleability check
41-
uint256 constant P256_N_DIV_2 =
42-
57896044605178124381348723474703786764998477612067880171211129530534256022184;
43-
44-
function verifySignature(
45-
bytes32 message_hash,
46-
uint256 r,
47-
uint256 s,
48-
uint256 x,
49-
uint256 y
50-
) internal view returns (bool) {
51-
// check for signature malleability
52-
if (s > P256_N_DIV_2) {
53-
return false;
54-
}
55-
56-
return verifySignatureAllowMalleability(message_hash, r, s, x, y);
57-
}
5872
}

src/P256Verifier.sol

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -40,28 +40,29 @@ contract P256Verifier {
4040
return abi.encodePacked(ret);
4141
}
4242

43-
// Parameters for the sec256r1 (P256) elliptic curve
44-
// Curve prime field modulus
43+
// Parameters for the secp256r1 (P256) elliptic curve
44+
/// P256 curve prime field modulus
4545
uint256 private constant p =
4646
0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;
47-
// Short weierstrass first coefficient
47+
/// Short weierstrass first coefficient
4848
uint256 private constant a = // The assumption a == -3 (mod p) is used throughout the codebase
4949
0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC;
50-
// Short weierstrass second coefficient
50+
/// Short weierstrass second coefficient
5151
uint256 private constant b =
5252
0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B;
53-
// Generating point affine coordinates
53+
/// Generating point affine x-coordinate
5454
uint256 private constant GX =
5555
0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296;
56+
/// Generating point affine y-coordinate
5657
uint256 private constant GY =
5758
0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5;
58-
// Curve order (number of points)
59+
/// Curve order (number of points)
5960
uint256 private constant n =
6061
0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551;
61-
// -2 mod p constant, used to speed up inversion and doubling (avoid negation)
62+
/// -2 mod p constant, used to speed up inversion and doubling (avoid negation)
6263
uint256 private constant minus_2modp =
6364
0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFD;
64-
// -2 mod n constant, used to speed up inversion
65+
/// -2 mod n constant, used to speed up inversion
6566
uint256 private constant minus_2modn =
6667
0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F;
6768

@@ -98,8 +99,8 @@ contract P256Verifier {
9899
}
99100

100101
/**
101-
* @dev Check if a point in affine coordinates is on the curve
102-
* Reject 0 point at infinity.
102+
* @dev Check if a point in affine coordinates is on the curve.
103+
* Reject the 0 point at infinity.
103104
*/
104105
function ecAff_isValidPubkey(
105106
uint256 x,
@@ -109,13 +110,6 @@ contract P256Verifier {
109110
return false;
110111
}
111112

112-
return ecAff_satisfiesCurveEqn(x, y);
113-
}
114-
115-
function ecAff_satisfiesCurveEqn(
116-
uint256 x,
117-
uint256 y
118-
) internal pure returns (bool) {
119113
uint256 LHS = mulmod(y, y, p); // y^2
120114
uint256 RHS = addmod(mulmod(mulmod(x, x, p), x, p), mulmod(a, x, p), p); // x^3 + a x
121115
RHS = addmod(RHS, b, p); // x^3 + a*x + b
@@ -124,9 +118,10 @@ contract P256Verifier {
124118
}
125119

126120
/**
127-
* @dev Computation of uG + vQ using Strauss-Shamir's trick, G basepoint, Q public key
128-
* returns tuple of (x coordinate of uG + vQ, boolean that is false if internal precompile staticcall fail)
129-
* Strauss-Shamir is described well in https://stackoverflow.com/a/50994362
121+
* @dev Computation of uG + vQ using Strauss-Shamir's trick, G basepoint,
122+
* Q public key. Strauss-Shamir is described well in the following post:
123+
* https://stackoverflow.com/a/50994362
124+
* @return X The x-coordinate of uG + vQ
130125
*/
131126
function ecZZ_mulmuladd(
132127
uint256 QX,
@@ -243,16 +238,15 @@ contract P256Verifier {
243238
}
244239

245240
/**
246-
* @dev Check if a point is the infinity point in ZZ rep.
247-
* Assumes point is on the EC or is the point at infinity.
241+
* @dev Checks if a point is the infinity point in ZZ rep, using only the zz
242+
* and zzz coordinates. Assumes the point is on the curve or at infinity.
248243
*/
249244
function ecZZ_IsInf(
250245
uint256 zz,
251246
uint256 zzz
252247
) internal pure returns (bool flag) {
253248
// invariant((zz == 0 && zzz == 0) || ecAff_isOnCurve(x, y) for affine
254249
// form of the point)
255-
256250
return (zz == 0 && zzz == 0);
257251
}
258252

src/WebAuthn.sol

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import "./P256.sol";
99
* @custom:security-contact [email protected]
1010
*/
1111
library WebAuthn {
12-
/// Checks whether prefix occurs in the beginning of str.
12+
/**
13+
* @dev Checks whether prefix occurs in the beginning of str.
14+
*/
1315
function startsWith(
1416
string memory prefix,
1517
string memory str
@@ -33,14 +35,20 @@ library WebAuthn {
3335
return true;
3436
}
3537

36-
bytes1 private constant AUTH_DATA_FLAGS_UP = 0x01; // Bit 0
37-
bytes1 private constant AUTH_DATA_FLAGS_UV = 0x04; // Bit 2
38-
bytes1 private constant AUTH_DATA_FLAGS_BE = 0x08; // Bit 3
39-
bytes1 private constant AUTH_DATA_FLAGS_BS = 0x10; // Bit 4
38+
/// Bit mask: authData bit 0, indicating user presence
39+
bytes1 private constant AUTH_DATA_FLAGS_UP = 0x01;
40+
/// Bit mask: authData bit 2, indicating user verification
41+
bytes1 private constant AUTH_DATA_FLAGS_UV = 0x04;
42+
/// Bit mask: authData bit 3, indicating backup eligibility
43+
bytes1 private constant AUTH_DATA_FLAGS_BE = 0x08;
44+
/// Bit mask: authData bit 4, indicating backup state
45+
bytes1 private constant AUTH_DATA_FLAGS_BS = 0x10;
4046

41-
/// Verifies the authFlags in authenticatorData. Numbers in inline comment
42-
/// correspond to the same numbered bullets in
43-
/// https://www.w3.org/TR/webauthn-2/#sctn-verifying-assertion.
47+
/**
48+
* @dev Verifies the authFlags in authenticatorData. Numbers in inline
49+
* comment correspond to the same numbered bullets in the WebAuthn spec:
50+
* https://www.w3.org/TR/webauthn-3/#sctn-authenticator-data
51+
*/
4452
function checkAuthFlags(
4553
bytes1 flags,
4654
bool requireUserVerification
@@ -72,8 +80,8 @@ library WebAuthn {
7280
}
7381

7482
/**
75-
* Verifies a Webauthn P256 signature (Authentication Assertion) as described
76-
* in https://www.w3.org/TR/webauthn-2/#sctn-verifying-assertion. We do not
83+
* @dev Verifies a Webauthn P256 signature (Authentication Assertion). See
84+
* https://www.w3.org/TR/webauthn-2/#sctn-verifying-assertion. We do not
7785
* verify all the steps as described in the specification, only ones relevant
7886
* to our context. Please carefully read through this list before usage.
7987
* Specifically, we do verify the following:

0 commit comments

Comments
 (0)