pragma solidity ^0.8.4; /** * @title EllipticCurve * * @author Tilman Drerup; * * @notice Implements elliptic curve math; Parametrized for SECP256R1. * * Includes components of code by Andreas Olofsson, Alexander Vlasov * (https://github.com/BANKEX/CurveArithmetics), and Avi Asayag * (https://github.com/orbs-network/elliptic-curve-solidity) * * Source: https://github.com/tdrerup/elliptic-curve-solidity * * @dev NOTE: To disambiguate public keys when verifying signatures, activate * condition 'rs[1] > lowSmax' in validateSignature(). */ contract EllipticCurve { // Set parameters for curve. uint256 constant a = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC; uint256 constant b = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B; uint256 constant gx = 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296; uint256 constant gy = 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5; uint256 constant p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF; uint256 constant n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551; uint256 constant lowSmax = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0; /** * @dev Inverse of u in the field of modulo m. */ function inverseMod(uint256 u, uint256 m) internal pure returns (uint256) { unchecked { if (u == 0 || u == m || m == 0) return 0; if (u > m) u = u % m; int256 t1; int256 t2 = 1; uint256 r1 = m; uint256 r2 = u; uint256 q; while (r2 != 0) { q = r1 / r2; (t1, t2, r1, r2) = (t2, t1 - int256(q) * t2, r2, r1 - q * r2); } if (t1 < 0) return (m - uint256(-t1)); return uint256(t1); } } /** * @dev Transform affine coordinates into projective coordinates. */ function toProjectivePoint(uint256 x0, uint256 y0) internal pure returns (uint256[3] memory P) { P[2] = addmod(0, 1, p); P[0] = mulmod(x0, P[2], p); P[1] = mulmod(y0, P[2], p); } /** * @dev Add two points in affine coordinates and return projective point. */ function addAndReturnProjectivePoint( uint256 x1, uint256 y1, uint256 x2, uint256 y2 ) internal pure returns (uint256[3] memory P) { uint256 x; uint256 y; (x, y) = add(x1, y1, x2, y2); P = toProjectivePoint(x, y); } /** * @dev Transform from projective to affine coordinates. */ function toAffinePoint( uint256 x0, uint256 y0, uint256 z0 ) internal pure returns (uint256 x1, uint256 y1) { uint256 z0Inv; z0Inv = inverseMod(z0, p); x1 = mulmod(x0, z0Inv, p); y1 = mulmod(y0, z0Inv, p); } /** * @dev Return the zero curve in projective coordinates. */ function zeroProj() internal pure returns ( uint256 x, uint256 y, uint256 z ) { return (0, 1, 0); } /** * @dev Return the zero curve in affine coordinates. */ function zeroAffine() internal pure returns (uint256 x, uint256 y) { return (0, 0); } /** * @dev Check if the curve is the zero curve. */ function isZeroCurve(uint256 x0, uint256 y0) internal pure returns (bool isZero) { if (x0 == 0 && y0 == 0) { return true; } return false; } /** * @dev Check if a point in affine coordinates is on the curve. */ function isOnCurve(uint256 x, uint256 y) internal pure returns (bool) { if (0 == x || x == p || 0 == y || y == p) { return false; } uint256 LHS = mulmod(y, y, p); // y^2 uint256 RHS = mulmod(mulmod(x, x, p), x, p); // x^3 if (a != 0) { RHS = addmod(RHS, mulmod(x, a, p), p); // x^3 + a*x } if (b != 0) { RHS = addmod(RHS, b, p); // x^3 + a*x + b } return LHS == RHS; } /** * @dev Double an elliptic curve point in projective coordinates. See * https://www.nayuki.io/page/elliptic-curve-point-addition-in-projective-coordinates */ function twiceProj( uint256 x0, uint256 y0, uint256 z0 ) internal pure returns ( uint256 x1, uint256 y1, uint256 z1 ) { uint256 t; uint256 u; uint256 v; uint256 w; if (isZeroCurve(x0, y0)) { return zeroProj(); } u = mulmod(y0, z0, p); u = mulmod(u, 2, p); v = mulmod(u, x0, p); v = mulmod(v, y0, p); v = mulmod(v, 2, p); x0 = mulmod(x0, x0, p); t = mulmod(x0, 3, p); z0 = mulmod(z0, z0, p); z0 = mulmod(z0, a, p); t = addmod(t, z0, p); w = mulmod(t, t, p); x0 = mulmod(2, v, p); w = addmod(w, p - x0, p); x0 = addmod(v, p - w, p); x0 = mulmod(t, x0, p); y0 = mulmod(y0, u, p); y0 = mulmod(y0, y0, p); y0 = mulmod(2, y0, p); y1 = addmod(x0, p - y0, p); x1 = mulmod(u, w, p); z1 = mulmod(u, u, p); z1 = mulmod(z1, u, p); } /** * @dev Add two elliptic curve points in projective coordinates. See * https://www.nayuki.io/page/elliptic-curve-point-addition-in-projective-coordinates */ function addProj( uint256 x0, uint256 y0, uint256 z0, uint256 x1, uint256 y1, uint256 z1 ) internal pure returns ( uint256 x2, uint256 y2, uint256 z2 ) { uint256 t0; uint256 t1; uint256 u0; uint256 u1; if (isZeroCurve(x0, y0)) { return (x1, y1, z1); } else if (isZeroCurve(x1, y1)) { return (x0, y0, z0); } t0 = mulmod(y0, z1, p); t1 = mulmod(y1, z0, p); u0 = mulmod(x0, z1, p); u1 = mulmod(x1, z0, p); if (u0 == u1) { if (t0 == t1) { return twiceProj(x0, y0, z0); } else { return zeroProj(); } } (x2, y2, z2) = addProj2(mulmod(z0, z1, p), u0, u1, t1, t0); } /** * @dev Helper function that splits addProj to avoid too many local variables. */ function addProj2( uint256 v, uint256 u0, uint256 u1, uint256 t1, uint256 t0 ) private pure returns ( uint256 x2, uint256 y2, uint256 z2 ) { uint256 u; uint256 u2; uint256 u3; uint256 w; uint256 t; t = addmod(t0, p - t1, p); u = addmod(u0, p - u1, p); u2 = mulmod(u, u, p); w = mulmod(t, t, p); w = mulmod(w, v, p); u1 = addmod(u1, u0, p); u1 = mulmod(u1, u2, p); w = addmod(w, p - u1, p); x2 = mulmod(u, w, p); u3 = mulmod(u2, u, p); u0 = mulmod(u0, u2, p); u0 = addmod(u0, p - w, p); t = mulmod(t, u0, p); t0 = mulmod(t0, u3, p); y2 = addmod(t, p - t0, p); z2 = mulmod(u3, v, p); } /** * @dev Add two elliptic curve points in affine coordinates. */ function add( uint256 x0, uint256 y0, uint256 x1, uint256 y1 ) internal pure returns (uint256, uint256) { uint256 z0; (x0, y0, z0) = addProj(x0, y0, 1, x1, y1, 1); return toAffinePoint(x0, y0, z0); } /** * @dev Double an elliptic curve point in affine coordinates. */ function twice(uint256 x0, uint256 y0) internal pure returns (uint256, uint256) { uint256 z0; (x0, y0, z0) = twiceProj(x0, y0, 1); return toAffinePoint(x0, y0, z0); } /** * @dev Multiply an elliptic curve point by a 2 power base (i.e., (2^exp)*P)). */ function multiplyPowerBase2( uint256 x0, uint256 y0, uint256 exp ) internal pure returns (uint256, uint256) { uint256 base2X = x0; uint256 base2Y = y0; uint256 base2Z = 1; for (uint256 i = 0; i < exp; i++) { (base2X, base2Y, base2Z) = twiceProj(base2X, base2Y, base2Z); } return toAffinePoint(base2X, base2Y, base2Z); } /** * @dev Multiply an elliptic curve point by a scalar. */ function multiplyScalar( uint256 x0, uint256 y0, uint256 scalar ) internal pure returns (uint256 x1, uint256 y1) { if (scalar == 0) { return zeroAffine(); } else if (scalar == 1) { return (x0, y0); } else if (scalar == 2) { return twice(x0, y0); } uint256 base2X = x0; uint256 base2Y = y0; uint256 base2Z = 1; uint256 z1 = 1; x1 = x0; y1 = y0; if (scalar % 2 == 0) { x1 = y1 = 0; } scalar = scalar >> 1; while (scalar > 0) { (base2X, base2Y, base2Z) = twiceProj(base2X, base2Y, base2Z); if (scalar % 2 == 1) { (x1, y1, z1) = addProj(base2X, base2Y, base2Z, x1, y1, z1); } scalar = scalar >> 1; } return toAffinePoint(x1, y1, z1); } /** * @dev Multiply the curve's generator point by a scalar. */ function multipleGeneratorByScalar(uint256 scalar) internal pure returns (uint256, uint256) { return multiplyScalar(gx, gy, scalar); } /** * @dev Validate combination of message, signature, and public key. */ function validateSignature( bytes32 message, uint256[2] memory rs, uint256[2] memory Q ) internal pure returns (bool) { // To disambiguate between public key solutions, include comment below. if (rs[0] == 0 || rs[0] >= n || rs[1] == 0) { // || rs[1] > lowSmax) return false; } if (!isOnCurve(Q[0], Q[1])) { return false; } uint256 x1; uint256 x2; uint256 y1; uint256 y2; uint256 sInv = inverseMod(rs[1], n); (x1, y1) = multiplyScalar(gx, gy, mulmod(uint256(message), sInv, n)); (x2, y2) = multiplyScalar(Q[0], Q[1], mulmod(rs[0], sInv, n)); uint256[3] memory P = addAndReturnProjectivePoint(x1, y1, x2, y2); if (P[2] == 0) { return false; } uint256 Px = inverseMod(P[2], p); Px = mulmod(P[0], mulmod(Px, Px, p), p); return Px % n == rs[0]; } }