From 6d5c78bfffed7ec8270d09a349bf0f7149840bf5 Mon Sep 17 00:00:00 2001 From: Preston Ong Date: Wed, 22 Nov 2023 11:09:41 +0800 Subject: [PATCH] DEV 2645: `p256-verifier` integration (#2) * forge install: p256-verifier * P256 import * cleanup * removed old library and replaced with p256-verifier * updated ci and FOUNDRY.toml --- .github/workflows/test.yml | 8 +- .gitmodules | 3 + foundry.toml | 4 + lib/p256-verifier | 1 + remappings.txt | 3 +- src/utils/EllipticCurveLib.sol | 363 ------------------------- src/utils/SigVerifyLib.sol | 7 +- src/utils/interfaces/ISigVerifyLib.sol | 2 +- 8 files changed, 22 insertions(+), 369 deletions(-) create mode 160000 lib/p256-verifier delete mode 100644 src/utils/EllipticCurveLib.sol diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 09880b1..aa86a0d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,12 @@ name: test -on: workflow_dispatch +on: + push: + branches: [main] + pull_request: + branches: [main] + + workflow_dispatch: env: FOUNDRY_PROFILE: ci diff --git a/.gitmodules b/.gitmodules index 3a3a0ee..8bb9964 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/solady"] path = lib/solady url = https://github.com/Vectorized/solady +[submodule "lib/p256-verifier"] + path = lib/p256-verifier + url = https://github.com/daimo-eth/p256-verifier diff --git a/foundry.toml b/foundry.toml index 25b918f..ab0ff47 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,4 +3,8 @@ src = "src" out = "out" libs = ["lib"] +solc = "0.8.21" +optimizer = true +optimizer_runs = 200 + # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/lib/p256-verifier b/lib/p256-verifier new file mode 160000 index 0000000..fcaa173 --- /dev/null +++ b/lib/p256-verifier @@ -0,0 +1 @@ +Subproject commit fcaa173c36e6a0fd55370f74f949998356869b2c diff --git a/remappings.txt b/remappings.txt index ab14b3b..f483f82 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1 +1,2 @@ -solady/=lib/solady/src/ \ No newline at end of file +solady/=lib/solady/src/ +p256-verifier/=lib/p256-verifier/src/ \ No newline at end of file diff --git a/src/utils/EllipticCurveLib.sol b/src/utils/EllipticCurveLib.sol deleted file mode 100644 index 339a185..0000000 --- a/src/utils/EllipticCurveLib.sol +++ /dev/null @@ -1,363 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -// Inspired by witnet/elliptic-curve-solidity - MIT licence -// https://github.com/witnet/elliptic-curve-solidity/blob/master/contracts/EllipticCurve.sol - -/** - * @title Elliptic Curve Library - * @dev Library providing arithmetic operations over elliptic curves. - * This library does not check whether the inserted points belong to the curve - * `isOnCurve` function should be used by the library user to check the aforementioned statement. - * @author Witnet Foundation - */ -library EllipticCurveLib { - // Pre-computed constant for 2 ** 255 - uint256 private constant U255_MAX_PLUS_1 = - 57896044618658097711785492504343953926634992332820282019728792003956564819968; - - uint256 constant a = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC; - uint256 constant b = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B; - uint256 constant gx = 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296; - uint256 constant gy = 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5; - uint256 constant p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF; - uint256 constant n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551; - - /// @dev Modular euclidean inverse of a number (mod p). - /// @param _x The number - /// @param _pp The modulus - /// @return q such that x*q = 1 (mod _pp) - function invMod(uint256 _x, uint256 _pp) internal pure returns (uint256) { - require(_x != 0 && _x != _pp && _pp != 0, "Invalid number"); - uint256 q = 0; - uint256 newT = 1; - uint256 r = _pp; - uint256 t; - while (_x != 0) { - t = r / _x; - (q, newT) = (newT, addmod(q, (_pp - mulmod(t, newT, _pp)), _pp)); - (r, _x) = (_x, r - t * _x); - } - - return q; - } - - /// @dev Modular exponentiation, b^e % _pp. - /// Source: https://github.com/androlo/standard-contracts/blob/master/contracts/src/crypto/ECCMath.sol - /// @param _base base - /// @param _exp exponent - /// @param _pp modulus - /// @return r such that r = b**e (mod _pp) - function expMod(uint256 _base, uint256 _exp, uint256 _pp) internal pure returns (uint256) { - require(_pp != 0, "Modulus is zero"); - - if (_base == 0) { - return 0; - } - if (_exp == 0) { - return 1; - } - - uint256 r = 1; - uint256 bit = U255_MAX_PLUS_1; - assembly { - for {} gt(bit, 0) {} { - r := mulmod(mulmod(r, r, _pp), exp(_base, iszero(iszero(and(_exp, bit)))), _pp) - r := mulmod(mulmod(r, r, _pp), exp(_base, iszero(iszero(and(_exp, div(bit, 2))))), _pp) - r := mulmod(mulmod(r, r, _pp), exp(_base, iszero(iszero(and(_exp, div(bit, 4))))), _pp) - r := mulmod(mulmod(r, r, _pp), exp(_base, iszero(iszero(and(_exp, div(bit, 8))))), _pp) - bit := div(bit, 16) - } - } - - return r; - } - - /// @dev Converts a point (x, y, z) expressed in Jacobian coordinates to affine coordinates (x', y', 1). - /// @param _x coordinate x - /// @param _y coordinate y - /// @param _z coordinate z - /// @param _pp the modulus - /// @return (x', y') affine coordinates - function toAffine(uint256 _x, uint256 _y, uint256 _z, uint256 _pp) internal pure returns (uint256, uint256) { - uint256 zInv = invMod(_z, _pp); - uint256 zInv2 = mulmod(zInv, zInv, _pp); - uint256 x2 = mulmod(_x, zInv2, _pp); - uint256 y2 = mulmod(_y, mulmod(zInv, zInv2, _pp), _pp); - - return (x2, y2); - } - - /// @dev Derives the y coordinate from a compressed-format point x [[SEC-1]](https://www.secg.org/SEC1-Ver-1.0.pdf). - /// @param _prefix parity byte (0x02 even, 0x03 odd) - /// @param _x coordinate x - /// @param _aa constant of curve - /// @param _bb constant of curve - /// @param _pp the modulus - /// @return y coordinate y - function deriveY(uint8 _prefix, uint256 _x, uint256 _aa, uint256 _bb, uint256 _pp) - internal - pure - returns (uint256) - { - require(_prefix == 0x02 || _prefix == 0x03, "Invalid compressed EC point prefix"); - - // x^3 + ax + b - uint256 y2 = addmod(mulmod(_x, mulmod(_x, _x, _pp), _pp), addmod(mulmod(_x, _aa, _pp), _bb, _pp), _pp); - y2 = expMod(y2, (_pp + 1) / 4, _pp); - // uint256 cmp = yBit ^ y_ & 1; - uint256 y = (y2 + _prefix) % 2 == 0 ? y2 : _pp - y2; - - return y; - } - - /// @dev Check whether point (x,y) is on curve defined by a, b, and _pp. - /// @param _x coordinate x of P1 - /// @param _y coordinate y of P1 - /// @param _aa constant of curve - /// @param _bb constant of curve - /// @param _pp the modulus - /// @return true if x,y in the curve, false else - function isOnCurve(uint256 _x, uint256 _y, uint256 _aa, uint256 _bb, uint256 _pp) internal pure returns (bool) { - if (0 == _x || _x >= _pp || 0 == _y || _y >= _pp) { - return false; - } - // y^2 - uint256 lhs = mulmod(_y, _y, _pp); - // x^3 - uint256 rhs = mulmod(mulmod(_x, _x, _pp), _x, _pp); - if (_aa != 0) { - // x^3 + a*x - rhs = addmod(rhs, mulmod(_x, _aa, _pp), _pp); - } - if (_bb != 0) { - // x^3 + a*x + b - rhs = addmod(rhs, _bb, _pp); - } - - return lhs == rhs; - } - - /// @dev Calculate inverse (x, -y) of point (x, y). - /// @param _x coordinate x of P1 - /// @param _y coordinate y of P1 - /// @param _pp the modulus - /// @return (x, -y) - function ecInv(uint256 _x, uint256 _y, uint256 _pp) internal pure returns (uint256, uint256) { - return (_x, (_pp - _y) % _pp); - } - - /// @dev Add two points (x1, y1) and (x2, y2) in affine coordinates. - /// @param _x1 coordinate x of P1 - /// @param _y1 coordinate y of P1 - /// @param _x2 coordinate x of P2 - /// @param _y2 coordinate y of P2 - /// @param _aa constant of the curve - /// @param _pp the modulus - /// @return (qx, qy) = P1+P2 in affine coordinates - function ecAdd(uint256 _x1, uint256 _y1, uint256 _x2, uint256 _y2, uint256 _aa, uint256 _pp) - internal - pure - returns (uint256, uint256) - { - uint256 x = 0; - uint256 y = 0; - uint256 z = 0; - - // Double if x1==x2 else add - if (_x1 == _x2) { - // y1 = -y2 mod p - if (addmod(_y1, _y2, _pp) == 0) { - return (0, 0); - } else { - // P1 = P2 - (x, y, z) = jacDouble(_x1, _y1, 1, _aa, _pp); - } - } else { - (x, y, z) = jacAdd(_x1, _y1, 1, _x2, _y2, 1, _pp); - } - // Get back to affine - return toAffine(x, y, z, _pp); - } - - /// @dev Substract two points (x1, y1) and (x2, y2) in affine coordinates. - /// @param _x1 coordinate x of P1 - /// @param _y1 coordinate y of P1 - /// @param _x2 coordinate x of P2 - /// @param _y2 coordinate y of P2 - /// @param _aa constant of the curve - /// @param _pp the modulus - /// @return (qx, qy) = P1-P2 in affine coordinates - function ecSub(uint256 _x1, uint256 _y1, uint256 _x2, uint256 _y2, uint256 _aa, uint256 _pp) - internal - pure - returns (uint256, uint256) - { - // invert square - (uint256 x, uint256 y) = ecInv(_x2, _y2, _pp); - // P1-square - return ecAdd(_x1, _y1, x, y, _aa, _pp); - } - - /// @dev Multiply point (x1, y1, z1) times d in affine coordinates. - /// @param _k scalar to multiply - /// @param _x coordinate x of P1 - /// @param _y coordinate y of P1 - /// @param _aa constant of the curve - /// @param _pp the modulus - /// @return (qx, qy) = d*P in affine coordinates - function ecMul(uint256 _k, uint256 _x, uint256 _y, uint256 _aa, uint256 _pp) - internal - pure - returns (uint256, uint256) - { - // Jacobian multiplication - (uint256 x1, uint256 y1, uint256 z1) = jacMul(_k, _x, _y, 1, _aa, _pp); - // Get back to affine - return toAffine(x1, y1, z1, _pp); - } - - /// @dev Adds two points (x1, y1, z1) and (x2 y2, z2). - /// @param _x1 coordinate x of P1 - /// @param _y1 coordinate y of P1 - /// @param _z1 coordinate z of P1 - /// @param _x2 coordinate x of square - /// @param _y2 coordinate y of square - /// @param _z2 coordinate z of square - /// @param _pp the modulus - /// @return (qx, qy, qz) P1+square in Jacobian - function jacAdd(uint256 _x1, uint256 _y1, uint256 _z1, uint256 _x2, uint256 _y2, uint256 _z2, uint256 _pp) - internal - pure - returns (uint256, uint256, uint256) - { - if (_x1 == 0 && _y1 == 0) { - return (_x2, _y2, _z2); - } - if (_x2 == 0 && _y2 == 0) { - return (_x1, _y1, _z1); - } - - // We follow the equations described in https://pdfs.semanticscholar.org/5c64/29952e08025a9649c2b0ba32518e9a7fb5c2.pdf Section 5 - uint256[4] memory zs; // z1^2, z1^3, z2^2, z2^3 - zs[0] = mulmod(_z1, _z1, _pp); - zs[1] = mulmod(_z1, zs[0], _pp); - zs[2] = mulmod(_z2, _z2, _pp); - zs[3] = mulmod(_z2, zs[2], _pp); - - // u1, s1, u2, s2 - zs = [mulmod(_x1, zs[2], _pp), mulmod(_y1, zs[3], _pp), mulmod(_x2, zs[0], _pp), mulmod(_y2, zs[1], _pp)]; - - // In case of zs[0] == zs[2] && zs[1] == zs[3], double function should be used - require(zs[0] != zs[2] || zs[1] != zs[3], "Use jacDouble function instead"); - - uint256[4] memory hr; - //h - hr[0] = addmod(zs[2], _pp - zs[0], _pp); - //r - hr[1] = addmod(zs[3], _pp - zs[1], _pp); - //h^2 - hr[2] = mulmod(hr[0], hr[0], _pp); - // h^3 - hr[3] = mulmod(hr[2], hr[0], _pp); - // qx = -h^3 -2u1h^2+r^2 - uint256 qx = addmod(mulmod(hr[1], hr[1], _pp), _pp - hr[3], _pp); - qx = addmod(qx, _pp - mulmod(2, mulmod(zs[0], hr[2], _pp), _pp), _pp); - // qy = -s1*z1*h^3+r(u1*h^2 -x^3) - uint256 qy = mulmod(hr[1], addmod(mulmod(zs[0], hr[2], _pp), _pp - qx, _pp), _pp); - qy = addmod(qy, _pp - mulmod(zs[1], hr[3], _pp), _pp); - // qz = h*z1*z2 - uint256 qz = mulmod(hr[0], mulmod(_z1, _z2, _pp), _pp); - return (qx, qy, qz); - } - - /// @dev Doubles a points (x, y, z). - /// @param _x coordinate x of P1 - /// @param _y coordinate y of P1 - /// @param _z coordinate z of P1 - /// @param _aa the a scalar in the curve equation - /// @param _pp the modulus - /// @return (qx, qy, qz) 2P in Jacobian - function jacDouble(uint256 _x, uint256 _y, uint256 _z, uint256 _aa, uint256 _pp) - internal - pure - returns (uint256, uint256, uint256) - { - if (_z == 0) { - return (_x, _y, _z); - } - - // We follow the equations described in https://pdfs.semanticscholar.org/5c64/29952e08025a9649c2b0ba32518e9a7fb5c2.pdf Section 5 - // Note: there is a bug in the paper regarding the m parameter, M=3*(x1^2)+a*(z1^4) - // x, y, z at this point represent the squares of _x, _y, _z - uint256 x = mulmod(_x, _x, _pp); //x1^2 - uint256 y = mulmod(_y, _y, _pp); //y1^2 - uint256 z = mulmod(_z, _z, _pp); //z1^2 - - // s - uint256 s = mulmod(4, mulmod(_x, y, _pp), _pp); - // m - uint256 m = addmod(mulmod(3, x, _pp), mulmod(_aa, mulmod(z, z, _pp), _pp), _pp); - - // x, y, z at this point will be reassigned and rather represent qx, qy, qz from the paper - // This allows to reduce the gas cost and stack footprint of the algorithm - // qx - x = addmod(mulmod(m, m, _pp), _pp - addmod(s, s, _pp), _pp); - // qy = -8*y1^4 + M(S-T) - y = addmod(mulmod(m, addmod(s, _pp - x, _pp), _pp), _pp - mulmod(8, mulmod(y, y, _pp), _pp), _pp); - // qz = 2*y1*z1 - z = mulmod(2, mulmod(_y, _z, _pp), _pp); - - return (x, y, z); - } - - /// @dev Multiply point (x, y, z) times d. - /// @param _d scalar to multiply - /// @param _x coordinate x of P1 - /// @param _y coordinate y of P1 - /// @param _z coordinate z of P1 - /// @param _aa constant of curve - /// @param _pp the modulus - /// @return (qx, qy, qz) d*P1 in Jacobian - function jacMul(uint256 _d, uint256 _x, uint256 _y, uint256 _z, uint256 _aa, uint256 _pp) - internal - pure - returns (uint256, uint256, uint256) - { - // Early return in case that `_d == 0` - if (_d == 0) { - return (_x, _y, _z); - } - - uint256 remaining = _d; - uint256 qx = 0; - uint256 qy = 0; - uint256 qz = 1; - - // Double and add algorithm - while (remaining != 0) { - if ((remaining & 1) != 0) { - (qx, qy, qz) = jacAdd(qx, qy, qz, _x, _y, _z, _pp); - } - remaining = remaining / 2; - (_x, _y, _z) = jacDouble(_x, _y, _z, _aa, _pp); - } - return (qx, qy, qz); - } - - function validateSignature(uint256 h, uint256 pgx, uint256 pgy, uint256 r, uint256 s) - internal - pure - returns (bool) - { - if (!isOnCurve(pgx, pgy, a, b, p)) { - return false; - } - uint256 s1 = invMod(s, n); - (uint256 _x, uint256 _y) = ecMul(mulmod(h, s1, n), gx, gy, a, p); - (uint256 _xx, uint256 _yy) = ecMul(mulmod(r, s1, n), pgx, pgy, a, p); - (uint256 Rx,) = ecAdd(_x, _y, _xx, _yy, a, p); - return Rx == r; - } -} diff --git a/src/utils/SigVerifyLib.sol b/src/utils/SigVerifyLib.sol index 287e292..7b81daf 100644 --- a/src/utils/SigVerifyLib.sol +++ b/src/utils/SigVerifyLib.sol @@ -1,9 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +import "p256-verifier/P256.sol"; + import "./interfaces/ISigVerifyLib.sol"; import "./RsaVerify.sol"; -import "./EllipticCurveLib.sol"; import "./BytesUtils.sol"; contract SigVerifyLib is ISigVerifyLib { @@ -83,7 +84,7 @@ contract SigVerifyLib is ISigVerifyLib { function verifyES256Signature(bytes memory tbs, bytes memory signature, bytes memory publicKey) public - pure + view returns (bool sigValid) { // Parse signature @@ -100,6 +101,6 @@ contract SigVerifyLib is ISigVerifyLib { uint256 gy = uint256(bytes32(publicKey.substring(32, 32))); // Verify signature - sigValid = EllipticCurveLib.validateSignature(uint256(sha256(tbs)), gx, gy, r, s); + sigValid = P256.verifySignature(sha256(tbs), r, s, gx, gy); } } diff --git a/src/utils/interfaces/ISigVerifyLib.sol b/src/utils/interfaces/ISigVerifyLib.sol index 5a2fa0c..8ff441a 100644 --- a/src/utils/interfaces/ISigVerifyLib.sol +++ b/src/utils/interfaces/ISigVerifyLib.sol @@ -57,6 +57,6 @@ interface ISigVerifyLib { function verifyES256Signature(bytes memory tbs, bytes memory signature, bytes memory publicKey) external - pure + view returns (bool sigValid); }