diff --git a/src/P256.sol b/src/P256.sol index 3c9c87a..81b3f24 100644 --- a/src/P256.sol +++ b/src/P256.sol @@ -5,18 +5,17 @@ pragma solidity 0.8.23; * Helper library for external contracts to verify P256 signatures. * Tries to use RIP-7212 precompile if available on the chain, and if not falls * back to more expensive Solidity implementation. - **/ + * + */ library P256 { address constant PRECOMPILE = address(0x100); address constant VERIFIER = 0xc2b78104907F722DABAc4C69f826a522B2754De4; - function verifySignatureAllowMalleability( - bytes32 message_hash, - uint256 r, - uint256 s, - uint256 x, - uint256 y - ) internal view returns (bool) { + function verifySignatureAllowMalleability(bytes32 message_hash, uint256 r, uint256 s, uint256 x, uint256 y) + internal + view + returns (bool) + { bytes memory args = abi.encode(message_hash, r, s, x, y); (bool success, bytes memory ret) = PRECOMPILE.staticcall(args); @@ -27,25 +26,20 @@ library P256 { return abi.decode(ret, (uint256)) == 1; } - (bool fallbackSuccess, bytes memory fallbackRet) = VERIFIER.staticcall( - args - ); + (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) { + 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; diff --git a/src/P256Verifier.sol b/src/P256Verifier.sol index acecdac..537265a 100644 --- a/src/P256Verifier.sol +++ b/src/P256Verifier.sol @@ -7,7 +7,8 @@ pragma solidity 0.8.23; * interface specified in the EIP-7212 precompile, allowing it to be used as a * fallback. It's based on Ledger's optimized implementation: * https://github.com/rdubois-crypto/FreshCryptoLib/tree/master/solidity - **/ + * + */ contract P256Verifier { /** * Precompiles don't use a function signature. The first byte of callldata @@ -41,38 +42,30 @@ contract P256Verifier { // Parameters for the sec256r1 (P256) elliptic curve // Curve prime field modulus - uint256 constant p = - 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF; + uint256 constant p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF; // Short weierstrass first coefficient uint256 constant a = // The assumption a == -3 (mod p) is used throughout the codebase - 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC; + 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC; // Short weierstrass second coefficient - uint256 constant b = - 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B; + uint256 constant b = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B; // Generating point affine coordinates - uint256 constant GX = - 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296; - uint256 constant GY = - 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5; + uint256 constant GX = 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296; + uint256 constant GY = 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5; // Curve order (number of points) - uint256 constant n = - 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551; + uint256 constant n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551; // -2 mod p constant, used to speed up inversion and doubling (avoid negation) - uint256 constant minus_2modp = - 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFD; + uint256 constant minus_2modp = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFD; // -2 mod n constant, used to speed up inversion - uint256 constant minus_2modn = - 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F; + uint256 constant minus_2modn = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F; /** * @dev ECDSA verification given signature and public key. */ - function ecdsa_verify( - bytes32 message_hash, - uint256 r, - uint256 s, - uint256[2] memory pubKey - ) private view returns (bool) { + function ecdsa_verify(bytes32 message_hash, uint256 r, uint256 s, uint256[2] memory pubKey) + private + view + returns (bool) + { // Check r and s are in the scalar field if (r == 0 || r >= n || s == 0 || s >= n) { return false; @@ -87,12 +80,7 @@ contract P256Verifier { uint256 scalar_u = mulmod(uint256(message_hash), sInv, n); // (h * s^-1) in scalar field uint256 scalar_v = mulmod(r, sInv, n); // (r * s^-1) in scalar field - uint256 r_x = ecZZ_mulmuladd( - pubKey[0], - pubKey[1], - scalar_u, - scalar_v - ); + uint256 r_x = ecZZ_mulmuladd(pubKey[0], pubKey[1], scalar_u, scalar_v); return r_x % n == r; } @@ -100,10 +88,7 @@ contract P256Verifier { * @dev Check if a point in affine coordinates is on the curve * Reject 0 point at infinity. */ - function ecAff_isValidPubkey( - uint256 x, - uint256 y - ) internal pure returns (bool) { + function ecAff_isValidPubkey(uint256 x, uint256 y) internal pure returns (bool) { if (x >= p || y >= p || (x == 0 && y == 0)) { return false; } @@ -111,10 +96,7 @@ contract P256Verifier { return ecAff_satisfiesCurveEqn(x, y); } - function ecAff_satisfiesCurveEqn( - uint256 x, - uint256 y - ) internal pure returns (bool) { + function ecAff_satisfiesCurveEqn(uint256 x, uint256 y) internal pure returns (bool) { uint256 LHS = mulmod(y, y, p); // y^2 uint256 RHS = addmod(mulmod(mulmod(x, x, p), x, p), mulmod(a, x, p), p); // x^3 + a x RHS = addmod(RHS, b, p); // x^3 + a*x + b @@ -148,7 +130,7 @@ contract P256Verifier { uint256 bitpair; // Find the first bit index that's active in either scalar_u or scalar_v. - while(index >= 0) { + while (index >= 0) { bitpair = compute_bitpair(uint256(index), scalar_u, scalar_v); index--; if (bitpair != 0) break; @@ -156,7 +138,7 @@ contract P256Verifier { // initialise (X, Y) depending on the first active bitpair. // invariant(bitpair != 0); // bitpair == 0 is only possible if u and v are 0. - + if (bitpair == 1) { (X, Y) = (GX, GY); } else if (bitpair == 2) { @@ -167,7 +149,7 @@ contract P256Verifier { uint256 TX; uint256 TY; - while(index >= 0) { + while (index >= 0) { (X, Y, zz, zzz) = ecZZ_double_zz(X, Y, zz, zzz); bitpair = compute_bitpair(uint256(index), scalar_u, scalar_v); @@ -192,7 +174,7 @@ contract P256Verifier { /** * @dev Compute the bits at `index` of u and v and return - * them as 2 bit concatenation. The bit at index 0 is on + * them as 2 bit concatenation. The bit at index 0 is on * if the `index`th bit of scalar_u is on and the bit at * index 1 is on if the `index`th bit of scalar_v is on. * Examples: @@ -208,12 +190,7 @@ contract P256Verifier { * @dev Add two elliptic curve points in affine coordinates * Assumes points are on the EC */ - function ecAff_add( - uint256 x1, - uint256 y1, - uint256 x2, - uint256 y2 - ) internal view returns (uint256, uint256) { + function ecAff_add(uint256 x1, uint256 y1, uint256 x2, uint256 y2) internal view returns (uint256, uint256) { // invariant(ecAff_IsZero(x1, y1) || ecAff_isOnCurve(x1, y1)); // invariant(ecAff_IsZero(x2, y2) || ecAff_isOnCurve(x2, y2)); @@ -232,10 +209,7 @@ contract P256Verifier { * @dev Check if a point is the infinity point in affine rep. * Assumes point is on the EC or is the point at infinity. */ - function ecAff_IsInf( - uint256 x, - uint256 y - ) internal pure returns (bool flag) { + function ecAff_IsInf(uint256 x, uint256 y) internal pure returns (bool flag) { // invariant((x == 0 && y == 0) || ecAff_isOnCurve(x, y)); return (x == 0 && y == 0); @@ -245,11 +219,8 @@ contract P256Verifier { * @dev Check if a point is the infinity point in ZZ rep. * Assumes point is on the EC or is the point at infinity. */ - function ecZZ_IsInf( - uint256 zz, - uint256 zzz - ) internal pure returns (bool flag) { - // invariant((zz == 0 && zzz == 0) || ecAff_isOnCurve(x, y) for affine + function ecZZ_IsInf(uint256 zz, uint256 zzz) internal pure returns (bool flag) { + // invariant((zz == 0 && zzz == 0) || ecAff_isOnCurve(x, y) for affine // form of the point) return (zz == 0 && zzz == 0); @@ -262,25 +233,25 @@ contract P256Verifier { * Matches https://github.com/supranational/blst/blob/9c87d4a09d6648e933c818118a4418349804ce7f/src/ec_ops.h#L705 closely * Handles points at infinity gracefully */ - function ecZZ_dadd_affine( - uint256 x1, - uint256 y1, - uint256 zz1, - uint256 zzz1, - uint256 x2, - uint256 y2 - ) internal pure returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3) { - if (ecAff_IsInf(x2, y2)) { // (X2, Y2) is point at infinity + function ecZZ_dadd_affine(uint256 x1, uint256 y1, uint256 zz1, uint256 zzz1, uint256 x2, uint256 y2) + internal + pure + returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3) + { + if (ecAff_IsInf(x2, y2)) { + // (X2, Y2) is point at infinity if (ecZZ_IsInf(zz1, zzz1)) return ecZZ_PointAtInf(); return (x1, y1, zz1, zzz1); - } else if (ecZZ_IsInf(zz1, zzz1)) { // (X1, Y1) is point at infinity + } else if (ecZZ_IsInf(zz1, zzz1)) { + // (X1, Y1) is point at infinity return (x2, y2, 1, 1); } uint256 comp_R = addmod(mulmod(y2, zzz1, p), p - y1, p); // R = S2 - y1 = y2*zzz1 - y1 uint256 comp_P = addmod(mulmod(x2, zz1, p), p - x1, p); // P = U2 - x1 = x2*zz1 - x1 - if (comp_P != 0) { // X1 != X2 + if (comp_P != 0) { + // X1 != X2 // invariant(x1 != x2); uint256 comp_PP = mulmod(comp_P, comp_P, p); // PP = P^2 uint256 comp_PPP = mulmod(comp_PP, comp_P, p); // PPP = P*PP @@ -297,12 +268,14 @@ contract P256Verifier { mulmod(p - y1, comp_PPP, p), // (-y1)*PPP p ); // R*(Q-x3) - y1*PPP - } else if (comp_R == 0) { // X1 == X2 and Y1 == Y2 + } else if (comp_R == 0) { + // X1 == X2 and Y1 == Y2 // invariant(x1 == x2 && y1 == y2); // Must be affine because (X2, Y2) is affine. (x3, y3, zz3, zzz3) = ecZZ_double_affine(x2, y2); - } else { // X1 == X2 and Y1 == -Y2 + } else { + // X1 == X2 and Y1 == -Y2 // invariant(x1 == x2 && y1 == p - y2); (x3, y3, zz3, zzz3) = ecZZ_PointAtInf(); } @@ -311,20 +284,23 @@ contract P256Verifier { } /** - * @dev Double a ZZ point + * @dev Double a ZZ point * Uses http://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#doubling-dbl-2008-s-1 * Handles point at infinity gracefully */ - function ecZZ_double_zz(uint256 x1, - uint256 y1, uint256 zz1, uint256 zzz1) internal pure returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3) { + function ecZZ_double_zz(uint256 x1, uint256 y1, uint256 zz1, uint256 zzz1) + internal + pure + returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3) + { if (ecZZ_IsInf(zz1, zzz1)) return ecZZ_PointAtInf(); - + uint256 comp_U = mulmod(2, y1, p); // U = 2*Y1 uint256 comp_V = mulmod(comp_U, comp_U, p); // V = U^2 uint256 comp_W = mulmod(comp_U, comp_V, p); // W = U*V uint256 comp_S = mulmod(x1, comp_V, p); // S = X1*V uint256 comp_M = addmod(mulmod(3, mulmod(x1, x1, p), p), mulmod(a, mulmod(zz1, zz1, p), p), p); //M = 3*(X1)^2 + a*(zz1)^2 - + x3 = addmod(mulmod(comp_M, comp_M, p), mulmod(minus_2modp, comp_S, p), p); // M^2 + (-2)*S y3 = addmod(mulmod(comp_M, addmod(comp_S, p - x3, p), p), mulmod(p - comp_W, y1, p), p); // M*(S+(-X3)) + (-W)*Y1 zz3 = mulmod(comp_V, zz1, p); // V*ZZ1 @@ -332,12 +308,15 @@ contract P256Verifier { } /** - * @dev Double an affine point and return as a ZZ point + * @dev Double an affine point and return as a ZZ point * Uses http://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#doubling-mdbl-2008-s-1 * Handles point at infinity gracefully */ - function ecZZ_double_affine(uint256 x1, - uint256 y1) internal pure returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3) { + function ecZZ_double_affine(uint256 x1, uint256 y1) + internal + pure + returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3) + { if (ecAff_IsInf(x1, y1)) return ecZZ_PointAtInf(); uint256 comp_U = mulmod(2, y1, p); // U = 2*Y1 @@ -345,7 +324,7 @@ contract P256Verifier { zzz3 = mulmod(comp_U, zz3, p); // W = U*V = zzz3 uint256 comp_S = mulmod(x1, zz3, p); // S = X1*V uint256 comp_M = addmod(mulmod(3, mulmod(x1, x1, p), p), a, p); // M = 3*(X1)^2 + a - + x3 = addmod(mulmod(comp_M, comp_M, p), mulmod(minus_2modp, comp_S, p), p); // M^2 + (-2)*S y3 = addmod(mulmod(comp_M, addmod(comp_S, p - x3, p), p), mulmod(p - zzz3, y1, p), p); // M*(S+(-X3)) + (-W)*Y1 } @@ -355,13 +334,12 @@ contract P256Verifier { * Assumes (zz)^(3/2) == zzz (i.e. zz == z^2 and zzz == z^3) * See https://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz-3.html */ - function ecZZ_SetAff( - uint256 x, - uint256 y, - uint256 zz, - uint256 zzz - ) internal view returns (uint256 x1, uint256 y1) { - if(ecZZ_IsInf(zz, zzz)) { + function ecZZ_SetAff(uint256 x, uint256 y, uint256 zz, uint256 zzz) + internal + view + returns (uint256 x1, uint256 y1) + { + if (ecZZ_IsInf(zz, zzz)) { (x1, y1) = ecAffine_PointAtInf(); return (x1, y1); } @@ -408,7 +386,7 @@ contract P256Verifier { /** * @dev u^-1 mod f = u^(phi(f) - 1) mod f = u^(f-2) mod f for prime f * by Fermat's little theorem, compute u^(f-2) mod f using modexp precompile - * Assume f != 0. If u is 0, then u^-1 mod f is undefined mathematically, + * Assume f != 0. If u is 0, then u^-1 mod f is undefined mathematically, * but this function returns 0. */ function modInv(uint256 u, uint256 f, uint256 minus_2modf) internal view returns (uint256 result) { diff --git a/src/WebAuthn.sol b/src/WebAuthn.sol index 121706b..4abf7cc 100644 --- a/src/WebAuthn.sol +++ b/src/WebAuthn.sol @@ -6,13 +6,11 @@ import "./P256.sol"; /** * Helper library for external contracts to verify WebAuthn signatures. - **/ + * + */ library WebAuthn { /// Checks whether prefix occurs in the beginning of str. - function startsWith( - string memory prefix, - string memory str - ) internal pure returns (bool) { + function startsWith(string memory prefix, string memory str) internal pure returns (bool) { bytes memory prefixBytes = bytes(prefix); bytes memory strBytes = bytes(str); @@ -40,10 +38,7 @@ library WebAuthn { /// 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. - function checkAuthFlags( - bytes1 flags, - bool requireUserVerification - ) internal pure returns (bool) { + function checkAuthFlags(bytes1 flags, bool requireUserVerification) internal pure returns (bool) { // 17. Verify that the UP bit of the flags in authData is set. if (flags & AUTH_DATA_FLAGS_UP != AUTH_DATA_FLAGS_UP) { return false; @@ -52,10 +47,7 @@ library WebAuthn { // 18. If user verification was determined to be required, verify that // the UV bit of the flags in authData is set. Otherwise, ignore the // value of the UV flag. - if ( - requireUserVerification && - (flags & AUTH_DATA_FLAGS_UV) != AUTH_DATA_FLAGS_UV - ) { + if (requireUserVerification && (flags & AUTH_DATA_FLAGS_UV) != AUTH_DATA_FLAGS_UV) { return false; } @@ -131,10 +123,7 @@ library WebAuthn { uint256 y ) internal view returns (bool) { // Check that authenticatorData has good flags - if ( - authenticatorData.length < 37 || - !checkAuthFlags(authenticatorData[32], requireUserVerification) - ) { + if (authenticatorData.length < 37 || !checkAuthFlags(authenticatorData[32], requireUserVerification)) { return false; } @@ -142,20 +131,14 @@ library WebAuthn { // and that the challenge is in the clientDataJSON // as per https://www.w3.org/TR/webauthn-2/#clientdatajson-serialization string memory challengeB64url = Base64URL.encode(challenge); - string memory prefix = string.concat( - '{"type":"webauthn.get","challenge":"', - challengeB64url, - '"' - ); + string memory prefix = string.concat('{"type":"webauthn.get","challenge":"', challengeB64url, '"'); if (!startsWith(prefix, clientDataJSON)) { return false; } // Check that the public key signed sha256(authenticatorData || sha256(clientDataJSON)) bytes32 clientDataJSONHash = sha256(bytes(clientDataJSON)); - bytes32 messageHash = sha256( - abi.encodePacked(authenticatorData, clientDataJSONHash) - ); + bytes32 messageHash = sha256(abi.encodePacked(authenticatorData, clientDataJSONHash)); return P256.verifySignature(messageHash, r, s, x, y); } diff --git a/test/GasBenchmark.t.sol b/test/GasBenchmark.t.sol index 1a12f1e..af9a9df 100644 --- a/test/GasBenchmark.t.sol +++ b/test/GasBenchmark.t.sol @@ -57,18 +57,16 @@ contract GasBenchmarkTest is Test { fcl_verifier = new FCLWrapperEIP7212(); } - /** Checks a single test vector: signature rs, pubkey Q = (x,y). */ - function evaluate( - address verifier_addr, - bytes32 hash, - uint256 r, - uint256 s, - uint256 x, - uint256 y - ) private returns (bool valid, uint gasUsed) { + /** + * Checks a single test vector: signature rs, pubkey Q = (x,y). + */ + function evaluate(address verifier_addr, bytes32 hash, uint256 r, uint256 s, uint256 x, uint256 y) + private + returns (bool valid, uint256 gasUsed) + { bytes memory input = abi.encodePacked(hash, r, s, x, y); - uint gasBefore = gasleft(); + uint256 gasBefore = gasleft(); (bool success, bytes memory res) = verifier_addr.staticcall(input); gasUsed = gasBefore - gasleft(); @@ -83,10 +81,7 @@ contract GasBenchmarkTest is Test { uint256[] our_gasUsed; uint256[] fcl_gasUsed; - function logSummaryStatistics( - uint256[] storage gasUsed, - string memory tag - ) internal view { + function logSummaryStatistics(uint256[] storage gasUsed, string memory tag) internal view { uint256 avg = 0; uint256 min = 2 ** 256 - 1; uint256 max = 0; @@ -124,22 +119,8 @@ contract GasBenchmarkTest is Test { uint256 s = uint256(vector.readBytes32(".s")); bytes32 hash = vector.readBytes32(".hash"); - (bool our_result, uint256 our_gasUsed_test) = evaluate( - address(our_verifier), - hash, - r, - s, - x, - y - ); - (bool fcl_result, uint256 fcl_gasUsed_test) = evaluate( - address(fcl_verifier), - hash, - r, - s, - x, - y - ); + (bool our_result, uint256 our_gasUsed_test) = evaluate(address(our_verifier), hash, r, s, x, y); + (bool fcl_result, uint256 fcl_gasUsed_test) = evaluate(address(fcl_verifier), hash, r, s, x, y); assertEq(our_result, fcl_result, "results don't match"); diff --git a/test/P256.t.sol b/test/P256.t.sol index fe7e69a..a6e9b19 100644 --- a/test/P256.t.sol +++ b/test/P256.t.sol @@ -39,13 +39,7 @@ contract P256Test is Test { bytes32 hash = 0x267f9ea080b54bbea2443dff8aa543604564329783b6a515c6663a691c555490; - bool res = P256.verifySignatureAllowMalleability( - hash, - r, - s, - pubKey[0], - pubKey[1] - ); + bool res = P256.verifySignatureAllowMalleability(hash, r, s, pubKey[0], pubKey[1]); assertEq(res, true); res = P256.verifySignature(hash, r, s, pubKey[0], pubKey[1]); @@ -59,13 +53,7 @@ contract P256Test is Test { bytes32 hash = 0x267f9ea080b54bbea2443dff8aa543604564329783b6a515c6663a691c555490; - bool res = P256.verifySignatureAllowMalleability( - hash, - r, - s, - pubKey[0], - pubKey[1] - ); + bool res = P256.verifySignatureAllowMalleability(hash, r, s, pubKey[0], pubKey[1]); assertEq(res, true); res = P256.verifySignature(hash, r, s, pubKey[0], pubKey[1]); @@ -78,42 +66,24 @@ contract P256Test is Test { bytes32 hash = 0x267f9ea080b54bbea2443dff8aa543604564329783b6a515c6663a691c555490; - uint gasBefore = gasleft(); - bool res = P256.verifySignatureAllowMalleability( - hash, - r, - s, - pubKey[0], - pubKey[1] - ); + uint256 gasBefore = gasleft(); + bool res = P256.verifySignatureAllowMalleability(hash, r, s, pubKey[0], pubKey[1]); assertEq(res, true); - uint gasUsedFallback = gasBefore - gasleft(); + uint256 gasUsedFallback = gasBefore - gasleft(); assert(gasUsedFallback > 100_000); // no precompile, used Solidity implementation vm.etch(P256.PRECOMPILE, type(FakePrecompile).runtimeCode); gasBefore = gasleft(); - res = P256.verifySignatureAllowMalleability( - hash, - r, - s, - pubKey[0], - pubKey[1] - ); + res = P256.verifySignatureAllowMalleability(hash, r, s, pubKey[0], pubKey[1]); assertEq(res, true); - uint gasUsedPrecompile = gasBefore - gasleft(); + uint256 gasUsedPrecompile = gasBefore - gasleft(); assert(gasUsedPrecompile < gasUsedFallback); // precompile, used precompile gasBefore = gasleft(); - res = P256.verifySignatureAllowMalleability( - hash, - r + 1, - s, - pubKey[0], - pubKey[1] - ); + res = P256.verifySignatureAllowMalleability(hash, r + 1, s, pubKey[0], pubKey[1]); assertEq(res, false); - uint gasUsedInvalidSignature = gasBefore - gasleft(); + uint256 gasUsedInvalidSignature = gasBefore - gasleft(); assert(gasUsedInvalidSignature > gasUsedFallback); // invalid signature, used precompile and failed, so fall back to Solidity implementation and failed with nearly double gas } } diff --git a/test/P256Verifier.t.sol b/test/P256Verifier.t.sol index 7d663d2..099be68 100644 --- a/test/P256Verifier.t.sol +++ b/test/P256Verifier.t.sol @@ -14,17 +14,16 @@ contract P256VerifierTest is Test { verifier = new P256Verifier(); } - /** Checks a single test vector: signature rs, pubkey Q = (x,y). */ - function evaluate( - bytes32 hash, - uint256 r, - uint256 s, - uint256 x, - uint256 y - ) private returns (bool valid, uint gasUsed) { + /** + * Checks a single test vector: signature rs, pubkey Q = (x,y). + */ + function evaluate(bytes32 hash, uint256 r, uint256 s, uint256 x, uint256 y) + private + returns (bool valid, uint256 gasUsed) + { bytes memory input = abi.encodePacked(hash, r, s, x, y); - uint gasBefore = gasleft(); + uint256 gasBefore = gasleft(); (bool success, bytes memory res) = address(verifier).staticcall(input); gasUsed = gasBefore - gasleft(); @@ -79,17 +78,10 @@ contract P256VerifierTest is Test { bool expected = vector.readBool(".valid"); string memory comment = vector.readString(".comment"); - (bool result, ) = evaluate(hash, r, s, x, y); + (bool result,) = evaluate(hash, r, s, x, y); string memory err = string( - abi.encodePacked( - "exp ", - expected ? "1" : "0", - ", we return ", - result ? "1" : "0", - ": ", - comment - ) + abi.encodePacked("exp ", expected ? "1" : "0", ", we return ", result ? "1" : "0", ": ", comment) ); assertTrue(result == expected, err); } @@ -98,10 +90,10 @@ contract P256VerifierTest is Test { function testWrongInputLength() public { // First valid Wycheproof vector bytes32 hash = 0xbb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca605023; - uint r = 19738613187745101558623338726804762177711919211234071563652772152683725073944; - uint s = 34753961278895633991577816754222591531863837041401341770838584739693604822390; - uint x = 18614955573315897657680976650685450080931919913269223958732452353593824192568; - uint y = 90223116347859880166570198725387569567414254547569925327988539833150573990206; + uint256 r = 19738613187745101558623338726804762177711919211234071563652772152683725073944; + uint256 s = 34753961278895633991577816754222591531863837041401341770838584739693604822390; + uint256 x = 18614955573315897657680976650685450080931919913269223958732452353593824192568; + uint256 y = 90223116347859880166570198725387569567414254547569925327988539833150573990206; bytes memory input = abi.encodePacked(hash, r, s, x, y); (bool success, bytes memory result) = address(verifier).call(input); bytes32 res = abi.decode(result, (bytes32)); @@ -119,20 +111,20 @@ contract P256VerifierTest is Test { uint256 p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF; bytes32 hash = bytes32(0); - (uint r, uint s, uint x, uint y) = (1, 1, 1, 1); + (uint256 r, uint256 s, uint256 x, uint256 y) = (1, 1, 1, 1); // In-bounds dummy key (1, 1) // Calls modexp, which takes gas. - (bool result, uint gasUsedWithModexp) = evaluate(hash, r, s, x, y); + (bool result, uint256 gasUsedWithModexp) = evaluate(hash, r, s, x, y); console2.log("gasUsed ", gasUsedWithModexp); assertEq(result, false); assertGt(gasUsedWithModexp, 5000); - uint failFastGasBound = (gasUsedWithModexp * 9) / 10; + uint256 failFastGasBound = (gasUsedWithModexp * 9) / 10; // Out-of-bounds public key. Fails fast, takes less gas. (x, y) = (0, 1); - uint gasUsed; + uint256 gasUsed; (result, gasUsed) = evaluate(hash, r, s, x, y); console2.log("gasUsed ", gasUsed); assertEq(result, false); diff --git a/test/WebAuthn.t.sol b/test/WebAuthn.t.sol index 4f3b78c..11147fa 100644 --- a/test/WebAuthn.t.sol +++ b/test/WebAuthn.t.sol @@ -15,12 +15,9 @@ contract WebAuthnTest is Test { string clientDataJSON = '{"type":"webauthn.get","challenge":"dGVzdA","origin":"https://funny-froyo-3f9b75.netlify.app"}'; bytes challenge = hex"74657374"; - bytes authenticatorData = - hex"e0b592a7dd54eedeec65206e031fc196b8e5915f9b389735860c83854f65dc0e1d00000000"; - uint256 r = - 0x32e005a53ae49a96ac88c715243638dd5c985fbd463c727d8eefd05bee4e2570; - uint256 s = - 0x7a4fef4d0b11187f95f69eefbb428df8ac799bbd9305066b1e9c9fe9a5bcf8c4; + bytes authenticatorData = hex"e0b592a7dd54eedeec65206e031fc196b8e5915f9b389735860c83854f65dc0e1d00000000"; + uint256 r = 0x32e005a53ae49a96ac88c715243638dd5c985fbd463c727d8eefd05bee4e2570; + uint256 s = 0x7a4fef4d0b11187f95f69eefbb428df8ac799bbd9305066b1e9c9fe9a5bcf8c4; function setUp() public { // Deploy P256 Verifier @@ -45,8 +42,7 @@ contract WebAuthnTest is Test { // Test startsWith is implemented correctly function testStartsWith() public { // End too early - string - memory customClientDataJSON = '{"type":"webauthn.get","challenge":'; + string memory customClientDataJSON = '{"type":"webauthn.get","challenge":'; bool ret = WebAuthn.verifySignature({ challenge: challenge, authenticatorData: authenticatorData, @@ -60,7 +56,8 @@ contract WebAuthnTest is Test { assertFalse(ret); // missing { in beginning - customClientDataJSON = '"type":"webauthn.get","challenge":"dGVzdA","origin":"https://funny-froyo-3f9b75.netlify.app"}'; + customClientDataJSON = + '"type":"webauthn.get","challenge":"dGVzdA","origin":"https://funny-froyo-3f9b75.netlify.app"}'; ret = WebAuthn.verifySignature({ challenge: challenge, authenticatorData: authenticatorData, @@ -74,7 +71,8 @@ contract WebAuthnTest is Test { assertFalse(ret); // missing closing quote on challenge - customClientDataJSON = '{"type":"webauthn.get","challenge":"dGVzdA,"origin":"https://funny-froyo-3f9b75.netlify.app"}'; + customClientDataJSON = + '{"type":"webauthn.get","challenge":"dGVzdA,"origin":"https://funny-froyo-3f9b75.netlify.app"}'; ret = WebAuthn.verifySignature({ challenge: challenge, authenticatorData: authenticatorData, @@ -123,7 +121,8 @@ contract WebAuthnTest is Test { // User Verification not set data from SimpleWebauthn // https://github.com/MasterKale/SimpleWebAuthn/blob/master/packages/server/src/helpers/parseAuthenticatorData.test.ts#L14 - customAuthenticatorData = hex"49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763810000008da1716578616d706c652e657874656e73696f6e78765468697320697320616e206578616d706c6520657874656e73696f6e2120496620796f7520726561642074686973206d6573736167652c20796f752070726f6261626c79207375636365737366756c6c792070617373696e6720636f6e666f726d616e63652074657374732e20476f6f64206a6f6221"; + customAuthenticatorData = + hex"49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763810000008da1716578616d706c652e657874656e73696f6e78765468697320697320616e206578616d706c6520657874656e73696f6e2120496620796f7520726561642074686973206d6573736167652c20796f752070726f6261626c79207375636365737366756c6c792070617373696e6720636f6e666f726d616e63652074657374732e20476f6f64206a6f6221"; ret = WebAuthn.verifySignature({ challenge: challenge, authenticatorData: customAuthenticatorData, diff --git a/test/external/FCL_elliptic.sol b/test/external/FCL_elliptic.sol index 5b8e885..8a2aada 100644 --- a/test/external/FCL_elliptic.sol +++ b/test/external/FCL_elliptic.sol @@ -26,31 +26,22 @@ library FCL_Elliptic_ZZ { // Set parameters for curve sec256r1. //curve prime field modulus - uint256 constant p = - 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF; + uint256 constant p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF; //short weierstrass first coefficient - uint256 constant a = - 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC; + uint256 constant a = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC; //short weierstrass second coefficient - uint256 constant b = - 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B; + uint256 constant b = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B; //generating point affine coordinates - uint256 constant gx = - 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296; - uint256 constant gy = - 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5; + uint256 constant gx = 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296; + uint256 constant gy = 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5; //curve order (number of points) - uint256 constant n = - 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551; + uint256 constant n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551; /* -2 mod p constant, used to speed up inversion and doubling (avoid negation)*/ - uint256 constant minus_2 = - 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFD; + uint256 constant minus_2 = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFD; /* -2 mod n constant, used to speed up inversion*/ - uint256 constant minus_2modn = - 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F; + uint256 constant minus_2modn = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F; - uint256 constant minus_1 = - 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + uint256 constant minus_1 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; /** * /* inversion mod n via a^(n-2), use of precompiled using little Fermat theorem @@ -68,9 +59,7 @@ library FCL_Elliptic_ZZ { mstore(add(pointer, 0xa0), n) // Call the precompiled contract 0x05 = ModExp - if iszero(call(not(0), 0x05, 0, pointer, 0xc0, pointer, 0x20)) { - revert(0, 0) - } + if iszero(call(not(0), 0x05, 0, pointer, 0xc0, pointer, 0x20)) { revert(0, 0) } result := mload(pointer) } } @@ -78,7 +67,6 @@ library FCL_Elliptic_ZZ { /** * /* @dev inversion mod nusing little Fermat theorem via a^(n-2), use of precompiled */ - function FCL_pModInv(uint256 u) internal returns (uint256 result) { uint256[6] memory pointer; assembly { @@ -92,9 +80,7 @@ library FCL_Elliptic_ZZ { mstore(add(pointer, 0xa0), p) // Call the precompiled contract 0x05 = ModExp - if iszero(call(not(0), 0x05, 0, pointer, 0xc0, pointer, 0x20)) { - revert(0, 0) - } + if iszero(call(not(0), 0x05, 0, pointer, 0xc0, pointer, 0x20)) { revert(0, 0) } result := mload(pointer) } } @@ -102,10 +88,7 @@ library FCL_Elliptic_ZZ { /** * /* @dev Convert from affine rep to XYZZ rep */ - function ecAff_SetZZ( - uint256 x0, - uint256 y0 - ) internal pure returns (uint256[4] memory P) { + function ecAff_SetZZ(uint256 x0, uint256 y0) internal pure returns (uint256[4] memory P) { unchecked { P[2] = 1; //ZZ P[3] = 1; //ZZZ @@ -118,12 +101,7 @@ library FCL_Elliptic_ZZ { * /* @dev Convert from XYZZ rep to affine rep */ /* https://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz-3.html#addition-add-2008-s*/ - function ecZZ_SetAff( - uint256 x, - uint256 y, - uint256 zz, - uint256 zzz - ) internal returns (uint256 x1, uint256 y1) { + function ecZZ_SetAff(uint256 x, uint256 y, uint256 zz, uint256 zzz) internal returns (uint256 x1, uint256 y1) { uint256 zzzInv = FCL_pModInv(zzz); //1/zzz y1 = mulmod(y, zzzInv, p); //Y/zzz uint256 _b = mulmod(zz, zzzInv, p); //1/z @@ -136,12 +114,11 @@ library FCL_Elliptic_ZZ { */ /* The "dbl-2008-s-1" doubling formulas */ - function ecZZ_Dbl( - uint256 x, - uint256 y, - uint256 zz, - uint256 zzz - ) internal pure returns (uint256 P0, uint256 P1, uint256 P2, uint256 P3) { + function ecZZ_Dbl(uint256 x, uint256 y, uint256 zz, uint256 zzz) + internal + pure + returns (uint256 P0, uint256 P1, uint256 P2, uint256 P3) + { unchecked { assembly { P0 := mulmod(2, y, p) //U = 2*Y1 @@ -149,11 +126,7 @@ library FCL_Elliptic_ZZ { P3 := mulmod(x, P2, p) // S = X1*V P1 := mulmod(P0, P2, p) // W=UV P2 := mulmod(P2, zz, p) //zz3=V*ZZ1 - zz := mulmod( - 3, - mulmod(addmod(x, sub(p, zz), p), addmod(x, zz, p), p), - p - ) //M=3*(X1-ZZ1)*(X1+ZZ1) + zz := mulmod(3, mulmod(addmod(x, sub(p, zz), p), addmod(x, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1) P0 := addmod(mulmod(zz, zz, p), mulmod(minus_2, P3, p), p) //X3=M^2-2S x := mulmod(zz, addmod(P3, sub(p, P0), p), p) //M(S-X3) P3 := mulmod(P1, zzz, p) //zzz3=W*zzz1 @@ -167,15 +140,11 @@ library FCL_Elliptic_ZZ { * @dev Sutherland2008 add a ZZ point with a normalized point and greedy formulae * warning: assume that P1(x1,y1)!=P2(x2,y2), true in multiplication loop with prime order (cofactor 1) */ - - function ecZZ_AddN( - uint256 x1, - uint256 y1, - uint256 zz1, - uint256 zzz1, - uint256 x2, - uint256 y2 - ) internal pure returns (uint256 P0, uint256 P1, uint256 P2, uint256 P3) { + function ecZZ_AddN(uint256 x1, uint256 y1, uint256 zz1, uint256 zzz1, uint256 x2, uint256 y2) + internal + pure + returns (uint256 P0, uint256 P1, uint256 P2, uint256 P3) + { unchecked { if (y1 == 0) { return (x2, y2, 1, 1); @@ -190,16 +159,8 @@ library FCL_Elliptic_ZZ { P2 := mulmod(zz1, P0, p) ////ZZ3 = ZZ1*PP P3 := mulmod(zzz1, P1, p) ////ZZZ3 = ZZZ1*PPP zz1 := mulmod(x1, P0, p) //Q = X1*PP - P0 := addmod( - addmod(mulmod(y2, y2, p), sub(p, P1), p), - mulmod(minus_2, zz1, p), - p - ) //R^2-PPP-2*Q - P1 := addmod( - mulmod(addmod(zz1, sub(p, P0), p), y2, p), - mulmod(y1, P1, p), - p - ) //R*(Q-X3) + P0 := addmod(addmod(mulmod(y2, y2, p), sub(p, P1), p), mulmod(minus_2, zz1, p), p) //R^2-PPP-2*Q + P1 := addmod(mulmod(addmod(zz1, sub(p, P0), p), y2, p), mulmod(y1, P1, p), p) //R*(Q-X3) } //end assembly } //end unchecked @@ -209,11 +170,7 @@ library FCL_Elliptic_ZZ { /** * @dev Return the zero curve in XYZZ coordinates. */ - function ecZZ_SetZero() - internal - pure - returns (uint256 x, uint256 y, uint256 zz, uint256 zzz) - { + function ecZZ_SetZero() internal pure returns (uint256 x, uint256 y, uint256 zz, uint256 zzz) { return (0, 0, 0, 0); } @@ -222,19 +179,13 @@ library FCL_Elliptic_ZZ { */ // uint256 x0, uint256 y0, uint256 zz0, uint256 zzz0 - function ecZZ_IsZero( - uint256, - uint256 y0, - uint256, - uint256 - ) internal pure returns (bool) { + function ecZZ_IsZero(uint256, uint256 y0, uint256, uint256) internal pure returns (bool) { return y0 == 0; } /** * @dev Return the zero curve in affine coordinates. Compatible with the double formulae (no special case) */ - function ecAff_SetZero() internal pure returns (uint256 x, uint256 y) { return (0, 0); } @@ -243,30 +194,20 @@ library FCL_Elliptic_ZZ { * @dev Check if the curve is the zero curve in affine rep. */ // uint256 x, uint256 y) - function ecAff_IsZero( - uint256, - uint256 y - ) internal pure returns (bool flag) { + function ecAff_IsZero(uint256, uint256 y) internal pure returns (bool flag) { return (y == 0); } /** * @dev Check if a point in affine coordinates is on the curve (reject Neutral that is indeed on the curve). */ - function ecAff_isOnCurve( - uint256 x, - uint256 y - ) internal pure returns (bool) { + function ecAff_isOnCurve(uint256 x, uint256 y) internal pure returns (bool) { if (0 == x || x == p || 0 == y || y == p) { return false; } unchecked { uint256 LHS = mulmod(y, y, p); // y^2 - uint256 RHS = addmod( - mulmod(mulmod(x, x, p), x, p), - mulmod(x, a, p), - p - ); // x^3+ax + uint256 RHS = addmod(mulmod(mulmod(x, x, p), x, p), mulmod(x, a, p), p); // x^3+ax RHS = addmod(RHS, b, p); // x^3 + a*x + b return LHS == RHS; @@ -276,13 +217,7 @@ library FCL_Elliptic_ZZ { /** * @dev Add two elliptic curve points in affine coordinates. */ - - function ecAff_add( - uint256 x0, - uint256 y0, - uint256 x1, - uint256 y1 - ) internal returns (uint256, uint256) { + function ecAff_add(uint256 x0, uint256 y0, uint256 x1, uint256 y1) internal returns (uint256, uint256) { uint256 zz0; uint256 zzz0; @@ -317,24 +252,11 @@ library FCL_Elliptic_ZZ { (H0, H1) = ecAff_add(gx, gy, Q0, Q1); //will not work if Q=P, obvious forbidden private key assembly { - for { - let T4 := add( - shl(1, and(shr(index, scalar_v), 1)), - and(shr(index, scalar_u), 1) - ) - } eq(T4, 0) { + for { let T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) } eq(T4, 0) { index := sub(index, 1) - T4 := add( - shl(1, and(shr(index, scalar_v), 1)), - and(shr(index, scalar_u), 1) - ) - } { - - } - zz := add( - shl(1, and(shr(index, scalar_v), 1)), - and(shr(index, scalar_u), 1) - ) + T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) + } {} + zz := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) if eq(zz, 1) { X := gx @@ -353,21 +275,13 @@ library FCL_Elliptic_ZZ { zz := 1 zzz := 1 - for { - - } gt(minus_1, index) { - index := sub(index, 1) - } { + for {} gt(minus_1, index) { index := sub(index, 1) } { // inlined EcZZ_Dbl let T1 := mulmod(2, Y, p) //U = 2*Y1, y free let T2 := mulmod(T1, T1, p) // V=U^2 let T3 := mulmod(X, T2, p) // S = X1*V T1 := mulmod(T1, T2, p) // W=UV - let T4 := mulmod( - 3, - mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), - p - ) //M=3*(X1-ZZ1)*(X1+ZZ1) + let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1) zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1 zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free @@ -377,10 +291,7 @@ library FCL_Elliptic_ZZ { { //value of dibit - T4 := add( - shl(1, and(shr(index, scalar_v), 1)), - and(shr(index, scalar_u), 1) - ) + T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) if iszero(T4) { Y := sub(p, Y) //restore the -Y inversion @@ -430,11 +341,7 @@ library FCL_Elliptic_ZZ { zzz := mulmod(TT1, zzz, p) //zzz3=W*zzz1 zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free - X := addmod( - mulmod(T4, T4, p), - mulmod(minus_2, T3, p), - p - ) //X3=M^2-2S + X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S T2 := mulmod(T4, addmod(T3, sub(p, X), p), p) //M(S-X3) Y := addmod(T2, mulmod(T1, Y, p), p) //Y3= M(S-X3)-W*Y1 @@ -448,16 +355,8 @@ library FCL_Elliptic_ZZ { zz := mulmod(zz, T4, p) zzz := mulmod(zzz, TT1, p) //zz3=V*ZZ1 let TT2 := mulmod(X, T4, p) - T4 := addmod( - addmod(mulmod(y2, y2, p), sub(p, TT1), p), - mulmod(minus_2, TT2, p), - p - ) - Y := addmod( - mulmod(addmod(TT2, sub(p, T4), p), y2, p), - mulmod(Y, TT1, p), - p - ) + T4 := addmod(addmod(mulmod(y2, y2, p), sub(p, TT1), p), mulmod(minus_2, TT2, p), p) + Y := addmod(mulmod(addmod(TT2, sub(p, T4), p), y2, p), mulmod(Y, TT1, p), p) X := T4 } @@ -475,9 +374,7 @@ library FCL_Elliptic_ZZ { mstore(add(T, 0xa0), p) // Call the precompiled contract 0x05 = ModExp - if iszero(call(not(0), 0x05, 0, T, 0xc0, T, 0x20)) { - revert(0, 0) - } + if iszero(call(not(0), 0x05, 0, T, 0xc0, T, 0x20)) { revert(0, 0) } //Y:=mulmod(Y,zzz,p)//Y/zzz //zz :=mulmod(zz, mload(T),p) //1/z @@ -493,11 +390,10 @@ library FCL_Elliptic_ZZ { //contract at given address dataPointer //(thx to Lakhdar https://github.com/Kelvyne for EVM storage explanations and tricks) // the external tool to generate tables from public key is in the /sage directory - function ecZZ_mulmuladd_S8_extcode( - uint256 scalar_u, - uint256 scalar_v, - address dataPointer - ) internal returns (uint256 X /*, uint Y*/) { + function ecZZ_mulmuladd_S8_extcode(uint256 scalar_u, uint256 scalar_v, address dataPointer) + internal + returns (uint256 X /*, uint Y*/ ) + { unchecked { uint256 zz; // third and coordinates of the point @@ -507,23 +403,13 @@ library FCL_Elliptic_ZZ { while (T[0] == 0) { zz = zz - 1; //tbd case of msb octobit is null - T[0] = - 64 * - (128 * - ((scalar_v >> zz) & 1) + - 64 * - ((scalar_v >> (zz - 64)) & 1) + - 32 * - ((scalar_v >> (zz - 128)) & 1) + - 16 * - ((scalar_v >> (zz - 192)) & 1) + - 8 * - ((scalar_u >> zz) & 1) + - 4 * - ((scalar_u >> (zz - 64)) & 1) + - 2 * - ((scalar_u >> (zz - 128)) & 1) + - ((scalar_u >> (zz - 192)) & 1)); + T[0] = 64 + * ( + 128 * ((scalar_v >> zz) & 1) + 64 * ((scalar_v >> (zz - 64)) & 1) + + 32 * ((scalar_v >> (zz - 128)) & 1) + 16 * ((scalar_v >> (zz - 192)) & 1) + + 8 * ((scalar_u >> zz) & 1) + 4 * ((scalar_u >> (zz - 64)) & 1) + + 2 * ((scalar_u >> (zz - 128)) & 1) + ((scalar_u >> (zz - 192)) & 1) + ); } assembly { extcodecopy(dataPointer, T, mload(T), 64) @@ -534,34 +420,18 @@ library FCL_Elliptic_ZZ { zz := 1 //loop over 1/4 of scalars thx to Shamir's trick over 8 points - for { - - } gt(index, 191) { - index := add(index, 191) - } { + for {} gt(index, 191) { index := add(index, 191) } { //inline Double { let TT1 := mulmod(2, Y, p) //U = 2*Y1, y free let T2 := mulmod(TT1, TT1, p) // V=U^2 let T3 := mulmod(X, T2, p) // S = X1*V let T1 := mulmod(TT1, T2, p) // W=UV - let T4 := mulmod( - 3, - mulmod( - addmod(X, sub(p, zz), p), - addmod(X, zz, p), - p - ), - p - ) //M=3*(X1-ZZ1)*(X1+ZZ1) + let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1) zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1 zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free - X := addmod( - mulmod(T4, T4, p), - mulmod(minus_2, T3, p), - p - ) //X3=M^2-2S + X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S //T2:=mulmod(T4,addmod(T3, sub(p, X),p),p)//M(S-X3) let T5 := mulmod(T4, addmod(X, sub(p, T3), p), p) //-M(S-X3)=M(X3-S) @@ -571,34 +441,16 @@ library FCL_Elliptic_ZZ { /* compute element to access in precomputed table */ } { - let T4 := add( - shl(13, and(shr(index, scalar_v), 1)), - shl(9, and(shr(index, scalar_u), 1)) - ) + let T4 := add(shl(13, and(shr(index, scalar_v), 1)), shl(9, and(shr(index, scalar_u), 1))) let index2 := sub(index, 64) - let T3 := add( - T4, - add( - shl(12, and(shr(index2, scalar_v), 1)), - shl(8, and(shr(index2, scalar_u), 1)) - ) - ) + let T3 := + add(T4, add(shl(12, and(shr(index2, scalar_v), 1)), shl(8, and(shr(index2, scalar_u), 1)))) let index3 := sub(index2, 64) - let T2 := add( - T3, - add( - shl(11, and(shr(index3, scalar_v), 1)), - shl(7, and(shr(index3, scalar_u), 1)) - ) - ) + let T2 := + add(T3, add(shl(11, and(shr(index3, scalar_v), 1)), shl(7, and(shr(index3, scalar_u), 1)))) index := sub(index3, 64) - let T1 := add( - T2, - add( - shl(10, and(shr(index, scalar_v), 1)), - shl(6, and(shr(index, scalar_u), 1)) - ) - ) + let T1 := + add(T2, add(shl(10, and(shr(index, scalar_v), 1)), shl(6, and(shr(index, scalar_u), 1)))) //tbd: check validity of formulae with (0,1) to remove conditional jump if iszero(T1) { @@ -622,11 +474,7 @@ library FCL_Elliptic_ZZ { continue } - let y2 := addmod( - mulmod(mload(add(T, 32)), zzz, p), - Y, - p - ) + let y2 := addmod(mulmod(mload(add(T, 32)), zzz, p), Y, p) let T2 := addmod(mulmod(mload(T), zz, p), sub(p, X), p) //special case ecAdd(P,P)=EcDbl @@ -645,11 +493,7 @@ library FCL_Elliptic_ZZ { zzz := mulmod(TT1, zzz, p) //zzz3=W*zzz1 zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free - X := addmod( - mulmod(T4, T4, p), - mulmod(minus_2, T3, p), - p - ) //X3=M^2-2S + X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S T2 := mulmod(T4, addmod(T3, sub(p, X), p), p) //M(S-X3) Y := addmod(T2, mulmod(T1, Y, p), p) //Y3= M(S-X3)-W*Y1 @@ -664,16 +508,8 @@ library FCL_Elliptic_ZZ { //zzz3=V*ZZ1 zzz := mulmod(zzz, T1, p) // W=UV/ let zz1 := mulmod(X, T4, p) - X := addmod( - addmod(mulmod(y2, y2, p), sub(p, T1), p), - mulmod(minus_2, zz1, p), - p - ) - Y := addmod( - mulmod(addmod(zz1, sub(p, X), p), y2, p), - mulmod(Y, T1, p), - p - ) + X := addmod(addmod(mulmod(y2, y2, p), sub(p, T1), p), mulmod(minus_2, zz1, p), p) + Y := addmod(mulmod(addmod(zz1, sub(p, X), p), y2, p), mulmod(Y, T1, p), p) } } //end loop mstore(add(T, 0x60), zz) @@ -690,9 +526,7 @@ library FCL_Elliptic_ZZ { mstore(add(T, 0xa0), p) // Call the precompiled contract 0x05 = ModExp - if iszero(call(not(0), 0x05, 0, T, 0xc0, T, 0x20)) { - revert(0, 0) - } + if iszero(call(not(0), 0x05, 0, T, 0xc0, T, 0x20)) { revert(0, 0) } zz := mload(T) X := mulmod(X, zz, p) //X/zz @@ -701,11 +535,10 @@ library FCL_Elliptic_ZZ { } // improving the extcodecopy trick : append array at end of contract - function ecZZ_mulmuladd_S8_hackmem( - uint256 scalar_u, - uint256 scalar_v, - uint256 dataPointer - ) internal returns (uint256 X /*, uint Y*/) { + function ecZZ_mulmuladd_S8_hackmem(uint256 scalar_u, uint256 scalar_v, uint256 dataPointer) + internal + returns (uint256 X /*, uint Y*/ ) + { uint256 zz; // third and coordinates of the point uint256[6] memory T; @@ -715,23 +548,13 @@ library FCL_Elliptic_ZZ { while (T[0] == 0) { zz = zz - 1; //tbd case of msb octobit is null - T[0] = - 64 * - (128 * - ((scalar_v >> zz) & 1) + - 64 * - ((scalar_v >> (zz - 64)) & 1) + - 32 * - ((scalar_v >> (zz - 128)) & 1) + - 16 * - ((scalar_v >> (zz - 192)) & 1) + - 8 * - ((scalar_u >> zz) & 1) + - 4 * - ((scalar_u >> (zz - 64)) & 1) + - 2 * - ((scalar_u >> (zz - 128)) & 1) + - ((scalar_u >> (zz - 192)) & 1)); + T[0] = 64 + * ( + 128 * ((scalar_v >> zz) & 1) + 64 * ((scalar_v >> (zz - 64)) & 1) + + 32 * ((scalar_v >> (zz - 128)) & 1) + 16 * ((scalar_v >> (zz - 192)) & 1) + + 8 * ((scalar_u >> zz) & 1) + 4 * ((scalar_u >> (zz - 64)) & 1) + + 2 * ((scalar_u >> (zz - 128)) & 1) + ((scalar_u >> (zz - 192)) & 1) + ); } assembly { codecopy(T, add(mload(T), dataPointer), 64) @@ -741,20 +564,12 @@ library FCL_Elliptic_ZZ { zz := 1 //loop over 1/4 of scalars thx to Shamir's trick over 8 points - for { - let index := 254 - } gt(index, 191) { - index := add(index, 191) - } { + for { let index := 254 } gt(index, 191) { index := add(index, 191) } { let T1 := mulmod(2, Y, p) //U = 2*Y1, y free let T2 := mulmod(T1, T1, p) // V=U^2 let T3 := mulmod(X, T2, p) // S = X1*V T1 := mulmod(T1, T2, p) // W=UV - let T4 := mulmod( - 3, - mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), - p - ) //M=3*(X1-ZZ1)*(X1+ZZ1) + let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1) zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1 zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free @@ -766,34 +581,13 @@ library FCL_Elliptic_ZZ { Y := addmod(mulmod(T1, Y, p), T2, p) //-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd /* compute element to access in precomputed table */ - T4 := add( - shl(13, and(shr(index, scalar_v), 1)), - shl(9, and(shr(index, scalar_u), 1)) - ) + T4 := add(shl(13, and(shr(index, scalar_v), 1)), shl(9, and(shr(index, scalar_u), 1))) index := sub(index, 64) - T4 := add( - T4, - add( - shl(12, and(shr(index, scalar_v), 1)), - shl(8, and(shr(index, scalar_u), 1)) - ) - ) + T4 := add(T4, add(shl(12, and(shr(index, scalar_v), 1)), shl(8, and(shr(index, scalar_u), 1)))) index := sub(index, 64) - T4 := add( - T4, - add( - shl(11, and(shr(index, scalar_v), 1)), - shl(7, and(shr(index, scalar_u), 1)) - ) - ) + T4 := add(T4, add(shl(11, and(shr(index, scalar_v), 1)), shl(7, and(shr(index, scalar_u), 1)))) index := sub(index, 64) - T4 := add( - T4, - add( - shl(10, and(shr(index, scalar_v), 1)), - shl(6, and(shr(index, scalar_u), 1)) - ) - ) + T4 := add(T4, add(shl(10, and(shr(index, scalar_v), 1)), shl(6, and(shr(index, scalar_u), 1)))) //index:=add(index,192), restore index, interleaved with loop //tbd: check validity of formulae with (0,1) to remove conditional jump @@ -808,27 +602,15 @@ library FCL_Elliptic_ZZ { // inlined EcZZ_AddN - let y2 := addmod( - mulmod(mload(add(T, 32)), zzz, p), - Y, - p - ) + let y2 := addmod(mulmod(mload(add(T, 32)), zzz, p), Y, p) T2 := addmod(mulmod(mload(T), zz, p), sub(p, X), p) T4 := mulmod(T2, T2, p) T1 := mulmod(T4, T2, p) T2 := mulmod(zz, T4, p) // W=UV zzz := mulmod(zzz, T1, p) //zz3=V*ZZ1 let zz1 := mulmod(X, T4, p) - T4 := addmod( - addmod(mulmod(y2, y2, p), sub(p, T1), p), - mulmod(minus_2, zz1, p), - p - ) - Y := addmod( - mulmod(addmod(zz1, sub(p, T4), p), y2, p), - mulmod(Y, T1, p), - p - ) + T4 := addmod(addmod(mulmod(y2, y2, p), sub(p, T1), p), mulmod(minus_2, zz1, p), p) + Y := addmod(mulmod(addmod(zz1, sub(p, T4), p), y2, p), mulmod(Y, T1, p), p) zz := T2 X := T4 } @@ -847,9 +629,7 @@ library FCL_Elliptic_ZZ { mstore(add(T, 0xa0), p) // Call the precompiled contract 0x05 = ModExp - if iszero(call(not(0), 0x05, 0, T, 0xc0, T, 0x20)) { - revert(0, 0) - } + if iszero(call(not(0), 0x05, 0, T, 0xc0, T, 0x20)) { revert(0, 0) } zz := mload(T) X := mulmod(X, zz, p) //X/zz @@ -860,11 +640,7 @@ library FCL_Elliptic_ZZ { /** * @dev ECDSA verification, given , signature, and public key. */ - function ecdsa_verify( - bytes32 message, - uint256[2] memory rs, - uint256[2] memory Q - ) internal returns (bool) { + function ecdsa_verify(bytes32 message, uint256[2] memory rs, uint256[2] memory Q) internal returns (bool) { if (rs[0] == 0 || rs[0] >= n || rs[1] == 0 || rs[1] >= n) { return false; } @@ -893,12 +669,10 @@ library FCL_Elliptic_ZZ { * generation of contract bytecode for precomputations is done using sagemath code * (see sage directory, WebAuthn_precompute.sage) */ - - function ecdsa_precomputed_verify( - bytes32 message, - uint256[2] calldata rs, - address Shamir8 - ) internal returns (bool) { + function ecdsa_precomputed_verify(bytes32 message, uint256[2] calldata rs, address Shamir8) + internal + returns (bool) + { if (rs[0] == 0 || rs[0] >= n || rs[1] == 0 || rs[1] >= n) { return false; } @@ -912,11 +686,7 @@ library FCL_Elliptic_ZZ { uint256 X; //Shamir 8 dimensions - X = ecZZ_mulmuladd_S8_extcode( - mulmod(uint256(message), sInv, n), - mulmod(rs[0], sInv, n), - Shamir8 - ); + X = ecZZ_mulmuladd_S8_extcode(mulmod(uint256(message), sInv, n), mulmod(rs[0], sInv, n), Shamir8); assembly { X := addmod(X, sub(n, calldataload(rs)), n) @@ -930,12 +700,10 @@ library FCL_Elliptic_ZZ { * generation of contract bytecode for precomputations is done using sagemath code * (see sage directory, WebAuthn_precompute.sage) */ - - function ecdsa_precomputed_hackmem( - bytes32 message, - uint256[2] calldata rs, - uint256 endcontract - ) internal returns (bool) { + function ecdsa_precomputed_hackmem(bytes32 message, uint256[2] calldata rs, uint256 endcontract) + internal + returns (bool) + { if (rs[0] == 0 || rs[0] >= n || rs[1] == 0 || rs[1] >= n) { return false; } @@ -948,11 +716,7 @@ library FCL_Elliptic_ZZ { uint256 X; //Shamir 8 dimensions - X = ecZZ_mulmuladd_S8_hackmem( - mulmod(uint256(message), sInv, n), - mulmod(rs[0], sInv, n), - endcontract - ); + X = ecZZ_mulmuladd_S8_hackmem(mulmod(uint256(message), sInv, n), mulmod(rs[0], sInv, n), endcontract); assembly { X := addmod(X, sub(n, calldataload(rs)), n)