From b6d004c514c640fe66846af9cd2a0b871e24ec90 Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Wed, 5 Jun 2024 12:06:12 +0100 Subject: [PATCH 1/4] Add Secp curve library and function to derive address from priv key --- README.md | 19 +- src/crypto/EllipticCurve.sol | 327 +++++++++++++++++++++++++++++++++++ src/crypto/Secp256k1.sol | 86 +++++++++ test/crypto/Secp256k1.t.sol | 18 ++ 4 files changed, 449 insertions(+), 1 deletion(-) create mode 100644 src/crypto/EllipticCurve.sol create mode 100644 src/crypto/Secp256k1.sol create mode 100644 test/crypto/Secp256k1.t.sol diff --git a/README.md b/README.md index f27ce96..79efd27 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,22 @@ contract Example { } ``` +## crypto/Secp256k1.sol + +Helper library to interact with the `secp256k1` curve. + +```solidity +import "src/crypto/Secp256k1.sol"; + +contract Example { + function example() public { + // string memory privateKey = Suave.privateKeyGen(Suave.CryptoSignature.SECP256); + string memory privateKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"; + address found = Secp256k1.deriveAddress(privateKey); + } +} +``` + ## Forge integration In order to use `forge`, you need to have a running `Suave` node and the `suave` binary in your path. @@ -210,13 +226,14 @@ Use the `setConfidentialInputs` function to set the confidential inputs during t import "forge-std/Test.sol"; import "src/Test.sol"; import "src/suavelib/Suave.sol"; +import "src/Context.sol"; contract TestForge is Test, SuaveEnabled { function testConfidentialInputs() public { bytes memory input = hex"abcd"; ctx.setConfidentialInputs(input); - bytes memory found2 = Suave.confidentialInputs(); + bytes memory found2 = Context.confidentialInputs(); assertEq0(input, found2); } } diff --git a/src/crypto/EllipticCurve.sol b/src/crypto/EllipticCurve.sol new file mode 100644 index 0000000..6bb3926 --- /dev/null +++ b/src/crypto/EllipticCurve.sol @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @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 EllipticCurve { + // Pre-computed constant for 2 ** 255 + uint256 private constant U255_MAX_PLUS_1 = + 57896044618658097711785492504343953926634992332820282019728792003956564819968; + + /// @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, "EllipticCurve: 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, "EllipticCurve:innvalid 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); + } +} diff --git a/src/crypto/Secp256k1.sol b/src/crypto/Secp256k1.sol new file mode 100644 index 0000000..8cc7d82 --- /dev/null +++ b/src/crypto/Secp256k1.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +import "./EllipticCurve.sol"; +import "forge-std/console2.sol"; +import "../utils/HexStrings.sol"; + +/// @notice Secp256k1 is a library with utilities to work with the secp256k1 curve. +library Secp256k1 { + uint256 internal constant GX = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798; + uint256 internal constant GY = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8; + uint256 internal constant AA = 0; + uint256 internal constant BB = 7; + uint256 internal constant PP = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F; + uint256 internal constant NN = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; + + function derivePubKey(uint256 privKey) internal pure returns (uint256 qx, uint256 qy) { + (qx, qy) = EllipticCurve.ecMul(privKey, GX, GY, AA, PP); + } + + function verify(address signer, bytes32 digest, bytes memory sig) internal pure returns (bool) { + uint8 v; + bytes32 r; + bytes32 s; + assembly { + v := mload(add(sig, 1)) + r := mload(add(sig, 33)) + s := mload(add(sig, 65)) + } + return signer == ecrecover(digest, v, r, s); + } + + function sign(uint256 privateKey, bytes32 digest) internal pure returns (bytes memory) { + // Step 0: Deterministic choice of k + // See RFC 6979 + // TODO: replace this with wiser choice + uint256 k = uint256( + sha256( + abi.encodePacked( + uint256(0x0101010101010101010101010101010101010101010101010101010101010101), uint8(0), digest + ) + ) + ); + + // Step 1: Ephemeral Key Pair Generation + (uint256 x1, uint256 y1) = EllipticCurve.ecMul(k, GX, GY, AA, PP); // Ephemeral internal key + + // Step 2: Calculate r and s + uint256 r = x1 % PP; + require(r != 0, "Invalid r value"); + + uint256 k_inv = EllipticCurve.invMod(k, NN); // Modular inverse of k + uint256 hashInt = uint256(digest); + uint256 s = mulmod(k_inv, addmod(hashInt, mulmod(r, privateKey, NN), NN), NN); + require(s != 0, "Invalid s value"); + + // Step 3: Determine recovery id (v) + uint8 v; + uint256 y_parity = y1 % 2; + + // Typically, 27 is added to v for legacy reasons, and an additional 2 if the chain ID is included + // Chain ID is omitted in this example + if (y_parity == 0) { + v = 27; + } else { + v = 28; + } + return abi.encodePacked(v, bytes32(r), bytes32(s)); + } + + function deriveAddress(uint256 privKey) internal pure returns (address) { + (uint256 qx, uint256 qy) = derivePubKey(privKey); + bytes memory ser = bytes.concat(bytes32(qx), bytes32(qy)); + return address(uint160(uint256(keccak256(ser)))); + } + + // @notice deriveAddress returns the address corresponding to the private key + // @param privKey is the private key + // @return address is the address derived from the private key + function deriveAddress(string memory privKey) internal pure returns (address) { + bytes memory privKeyBytes = HexStrings.fromHexString(privKey); + require(privKeyBytes.length == 32, "Invalid private key length"); + + return deriveAddress(uint256(bytes32(privKeyBytes))); + } +} diff --git a/test/crypto/Secp256k1.t.sol b/test/crypto/Secp256k1.t.sol new file mode 100644 index 0000000..17af2ad --- /dev/null +++ b/test/crypto/Secp256k1.t.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "src/Test.sol"; +import "src/suavelib/Suave.sol"; +import "src/Context.sol"; +import "src/crypto/Secp256k1.sol"; + +contract TestSecp256K1 is Test, SuaveEnabled { + function testRecoverAddress() public { + string memory signingKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"; + + address expected = 0x71562b71999873DB5b286dF957af199Ec94617F7; + address found = Secp256k1.deriveAddress(signingKey); + assert(expected == found); + } +} From ffbdaed65f9135dcc7e01d373fbaa5c67d39f8f8 Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Thu, 6 Jun 2024 10:33:30 +0100 Subject: [PATCH 2/4] Use deps --- .gitmodules | 3 +++ lib/elliptic-curve-solidity | 1 + src/crypto/Secp256k1.sol | 53 +------------------------------------ 3 files changed, 5 insertions(+), 52 deletions(-) create mode 160000 lib/elliptic-curve-solidity diff --git a/.gitmodules b/.gitmodules index dfdd2af..9e73484 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "lib/Solidity-RLP"] path = lib/Solidity-RLP url = https://github.com/hamdiallam/Solidity-RLP +[submodule "lib/elliptic-curve-solidity"] + path = lib/elliptic-curve-solidity + url = https://github.com/witnet/elliptic-curve-solidity diff --git a/lib/elliptic-curve-solidity b/lib/elliptic-curve-solidity new file mode 160000 index 0000000..3475478 --- /dev/null +++ b/lib/elliptic-curve-solidity @@ -0,0 +1 @@ +Subproject commit 347547890840fd501809dfe0b855206407136ec0 diff --git a/src/crypto/Secp256k1.sol b/src/crypto/Secp256k1.sol index 8cc7d82..6b27593 100644 --- a/src/crypto/Secp256k1.sol +++ b/src/crypto/Secp256k1.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.0; -import "./EllipticCurve.sol"; -import "forge-std/console2.sol"; +import "elliptic-curve-solidity/contracts/EllipticCurve.sol"; import "../utils/HexStrings.sol"; /// @notice Secp256k1 is a library with utilities to work with the secp256k1 curve. @@ -18,56 +17,6 @@ library Secp256k1 { (qx, qy) = EllipticCurve.ecMul(privKey, GX, GY, AA, PP); } - function verify(address signer, bytes32 digest, bytes memory sig) internal pure returns (bool) { - uint8 v; - bytes32 r; - bytes32 s; - assembly { - v := mload(add(sig, 1)) - r := mload(add(sig, 33)) - s := mload(add(sig, 65)) - } - return signer == ecrecover(digest, v, r, s); - } - - function sign(uint256 privateKey, bytes32 digest) internal pure returns (bytes memory) { - // Step 0: Deterministic choice of k - // See RFC 6979 - // TODO: replace this with wiser choice - uint256 k = uint256( - sha256( - abi.encodePacked( - uint256(0x0101010101010101010101010101010101010101010101010101010101010101), uint8(0), digest - ) - ) - ); - - // Step 1: Ephemeral Key Pair Generation - (uint256 x1, uint256 y1) = EllipticCurve.ecMul(k, GX, GY, AA, PP); // Ephemeral internal key - - // Step 2: Calculate r and s - uint256 r = x1 % PP; - require(r != 0, "Invalid r value"); - - uint256 k_inv = EllipticCurve.invMod(k, NN); // Modular inverse of k - uint256 hashInt = uint256(digest); - uint256 s = mulmod(k_inv, addmod(hashInt, mulmod(r, privateKey, NN), NN), NN); - require(s != 0, "Invalid s value"); - - // Step 3: Determine recovery id (v) - uint8 v; - uint256 y_parity = y1 % 2; - - // Typically, 27 is added to v for legacy reasons, and an additional 2 if the chain ID is included - // Chain ID is omitted in this example - if (y_parity == 0) { - v = 27; - } else { - v = 28; - } - return abi.encodePacked(v, bytes32(r), bytes32(s)); - } - function deriveAddress(uint256 privKey) internal pure returns (address) { (uint256 qx, uint256 qy) = derivePubKey(privKey); bytes memory ser = bytes.concat(bytes32(qx), bytes32(qy)); From 37fa5e608bbf3c5dd7e42eca3681426729b0c892 Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Fri, 7 Jun 2024 10:39:07 +0100 Subject: [PATCH 3/4] Remove unused file --- src/crypto/EllipticCurve.sol | 327 ----------------------------------- 1 file changed, 327 deletions(-) delete mode 100644 src/crypto/EllipticCurve.sol diff --git a/src/crypto/EllipticCurve.sol b/src/crypto/EllipticCurve.sol deleted file mode 100644 index 6bb3926..0000000 --- a/src/crypto/EllipticCurve.sol +++ /dev/null @@ -1,327 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -/** - * @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 EllipticCurve { - // Pre-computed constant for 2 ** 255 - uint256 private constant U255_MAX_PLUS_1 = - 57896044618658097711785492504343953926634992332820282019728792003956564819968; - - /// @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, "EllipticCurve: 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, "EllipticCurve:innvalid 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); - } -} From 6c17ac0c3340d0a760a1d9f9d59e662e4f870d7c Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Fri, 7 Jun 2024 15:45:45 +0100 Subject: [PATCH 4/4] Fix std checker --- tools/stdchecker/remappings.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/stdchecker/remappings.txt b/tools/stdchecker/remappings.txt index 94c25ce..eeca152 100644 --- a/tools/stdchecker/remappings.txt +++ b/tools/stdchecker/remappings.txt @@ -3,3 +3,4 @@ Solidity-RLP/=../../lib/Solidity-RLP/contracts/ ds-test/=../../lib/forge-std/lib/ds-test/src/ forge-std/=../../lib/forge-std/src/ solady/=../../lib/solady/ +elliptic-curve-solidity/=../../lib/elliptic-curve-solidity \ No newline at end of file