Skip to content

Commit

Permalink
format with tests passing
Browse files Browse the repository at this point in the history
  • Loading branch information
0xBigBoss committed Jul 11, 2024
1 parent d0f590d commit a61d00e
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 574 deletions.
36 changes: 15 additions & 21 deletions src/P256.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand Down
148 changes: 63 additions & 85 deletions src/P256Verifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -87,34 +80,23 @@ 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;
}

/**
* @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;
}

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
Expand Down Expand Up @@ -148,15 +130,15 @@ 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;
}

// 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) {
Expand All @@ -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);
Expand All @@ -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:
Expand All @@ -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));

Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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
Expand All @@ -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();
}
Expand All @@ -311,41 +284,47 @@ 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
zzz3 = mulmod(comp_W, zzz1, p); // W*ZZZ1
}

/**
* @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
zz3 = mulmod(comp_U, comp_U, p); // V = U^2 = zz3
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
}
Expand All @@ -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);
}
Expand Down Expand Up @@ -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) {
Expand Down
Loading

0 comments on commit a61d00e

Please sign in to comment.