From 3c5963eab9861f5176877b566a96067792ef53df Mon Sep 17 00:00:00 2001 From: Uncle Fatso Date: Tue, 28 Oct 2025 20:03:26 +0300 Subject: [PATCH] update elliptic curve libraries Signed-off-by: Uncle Fatso --- src/libraries/ECMath.sol | 3 ++ src/libraries/ECMathProjective.sol | 81 +++++++++++++++++++----------- 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/src/libraries/ECMath.sol b/src/libraries/ECMath.sol index 8e7b41c..53a230a 100644 --- a/src/libraries/ECMath.sol +++ b/src/libraries/ECMath.sol @@ -10,6 +10,9 @@ pragma solidity ^0.8.0; ** @author Witnet Foundation */ library EllipticCurve { + // Pre-computed constant for 2 ** 128 - 1 + uint256 private constant U128_MAX = 340282366920938463463374607431768211455; + // Pre-computed constant for 2 ** 255 uint256 private constant U255_MAX_PLUS_1 = 57896044618658097711785492504343953926634992332820282019728792003956564819968; diff --git a/src/libraries/ECMathProjective.sol b/src/libraries/ECMathProjective.sol index 079f5d8..090da88 100644 --- a/src/libraries/ECMathProjective.sol +++ b/src/libraries/ECMathProjective.sol @@ -14,8 +14,6 @@ library EllipticCurveProjective { uint256 public constant P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F; uint256 public constant N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; - uint256 private constant B3 = 21; - function findMaxBitLength( uint256 k1, uint256 k2, @@ -72,9 +70,41 @@ library EllipticCurveProjective { } } + function mulAddProjectiveSingle( + uint256 x1, uint256 y1, uint256 k1 + ) internal pure returns (uint256 x2, uint256 y2, uint256 z2) { + // We implement the Straus-Shamir trick described in + // Trading Inversions for Multiplications in Elliptic Curve Cryptography. + // (https://eprint.iacr.org/2003/257.pdf Page 7). + + // TODO: handle edge case k1 == 0 + uint256 bits = findMaxBitLength(k1, 0, 0, 0); + + x2 = 0; + y2 = 1; + z2 = 0; + + for (; bits > 0;) { + unchecked { --bits; } + + (x2, y2, z2) = projectiveDouble(x2, y2, z2); + + uint8 mask; + assembly { + mask := and(shr(bits, k1), 1) + } + + if (mask != 0) { + (x2, y2, z2) = projectiveAddMixed( + x2, y2, z2, x1, y1 + ); + } + } + } + function mulAddProjectivePair( - uint256 x1, uint256 y1, uint256 k1, - uint256 x2, uint256 y2, uint256 k2 + uint256 x1, uint256 y1, uint256 z1, uint256 k1, + uint256 x2, uint256 y2, uint256 z2, uint256 k2 ) internal pure returns (uint256 x3, uint256 y3, uint256 z3) { // We implement the Straus-Shamir trick described in // Trading Inversions for Multiplications in Elliptic Curve Cryptography. @@ -86,10 +116,10 @@ library EllipticCurveProjective { uint256[4] memory precomputedYs; uint256[4] memory precomputedZs; - precomputedXs[1] = x2; precomputedYs[1] = y2; precomputedZs[1] = 1; // 01: P2 - precomputedXs[2] = x1; precomputedYs[2] = y1; precomputedZs[2] = 1; // 10: P1 + precomputedXs[1] = x2; precomputedYs[1] = y2; precomputedZs[1] = z2; // 01: P2 + precomputedXs[2] = x1; precomputedYs[2] = y1; precomputedZs[2] = z1; // 10: P1 - (x3, y3, z3) = projectiveAddMixed(x1, y1, 1, x2, y2); // 11: P1+P2 + (x3, y3, z3) = projectiveAdd(x1, y1, z1, x2, y2, z2); // 11: P1+P2 precomputedXs[3] = x3; precomputedYs[3] = y3; precomputedZs[3] = z3; @@ -111,18 +141,10 @@ library EllipticCurveProjective { ) } - if (mask == 0) { - continue; - } - - if (mask == 3) { + if (mask != 0) { (x3, y3, z3) = projectiveAdd( x3, y3, z3, precomputedXs[mask], precomputedYs[mask], precomputedZs[mask] ); - } else { - (x3, y3, z3) = projectiveAddMixed( - x3, y3, z3, precomputedXs[mask], precomputedYs[mask] - ); } } } @@ -204,7 +226,7 @@ library EllipticCurveProjective { uint256 x2, uint256 y2, uint256 k2, uint256 x3, uint256 y3, uint256 k3, uint256 x4, uint256 y4, uint256 k4 - ) internal pure returns (uint256 x5, uint256 y5, uint256 z5) { + ) internal pure returns (uint256, uint256, uint256) { // We implement the Straus-Shamir trick described in // Trading Inversions for Multiplications in Elliptic Curve Cryptography. // (https://eprint.iacr.org/2003/257.pdf Page 7). @@ -267,14 +289,14 @@ library EllipticCurveProjective { y4 ); // 1111: P4+P3+P2+P1 - x5 = 0; - y5 = 1; - z5 = 0; + x1 = 0; + y1 = 1; + uint256 z1 = 0; for (; bits > 0;) { unchecked { --bits; } - (x5, y5, z5) = projectiveDouble(x5, y5, z5); + (x1, y1, z1) = projectiveDouble(x1, y1, z1); uint8 mask; uint16 bitmask; @@ -300,15 +322,16 @@ library EllipticCurveProjective { } if (bitmask == 1) { - (x5, y5, z5) = projectiveAddMixed( - x5, y5, z5, precomputedXs[mask], precomputedYs[mask] + (x1, y1, z1) = projectiveAddMixed( + x1, y1, z1, precomputedXs[mask], precomputedYs[mask] ); } else { - (x5, y5, z5) = projectiveAdd( - x5, y5, z5, precomputedXs[mask], precomputedYs[mask], precomputedZs[mask] + (x1, y1, z1) = projectiveAdd( + x1, y1, z1, precomputedXs[mask], precomputedYs[mask], precomputedZs[mask] ); } } + return (x1, y1, z1); } function projectiveAddMixed( @@ -335,10 +358,10 @@ library EllipticCurveProjective { Y3 = mulmod(X2, Z1, P); // 8. Y3 ← X2 · Z1 => (X2·Z1) Y3 = addmod(Y3, X1, P); // 9. Y3 ← Y3 + X1 => (X2·Z1 + X1) t0 = mulmod(3, t0, P); // 10. t0 ← X3 + t0 => (3·(X1·X2)) - uint256 t2 = mulmod(B3, Z1, P); // 11. t2 ← b3 · Z1 => (b3·Z1) + uint256 t2 = mulmod(21, Z1, P); // 11. t2 ← b3 · Z1 => (b3·Z1) Z3 = addmod(t1, t2, P); // 12. Z3 ← t1 + t2 => (Y1·Y2 + b·3·Z1) t1 = addmod(t1, P - t2, P); // 13. t1 ← t1 − t2 => (Y1·Y2 - b·3·Z1) - Y3 = mulmod(B3, Y3, P); // 14. Y3 ← b3 · Y3 => 3·b·(X2·Z1 + X1) + Y3 = mulmod(21, Y3, P); // 14. Y3 ← b3 · Y3 => 3·b·(X2·Z1 + X1) X3 = mulmod(t4, Y3, P); // 15. X3 ← t4 · Y3 => (Y2·Z1 + Y1)·b·3·(X2·Z1 + X1) t2 = mulmod(t3, t1, P); // 16. t2 ← t3 · t1 => ((X2·Y1 + X1·Y2)·(Y1·Y2 - b3·Z1)) X3 = addmod(t2, P - X3, P); // 17. X3 ← t2 − X3 => ((X2·Y1 + X1·Y2)·(Y1·Y2 - b3·Z1) - 3·B·(Y2·Z1 + Y1)·(X2·Z1 + X1)) @@ -385,10 +408,10 @@ library EllipticCurveProjective { Y3 = addmod(X3, P - Y3, P); // 18. Y3 ← X3 - Y3 => ((X1 + Z1)·(X2 + Z2) - X1·X2 - Z1·Z2) X3 = addmod(t0, t0, P); // 19. X3 ← t0 + t0 => (2·X1·X2) t0 = addmod(X3, t0, P); // 20. t0 ← X3 + t0 => (3·X1·X2) - t2 = mulmod(B3, t2, P); // 21. t2 ← B3 · t2 => 3b · Z1·Z2 + t2 = mulmod(21, t2, P); // 21. t2 ← B3 · t2 => 3b · Z1·Z2 Z3 = addmod(t1, t2, P); // 22. Z3 ← t1 + t2 => Y1·Y2 + 3·b·Z1·Z2 t1 = addmod(t1, P - t2, P); // 23. t1 ← t1 - t2 => Y1·Y2 - 3·b·Z1·Z2 - Y3 = mulmod(B3, Y3, P); // 24. Y3 ← B3 · Y3 => 3b · ((X1+Z1)(X2+Z2) - X1·X2 - Z1·Z2) + Y3 = mulmod(21, Y3, P); // 24. Y3 ← B3 · Y3 => 3b · ((X1+Z1)(X2+Z2) - X1·X2 - Z1·Z2) X3 = mulmod(t4, Y3, P); // 25. X3 ← t4 · Y3 => 3b·((Y1+Z1)(Y2+Z2)-Y1·Y2-Z1·Z2) · ((X1+Z1)(X2+Z2)-X1·X2-Z1·Z2) t2 = mulmod(t3, t1, P); // 26. t2 ← t3 · t1 => ((X1+Y1)(X2+Y2)-X1·X2-Y1·Y2) · (Y1·Y2 - 3·b·Z1·Z2) X3 = addmod(t2, P - X3, P); // 27. X3 ← t2 - X3 => (X1Y2+X2Y1)(Y1·Y2-3·b·Z1·Z2) - 3b(Y1·Z2+Y2·Z1)(X1·Z2+X2·Z1)