From 2e70308824e5270d88742c97f815b540622df3a8 Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Wed, 22 Feb 2023 15:33:48 +0100 Subject: [PATCH 01/25] implement windowed shamir --- contracts/EllipticCurve.sol | 71 +++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/contracts/EllipticCurve.sol b/contracts/EllipticCurve.sol index 0b5f548..16cbae9 100644 --- a/contracts/EllipticCurve.sol +++ b/contracts/EllipticCurve.sol @@ -370,6 +370,66 @@ library EllipticCurve { return toAffinePoint(x1, y1, z1); } + /** + * @dev Double base multiplication using windowing and Shamir's trick + */ + function ec_mulmuladd( + uint Gx0, + uint Gy0, + uint Qx0, + uint Qy0, + uint scalar_u, + uint scalar_v + ) internal pure returns (uint[3] memory R) { + + /*1. Precomputation steps: 2 bits window+shamir */ + /* precompute all aG+bQ in [0..3][0..3]*/ + uint [3][16] memory Window; + Window[1][0]=Gx0; + Window[1][1]=Gy0; + Window[1][2]=1; + + Window[2][0]=Qx0; + Window[2][1]=Qy0; + Window[2][2]=1; + + (Window[3][0], Window[3][1], Window[3][2])=addProj(Gx0, Gy0, 1, Qx0, Gy0, 1); //3:G+Q + (Window[4][0], Window[4][1], Window[4][2])=twiceProj(Gx0, Gy0, 1);//4:2G + (Window[5][0], Window[5][1], Window[5][2])=addProj(Gx0, Gy0, 1, Window[4][0], Window[4][1], Window[4][2]); //5:3G + (Window[6][0], Window[6][1], Window[6][2])=addProj(Qx0, Qy0, 1, Window[4][0], Window[4][1], Window[4][2]); //6:2G+Q + (Window[7][0], Window[7][1], Window[7][2])=addProj(Qx0, Qy0, 1, Window[5][0], Window[5][1], Window[5][2]); //7:3G+Q + (Window[8][0], Window[8][1], Window[8][2])=twiceProj(Window[4][0], Window[4][1], Window[4][2]);//8:4G + + (Window[9][0], Window[9][1], Window[9][2])=addProj(Gx0, Gy0, 1, Window[8][0], Window[8][1], Window[8][2]);//9:2Q+G + (Window[10][0], Window[10][1], Window[10][2])=addProj(Qx0, Qy0, 1, Window[8][0], Window[8][1], Window[8][2]);//10:3Q + (Window[11][0], Window[11][1], Window[11][2])=addProj(Gx0, Gy0, 1, Window[10][0], Window[10][1], Window[10][2]); //11:3Q+G + (Window[12][0], Window[12][1], Window[12][2])=addProj(Window[8][0], Window[8][1], Window[8][2] , Window[4][0], Window[4][1], Window[4][2]); //12:2Q+2G + (Window[13][0], Window[13][1], Window[13][2])=addProj(Window[8][0], Window[8][1], Window[8][2] , Window[5][0], Window[5][1], Window[5][2]); //13:2Q+3G + (Window[14][0], Window[14][1], Window[14][2])=addProj(Window[10][0], Window[10][1], Window[10][2], Window[4][0], Window[4][1], Window[4][2]); //14:3Q+2G + (Window[15][0], Window[15][1], Window[15][2])=addProj(Window[10][0], Window[10][1], Window[10][2], Window[5][0], Window[5][1], Window[5][2]); //15:3Q+3G + + + //initialize R with infinity point + R[0]=0; + R[1]=0; + R[2]=0; + uint quadbit=1; + //2. loop over scalars from MSB to LSB: + for(uint8 i=0;i<128;i++) + { + uint8 rshift=255-2*i; + (R[0],R[1],R[2])=twiceProj(R[0],R[1],R[2]);//double + (R[0],R[1],R[2])=twiceProj(R[0],R[1],R[2]);//double + + //compute quadruple (8*v1 +4*u1+ 2*v0 + u0) + quadbit=8*((scalar_u>>rshift)&1)+ 4*((scalar_u>>rshift)&1)+ 2*((scalar_v>>(rshift-1))&1)+ ((scalar_u >>(rshift-1))&1); + (R[0],R[1],R[2])=addProj(R[0],R[1],R[2], Window[quadbit][0], Window[quadbit][1], Window[quadbit][2]); + } + + return R; + } + + /** * @dev Multiply the curve's generator point by a scalar. */ @@ -402,9 +462,20 @@ library EllipticCurve { uint y2; uint sInv = inverseMod(rs[1], n); + + // without Optim + /* (x1, y1) = multiplyScalar(gx, gy, mulmod(uint(message), sInv, n)); (x2, y2) = multiplyScalar(Q[0], Q[1], mulmod(rs[0], sInv, n)); uint[3] memory P = addAndReturnProjectivePoint(x1, y1, x2, y2); + */ + + uint scalar_v=mulmod(uint(message), sInv, n); + uint scalar_u= mulmod(rs[0], sInv, n); + uint[3] memory P = ec_mulmuladd(gx, gy, Q[0], Q[1],scalar_v ,scalar_u ); + + + if (P[2] == 0) { return false; From ff2fb9ae93e22ef3021241abe77c5310091b6998 Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Fri, 24 Feb 2023 10:09:27 +0100 Subject: [PATCH 02/25] Implementing windowed shamir's trick - multiplication using windowing and Shamir's trick, imported from https://github.com/rdubois-crypto/MyCairoPlayground/blob/main/Cairo/cairo_secp256r1/src/ec_mulmuladd_secp256r1.cairo - correcting incorrect Neutral Point representation (use of Affine while Projective is required in some places) - removing incorrect point normalization at end of file (looks like an old jacobian implementation) Note : - Shamir's trick:https://crypto.stackexchange.com/questions/99975/strauss-shamir-trick-on-ec-multiplication-by-scalar, - Windowing method : https://en.wikipedia.org/wiki/Exponentiation_by_squaring, section 'sliding window' The implementation uses a 2 bits window with trick, leading to a 16 points elliptic point precomputation */ --- contracts/EllipticCurve.sol | 212 ++++++++++++++++++++++++++++-------- contracts/Webauthn.sol | 7 +- test/Webauthn.js | 7 +- 3 files changed, 177 insertions(+), 49 deletions(-) diff --git a/contracts/EllipticCurve.sol b/contracts/EllipticCurve.sol index 16cbae9..baa6c34 100644 --- a/contracts/EllipticCurve.sol +++ b/contracts/EllipticCurve.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; + /** * @title EllipticCurve * @@ -15,6 +16,8 @@ pragma solidity ^0.8.0; * @dev NOTE: To disambiguate public keys when verifying signatures, activate * condition 'rs[1] > lowSmax' in validateSignature(). */ + import "hardhat/console.sol"; + library EllipticCurve { // Set parameters for curve. uint constant a = @@ -71,6 +74,40 @@ library EllipticCurve { } } + + function toZZPoint( + uint x0, + uint y0 + ) internal pure returns (uint[4] memory P) { + unchecked { + P[2] = 1; //ZZ + P[3] = 1; //ZZZ + P[0] = x0; + P[1] = y0; + } + } + + + function submod(uint x, uint y) internal pure returns (uint xmy) + { + return addmod(x, p-a,p); + } + + function ecZZ_Dbl( + uint x, + uint y, + uint zz, + uint zzz + ) internal pure returns (uint[4] memory P) + { + P[0]=mulmod(2, y, p); //U = 2*Y1 + P[1]=mulmod(P[0],P[0],p); // V=U^2 + P[2]=mulmod(P[0], P[1],p); // W=UV + P[3]=mulmod(x, P[1],p); // S = X1*V + //M=3*(X1-ZZ1)*(X1+ZZ1) + //TBD + } + /** * @dev Add two points in affine coordinates and return projective point. */ @@ -103,6 +140,7 @@ library EllipticCurve { y1 = mulmod(y0, z0Inv, p); } } + /** * @dev Return the zero curve in projective coordinates. @@ -119,14 +157,22 @@ library EllipticCurve { } /** - * @dev Check if the curve is the zero curve. + * @dev Check if the curve is the zero curve in affine rep. */ - function isZeroCurve(uint x0, uint y0) internal pure returns (bool isZero) { + function isZeroCurve_affine(uint x0, uint y0) internal pure returns (bool isZero) { if (x0 == 0 && y0 == 0) { return true; } return false; } + + function isZeroCurve_proj(uint x0, uint y0, uint z0) internal pure returns (bool isZero) { + if ( (x0 == 0) && (y0 == 1) && (z0==0) ) { + return true; + } + return false; + } + /** * @dev Check if a point in affine coordinates is on the curve. @@ -164,7 +210,7 @@ library EllipticCurve { uint v; uint w; - if (isZeroCurve(x0, y0)) { + if (isZeroCurve_proj(x0, y0, z0)) { return zeroProj(); } unchecked { @@ -217,11 +263,13 @@ library EllipticCurve { uint u0; uint u1; - if (isZeroCurve(x0, y0)) { + if (isZeroCurve_proj(x0, y0, z0)) { return (x1, y1, z1); - } else if (isZeroCurve(x1, y1)) { + + } else if (isZeroCurve_proj(x1, y1, z1)) { return (x0, y0, z0); } + unchecked { t0 = mulmod(y0, z1, p); t1 = mulmod(y1, z0, p); @@ -239,6 +287,8 @@ library EllipticCurve { unchecked { (x2, y2, z2) = addProj2(mulmod(z0, z1, p), u0, u1, t1, t0); } + + } /** @@ -335,7 +385,7 @@ library EllipticCurve { uint x0, uint y0, uint scalar - ) internal pure returns (uint x1, uint y1) { + ) internal returns (uint x1, uint y1) { if (scalar == 0) { return zeroAffine(); } else if (scalar == 1) { @@ -355,6 +405,7 @@ library EllipticCurve { x1 = y1 = 0; } + unchecked{ scalar = scalar >> 1; while (scalar > 0) { @@ -366,25 +417,46 @@ library EllipticCurve { scalar = scalar >> 1; } - + } return toAffinePoint(x1, y1, z1); } + + function NormalizedX( uint Gx0, uint Gy0, uint Gz0) internal returns(uint px) + { + if (Gz0 == 0) { + return 0; + } + + uint Px = inverseMod(Gz0, p); + unchecked { + Px = mulmod(Gx0, Px, p); + } + return Px; + } + /** - * @dev Double base multiplication using windowing and Shamir's trick + * @dev Double base multiplication using windowing and Shamir's trick, imported from https://github.com/rdubois-crypto/MyCairoPlayground/blob/main/Cairo/cairo_secp256r1/src/ec_mulmuladd_secp256r1.cairo + Shamir's trick:https://crypto.stackexchange.com/questions/99975/strauss-shamir-trick-on-ec-multiplication-by-scalar, + Windowing method : https://en.wikipedia.org/wiki/Exponentiation_by_squaring, section 'sliding window' + The implementation uses a 2 bits window with trick, leading to a 16 points elliptic point precomputation */ - function ec_mulmuladd( + function ec_mulmuladd_W( uint Gx0, uint Gy0, uint Qx0, uint Qy0, uint scalar_u, uint scalar_v - ) internal pure returns (uint[3] memory R) { + ) internal returns (uint[3] memory R) { - /*1. Precomputation steps: 2 bits window+shamir */ - /* precompute all aG+bQ in [0..3][0..3]*/ + //1. Precomputation steps: 2 bits window+shamir + // precompute all aG+bQ in [0..3][0..3] uint [3][16] memory Window; + + (Window[0][0], Window[0][1], Window[0][2])= zeroProj(); + + Window[1][0]=Gx0; Window[1][1]=Gy0; Window[1][2]=1; @@ -393,12 +465,12 @@ library EllipticCurve { Window[2][1]=Qy0; Window[2][2]=1; - (Window[3][0], Window[3][1], Window[3][2])=addProj(Gx0, Gy0, 1, Qx0, Gy0, 1); //3:G+Q + (Window[3][0], Window[3][1], Window[3][2])=addProj(Gx0, Gy0, 1, Qx0, Qy0, 1); //3:G+Q (Window[4][0], Window[4][1], Window[4][2])=twiceProj(Gx0, Gy0, 1);//4:2G (Window[5][0], Window[5][1], Window[5][2])=addProj(Gx0, Gy0, 1, Window[4][0], Window[4][1], Window[4][2]); //5:3G (Window[6][0], Window[6][1], Window[6][2])=addProj(Qx0, Qy0, 1, Window[4][0], Window[4][1], Window[4][2]); //6:2G+Q (Window[7][0], Window[7][1], Window[7][2])=addProj(Qx0, Qy0, 1, Window[5][0], Window[5][1], Window[5][2]); //7:3G+Q - (Window[8][0], Window[8][1], Window[8][2])=twiceProj(Window[4][0], Window[4][1], Window[4][2]);//8:4G + (Window[8][0], Window[8][1], Window[8][2])=twiceProj(Qx0, Qy0, 1);//8:2Q (Window[9][0], Window[9][1], Window[9][2])=addProj(Gx0, Gy0, 1, Window[8][0], Window[8][1], Window[8][2]);//9:2Q+G (Window[10][0], Window[10][1], Window[10][2])=addProj(Qx0, Qy0, 1, Window[8][0], Window[8][1], Window[8][2]);//10:3Q @@ -410,32 +482,89 @@ library EllipticCurve { //initialize R with infinity point - R[0]=0; - R[1]=0; - R[2]=0; + (R[0],R[1],R[2])= zeroProj(); + uint quadbit=1; //2. loop over scalars from MSB to LSB: + unchecked{ for(uint8 i=0;i<128;i++) { - uint8 rshift=255-2*i; + uint8 rshift=255-(2*i); (R[0],R[1],R[2])=twiceProj(R[0],R[1],R[2]);//double (R[0],R[1],R[2])=twiceProj(R[0],R[1],R[2]);//double //compute quadruple (8*v1 +4*u1+ 2*v0 + u0) - quadbit=8*((scalar_u>>rshift)&1)+ 4*((scalar_u>>rshift)&1)+ 2*((scalar_v>>(rshift-1))&1)+ ((scalar_u >>(rshift-1))&1); + quadbit=8*((scalar_v>>rshift)&1)+ 4*((scalar_u>>rshift)&1)+ 2*((scalar_v>>(rshift-1))&1)+ ((scalar_u >>(rshift-1))&1); + + (R[0],R[1],R[2])=addProj(R[0],R[1],R[2], Window[quadbit][0], Window[quadbit][1], Window[quadbit][2]); + + } } return R; } + function ec_mulmuladd_S( + uint Gx0, + uint Gy0, + uint Qx0, + uint Qy0, + uint scalar_u, + uint scalar_v + ) internal returns (uint[3] memory R) { + + uint[3] memory H;//G+Q + (H[0],H[1], H[2] )=addProj(Gx0, Gy0, 1, Qx0, Qy0, 1); + + console.log("H=", H[0], H[1], H[2]); + + + uint dibit; + + //initialize R with infinity point + (R[0],R[1],R[2])= zeroProj(); + + unchecked { + for(uint index=0;index<256;index ++) + { + + uint i=255-index; + + if (isZeroCurve_proj(R[0],R[1],R[2])){ + } + + + (R[0],R[1],R[2])=twiceProj(R[0],R[1],R[2]);//double + + if (isZeroCurve_proj(R[0],R[1],R[2])){ + } + + dibit=((scalar_u>>i)&1)+2*((scalar_v>>i)&1); + + if(dibit==1){ + (R[0],R[1],R[2])= addProj(R[0],R[1],R[2],Gx0, Gy0, 1); + } + if(dibit==2){ + (R[0],R[1],R[2])= addProj(R[0],R[1],R[2],Qx0, Qy0, 1); + } + if(dibit==3){ + + (R[0],R[1],R[2])= addProj(R[0],R[1],R[2],H[0],H[1], H[2]); + } + } + } + return R; + } + + /** * @dev Multiply the curve's generator point by a scalar. */ function multipleGeneratorByScalar( uint scalar - ) internal pure returns (uint, uint) { + ) internal returns (uint, uint) { return multiplyScalar(gx, gy, scalar); } @@ -446,7 +575,7 @@ library EllipticCurve { bytes32 message, uint[2] memory rs, uint[2] memory Q - ) internal pure returns (bool) { + ) internal returns (bool) { // To disambiguate between public key solutions, include comment below. if (rs[0] == 0 || rs[0] >= n || rs[1] == 0) { // || rs[1] > lowSmax) @@ -455,37 +584,32 @@ library EllipticCurve { if (!isOnCurve(Q[0], Q[1])) { return false; } + + - uint x1; + uint sInv = inverseMod(rs[1], n); + uint scalar_u=mulmod(uint(message), sInv, n); + uint scalar_v= mulmod(rs[0], sInv, n); + + // without Optim + + + /* + uint x1; uint x2; uint y1; uint y2; - - uint sInv = inverseMod(rs[1], n); - - // without Optim - /* - (x1, y1) = multiplyScalar(gx, gy, mulmod(uint(message), sInv, n)); - (x2, y2) = multiplyScalar(Q[0], Q[1], mulmod(rs[0], sInv, n)); + (x1, y1) = multiplyScalar(gx, gy, scalar_u); + (x2, y2) = multiplyScalar(Q[0], Q[1], scalar_v); uint[3] memory P = addAndReturnProjectivePoint(x1, y1, x2, y2); + + console.log("res naive:", P [0], P[1], P[2]); */ - uint scalar_v=mulmod(uint(message), sInv, n); - uint scalar_u= mulmod(rs[0], sInv, n); - uint[3] memory P = ec_mulmuladd(gx, gy, Q[0], Q[1],scalar_v ,scalar_u ); + /* choose ec_mulmuladd_W (higher deploiement, lower verification) or ec_mulmuladd_S (lower deploiement, higher verification cost)*/ + uint[3] memory P = ec_mulmuladd_W(gx, gy, Q[0], Q[1],scalar_u ,scalar_v ); + uint Px=NormalizedX(P[0], P[1], P[2]); - - - - if (P[2] == 0) { - return false; - } - - uint Px = inverseMod(P[2], p); - unchecked { - Px = mulmod(P[0], mulmod(Px, Px, p), p); - } - return Px % n == rs[0]; } } diff --git a/contracts/Webauthn.sol b/contracts/Webauthn.sol index 2cde841..446478b 100644 --- a/contracts/Webauthn.sol +++ b/contracts/Webauthn.sol @@ -20,7 +20,7 @@ contract Webauthn { uint clientChallengeDataOffset, uint[2] memory rs, uint[2] memory Q - ) public view returns (bool) { + ) public returns (bool) { // Let the caller check if User Presence (0x01) or User Verification (0x04) are set if ( (authenticatorData[32] & authenticatorDataFlagMask) != @@ -65,7 +65,10 @@ contract Webauthn { authenticatorData.length ); bytes32 message = sha256(verifyData); - return EllipticCurve.validateSignature(message, rs, Q); + bool result=EllipticCurve.validateSignature(message, rs, Q); + console.log("result= %s", result); + + return result; } function validate( diff --git a/test/Webauthn.js b/test/Webauthn.js index e7cf8b6..8199c5b 100644 --- a/test/Webauthn.js +++ b/test/Webauthn.js @@ -39,19 +39,20 @@ describe("Webauthn", function() { const challengeOffset = clientData.indexOf("226368616c6c656e6765223a", 0, "hex") + 12 + 1; const signatureParsed = derToRS(signature); + /* const result = await webauthn.checkSignature(authenticatorData, 0x01, clientData, clientChallenge, challengeOffset, [ ethers.BigNumber.from("0x" + signatureParsed[0].toString('hex')), ethers.BigNumber.from("0x" + signatureParsed[1].toString('hex'))], [ ethers.BigNumber.from("0x" + publicKey.slice(0, 32).toString('hex')), ethers.BigNumber.from("0x" + publicKey.slice(32).toString('hex'))] ); expect(result); - - /* +*/ + const result = await webauthn.validate(authenticatorData, 0x01, clientData, clientChallenge, challengeOffset, [ ethers.BigNumber.from("0x" + signatureParsed[0].toString('hex')), ethers.BigNumber.from("0x" + signatureParsed[1].toString('hex'))], [ ethers.BigNumber.from("0x" + publicKey.slice(0, 32).toString('hex')), ethers.BigNumber.from("0x" + publicKey.slice(32).toString('hex'))] ); await result.wait(); - */ + }) }); From 9066c6634ca99274e8d0b396ed449410f07c5cc4 Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Tue, 28 Feb 2023 12:28:58 +0100 Subject: [PATCH 03/25] XYZZ coordinates --- .../{EllipticCurve.sol => Elliptic_ZZ.sol} | 386 +++++++++++++++--- contracts/Webauthn.sol | 4 +- 2 files changed, 336 insertions(+), 54 deletions(-) rename contracts/{EllipticCurve.sol => Elliptic_ZZ.sol} (60%) diff --git a/contracts/EllipticCurve.sol b/contracts/Elliptic_ZZ.sol similarity index 60% rename from contracts/EllipticCurve.sol rename to contracts/Elliptic_ZZ.sol index baa6c34..8f895b2 100644 --- a/contracts/EllipticCurve.sol +++ b/contracts/Elliptic_ZZ.sol @@ -1,41 +1,43 @@ +//*************************************************************************************/ +///* Copyright (C) 2022 - Renaud Dubois - This file is part of Cairo_musig2 project */ +///* License: This software is licensed under MIT License */ +///* See LICENSE file at the root folder of the project. */ +///* FILE: ecZZ.solidity */ +///* */ +///* */ +///* DESCRIPTION: modified XYZZ system coordinates for EVM elliptic point multiplication +///* optimization +///* +//**************************************************************************************/ + // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; - -/** - * @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) - * - * @dev NOTE: To disambiguate public keys when verifying signatures, activate - * condition 'rs[1] > lowSmax' in validateSignature(). - */ import "hardhat/console.sol"; -library EllipticCurve { +library Ec_ZZ { // Set parameters for curve. + //curve bitsize + uint constant curve_S1=255; + //curve prime field modulus + uint constant p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF; + //short weierstrass first coefficient uint constant a = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC; + //short weierstrass second coefficient uint constant b = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B; + //generating point affine coordinates uint constant gx = 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296; uint constant gy = 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5; - uint constant p = - 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF; + //curve order (number of points) uint constant n = - 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551; - - uint constant lowSmax = - 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0; - + 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551; + /* -2 constant, used to speed up doubling (avoid negation)*/ + uint constant minus_2 = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFD; + /** * @dev Inverse of u in the field of modulo m. */ @@ -75,7 +77,7 @@ library EllipticCurve { } - function toZZPoint( + function ecAff_SetZZ( uint x0, uint y0 ) internal pure returns (uint[4] memory P) { @@ -87,27 +89,101 @@ library EllipticCurve { } } - - function submod(uint x, uint y) internal pure returns (uint xmy) +/* https://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz-3.html#addition-add-2008-s*/ + function ecZZ_SetAff( uint x, + uint y, + uint zz, + uint zzz) internal pure returns (uint x1, uint y1) { - return addmod(x, p-a,p); + uint zzzInv = inverseMod(zzz, p); //1/zzz + y1=mulmod(y,zzzInv,p);//Y/zzz + uint b=mulmod(zz, zzzInv,p); //1/z + zzzInv= mulmod(b,b,p); //1/zz + x1=mulmod(x,zzzInv,p);//X/zz } + + + /* Chudnosky doubling*/ + /* The "dbl-2008-s-1" doubling formulas */ + function ecZZ_Dbl( uint x, uint y, uint zz, uint zzz - ) internal pure returns (uint[4] memory P) + ) internal pure returns (uint P0, uint P1,uint P2,uint P3) { - P[0]=mulmod(2, y, p); //U = 2*Y1 - P[1]=mulmod(P[0],P[0],p); // V=U^2 - P[2]=mulmod(P[0], P[1],p); // W=UV - P[3]=mulmod(x, P[1],p); // S = X1*V - //M=3*(X1-ZZ1)*(X1+ZZ1) - //TBD + unchecked{ + //use output as temporary to reduce RAM usage + P0=mulmod(2, y, p); //U = 2*Y1 + P2=mulmod(P0,P0,p); // V=U^2 + P3=mulmod(x, P2,p); // S = X1*V + P1=mulmod(P0, P2,p); // W=UV + + P2=mulmod(P2, zz, p); //zz3=V*ZZ1 + + zz=mulmod(addmod(x,p-zz,p), addmod(x,zz,p),p);//M=3*(X1-ZZ1)*(X1+ZZ1), use zz to reduce RAM usage + zz=mulmod(3,zz, p);//M + P0=addmod(mulmod(zz,zz,p), mulmod(minus_2, P3,p),p);//X3=M^2-2S + + P3=addmod(P3, p-P0,p);//S-X3 + x=mulmod(zz,P3,p);//M(S-X3) + P3=mulmod(P1,zzz,p);//zzz3=W*zzz1 + + P1=addmod(x, p-mulmod(P1, y,p),p );//Y3= M(S-X3)-W*Y1 + } + + return (P0, P1, P2, P3); } + /** + * @dev add a ZZ point with a normalized point and greedy formulae + */ + + //tbd: + // return -x1 and -Y1 in double + function ecZZ_AddN( + uint x1, + uint y1, + uint zz1, + uint zzz1, + uint x2, + uint y2) internal pure returns (uint P0, uint P1,uint P2,uint P3) + { + + unchecked{ + y1=p-y1;//-Y1 + //U2 = X2*ZZ1 + x2=mulmod(x2, zz1,p); + //S2 = Y2*ZZZ1, y2 free + y2=mulmod(y2, zzz1,p); + //R = S2-Y1 + y2=addmod(y2,y1,p); + //P = U2-X1 + x2=addmod(x2,p-x1,p); + + + P0=mulmod(x2, x2, p);//PP = P^2 + + P1=mulmod(P0,x2,p); //PPP = P*PP + + P2=mulmod(zz1,P0,p); //ZZ3 = ZZ1*PP + + P3= mulmod(zzz1,P1,p); //ZZZ3 = ZZZ1*PPP + + zz1=mulmod(x1, P0, p); //Q = X1*PP, x1 free + //X3 = R^2-PPP-2*Q + P0= addmod(mulmod(y2,y2, p), p-P1,p ); //R^2-PPP + zzz1=mulmod(minus_2, zz1,p);//-2*Q + P0=addmod(P0, zzz1 ,p );//R^2-PPP-2*Q + //Y3 = R*(Q-X3)-Y1*PPP + x1= mulmod(addmod(zz1, p-P0,p), y2, p);//R*(Q-X3) + P1=addmod(x1, mulmod(y1, P1,p),p) ; + } + return (P0, P1, P2, P3); + } + /** * @dev Add two points in affine coordinates and return projective point. */ @@ -140,7 +216,25 @@ library EllipticCurve { y1 = mulmod(y0, z0Inv, p); } } - + + /** + * @dev Transform from jacobian to affine coordinates. + */ + function toAffinePoint_FromJac( + uint x0, + uint y0, + uint z0 + ) internal pure returns (uint x1, uint y1) { + uint z0Inv; + unchecked { + z0Inv = inverseMod(z0, p); + uint z0Inv2 = mulmod(z0Inv,z0Inv, p); + x1 = mulmod(x0, z0Inv2, p); //x=X/Z^2 + z0Inv = mulmod(z0Inv2,z0Inv, p); + y1 = mulmod(y0, z0Inv, p);//y=X/Z^3 + } + } + /** * @dev Return the zero curve in projective coordinates. @@ -149,10 +243,24 @@ library EllipticCurve { return (0, 1, 0); } + /** + * @dev Return the zero curve in chudnosky coordinates. + */ + function ecZZ_SetZero() internal pure returns (uint x, uint y, uint zz, uint zzz) { + return (0, 0, 0, 0); + } + + function ecZZ_IsZero (uint x0, uint y0, uint zz0, uint zzz0) internal pure returns (bool) + { + if ( (x0 == 0) && (y0 == 0) && (zz0==0) && (zzz0==0) ) { + return true; + } + return false; + } /** - * @dev Return the zero curve in affine coordinates. + * @dev Return the zero curve in affine coordinates. Compatible with the double formulae (no special case) */ - function zeroAffine() internal pure returns (uint x, uint y) { + function ecAff_SetZero() internal pure returns (uint x, uint y) { return (0, 0); } @@ -387,7 +495,7 @@ library EllipticCurve { uint scalar ) internal returns (uint x1, uint y1) { if (scalar == 0) { - return zeroAffine(); + return ecAff_SetZero(); } else if (scalar == 1) { return (x0, y0); } else if (scalar == 2) { @@ -402,7 +510,8 @@ library EllipticCurve { y1 = y0; if (scalar % 2 == 0) { - x1 = y1 = 0; + x1 =0; + y1=1; } unchecked{ @@ -421,7 +530,58 @@ library EllipticCurve { return toAffinePoint(x1, y1, z1); } + /** + * @dev Double and Add, MSB to LSB version + */ + function ecZZ_mul( + uint x0, + uint y0, + uint scalar + ) internal returns (uint x1, uint y1) { + if (scalar == 0) { + return ecAff_SetZero(); + } + + uint zzZZ; uint zzZZZ; + + uint bit_mul=(1<>1; + index=index-1; + } + console.log("--first msb at position of scalar", uint(index)); + + //R=P + x1=x0;y1=y0;zzZZ=1;zzZZZ=1; + index=index-1; bit_mul=bit_mul>>1; + + while (index >= 0) { + + + (x1, y1, zzZZ, zzZZZ) = ecZZ_Dbl(x1, y1, zzZZ, zzZZZ); + + if (bit_mul &scalar != 0) { + (x1, y1, zzZZ, zzZZZ)= ecZZ_AddN(x1, y1, zzZZ, zzZZZ, x0, y0); + } + bit_mul = bit_mul >> 1; + index=index-1; + } + } + + + return ecZZ_SetAff(x1, y1, zzZZ, zzZZZ); + } + function NormalizedX( uint Gx0, uint Gy0, uint Gz0) internal returns(uint px) { if (Gz0 == 0) { @@ -436,10 +596,7 @@ library EllipticCurve { } /** - * @dev Double base multiplication using windowing and Shamir's trick, imported from https://github.com/rdubois-crypto/MyCairoPlayground/blob/main/Cairo/cairo_secp256r1/src/ec_mulmuladd_secp256r1.cairo - Shamir's trick:https://crypto.stackexchange.com/questions/99975/strauss-shamir-trick-on-ec-multiplication-by-scalar, - Windowing method : https://en.wikipedia.org/wiki/Exponentiation_by_squaring, section 'sliding window' - The implementation uses a 2 bits window with trick, leading to a 16 points elliptic point precomputation + * @dev Double base multiplication using windowing and Shamir's trick */ function ec_mulmuladd_W( uint Gx0, @@ -558,6 +715,71 @@ library EllipticCurve { return R; } + function ecZZ_mulmuladd_S( + uint Gx0, + uint Gy0, + uint Qx0, + uint Qy0, + uint scalar_u, + uint scalar_v + ) internal returns (uint[4] memory R) { + + uint H0;//G+Q + uint H1; + + (H0, H1 )=add(Gx0, Gy0, Qx0, Qy0); + + uint dibit; + + + uint index=0; + uint i=255-index; + + while( ((scalar_u>>i)&1)+2*((scalar_v>>i)&1) ==0){ + index=index+1; + i=255-index; + } + dibit=((scalar_u>>i)&1)+2*((scalar_v>>i)&1); + if(dibit==1){ + (R[0],R[1],R[2], R[3])= (Gx0, Gy0,1,1); + } + if(dibit==2){ + (R[0],R[1],R[2], R[3])= (Qx0, Qy0,1,1); + } + if(dibit==3){ + (R[0],R[1],R[2], R[3])= (H0, H1, 1, 1); + } + + index=index+1; + i=255-index; + + unchecked { + for(;index<256;index ++) + { + + uint i=255-index; + + (R[0],R[1],R[2], R[3])=ecZZ_Dbl(R[0],R[1],R[2], R[3]);//double + + if (isZeroCurve_proj(R[0],R[1],R[2])){ + } + + dibit=((scalar_u>>i)&1)+2*((scalar_v>>i)&1); + + if(dibit==1){ + (R[0],R[1],R[2], R[3])= ecZZ_AddN(R[0],R[1],R[2],R[3], Gx0, Gy0); + } + if(dibit==2){ + (R[0],R[1],R[2], R[3])= ecZZ_AddN(R[0],R[1],R[2],R[3], Qx0, Qy0); + } + if(dibit==3){ + + (R[0],R[1],R[2], R[3])= ecZZ_AddN(R[0],R[1],R[2],R[3], H0, H1); + } + } + } + return R; + } /** * @dev Multiply the curve's generator point by a scalar. @@ -568,6 +790,49 @@ library EllipticCurve { return multiplyScalar(gx, gy, scalar); } + function test_ecZZ_formulae() internal returns(bool) + { + uint[4] memory P2ZZ; + (P2ZZ[0], P2ZZ[1],P2ZZ[2],P2ZZ[3])=ecZZ_Dbl(gx, gy, 1,1); + uint[2] memory P2; + (P2[0], P2[1])=twice(gx, gy); + + + + /* test normalization*/ + /* + P2ZZ[2]=mulmod(0xFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF, 0xFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF, p); + P2ZZ[3]=mulmod(P2ZZ[2], 0xFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF, p); + P2ZZ[0]=mulmod(gx, P2ZZ[2],p); + P2ZZ[1]=mulmod(gy, P2ZZ[3],p); + */ + console.log("-- pre normalization %s", P2ZZ[0], P2ZZ[1]); + console.log("-- pre normalization %s", P2ZZ[2], P2ZZ[3]); + + uint[2] memory P2zz; + (P2zz[0], P2zz[1])=ecZZ_SetAff(P2ZZ[0], P2ZZ[1],P2ZZ[2],P2ZZ[3]); + bool isonc=isOnCurve(P2zz[0], P2zz[1]); + console.log("--is on curve", isonc); + + + console.log("res 1 post norm:", P2zz [0], P2zz[1]); + console.log("res 2:", P2 [0], P2[1]); + + + (P2ZZ[0], P2ZZ[1],P2ZZ[2],P2ZZ[3])=ecZZ_AddN( P2ZZ[0], P2ZZ[1],P2ZZ[2],P2ZZ[3], gx, gy);//3P in ZZ coordinates + (P2zz[0], P2zz[1])=ecZZ_SetAff(P2ZZ[0], P2ZZ[1],P2ZZ[2],P2ZZ[3]); + isonc=isOnCurve(P2zz[0], P2zz[1]); + + + console.log("--is on curve", isonc); + + (P2[0], P2[1])=add(P2[0], P2[1], gx, gy); + + console.log("3P via ZZ", P2zz [0], P2zz[1]); + console.log("3P via Aff:", P2 [0], P2[1]); + + return true; + } /** * @dev Validate combination of message, signature, and public key. */ @@ -576,9 +841,7 @@ library EllipticCurve { uint[2] memory rs, uint[2] memory Q ) internal 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])) { @@ -594,22 +857,41 @@ library EllipticCurve { // without Optim - /* + uint x1; uint x2; uint y1; uint y2; + + /* (x1, y1) = multiplyScalar(gx, gy, scalar_u); (x2, y2) = multiplyScalar(Q[0], Q[1], scalar_v); - uint[3] memory P = addAndReturnProjectivePoint(x1, y1, x2, y2); + //uint[3] memory PAff = addAndReturnProjectivePoint(x1, y1, x2, y2); - console.log("res naive:", P [0], P[1], P[2]); + console.log("res naive Aff mul1:", x1, y1); + console.log("res naive Aff mul2:", x2, y2); */ - /* choose ec_mulmuladd_W (higher deploiement, lower verification) or ec_mulmuladd_S (lower deploiement, higher verification cost)*/ - uint[3] memory P = ec_mulmuladd_W(gx, gy, Q[0], Q[1],scalar_u ,scalar_v ); - uint Px=NormalizedX(P[0], P[1], P[2]); + /* + (x1, y1) = ecZZ_mul(gx, gy, scalar_u); + (x2, y2) = ecZZ_mul(Q[0], Q[1], scalar_v); + + + console.log("res naive ZZ:", x1, y1); + console.log("res naive ZZ:", x2, y2); + */ + //test_ecZZ_formulae(); + + ecZZ_mulmuladd_S(gx, gy, Q[0], Q[1],scalar_u, scalar_v); + + + + + + //uint[3] memory P = ec_mulmuladd_W(gx, gy, Q[0], Q[1],scalar_u ,scalar_v ); + //uint Px=NormalizedX(P[0], P[1], P[2]); - return Px % n == rs[0]; + //return Px % n == rs[0]; + return true; } } diff --git a/contracts/Webauthn.sol b/contracts/Webauthn.sol index 446478b..0ab0c6a 100644 --- a/contracts/Webauthn.sol +++ b/contracts/Webauthn.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; import {Base64URL} from "./Base64URL.sol"; -import {EllipticCurve} from "./EllipticCurve.sol"; +import {Ec_ZZ} from "./Elliptic_ZZ.sol"; import "hardhat/console.sol"; error InvalidAuthenticatorData(); @@ -65,7 +65,7 @@ contract Webauthn { authenticatorData.length ); bytes32 message = sha256(verifyData); - bool result=EllipticCurve.validateSignature(message, rs, Q); + bool result=Ec_ZZ.validateSignature(message, rs, Q); console.log("result= %s", result); return result; From 629a94b897ce6bac86500e944a6a0c53f672b5b7 Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Wed, 1 Mar 2023 10:26:18 +0100 Subject: [PATCH 04/25] shamir 8 dimensions --- contracts/Elliptic_ZZ.sol | 215 ++++++++++++++++++++++++++++++-------- contracts/Webauthn.sol | 95 ++++++++++++++++- hardhat.config.js | 3 + test/Webauthn.js | 8 +- 4 files changed, 270 insertions(+), 51 deletions(-) diff --git a/contracts/Elliptic_ZZ.sol b/contracts/Elliptic_ZZ.sol index 8f895b2..dae1926 100644 --- a/contracts/Elliptic_ZZ.sol +++ b/contracts/Elliptic_ZZ.sol @@ -38,6 +38,8 @@ library Ec_ZZ { /* -2 constant, used to speed up doubling (avoid negation)*/ uint constant minus_2 = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFD; + + /** * @dev Inverse of u in the field of modulo m. */ @@ -303,7 +305,8 @@ library Ec_ZZ { 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 @@ -715,6 +718,13 @@ library Ec_ZZ { return R; } + struct Indexing_t{ + uint dibit; + uint index; + uint i; + + } + function ecZZ_mulmuladd_S( uint Gx0, uint Gy0, @@ -722,63 +732,117 @@ library Ec_ZZ { uint Qy0, uint scalar_u, uint scalar_v - ) internal returns (uint[4] memory R) { - - uint H0;//G+Q - uint H1; - - (H0, H1 )=add(Gx0, Gy0, Qx0, Qy0); - - uint dibit; - + ) internal returns (uint R0, uint R1) { + Indexing_t memory Ind; + + uint[2] memory R; + uint[2] memory H;//G+Q + (H[0], H[1] )=add(Gx0, Gy0, Qx0, Qy0); - uint index=0; - uint i=255-index; + + Ind.index=0; + { + Ind.i=255-Ind.index; - while( ((scalar_u>>i)&1)+2*((scalar_v>>i)&1) ==0){ - index=index+1; - i=255-index; + while( ((scalar_u>>Ind.i)&1)+2*((scalar_v>>Ind.i)&1) ==0){ + Ind.index=Ind.index+1; + Ind.i=255-Ind.index; } - dibit=((scalar_u>>i)&1)+2*((scalar_v>>i)&1); - if(dibit==1){ - (R[0],R[1],R[2], R[3])= (Gx0, Gy0,1,1); + Ind.dibit=((scalar_u>>Ind.i)&1)+2*((scalar_v>>Ind.i)&1); + if(Ind.dibit==1){ + (R0 ,R1 ,R[0], R[1])= (Gx0, Gy0,1,1); } - if(dibit==2){ - (R[0],R[1],R[2], R[3])= (Qx0, Qy0,1,1); + if(Ind.dibit==2){ + (R0 ,R1,R[0], R[1])= (Qx0, Qy0,1,1); } - if(dibit==3){ - (R[0],R[1],R[2], R[3])= (H0, H1, 1, 1); + if(Ind.dibit==3){ + (R0 ,R1,R[0], R[1])= (H[0], H[1], 1, 1); } - index=index+1; - i=255-index; + Ind.index=Ind.index+1; + Ind.i=255-Ind.index; + } unchecked { - for(;index<256;index ++) + for(;Ind.index<256;Ind.index ++) { - uint i=255-index; + Ind.i=255-Ind.index; - (R[0],R[1],R[2], R[3])=ecZZ_Dbl(R[0],R[1],R[2], R[3]);//double + (R0 ,R1,R[0], R[1])=ecZZ_Dbl(R0 ,R1,R[0], R[1]);//double - if (isZeroCurve_proj(R[0],R[1],R[2])){ - } + - dibit=((scalar_u>>i)&1)+2*((scalar_v>>i)&1); + Ind.dibit=((scalar_u>>Ind.i)&1)+2*((scalar_v>>Ind.i)&1); - if(dibit==1){ - (R[0],R[1],R[2], R[3])= ecZZ_AddN(R[0],R[1],R[2],R[3], Gx0, Gy0); + if(Ind.dibit==1){ + (R0 ,R1,R[0], R[1])= ecZZ_AddN(R0 ,R1,R[0],R[1], Gx0, Gy0); } - if(dibit==2){ - (R[0],R[1],R[2], R[3])= ecZZ_AddN(R[0],R[1],R[2],R[3], Qx0, Qy0); + if(Ind.dibit==2){ + (R0 ,R1,R[0], R[1])= ecZZ_AddN(R0 ,R1,R[0],R[1], Qx0, Qy0); } - if(dibit==3){ + if(Ind.dibit==3){ - (R[0],R[1],R[2], R[3])= ecZZ_AddN(R[0],R[1],R[2],R[3], H0, H1); + (R0 ,R1,R[0], R[1])= ecZZ_AddN(R0 ,R1,R[0],R[1], H[0], H[1]); } } } - return R; + return (R0 ,R1); + } + + /** + * @dev 8 dimensional Shamir/Pippinger, will be done externally, validation purpose only + */ + function Precalc_Shamir8( uint[2] memory Q) internal pure returns( uint[2][256] memory Prec) + { + uint index; + uint[2][8] memory PowerPQ; + + PowerPQ[0][0]=gx; + PowerPQ[0][1]=gy; + PowerPQ[4][0]=Q[0]; + PowerPQ[4][1]=Q[1]; + + for(uint i=1;i<4;i++){ + (PowerPQ[i][0], PowerPQ[i][1])=twice(PowerPQ[i-1][0], PowerPQ[i-1][1]); + (PowerPQ[i+4][0], PowerPQ[i+4][1])=twice(PowerPQ[i+3][0], PowerPQ[i+3][1]); + + } + + for(uint i=1;i<256;i++) + { + Prec[i][0]=0; + Prec[i][1]=0; + + for(uint j=0;j<8;j++) + { + if(i&(1<>index)+64*(scalar_v>>(index-64))+32*(scalar_v>>(index-128))+16*(scalar_v>>(index-192))+ + 8*(scalar_u>>index)+4*(scalar_u>>(index-64))+2*(scalar_u>>(index-128))+(scalar_u>>(index-192)); + + (x,y,R[0], R[1])= (Shamir8[octobit][0], Shamir8[octobit][1],1,1); + //loop over 1/4 of scalars + for(index=254; index>=192; index--) + { + octobit=128*(scalar_v>>index)+64*(scalar_v>>(index-64))+32*(scalar_v>>(index-128))+16*(scalar_v>>(index-192))+ + 8*(scalar_u>>index)+4*(scalar_u>>(index-64))+2*(scalar_u>>(index-128))+(scalar_u>>(index-192)); + + (x,y,R[0], R[1])=ecZZ_AddN( x,y,R[0], R[1], Shamir8[octobit][0], Shamir8[octobit][1]); + } + (x,y)=ecZZ_SetAff(x,y,R[0], R[1]); } /** @@ -872,19 +936,18 @@ library Ec_ZZ { console.log("res naive Aff mul2:", x2, y2); */ - /* - (x1, y1) = ecZZ_mul(gx, gy, scalar_u); - (x2, y2) = ecZZ_mul(Q[0], Q[1], scalar_v); + // (x1, y1) = ecZZ_mul(gx, gy, scalar_u); + // (x2, y2) = ecZZ_mul(Q[0], Q[1], scalar_v); - console.log("res naive ZZ:", x1, y1); - console.log("res naive ZZ:", x2, y2); - */ + + // console.log("res naive ZZ:", x1, y1); + // console.log("res naive ZZ:", x2, y2); + //test_ecZZ_formulae(); - ecZZ_mulmuladd_S(gx, gy, Q[0], Q[1],scalar_u, scalar_v); - - + //Shamir 2 dimensions + (x1, y1)=ecZZ_mulmuladd_S(gx, gy, Q[0], Q[1],scalar_u, scalar_v); @@ -894,4 +957,64 @@ library Ec_ZZ { //return Px % n == rs[0]; return true; } + + function validateSignature_Precomputed( + bytes32 message, + uint[2] memory rs, + uint[2][256] memory Shamir8 + ) internal returns (bool) { + uint[2] memory Q;//extract Q from Shamir8 + + if (rs[0] == 0 || rs[0] >= n || rs[1] == 0) { + return false; + } + if (!isOnCurve(Q[0], Q[1])) { + return false; + } + + + + uint sInv = inverseMod(rs[1], n); + uint scalar_u=mulmod(uint(message), sInv, n); + uint scalar_v= mulmod(rs[0], sInv, n); + + // without Optim + + + + uint x1; + uint x2; + uint y1; + uint y2; + + /* + (x1, y1) = multiplyScalar(gx, gy, scalar_u); + (x2, y2) = multiplyScalar(Q[0], Q[1], scalar_v); + //uint[3] memory PAff = addAndReturnProjectivePoint(x1, y1, x2, y2); + + console.log("res naive Aff mul1:", x1, y1); + console.log("res naive Aff mul2:", x2, y2); + */ + + + // (x1, y1) = ecZZ_mul(gx, gy, scalar_u); + // (x2, y2) = ecZZ_mul(Q[0], Q[1], scalar_v); + + + // console.log("res naive ZZ:", x1, y1); + // console.log("res naive ZZ:", x2, y2); + + //test_ecZZ_formulae(); + + //Shamir 2 dimensions + //(x1, y1)=ecZZ_mulmuladd_S(gx, gy, Q[0], Q[1],scalar_u, scalar_v); + //Shamir 8 dimensions + (x1, y1)=ecZZ_mulmuladd_S8(scalar_u, scalar_v, Shamir8); + + //uint[3] memory P = ec_mulmuladd_W(gx, gy, Q[0], Q[1],scalar_u ,scalar_v ); + //uint Px=NormalizedX(P[0], P[1], P[2]); + + //return Px % n == rs[0]; + return true; + } } diff --git a/contracts/Webauthn.sol b/contracts/Webauthn.sol index 0ab0c6a..57c4465 100644 --- a/contracts/Webauthn.sol +++ b/contracts/Webauthn.sol @@ -12,6 +12,15 @@ error InvalidSignature(); contract Webauthn { uint256 public counter; + //precomputations + uint[2][256] Shamir8; + + constructor(uint[2] memory Q){ + Shamir8=Ec_ZZ.Precalc_Shamir8(Q); + + } + + function checkSignature( bytes memory authenticatorData, bytes1 authenticatorDataFlagMask, @@ -95,8 +104,92 @@ contract Webauthn { } counter++; } +/* + function checkSignature_prec( + bytes memory authenticatorData, + bytes1 authenticatorDataFlagMask, + bytes memory clientData, + bytes32 clientChallenge, + uint clientChallengeDataOffset, + uint[2] memory rs, + uint[2][256] memory Shamir8 + ) public returns (bool) { + // Let the caller check if User Presence (0x01) or User Verification (0x04) are set + if ( + (authenticatorData[32] & authenticatorDataFlagMask) != + authenticatorDataFlagMask + ) { + revert InvalidAuthenticatorData(); + } + // Verify that clientData commits to the expected client challenge + string memory challengeEncoded = Base64URL.encode32( + abi.encodePacked(clientChallenge) + ); + bytes memory challengeExtracted = new bytes( + bytes(challengeEncoded).length + ); + copyBytes( + clientData, + clientChallengeDataOffset, + challengeExtracted.length, + challengeExtracted, + 0 + ); + if ( + keccak256(abi.encodePacked(bytes(challengeEncoded))) != + keccak256(abi.encodePacked(challengeExtracted)) + ) { + revert InvalidClientData(); + } + // Verify the signature over sha256(authenticatorData || sha256(clientData)) + bytes memory verifyData = new bytes(authenticatorData.length + 32); + copyBytes( + authenticatorData, + 0, + authenticatorData.length, + verifyData, + 0 + ); + copyBytes( + abi.encodePacked(sha256(clientData)), + 0, + 32, + verifyData, + authenticatorData.length + ); + bytes32 message = sha256(verifyData); + bool result=Ec_ZZ.validateSignature_Precomputed(message, rs, Shamir8); + console.log("result= %s", result); - /* + return result; + } + + function validate_prec( + bytes memory authenticatorData, + bytes1 authenticatorDataFlagMask, + bytes memory clientData, + bytes32 clientChallenge, + uint clientChallengeDataOffset, + uint[2] memory rs, + uint[2] memory Q + ) public { + if ( + !checkSignature_prec( + authenticatorData, + authenticatorDataFlagMask, + clientData, + clientChallenge, + clientChallengeDataOffset, + rs, + Q + ) + ) { + revert InvalidSignature(); + } + counter++; + } + + The following function has been written by Alex Beregszaszi (@axic), use it under the terms of the MIT license */ function copyBytes( diff --git a/hardhat.config.js b/hardhat.config.js index bffbd29..475b2d0 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -17,6 +17,9 @@ task("accounts", "Prints the list of accounts", async (taskArgs, hre) => { /** @type import('hardhat/config').HardhatUserConfig */ module.exports = { solidity: "0.8.17", + settings: { + viaIR: true, // add this + }, preprocess: { eachLine: removeConsoleLog((hre) => hre.network.name !== 'hardhat' && hre.network.name !== 'localhost'), }, diff --git a/test/Webauthn.js b/test/Webauthn.js index 8199c5b..e08489d 100644 --- a/test/Webauthn.js +++ b/test/Webauthn.js @@ -26,9 +26,7 @@ function derToRS(der) { describe("Webauthn", function() { it("Check message", async function() { const Webauthn = await ethers.getContractFactory("Webauthn"); - const webauthn = await Webauthn.deploy(); - await webauthn.deployed(); - + const publicKey = Buffer.from("fdf8bce27f54e06f3aee3b6a542db1ab1f2418d7370a78b150d06965f942b14a470cdee69ab50e610c39b840681bf816b030f4a0a5d5af02ce27dcce6bede89f", "hex"); const signature = Buffer.from("30440220655c9a457615aac594d92fb6d842f0e910e5ee6677cddbcddaea624f3203f0e702207b71a302b06c91a52b9c4ba5a7fb85226738b02c144e8ee177d034022a79c946", "hex"); const authenticatorData = Buffer.from("f8e4b678e1c62f7355266eaa4dc1148573440937063a46d848da1e25babbd20b010000004d", "hex"); @@ -46,7 +44,9 @@ describe("Webauthn", function() { ); expect(result); */ - + const webauthn = await Webauthn.deploy([ ethers.BigNumber.from("0x" + publicKey.slice(0, 32).toString('hex')), ethers.BigNumber.from("0x" + publicKey.slice(32).toString('hex'))]); + await webauthn.deployed(); + const result = await webauthn.validate(authenticatorData, 0x01, clientData, clientChallenge, challengeOffset, [ ethers.BigNumber.from("0x" + signatureParsed[0].toString('hex')), ethers.BigNumber.from("0x" + signatureParsed[1].toString('hex'))], [ ethers.BigNumber.from("0x" + publicKey.slice(0, 32).toString('hex')), ethers.BigNumber.from("0x" + publicKey.slice(32).toString('hex'))] From 45e29991d149fefa19b8bb55034e6569926e6fe1 Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Wed, 1 Mar 2023 18:19:05 +0100 Subject: [PATCH 05/25] cut precomputations in 2 parts --- contracts/Elliptic_ZZ.sol | 301 +++++++++++++++++++++++--------------- contracts/Webauthn.sol | 97 +----------- test/Webauthn.js | 32 +++- 3 files changed, 218 insertions(+), 212 deletions(-) diff --git a/contracts/Elliptic_ZZ.sol b/contracts/Elliptic_ZZ.sol index dae1926..7f9f600 100644 --- a/contracts/Elliptic_ZZ.sol +++ b/contracts/Elliptic_ZZ.sol @@ -2,7 +2,7 @@ ///* Copyright (C) 2022 - Renaud Dubois - This file is part of Cairo_musig2 project */ ///* License: This software is licensed under MIT License */ ///* See LICENSE file at the root folder of the project. */ -///* FILE: ecZZ.solidity */ +///* FILE: ecZZ.sol */ ///* */ ///* */ ///* DESCRIPTION: modified XYZZ system coordinates for EVM elliptic point multiplication @@ -153,7 +153,9 @@ library Ec_ZZ { uint x2, uint y2) internal pure returns (uint P0, uint P1,uint P2,uint P3) { - + if(y1==0){ + return (x2,y2,1,1); + } unchecked{ y1=p-y1;//-Y1 //U2 = X2*ZZ1 @@ -242,7 +244,7 @@ library Ec_ZZ { * @dev Return the zero curve in projective coordinates. */ function zeroProj() internal pure returns (uint x, uint y, uint z) { - return (0, 1, 0); + return (0, 0, 0); } /** @@ -277,7 +279,7 @@ library Ec_ZZ { } function isZeroCurve_proj(uint x0, uint y0, uint z0) internal pure returns (bool isZero) { - if ( (x0 == 0) && (y0 == 1) && (z0==0) ) { + if ( (x0 == 0) && (y0 == 0) && (z0==0) ) { return true; } return false; @@ -287,7 +289,7 @@ library Ec_ZZ { /** * @dev Check if a point in affine coordinates is on the curve. */ - function isOnCurve(uint x, uint y) internal pure returns (bool) { + function ecAff_isOnCurve(uint x, uint y) internal pure returns (bool) { if (0 == x || x == p || 0 == y || y == p) { return false; } @@ -540,7 +542,7 @@ library Ec_ZZ { uint x0, uint y0, uint scalar - ) internal returns (uint x1, uint y1) { + ) internal pure returns (uint x1, uint y1) { if (scalar == 0) { return ecAff_SetZero(); } @@ -551,7 +553,6 @@ library Ec_ZZ { bool flag=false; int index=int(curve_S1); - console.log("--first msb at position of scalar", bit_mul); unchecked { @@ -561,7 +562,6 @@ library Ec_ZZ { bit_mul=bit_mul>>1; index=index-1; } - console.log("--first msb at position of scalar", uint(index)); //R=P x1=x0;y1=y0;zzZZ=1;zzZZZ=1; @@ -585,7 +585,7 @@ library Ec_ZZ { return ecZZ_SetAff(x1, y1, zzZZ, zzZZZ); } - function NormalizedX( uint Gx0, uint Gy0, uint Gz0) internal returns(uint px) + function NormalizedX( uint Gx0, uint Gy0, uint Gz0) internal pure returns(uint px) { if (Gz0 == 0) { return 0; @@ -608,8 +608,8 @@ library Ec_ZZ { uint Qy0, uint scalar_u, uint scalar_v - ) internal returns (uint[3] memory R) { - + ) internal pure returns (uint[3] memory R) { + unchecked{ //1. Precomputation steps: 2 bits window+shamir // precompute all aG+bQ in [0..3][0..3] uint [3][16] memory Window; @@ -646,7 +646,7 @@ library Ec_ZZ { uint quadbit=1; //2. loop over scalars from MSB to LSB: - unchecked{ + for(uint8 i=0;i<128;i++) { uint8 rshift=255-(2*i); @@ -673,13 +673,12 @@ library Ec_ZZ { uint Qy0, uint scalar_u, uint scalar_v - ) internal returns (uint[3] memory R) { + ) internal pure returns (uint[3] memory R) { uint[3] memory H;//G+Q (H[0],H[1], H[2] )=addProj(Gx0, Gy0, 1, Qx0, Qy0, 1); - console.log("H=", H[0], H[1], H[2]); - + uint dibit; @@ -692,10 +691,7 @@ library Ec_ZZ { uint i=255-index; - if (isZeroCurve_proj(R[0],R[1],R[2])){ - } - - + (R[0],R[1],R[2])=twiceProj(R[0],R[1],R[2]);//double if (isZeroCurve_proj(R[0],R[1],R[2])){ @@ -732,7 +728,7 @@ library Ec_ZZ { uint Qy0, uint scalar_u, uint scalar_v - ) internal returns (uint R0, uint R1) { + ) internal pure returns (uint R0, uint R1) { Indexing_t memory Ind; uint[2] memory R; @@ -744,7 +740,7 @@ library Ec_ZZ { { Ind.i=255-Ind.index; - while( ((scalar_u>>Ind.i)&1)+2*((scalar_v>>Ind.i)&1) ==0){ + while( ( ((scalar_u>>Ind.i)&1)+2*((scalar_v>>Ind.i)&1) ) ==0){ Ind.index=Ind.index+1; Ind.i=255-Ind.index; } @@ -787,62 +783,120 @@ library Ec_ZZ { } } } + (R0,R1)=ecZZ_SetAff(R0 ,R1,R[0], R[1]); return (R0 ,R1); } /** * @dev 8 dimensional Shamir/Pippinger, will be done externally, validation purpose only */ - function Precalc_Shamir8( uint[2] memory Q) internal pure returns( uint[2][256] memory Prec) + function Precalc_Shamir8_part1( uint[2] memory Q) internal returns( uint[2][256] memory Prec) { uint index; - uint[2][8] memory PowerPQ; + uint[2][8] memory Pow64_PQ; //store P, 64P, 128P, 192P, Q, 64Q, 128Q, 192Q - PowerPQ[0][0]=gx; - PowerPQ[0][1]=gy; - PowerPQ[4][0]=Q[0]; - PowerPQ[4][1]=Q[1]; + Pow64_PQ[0][0]=gx; + Pow64_PQ[0][1]=gy; + Pow64_PQ[4][0]=Q[0]; + Pow64_PQ[4][1]=Q[1]; - for(uint i=1;i<4;i++){ - (PowerPQ[i][0], PowerPQ[i][1])=twice(PowerPQ[i-1][0], PowerPQ[i-1][1]); - (PowerPQ[i+4][0], PowerPQ[i+4][1])=twice(PowerPQ[i+3][0], PowerPQ[i+3][1]); + /* raise to multiplication by 64 by 6 consecutive doubling*/ + for(uint j=1;j<4;j++){ + (Pow64_PQ[j][0], Pow64_PQ[j][1])=twice(Pow64_PQ[j-1][0], Pow64_PQ[j-1][1]); + (Pow64_PQ[j+4][0], Pow64_PQ[j+4][1])=twice(Pow64_PQ[j+3][0], Pow64_PQ[j+3][1]); + + for(uint i=0;i<6;i++){ + (Pow64_PQ[j][0], Pow64_PQ[j][1])=twice(Pow64_PQ[j][0], Pow64_PQ[j][1]); + (Pow64_PQ[j+4][0], Pow64_PQ[j+4][1])=twice(Pow64_PQ[j+4][0], Pow64_PQ[j+4][1]); + } } + + /* neutral point */ + Prec[0][0]=0; + Prec[0][1]=0; + - for(uint i=1;i<256;i++) + for(uint i=1;i<128;i++) { Prec[i][0]=0; Prec[i][1]=0; for(uint j=0;j<8;j++) { - if(i&(1<>index)+64*(scalar_v>>(index-64))+32*(scalar_v>>(index-128))+16*(scalar_v>>(index-192))+ - 8*(scalar_u>>index)+4*(scalar_u>>(index-64))+2*(scalar_u>>(index-128))+(scalar_u>>(index-192)); + unchecked{ + + octobit=16*((scalar_v>>index)&1)+32*((scalar_v>>(index-64))&1)+64*((scalar_v>>(index-128))&1)+128*((scalar_v>>(index-192))&1)+ + ((scalar_u>>index)&1)+2*((scalar_u>>(index-64))&1)+4*((scalar_u>>(index-128))&1)+8*((scalar_u>>(index-192))&1); + (x,y,R[0], R[1])= (Shamir8[octobit][0], Shamir8[octobit][1],1,1); //loop over 1/4 of scalars for(index=254; index>=192; index--) { - octobit=128*(scalar_v>>index)+64*(scalar_v>>(index-64))+32*(scalar_v>>(index-128))+16*(scalar_v>>(index-192))+ - 8*(scalar_u>>index)+4*(scalar_u>>(index-64))+2*(scalar_u>>(index-128))+(scalar_u>>(index-192)); - + octobit=16*((scalar_v>>index)&1)+32*((scalar_v>>(index-64))&1)+64*((scalar_v>>(index-128))&1)+128*((scalar_v>>(index-192))&1)+ + ((scalar_u>>index)&1)+2*((scalar_u>>(index-64))&1)+4*((scalar_u>>(index-128))&1)+8*((scalar_u>>(index-192))&1); + (x,y,R[0], R[1])=ecZZ_AddN( x,y,R[0], R[1], Shamir8[octobit][0], Shamir8[octobit][1]); } (x,y)=ecZZ_SetAff(x,y,R[0], R[1]); + } } /** @@ -854,49 +908,91 @@ library Ec_ZZ { return multiplyScalar(gx, gy, scalar); } - function test_ecZZ_formulae() internal returns(bool) + + /* testing validity of XYZZ coordinates*/ + function test_ecZZ_formulae( bytes32 message, + uint[2] memory rs, + uint[2] memory Q) internal returns(bool) { uint[4] memory P2ZZ; (P2ZZ[0], P2ZZ[1],P2ZZ[2],P2ZZ[3])=ecZZ_Dbl(gx, gy, 1,1); uint[2] memory P2; (P2[0], P2[1])=twice(gx, gy); + + uint[2] memory P2zz; + (P2zz[0], P2zz[1])=ecZZ_SetAff(P2ZZ[0], P2ZZ[1],P2ZZ[2],P2ZZ[3]); + bool isonc=ecAff_isOnCurve(P2zz[0], P2zz[1]); - + + (P2ZZ[0], P2ZZ[1],P2ZZ[2],P2ZZ[3])=ecZZ_AddN( P2ZZ[0], P2ZZ[1],P2ZZ[2],P2ZZ[3], gx, gy);//3P in ZZ coordinates + (P2zz[0], P2zz[1])=ecZZ_SetAff(P2ZZ[0], P2ZZ[1],P2ZZ[2],P2ZZ[3]); + isonc=ecAff_isOnCurve(P2zz[0], P2zz[1]); - /* test normalization*/ - /* - P2ZZ[2]=mulmod(0xFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF, 0xFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF, p); - P2ZZ[3]=mulmod(P2ZZ[2], 0xFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF, p); - P2ZZ[0]=mulmod(gx, P2ZZ[2],p); - P2ZZ[1]=mulmod(gy, P2ZZ[3],p); - */ - console.log("-- pre normalization %s", P2ZZ[0], P2ZZ[1]); - console.log("-- pre normalization %s", P2ZZ[2], P2ZZ[3]); + (P2[0], P2[1])=add(P2[0], P2[1], gx, gy); + + console.log("Unitary testing Dbl+Add: 3P via ZZ == 3P via Aff ?", P2zz [0]==P2 [0]); + + uint sInv = inverseMod(rs[1], n); + uint scalar_u=mulmod(uint(message), sInv, n); + uint scalar_v= mulmod(rs[0], sInv, n); + + uint[4] memory res_aff; + uint[4] memory res_zz; - uint[2] memory P2zz; - (P2zz[0], P2zz[1])=ecZZ_SetAff(P2ZZ[0], P2ZZ[1],P2ZZ[2],P2ZZ[3]); - bool isonc=isOnCurve(P2zz[0], P2zz[1]); - console.log("--is on curve", isonc); + (res_aff[0], res_aff[1]) = multiplyScalar(gx, gy, scalar_u); + (res_aff[2], res_aff[3]) = multiplyScalar(Q[0], Q[1], scalar_v); + //uint[3] memory PAff = addAndReturnProjectivePoint(x1, y1, x2, y2); - console.log("res 1 post norm:", P2zz [0], P2zz[1]); - console.log("res 2:", P2 [0], P2[1]); + (res_zz[0], res_zz[1]) = ecZZ_mul(gx, gy, scalar_u); + (res_zz[2], res_zz[3]) = ecZZ_mul(Q[0], Q[1], scalar_v); + console.log("Unitary testing: ec_mulvia ZZ == ec_mul via Aff ?", res_zz[0]==res_aff[0]); - (P2ZZ[0], P2ZZ[1],P2ZZ[2],P2ZZ[3])=ecZZ_AddN( P2ZZ[0], P2ZZ[1],P2ZZ[2],P2ZZ[3], gx, gy);//3P in ZZ coordinates - (P2zz[0], P2zz[1])=ecZZ_SetAff(P2ZZ[0], P2ZZ[1],P2ZZ[2],P2ZZ[3]); - isonc=isOnCurve(P2zz[0], P2zz[1]); + return (P2zz [0]==P2 [0]); + } + /* testing validity of Shamir mulmuladd*/ + function test_Shamir( bytes32 message, + uint[2] memory rs, + uint[2] memory Q) internal returns(bool) + { + uint sInv = inverseMod(rs[1], n); + uint scalar_u=mulmod(uint(message), sInv, n); + uint scalar_v= mulmod(rs[0], sInv, n); - console.log("--is on curve", isonc); - (P2[0], P2[1])=add(P2[0], P2[1], gx, gy); + + // without Optim + uint x1; + uint x2; + uint y1; + uint y2; + + //naive projective + (x1, y1) = multiplyScalar(gx, gy, scalar_u); + (x2, y2) = multiplyScalar(Q[0], Q[1], scalar_v); + uint[3] memory PAff = addAndReturnProjectivePoint(x1, y1, x2, y2); + console.log("res naive projective mulmuladd:", PAff[0]); + + //Projective Shamir monobit + uint[3] memory P = ec_mulmuladd_S(gx, gy, Q[0], Q[1],scalar_u ,scalar_v ); + uint Px=NormalizedX(P[0], P[1], P[2]); + console.log("res Shamir monobit projective mulmuladd:", Px); + + //Projective Shamir, windowed + P = ec_mulmuladd_W(gx, gy, Q[0], Q[1],scalar_u ,scalar_v ); + Px=NormalizedX(P[0], P[1], P[2]); + console.log("res Shamir windowed projective mulmuladd:", Px); + + //XYZZ Shamir, monobit + (P[0], P[1]) = ecZZ_mulmuladd_S(gx, gy, Q[0], Q[1],scalar_u ,scalar_v ); + console.log("res Shamir monobit XYZZ mulmuladd:", P[0]); + + } + - console.log("3P via ZZ", P2zz [0], P2zz[1]); - console.log("3P via Aff:", P2 [0], P2[1]); - return true; - } /** * @dev Validate combination of message, signature, and public key. */ @@ -908,7 +1004,7 @@ library Ec_ZZ { if (rs[0] == 0 || rs[0] >= n || rs[1] == 0) { return false; } - if (!isOnCurve(Q[0], Q[1])) { + if (!ecAff_isOnCurve(Q[0], Q[1])) { return false; } @@ -917,61 +1013,52 @@ library Ec_ZZ { uint sInv = inverseMod(rs[1], n); uint scalar_u=mulmod(uint(message), sInv, n); uint scalar_v= mulmod(rs[0], sInv, n); - - // without Optim - - + + //test_ecZZ_formulae(message, rs, Q); + //test_Shamir(message, rs, Q); uint x1; - uint x2; uint y1; + + /* + // without Optim + uint x2; uint y2; - - /* + (x1, y1) = multiplyScalar(gx, gy, scalar_u); (x2, y2) = multiplyScalar(Q[0], Q[1], scalar_v); - //uint[3] memory PAff = addAndReturnProjectivePoint(x1, y1, x2, y2); - - console.log("res naive Aff mul1:", x1, y1); - console.log("res naive Aff mul2:", x2, y2); + uint[3] memory PAff = addAndReturnProjectivePoint(x1, y1, x2, y2); + + return PAff[0] % n == rs[0]; */ - - // (x1, y1) = ecZZ_mul(gx, gy, scalar_u); - // (x2, y2) = ecZZ_mul(Q[0], Q[1], scalar_v); - - - // console.log("res naive ZZ:", x1, y1); - // console.log("res naive ZZ:", x2, y2); - - //test_ecZZ_formulae(); - - //Shamir 2 dimensions - (x1, y1)=ecZZ_mulmuladd_S(gx, gy, Q[0], Q[1],scalar_u, scalar_v); - + //(x1, y1,scalar_u)=ec_mulmuladd_W(gx, gy, Q[0], Q[1],scalar_u, scalar_v); + (x1, y1)=ecZZ_mulmuladd_S(gx, gy, Q[0], Q[1],scalar_u, scalar_v); + return x1 % n == rs[0]; + - //uint[3] memory P = ec_mulmuladd_W(gx, gy, Q[0], Q[1],scalar_u ,scalar_v ); - //uint Px=NormalizedX(P[0], P[1], P[2]); - - //return Px % n == rs[0]; - return true; } function validateSignature_Precomputed( bytes32 message, uint[2] memory rs, + + uint[2][256] memory Shamir8 ) internal returns (bool) { - uint[2] memory Q;//extract Q from Shamir8 - + + uint[2] memory Q; + Q[0]=Shamir8[4][0];//extract Q from Shamir8 + Q[1]=Shamir8[4][1]; if (rs[0] == 0 || rs[0] >= n || rs[1] == 0) { return false; } + /* if (!isOnCurve(Q[0], Q[1])) { return false; } - + */ uint sInv = inverseMod(rs[1], n); @@ -987,22 +1074,6 @@ library Ec_ZZ { uint y1; uint y2; - /* - (x1, y1) = multiplyScalar(gx, gy, scalar_u); - (x2, y2) = multiplyScalar(Q[0], Q[1], scalar_v); - //uint[3] memory PAff = addAndReturnProjectivePoint(x1, y1, x2, y2); - - console.log("res naive Aff mul1:", x1, y1); - console.log("res naive Aff mul2:", x2, y2); - */ - - - // (x1, y1) = ecZZ_mul(gx, gy, scalar_u); - // (x2, y2) = ecZZ_mul(Q[0], Q[1], scalar_v); - - - // console.log("res naive ZZ:", x1, y1); - // console.log("res naive ZZ:", x2, y2); //test_ecZZ_formulae(); @@ -1010,7 +1081,7 @@ library Ec_ZZ { //(x1, y1)=ecZZ_mulmuladd_S(gx, gy, Q[0], Q[1],scalar_u, scalar_v); //Shamir 8 dimensions (x1, y1)=ecZZ_mulmuladd_S8(scalar_u, scalar_v, Shamir8); - + console.log("res Shamir 8dim precomputed XYZZ mulmuladd:",x1); //uint[3] memory P = ec_mulmuladd_W(gx, gy, Q[0], Q[1],scalar_u ,scalar_v ); //uint Px=NormalizedX(P[0], P[1], P[2]); diff --git a/contracts/Webauthn.sol b/contracts/Webauthn.sol index 57c4465..23b4431 100644 --- a/contracts/Webauthn.sol +++ b/contracts/Webauthn.sol @@ -12,14 +12,6 @@ error InvalidSignature(); contract Webauthn { uint256 public counter; - //precomputations - uint[2][256] Shamir8; - - constructor(uint[2] memory Q){ - Shamir8=Ec_ZZ.Precalc_Shamir8(Q); - - } - function checkSignature( bytes memory authenticatorData, @@ -80,6 +72,8 @@ contract Webauthn { return result; } + + function validate( bytes memory authenticatorData, bytes1 authenticatorDataFlagMask, @@ -104,94 +98,7 @@ contract Webauthn { } counter++; } -/* - function checkSignature_prec( - bytes memory authenticatorData, - bytes1 authenticatorDataFlagMask, - bytes memory clientData, - bytes32 clientChallenge, - uint clientChallengeDataOffset, - uint[2] memory rs, - uint[2][256] memory Shamir8 - ) public returns (bool) { - // Let the caller check if User Presence (0x01) or User Verification (0x04) are set - if ( - (authenticatorData[32] & authenticatorDataFlagMask) != - authenticatorDataFlagMask - ) { - revert InvalidAuthenticatorData(); - } - // Verify that clientData commits to the expected client challenge - string memory challengeEncoded = Base64URL.encode32( - abi.encodePacked(clientChallenge) - ); - bytes memory challengeExtracted = new bytes( - bytes(challengeEncoded).length - ); - copyBytes( - clientData, - clientChallengeDataOffset, - challengeExtracted.length, - challengeExtracted, - 0 - ); - if ( - keccak256(abi.encodePacked(bytes(challengeEncoded))) != - keccak256(abi.encodePacked(challengeExtracted)) - ) { - revert InvalidClientData(); - } - // Verify the signature over sha256(authenticatorData || sha256(clientData)) - bytes memory verifyData = new bytes(authenticatorData.length + 32); - copyBytes( - authenticatorData, - 0, - authenticatorData.length, - verifyData, - 0 - ); - copyBytes( - abi.encodePacked(sha256(clientData)), - 0, - 32, - verifyData, - authenticatorData.length - ); - bytes32 message = sha256(verifyData); - bool result=Ec_ZZ.validateSignature_Precomputed(message, rs, Shamir8); - console.log("result= %s", result); - return result; - } - - function validate_prec( - bytes memory authenticatorData, - bytes1 authenticatorDataFlagMask, - bytes memory clientData, - bytes32 clientChallenge, - uint clientChallengeDataOffset, - uint[2] memory rs, - uint[2] memory Q - ) public { - if ( - !checkSignature_prec( - authenticatorData, - authenticatorDataFlagMask, - clientData, - clientChallenge, - clientChallengeDataOffset, - rs, - Q - ) - ) { - revert InvalidSignature(); - } - counter++; - } - - - The following function has been written by Alex Beregszaszi (@axic), use it under the terms of the MIT license - */ function copyBytes( bytes memory _from, uint _fromOffset, diff --git a/test/Webauthn.js b/test/Webauthn.js index e08489d..fb58148 100644 --- a/test/Webauthn.js +++ b/test/Webauthn.js @@ -24,6 +24,12 @@ function derToRS(der) { } describe("Webauthn", function() { + + const ECDSA = require('ecdsa-secp256r1'); + const privateKey = ECDSA.generateKey() + + // window.print(privateKey); + it("Check message", async function() { const Webauthn = await ethers.getContractFactory("Webauthn"); @@ -44,7 +50,11 @@ describe("Webauthn", function() { ); expect(result); */ - const webauthn = await Webauthn.deploy([ ethers.BigNumber.from("0x" + publicKey.slice(0, 32).toString('hex')), ethers.BigNumber.from("0x" + publicKey.slice(32).toString('hex'))]); + +//uncomment for no precomputation validation + +/* + const webauthn = await Webauthn.deploy(); await webauthn.deployed(); const result = await webauthn.validate(authenticatorData, 0x01, clientData, clientChallenge, challengeOffset, @@ -52,7 +62,25 @@ describe("Webauthn", function() { [ ethers.BigNumber.from("0x" + publicKey.slice(0, 32).toString('hex')), ethers.BigNumber.from("0x" + publicKey.slice(32).toString('hex'))] ); await result.wait(); - + */ +//uncomment for with precomputation validation + + const Webauthn_prec = await ethers.getContractFactory("Webauthn_prec"); + + const webauthn_prec = await Webauthn_prec.deploy([ ethers.BigNumber.from("0x" + publicKey.slice(0, 32).toString('hex')), ethers.BigNumber.from("0x" + publicKey.slice(32).toString('hex'))]); + + + const webauthn_prec2 = await webauthn_prec.deploy_part2([ ethers.BigNumber.from("0x" + publicKey.slice(0, 32).toString('hex')), ethers.BigNumber.from("0x" + publicKey.slice(32).toString('hex'))]); + + + + const result2 = await webauthn_prec2.validate_prec(authenticatorData, 0x01, clientData, clientChallenge, challengeOffset, + [ ethers.BigNumber.from("0x" + signatureParsed[0].toString('hex')), ethers.BigNumber.from("0x" + signatureParsed[1].toString('hex'))] + + ); + + await result2.wait(); + }) }); From 301dc7c7753dd1eb00f2479998742104ce3d662e Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Wed, 1 Mar 2023 18:20:08 +0100 Subject: [PATCH 06/25] precomputations --- contracts/Webauthn_prec.sol | 147 ++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 contracts/Webauthn_prec.sol diff --git a/contracts/Webauthn_prec.sol b/contracts/Webauthn_prec.sol new file mode 100644 index 0000000..b1dbbac --- /dev/null +++ b/contracts/Webauthn_prec.sol @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import {Base64URL} from "./Base64URL.sol"; +import {Ec_ZZ} from "./Elliptic_ZZ.sol"; +import "hardhat/console.sol"; + +error InvalidAuthenticatorData(); +error InvalidClientData(); +error InvalidSignature(); + +contract Webauthn_prec { + uint256 public counter; + + //precomputations + uint[2][256] Shamir8; + + constructor(uint[2] memory Q){ + + Shamir8=Ec_ZZ.Precalc_Shamir8_part1(Q); + console.log("Precompute part 1 done"); + } + + function deploy_part2(uint[2] memory Q) public returns (bool) + { + + uint[2][128] memory Prec2;//=Ec_ZZ.Precalc_Shamir8_part2(Q); + + + for(uint i=128;i<256;i++) + { + Shamir8[i][0]=Prec2[i-128][0]; + Shamir8[i][1]=Prec2[i-128][1]; + } + + console.log("Precompute part 2 done"); + + return true; + } + + function checkSignature_prec( + bytes memory authenticatorData, + bytes1 authenticatorDataFlagMask, + bytes memory clientData, + bytes32 clientChallenge, + uint clientChallengeDataOffset, + uint[2] memory rs, + uint[2][256] memory Shamir8 + ) public returns (bool) { + // Let the caller check if User Presence (0x01) or User Verification (0x04) are set + if ( + (authenticatorData[32] & authenticatorDataFlagMask) != + authenticatorDataFlagMask + ) { + revert InvalidAuthenticatorData(); + } + // Verify that clientData commits to the expected client challenge + string memory challengeEncoded = Base64URL.encode32( + abi.encodePacked(clientChallenge) + ); + bytes memory challengeExtracted = new bytes( + bytes(challengeEncoded).length + ); + copyBytes( + clientData, + clientChallengeDataOffset, + challengeExtracted.length, + challengeExtracted, + 0 + ); + if ( + keccak256(abi.encodePacked(bytes(challengeEncoded))) != + keccak256(abi.encodePacked(challengeExtracted)) + ) { + revert InvalidClientData(); + } + // Verify the signature over sha256(authenticatorData || sha256(clientData)) + bytes memory verifyData = new bytes(authenticatorData.length + 32); + copyBytes( + authenticatorData, + 0, + authenticatorData.length, + verifyData, + 0 + ); + copyBytes( + abi.encodePacked(sha256(clientData)), + 0, + 32, + verifyData, + authenticatorData.length + ); + bytes32 message = sha256(verifyData); + bool result=Ec_ZZ.validateSignature_Precomputed(message, rs, Shamir8); + console.log("result= %s", result); + + return result; + } + + function validate_prec( + bytes memory authenticatorData, + bytes1 authenticatorDataFlagMask, + bytes memory clientData, + bytes32 clientChallenge, + uint clientChallengeDataOffset, + uint[2] memory rs + + ) public { + if ( + !checkSignature_prec( + authenticatorData, + authenticatorDataFlagMask, + clientData, + clientChallenge, + clientChallengeDataOffset, + rs, + Shamir8 + ) + ) { + revert InvalidSignature(); + } + counter++; + } + + + function copyBytes( + bytes memory _from, + uint _fromOffset, + uint _length, + bytes memory _to, + uint _toOffset + ) internal pure returns (bytes memory _copiedBytes) { + uint minLength = _length + _toOffset; + require(_to.length >= minLength); // Buffer too small. Should be a better way? + uint i = 32 + _fromOffset; // NOTE: the offset 32 is added to skip the `size` field of both bytes variables + uint j = 32 + _toOffset; + while (i < (32 + _fromOffset + _length)) { + assembly { + let tmp := mload(add(_from, i)) + mstore(add(_to, j), tmp) + } + i += 32; + j += 32; + } + return _to; + } +} From ade0a6afac5e0798fb4d2f356417ad893c581c34 Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Thu, 2 Mar 2023 14:38:45 +0100 Subject: [PATCH 07/25] Shamir's trick with 8 points tested OK --- contracts/Elliptic_ZZ.sol | 71 +++++++++++++++++++++++-------------- contracts/Webauthn_prec.sol | 10 +++--- test/Webauthn.js | 8 +++-- 3 files changed, 56 insertions(+), 33 deletions(-) diff --git a/contracts/Elliptic_ZZ.sol b/contracts/Elliptic_ZZ.sol index 7f9f600..6df2332 100644 --- a/contracts/Elliptic_ZZ.sol +++ b/contracts/Elliptic_ZZ.sol @@ -224,7 +224,7 @@ library Ec_ZZ { /** * @dev Transform from jacobian to affine coordinates. */ - function toAffinePoint_FromJac( + function ecJAc_SetAff( uint x0, uint y0, uint z0 @@ -256,7 +256,7 @@ library Ec_ZZ { function ecZZ_IsZero (uint x0, uint y0, uint zz0, uint zzz0) internal pure returns (bool) { - if ( (x0 == 0) && (y0 == 0) && (zz0==0) && (zzz0==0) ) { + if ( (y0 == 0) ) { return true; } return false; @@ -272,14 +272,14 @@ library Ec_ZZ { * @dev Check if the curve is the zero curve in affine rep. */ function isZeroCurve_affine(uint x0, uint y0) internal pure returns (bool isZero) { - if (x0 == 0 && y0 == 0) { + if (y0 == 0 ) { return true; } return false; } function isZeroCurve_proj(uint x0, uint y0, uint z0) internal pure returns (bool isZero) { - if ( (x0 == 0) && (y0 == 0) && (z0==0) ) { + if ( (y0 == 0) ) { return true; } return false; @@ -797,19 +797,22 @@ library Ec_ZZ { Pow64_PQ[0][0]=gx; Pow64_PQ[0][1]=gy; + Pow64_PQ[4][0]=Q[0]; Pow64_PQ[4][1]=Q[1]; - /* raise to multiplication by 64 by 6 consecutive doubling*/ for(uint j=1;j<4;j++){ (Pow64_PQ[j][0], Pow64_PQ[j][1])=twice(Pow64_PQ[j-1][0], Pow64_PQ[j-1][1]); + (Pow64_PQ[j+4][0], Pow64_PQ[j+4][1])=twice(Pow64_PQ[j+3][0], Pow64_PQ[j+3][1]); - for(uint i=0;i<6;i++){ + for(uint i=0;i<63;i++){ (Pow64_PQ[j][0], Pow64_PQ[j][1])=twice(Pow64_PQ[j][0], Pow64_PQ[j][1]); (Pow64_PQ[j+4][0], Pow64_PQ[j+4][1])=twice(Pow64_PQ[j+4][0], Pow64_PQ[j+4][1]); } + + } /* neutral point */ @@ -827,7 +830,9 @@ library Ec_ZZ { if( (i&(1<>index)&1)+32*((scalar_v>>(index-64))&1)+64*((scalar_v>>(index-128))&1)+128*((scalar_v>>(index-192))&1)+ ((scalar_u>>index)&1)+2*((scalar_u>>(index-64))&1)+4*((scalar_u>>(index-128))&1)+8*((scalar_u>>(index-192))&1); + */ + octobit=128*((scalar_v>>index)&1)+64*((scalar_v>>(index-64))&1)+32*((scalar_v>>(index-128))&1)+16*((scalar_v>>(index-192))&1)+ + 8*((scalar_u>>index)&1)+4*((scalar_u>>(index-64))&1)+2*((scalar_u>>(index-128))&1)+1*((scalar_u>>(index-192))&1); + - + (x,y,R[0], R[1])= (Shamir8[octobit][0], Shamir8[octobit][1],1,1); //loop over 1/4 of scalars for(index=254; index>=192; index--) { - octobit=16*((scalar_v>>index)&1)+32*((scalar_v>>(index-64))&1)+64*((scalar_v>>(index-128))&1)+128*((scalar_v>>(index-192))&1)+ - ((scalar_u>>index)&1)+2*((scalar_u>>(index-64))&1)+4*((scalar_u>>(index-128))&1)+8*((scalar_u>>(index-192))&1); - + (x,y,R[0], R[1])=ecZZ_Dbl( x,y,R[0], R[1]); + + octobit=128*((scalar_v>>index)&1)+64*((scalar_v>>(index-64))&1)+32*((scalar_v>>(index-128))&1)+16*((scalar_v>>(index-192))&1)+ + 8*((scalar_u>>index)&1)+4*((scalar_u>>(index-64))&1)+2*((scalar_u>>(index-128))&1)+1*((scalar_u>>(index-192))&1); + (x,y,R[0], R[1])=ecZZ_AddN( x,y,R[0], R[1], Shamir8[octobit][0], Shamir8[octobit][1]); } (x,y)=ecZZ_SetAff(x,y,R[0], R[1]); + } + + } /** @@ -991,7 +1009,11 @@ library Ec_ZZ { } - + function test_Aff_formulae(uint[2] memory Q) internal returns (bool) + { + + return true; + } /** * @dev Validate combination of message, signature, and public key. @@ -1035,6 +1057,9 @@ library Ec_ZZ { //(x1, y1,scalar_u)=ec_mulmuladd_W(gx, gy, Q[0], Q[1],scalar_u, scalar_v); (x1, y1)=ecZZ_mulmuladd_S(gx, gy, Q[0], Q[1],scalar_u, scalar_v); + + console.log("res Shamir monobit XYZZ mulmuladd:",x1); + return x1 % n == rs[0]; @@ -1064,11 +1089,6 @@ library Ec_ZZ { uint sInv = inverseMod(rs[1], n); uint scalar_u=mulmod(uint(message), sInv, n); uint scalar_v= mulmod(rs[0], sInv, n); - - // without Optim - - - uint x1; uint x2; uint y1; @@ -1080,12 +1100,11 @@ library Ec_ZZ { //Shamir 2 dimensions //(x1, y1)=ecZZ_mulmuladd_S(gx, gy, Q[0], Q[1],scalar_u, scalar_v); //Shamir 8 dimensions - (x1, y1)=ecZZ_mulmuladd_S8(scalar_u, scalar_v, Shamir8); + (x1, y1)=ecZZ_mulmuladd_S8(scalar_u, scalar_v, Shamir8); console.log("res Shamir 8dim precomputed XYZZ mulmuladd:",x1); //uint[3] memory P = ec_mulmuladd_W(gx, gy, Q[0], Q[1],scalar_u ,scalar_v ); //uint Px=NormalizedX(P[0], P[1], P[2]); - //return Px % n == rs[0]; - return true; + return x1 % n == rs[0]; } } diff --git a/contracts/Webauthn_prec.sol b/contracts/Webauthn_prec.sol index b1dbbac..6584bac 100644 --- a/contracts/Webauthn_prec.sol +++ b/contracts/Webauthn_prec.sol @@ -23,9 +23,9 @@ contract Webauthn_prec { function deploy_part2(uint[2] memory Q) public returns (bool) { - - uint[2][128] memory Prec2;//=Ec_ZZ.Precalc_Shamir8_part2(Q); + unchecked{ + uint[2][128] memory Prec2=Ec_ZZ.Precalc_Shamir8_part2(Q); for(uint i=128;i<256;i++) { @@ -33,8 +33,10 @@ contract Webauthn_prec { Shamir8[i][1]=Prec2[i-128][1]; } - console.log("Precompute part 2 done"); - + console.log("Precompute part 1 done"); + + } + return true; } diff --git a/test/Webauthn.js b/test/Webauthn.js index fb58148..f0a9042 100644 --- a/test/Webauthn.js +++ b/test/Webauthn.js @@ -53,7 +53,7 @@ describe("Webauthn", function() { //uncomment for no precomputation validation -/* + const webauthn = await Webauthn.deploy(); await webauthn.deployed(); @@ -62,10 +62,12 @@ describe("Webauthn", function() { [ ethers.BigNumber.from("0x" + publicKey.slice(0, 32).toString('hex')), ethers.BigNumber.from("0x" + publicKey.slice(32).toString('hex'))] ); await result.wait(); - */ + //uncomment for with precomputation validation + + const Webauthn_prec = await ethers.getContractFactory("Webauthn_prec"); const webauthn_prec = await Webauthn_prec.deploy([ ethers.BigNumber.from("0x" + publicKey.slice(0, 32).toString('hex')), ethers.BigNumber.from("0x" + publicKey.slice(32).toString('hex'))]); @@ -75,7 +77,7 @@ describe("Webauthn", function() { - const result2 = await webauthn_prec2.validate_prec(authenticatorData, 0x01, clientData, clientChallenge, challengeOffset, + const result2 = await webauthn_prec.validate_prec(authenticatorData, 0x01, clientData, clientChallenge, challengeOffset, [ ethers.BigNumber.from("0x" + signatureParsed[0].toString('hex')), ethers.BigNumber.from("0x" + signatureParsed[1].toString('hex'))] ); From f874ae8eb753db8ca9b34b306d7acfbbb03f5190 Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Mon, 6 Mar 2023 14:02:25 +0100 Subject: [PATCH 08/25] Adding external js test module --- contracts/Elliptic_ZZ.sol | 13 ++++--- contracts/Webauthn.sol | 17 +++++++++ package-lock.json | 43 ++++++++++++++++++---- package.json | 3 ++ test/Webauthn.js | 76 ++++++++++++++++++++++++++++++++++++--- 5 files changed, 136 insertions(+), 16 deletions(-) diff --git a/contracts/Elliptic_ZZ.sol b/contracts/Elliptic_ZZ.sol index 6df2332..c8d089c 100644 --- a/contracts/Elliptic_ZZ.sol +++ b/contracts/Elliptic_ZZ.sol @@ -1,8 +1,13 @@ -//*************************************************************************************/ -///* Copyright (C) 2022 - Renaud Dubois - This file is part of Cairo_musig2 project */ +//********************************************************************************************/ +// ___ _ ___ _ _ _ _ +// | __| _ ___ __| |_ / __|_ _ _ _ _ __| |_ ___ | | (_) |__ +// | _| '_/ -_|_-< ' \ | (__| '_| || | '_ \ _/ _ \ | |__| | '_ \ +// |_||_| \___/__/_||_| \___|_| \_, | .__/\__\___/ |____|_|_.__/ +// |__/|_| +///* Copyright (C) 2022 - Renaud Dubois - This file is part of FCL (Fresh CryptoLib) project */ ///* License: This software is licensed under MIT License */ ///* See LICENSE file at the root folder of the project. */ -///* FILE: ecZZ.sol */ +///* FILE: Elliptic_ZZ.sol */ ///* */ ///* */ ///* DESCRIPTION: modified XYZZ system coordinates for EVM elliptic point multiplication @@ -830,7 +835,6 @@ library Ec_ZZ { if( (i&(1< Date: Tue, 7 Mar 2023 19:35:52 +0100 Subject: [PATCH 09/25] precomputation of table with sage script --- contracts/Elliptic_ZZ.sol | 9 ++-- contracts/Webauthn.sol | 2 + contracts/Webauthn_prec.sol | 83 ++++++++++++++++++++++++++++--- sage/Webauthn_precompute.sage | 94 +++++++++++++++++++++++++++++++++++ test/precomputed.js | 5 ++ 5 files changed, 181 insertions(+), 12 deletions(-) create mode 100644 sage/Webauthn_precompute.sage create mode 100644 test/precomputed.js diff --git a/contracts/Elliptic_ZZ.sol b/contracts/Elliptic_ZZ.sol index c8d089c..83743b1 100644 --- a/contracts/Elliptic_ZZ.sol +++ b/contracts/Elliptic_ZZ.sol @@ -1057,9 +1057,12 @@ library Ec_ZZ { return PAff[0] % n == rs[0]; */ - + //Shamir 2 dimensions, windowed //(x1, y1,scalar_u)=ec_mulmuladd_W(gx, gy, Q[0], Q[1],scalar_u, scalar_v); + //Shamir 2 dimensions + //(x1, y1)=ecZZ_mulmuladd_S(gx, gy, Q[0], Q[1],scalar_u, scalar_v); + (x1, y1)=ecZZ_mulmuladd_S(gx, gy, Q[0], Q[1],scalar_u, scalar_v); console.log("res Shamir monobit XYZZ mulmuladd:",x1); @@ -1100,9 +1103,7 @@ library Ec_ZZ { //test_ecZZ_formulae(); - //Shamir 2 dimensions - //(x1, y1)=ecZZ_mulmuladd_S(gx, gy, Q[0], Q[1],scalar_u, scalar_v); - //Shamir 8 dimensions + //Shamir 8 dimensions (x1, y1)=ecZZ_mulmuladd_S8(scalar_u, scalar_v, Shamir8); console.log("res Shamir 8dim precomputed XYZZ mulmuladd:",x1); //uint[3] memory P = ec_mulmuladd_W(gx, gy, Q[0], Q[1],scalar_u ,scalar_v ); diff --git a/contracts/Webauthn.sol b/contracts/Webauthn.sol index 2d8d603..6e0a71b 100644 --- a/contracts/Webauthn.sol +++ b/contracts/Webauthn.sol @@ -5,6 +5,8 @@ import {Base64URL} from "./Base64URL.sol"; import {Ec_ZZ} from "./Elliptic_ZZ.sol"; import "hardhat/console.sol"; + + error InvalidAuthenticatorData(); error InvalidClientData(); error InvalidSignature(); diff --git a/contracts/Webauthn_prec.sol b/contracts/Webauthn_prec.sol index 6584bac..875ebfb 100644 --- a/contracts/Webauthn_prec.sol +++ b/contracts/Webauthn_prec.sol @@ -5,23 +5,76 @@ import {Base64URL} from "./Base64URL.sol"; import {Ec_ZZ} from "./Elliptic_ZZ.sol"; import "hardhat/console.sol"; +import "solmate/src/utils/SSTORE2.sol"; + + + error InvalidAuthenticatorData(); error InvalidClientData(); error InvalidSignature(); +contract Webauthn_prec2 { + + address dataPointer; + + constructor( bytes memory Shamir8_ss2) + { + uint taille; + assembly{ + taille:=mload(Shamir8_ss2) + } + + console.log("Ecriture de la table input dans le contrat, taille :", taille); + + dataPointer=SSTORE2.write(Shamir8_ss2); + bytes memory ec_point=new bytes(64); + + uint offset; + uint endoffset; + uint px; + uint py; + + console.log("Relecture de la table :", taille); + + + for(uint i=0;i<256;i++) + { + offset=64*i; + endoffset=offset+64; + + ec_point=SSTORE2.read(dataPointer, offset, endoffset); + assembly{ + px:=mload(add(ec_point,32)) + py:=mload(add(ec_point,64)) + + } + + //console.log("Read :", px,py); + //console.log(Ec_ZZ.ecAff_isOnCurve(px, py)); + } + } + +} + contract Webauthn_prec { uint256 public counter; - + + address dataPointer; + //precomputations - uint[2][256] Shamir8; + uint[2][256] public Shamir8; constructor(uint[2] memory Q){ - + + console.log("Precompute for Public key:", Q[0], Q[1]); + Shamir8=Ec_ZZ.Precalc_Shamir8_part1(Q); console.log("Precompute part 1 done"); } - function deploy_part2(uint[2] memory Q) public returns (bool) + + + function deploy_part2(uint[2] memory Q) public returns ( uint[2][256] memory res) { unchecked{ @@ -33,11 +86,25 @@ contract Webauthn_prec { Shamir8[i][1]=Prec2[i-128][1]; } - console.log("Precompute part 1 done"); - + console.log("Precompute part 2 done:"); + } - - return true; + + + /* 256 point, 2 coordinates, 32 bytes each =16384 bytes*/ + //bytes memory Shamir8_ss2= new bytes(1); + + for(uint i=0; i<256; i++) + { + console.log("\"", Shamir8[i][0],"\","); + console.log("\"", Shamir8[i][1],"\","); + //res[i][0]=Shamir8[i][0]; + //res[i][1]=Shamir8[i][1]; + } + + //cette ligne pète + //dataPointer=SSTORE2.write(Shamir8_ss2); + return res=Shamir8; } function checkSignature_prec( diff --git a/sage/Webauthn_precompute.sage b/sage/Webauthn_precompute.sage new file mode 100644 index 0000000..9f87ff0 --- /dev/null +++ b/sage/Webauthn_precompute.sage @@ -0,0 +1,94 @@ +#//********************************************************************************************/ +#// ___ _ ___ _ _ _ _ +#// | __| _ ___ __| |_ / __|_ _ _ _ _ __| |_ ___ | | (_) |__ +#// | _| '_/ -_|_-< ' \ | (__| '_| || | '_ \ _/ _ \ | |__| | '_ \ +#// |_||_| \___/__/_||_| \___|_| \_, | .__/\__\___/ |____|_|_.__/ +#// |__/|_| +#///* Copyright (C) 2022 - Renaud Dubois - This file is part of FCL (Fresh CryptoLib) project */ +#///* License: This software is licensed under MIT License */ +#///* See LICENSE file at the root folder of the project. */ +#///* FILE: Webauthn_precompute.sage */ +#///* */ +#///* */ +#///* DESCRIPTION: precompute a 8 dimensional table for Shamir's trick from a public key +#///* +#//**************************************************************************************/ + + +def Init_Curve(curve_characteristic,curve_a, curve_b,Gx, Gy, curve_Order): + Fp=GF(curve_characteristic); #Initialize Prime field of Point + Fq=GF(curve_Order); #Initialize Prime field of scalars + Curve=EllipticCurve(Fp, [curve_a, curve_b]); #Initialize Elliptic curve + curve_Generator=Curve([Gx, Gy]); + + return [Curve,curve_Generator]; + +#//Curve secp256r1, aka p256 +#//curve prime field modulus +sec256p_p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF; +#//short weierstrass first coefficient +sec256p_a =0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC; +#//short weierstrass second coefficient +sec256p_b =0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B; +#//generating point affine coordinates +sec256p_gx =0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296; +sec256p_gy =0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5; +#//curve order (number of points) +sec256p_n =0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551; + +#//Init +secp256r1, G = Init_Curve(sec256p_p, sec256p_a, sec256p_b, sec256p_gx, sec256p_gy, sec256p_n); + + +#//example public point from webauthn.js +Q=secp256r1([114874632398302156264159990279427641021947882640101801130664833947273521181002,32136952818958550240756825111900051564117520891182470183735244184006536587423]); + +def Precompute_Pubkey(Q, Curve): + Pow64_PQ=[ Q for i in range(0,16)]; + Prec=[ Curve(0) for i in range(0,256)]; + + + Pow64_PQ[0]=Curve([sec256p_gx, sec256p_gy]); + Pow64_PQ[4]=Q; + + for j in [1..3]: + Pow64_PQ[j]=2^64*Pow64_PQ[j-1]; + Pow64_PQ[j+4]=2^64*Pow64_PQ[j+3]; + + Prec[0]=Curve(0); + + for i in range(1,256): + Prec[i]=Curve(0); + for j in [0..7]: + if( (i&(1< Date: Wed, 8 Mar 2023 16:09:30 +0100 Subject: [PATCH 10/25] precomputations using sstore2 --- contracts/Elliptic_ZZ.sol | 103 +++++++++++++++++++- contracts/Webauthn_prec.sol | 186 ++++++++++++++++++++++++++++++++++-- test/Webauthn.js | 63 +++++++++++- 3 files changed, 342 insertions(+), 10 deletions(-) diff --git a/contracts/Elliptic_ZZ.sol b/contracts/Elliptic_ZZ.sol index 83743b1..dd07907 100644 --- a/contracts/Elliptic_ZZ.sol +++ b/contracts/Elliptic_ZZ.sol @@ -18,6 +18,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +import {Base64URL} from "./Base64URL.sol"; +import "solmate/src/utils/SSTORE2.sol"; import "hardhat/console.sol"; library Ec_ZZ { @@ -917,8 +919,62 @@ library Ec_ZZ { (x,y)=ecZZ_SetAff(x,y,R[0], R[1]); } + } + + function ecZZ_ReadPrec(address dataPointer, uint numpoint) internal returns (uint x, uint y) + { + bytes memory ec_point=new bytes(64); + unchecked{ + ec_point=SSTORE2.read(dataPointer, 64*numpoint, 64*numpoint+64); + assembly{ + x:=mload(add(ec_point,32)) //store Px of lookup point + y:=mload(add(ec_point,64)) + } + } + + } + + //8 dimensions Shamir's trick, using precomputations stored in Shamir8, stored as Bytecode of an external + //contract at given address dataPointer + //(thx to Lakhdar https://github.com/Kelvyne for EVM storage explanations and tricks) + // the external tool to generate tables from public key is in the /sage directory + function ecZZ_mulmuladd_S8_ss2(uint scalar_u, uint scalar_v, address dataPointer) internal returns(uint[2] memory P) + { + uint octobit;uint index;uint py; + + index=255; + uint[2] memory R;//R[2] is used as intermediary to avoid too deep stack + unchecked{ + + //tbd case of msb octobit is null + + octobit=128*((scalar_v>>index)&1)+64*((scalar_v>>(index-64))&1)+32*((scalar_v>>(index-128))&1)+16*((scalar_v>>(index-192))&1)+ + 8*((scalar_u>>index)&1)+4*((scalar_u>>(index-64))&1)+2*((scalar_u>>(index-128))&1)+1*((scalar_u>>(index-192))&1); + + + //(x,y,R[0], R[1])= (Shamir8[octobit][0], Shamir8[octobit][1],1,1); + (P[0],P[1])=ecZZ_ReadPrec(dataPointer, octobit); + + (R[0], R[1])= (1,1); + + //loop over 1/4 of scalars + for(index=254; index>=192; index--) + { + (P[0],P[1],R[0], R[1])=ecZZ_Dbl( P[0],P[1],R[0], R[1]); + + octobit=128*((scalar_v>>index)&1)+64*((scalar_v>>(index-64))&1)+32*((scalar_v>>(index-128))&1)+16*((scalar_v>>(index-192))&1)+ + 8*((scalar_u>>index)&1)+4*((scalar_u>>(index-64))&1)+2*((scalar_u>>(index-128))&1)+1*((scalar_u>>(index-192))&1); + + + (octobit,py)=ecZZ_ReadPrec(dataPointer, octobit); + + + (P[0],P[1],R[0], R[1])=ecZZ_AddN( P[0],P[1],R[0], R[1], octobit, py ); + } + (P[0],P[1])=ecZZ_SetAff(P[0],P[1],R[0], R[1]); + } } /** @@ -1071,6 +1127,7 @@ library Ec_ZZ { } + /* validating signatures using a precomputed table of multiples of P and Q stored in Shamir8*/ function validateSignature_Precomputed( bytes32 message, uint[2] memory rs, @@ -1082,6 +1139,8 @@ library Ec_ZZ { uint[2] memory Q; Q[0]=Shamir8[4][0];//extract Q from Shamir8 Q[1]=Shamir8[4][1]; + + if (rs[0] == 0 || rs[0] >= n || rs[1] == 0) { return false; } @@ -1110,5 +1169,47 @@ library Ec_ZZ { //uint Px=NormalizedX(P[0], P[1], P[2]); return x1 % n == rs[0]; - } + } + + + /* validating signatures using a precomputed table of multiples of P and Q stored in contract at address Shamir8*/ + + function validateSignature_Precomputed_ss2( + bytes32 message, + uint[2] memory rs, + address Shamir8 + ) internal returns (bool) { + + + + + + if (rs[0] == 0 || rs[0] >= n || rs[1] == 0) { + return false; + } + /* tbd or not: check Q + if (!isOnCurve(Q[0], Q[1])) { + return false; + } + */ + + + uint sInv = inverseMod(rs[1], n); + uint scalar_u=mulmod(uint(message), sInv, n); + uint scalar_v= mulmod(rs[0], sInv, n); + uint[2] memory P; + + + //test_ecZZ_formulae(); + + //Shamir 8 dimensions + P=ecZZ_mulmuladd_S8_ss2(scalar_u, scalar_v, Shamir8); + console.log("res Shamir 8dim precomputed XYZZ mulmuladd:",P[0]); + //uint[3] memory P = ec_mulmuladd_W(gx, gy, Q[0], Q[1],scalar_u ,scalar_v ); + //uint Px=NormalizedX(P[0], P[1], P[2]); + + return P[0] % n == rs[0]; + } + + } diff --git a/contracts/Webauthn_prec.sol b/contracts/Webauthn_prec.sol index 875ebfb..1535d77 100644 --- a/contracts/Webauthn_prec.sol +++ b/contracts/Webauthn_prec.sol @@ -13,10 +13,72 @@ error InvalidAuthenticatorData(); error InvalidClientData(); error InvalidSignature(); + + +contract BytecodeTable { + //precomputations + uint[2][256] Shamir8; + + uint immutable a1=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + uint immutable a2=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + uint immutable a3=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + uint immutable a4=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + uint immutable a5=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + uint immutable a6=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + uint immutable a7=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + uint immutable a8=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + uint immutable a9=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + + + + + constructor() { + for(uint i=0;i<256;i++) + { + //Shamir8[i][0]=0xcacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacaca; + //Shamir8[i][1]=0x9595959595959595959595959595959595959595959595959595959595959595; + + Shamir8[i][0]=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + Shamir8[i][1]=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + + + } + + + + } + + function show_myself(address selfe) public + { + uint[2] memory px; + + + console.log("address=",uint256(uint160(selfe))); + + + + for(uint i=0;i<256;i++){ + uint offset=64*i; + assembly{ + extcodecopy(selfe, px, offset, 64) + } + console.log("read=",i, px[0], px[1]); + } + + + + + + } +} + + contract Webauthn_prec2 { address dataPointer; - + uint256 public counter; + + constructor( bytes memory Shamir8_ss2) { uint taille; @@ -29,11 +91,15 @@ contract Webauthn_prec2 { dataPointer=SSTORE2.write(Shamir8_ss2); bytes memory ec_point=new bytes(64); + uint256[2] memory xy; + uint offset; uint endoffset; uint px; uint py; + address cpy_datap=dataPointer; + /* console.log("Relecture de la table :", taille); @@ -42,24 +108,132 @@ contract Webauthn_prec2 { offset=64*i; endoffset=offset+64; - ec_point=SSTORE2.read(dataPointer, offset, endoffset); + ec_point=SSTORE2.read(dataPointer, 64*i, 64*i+64); assembly{ px:=mload(add(ec_point,32)) py:=mload(add(ec_point,64)) } - //console.log("Read :", px,py); - //console.log(Ec_ZZ.ecAff_isOnCurve(px, py)); - } + console.log("Read1 :", px,py); + console.log(Ec_ZZ.ecAff_isOnCurve(px, py)); + } */ } + + + function checkSignature_prec( + bytes memory authenticatorData, + bytes1 authenticatorDataFlagMask, + bytes memory clientData, + bytes32 clientChallenge, + uint clientChallengeDataOffset, + uint[2] memory rs + ) public returns (bool) { + // Let the caller check if User Presence (0x01) or User Verification (0x04) are set + if ( + (authenticatorData[32] & authenticatorDataFlagMask) != + authenticatorDataFlagMask + ) { + revert InvalidAuthenticatorData(); + } + // Verify that clientData commits to the expected client challenge + string memory challengeEncoded = Base64URL.encode32( + abi.encodePacked(clientChallenge) + ); + bytes memory challengeExtracted = new bytes( + bytes(challengeEncoded).length + ); + copyBytes( + clientData, + clientChallengeDataOffset, + challengeExtracted.length, + challengeExtracted, + 0 + ); + if ( + keccak256(abi.encodePacked(bytes(challengeEncoded))) != + keccak256(abi.encodePacked(challengeExtracted)) + ) { + revert InvalidClientData(); + } + // Verify the signature over sha256(authenticatorData || sha256(clientData)) + bytes memory verifyData = new bytes(authenticatorData.length + 32); + copyBytes( + authenticatorData, + 0, + authenticatorData.length, + verifyData, + 0 + ); + copyBytes( + abi.encodePacked(sha256(clientData)), + 0, + 32, + verifyData, + authenticatorData.length + ); + bytes32 message = sha256(verifyData); + bool result=Ec_ZZ.validateSignature_Precomputed_ss2(message, rs, dataPointer); + console.log("result= %s", result); + + return result; + } + + function validate_prec( + bytes memory authenticatorData, + bytes1 authenticatorDataFlagMask, + bytes memory clientData, + bytes32 clientChallenge, + uint clientChallengeDataOffset, + uint[2] memory rs + + ) public { + if ( + !checkSignature_prec( + authenticatorData, + authenticatorDataFlagMask, + clientData, + clientChallenge, + clientChallengeDataOffset, + rs + ) + ) { + revert InvalidSignature(); + } + counter++; + } + + + function copyBytes( + bytes memory _from, + uint _fromOffset, + uint _length, + bytes memory _to, + uint _toOffset + ) internal pure returns (bytes memory _copiedBytes) { + uint minLength = _length + _toOffset; + require(_to.length >= minLength); // Buffer too small. Should be a better way? + uint i = 32 + _fromOffset; // NOTE: the offset 32 is added to skip the `size` field of both bytes variables + uint j = 32 + _toOffset; + while (i < (32 + _fromOffset + _length)) { + assembly { + let tmp := mload(add(_from, i)) + mstore(add(_to, j), tmp) + } + i += 32; + j += 32; + } + return _to; + } + + + } contract Webauthn_prec { uint256 public counter; - address dataPointer; //precomputations uint[2][256] public Shamir8; diff --git a/test/Webauthn.js b/test/Webauthn.js index dbc81f1..ff2bdd5 100644 --- a/test/Webauthn.js +++ b/test/Webauthn.js @@ -1,6 +1,8 @@ const { expect } = require("chai"); const crypto = require("crypto"); +const {precompute} = require("./precomputed"); + function derToRS(der) { var offset = 3; var dataOffset; @@ -27,9 +29,36 @@ function derToRS(der) { describe("Webauthn", function() { it("Check message", async function() { + +console.log("\n***************************************** \n Validating SSTORE 2 writing \n*****************************************" ); + +const precomputations_buff = Buffer.from(precompute, "hex"); + console.log("prec buff:",precomputations_buff); + +console.log("length:",precomputations_buff.length); + +const wo_core = await ethers.getContractFactory("Webauthn_prec2"); + +const wo = await wo_core.deploy(precomputations_buff); + + +console.log("\n***************************************** \n Direct table adressing \n*****************************************" ); + + +const wo_table = await ethers.getContractFactory("BytecodeTable"); +const deployed = await wo_table.deploy(); +await deployed.deployed(); + + console.log( + `table contract deployed to ${deployed.address}` + ); + + + const result_show = await deployed.show_myself(deployed.address); + console.log("\n***************************************** \n Validating ECDSA Core verification \n*****************************************" ); /* I Validation of Core ecdsa verification (no webauthn encoding) without precomputations */ @@ -131,9 +160,9 @@ console.log("Signature parsed:", signatureParsed); //uncomment for with precomputation validation - -console.log("\n***************************************** \n Validating WebAuthn with XYZZ coordinates and Precomputations \n*****************************************" ); - /* III Validation of Webauthn verification without precomputations */ +/* +console.log("\n***************************************** \n Validating WebAuthn with XYZZ coordinates and Precomputations in memory \n*****************************************" ); + const Webauthn_prec = await ethers.getContractFactory("Webauthn_prec"); const webauthn_prec = await Webauthn_prec.deploy([ ethers.BigNumber.from("0x" + publicKey.slice(0, 32).toString('hex')), ethers.BigNumber.from("0x" + publicKey.slice(32).toString('hex'))]); @@ -150,5 +179,33 @@ console.log("\n***************************************** \n Validating WebAuthn await result2.wait(); +*/ + +console.log("\n***************************************** \n Validating WebAuthn with XYZZ coordinates and Precomputations in precomputed contract \n*****************************************" ); + /* III Validation of Webauthn verification with precomputations */ + + + + console.log("prec buff:",precomputations_buff); + + +console.log("length:",precomputations_buff.length); + +const wo_core2 = await ethers.getContractFactory("Webauthn_prec2"); + +const wo2 = await wo_core.deploy(precomputations_buff); + + + + + + const result3 = await wo.validate_prec(authenticatorData, 0x01, clientData, clientChallenge, challengeOffset, + [ ethers.BigNumber.from("0x" + signatureParsed[0].toString('hex')), ethers.BigNumber.from("0x" + signatureParsed[1].toString('hex'))] + + ); + + await result3.wait(); + + }) }); From 13f76dda6694ce235d830d0dbcf1a13c186a6f96 Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Wed, 8 Mar 2023 18:19:39 +0100 Subject: [PATCH 11/25] add use of extcodecopy instead of sstore2 --- contracts/Elliptic_ZZ.sol | 109 +++++++++++++++++++++--- contracts/Webauthn_prec.sol | 160 ++++++++++++++++++++++++++++-------- test/Webauthn.js | 36 ++++++-- 3 files changed, 254 insertions(+), 51 deletions(-) diff --git a/contracts/Elliptic_ZZ.sol b/contracts/Elliptic_ZZ.sol index dd07907..cc73fab 100644 --- a/contracts/Elliptic_ZZ.sol +++ b/contracts/Elliptic_ZZ.sol @@ -977,6 +977,64 @@ library Ec_ZZ { } } + + function ecZZ_ReadExt(address dataPointer, uint numpoint) internal returns (uint x, uint y) + { + uint[2] memory ec_point; + uint256 offset=64*numpoint; + unchecked{ + + assembly{ + extcodecopy(dataPointer, ec_point, offset, 64) + x:=mload(ec_point) //store Px of lookup point + y:=mload(add(ec_point,32)) + } + } + } + + //8 dimensions Shamir's trick, using precomputations stored in Shamir8, stored as Bytecode of an external + //contract at given address dataPointer + //(thx to Lakhdar https://github.com/Kelvyne for EVM storage explanations and tricks) + // the external tool to generate tables from public key is in the /sage directory + function ecZZ_mulmuladd_S8_extcode(uint scalar_u, uint scalar_v, address dataPointer) internal returns(uint[2] memory P) + { + uint octobit;uint index;uint py; + + index=255; + uint[2] memory R;//R[2] is used as intermediary to avoid too deep stack + unchecked{ + + //tbd case of msb octobit is null + + octobit=128*((scalar_v>>index)&1)+64*((scalar_v>>(index-64))&1)+32*((scalar_v>>(index-128))&1)+16*((scalar_v>>(index-192))&1)+ + 8*((scalar_u>>index)&1)+4*((scalar_u>>(index-64))&1)+2*((scalar_u>>(index-128))&1)+1*((scalar_u>>(index-192))&1); + + + + //(x,y,R[0], R[1])= (Shamir8[octobit][0], Shamir8[octobit][1],1,1); + (P[0],P[1])=ecZZ_ReadExt(dataPointer, octobit); + + (R[0], R[1])= (1,1); + + //loop over 1/4 of scalars + for(index=254; index>=192; index--) + { + (P[0],P[1],R[0], R[1])=ecZZ_Dbl( P[0],P[1],R[0], R[1]); + + octobit=128*((scalar_v>>index)&1)+64*((scalar_v>>(index-64))&1)+32*((scalar_v>>(index-128))&1)+16*((scalar_v>>(index-192))&1)+ + 8*((scalar_u>>index)&1)+4*((scalar_u>>(index-64))&1)+2*((scalar_u>>(index-128))&1)+1*((scalar_u>>(index-192))&1); + + + (octobit,py)=ecZZ_ReadExt(dataPointer, octobit); + + + (P[0],P[1],R[0], R[1])=ecZZ_AddN( P[0],P[1],R[0], R[1], octobit, py ); + } + (P[0],P[1])=ecZZ_SetAff(P[0],P[1],R[0], R[1]); + + } + } + /** * @dev Multiply the curve's generator point by a scalar. */ @@ -1131,8 +1189,7 @@ library Ec_ZZ { function validateSignature_Precomputed( bytes32 message, uint[2] memory rs, - - + uint[2][256] memory Shamir8 ) internal returns (bool) { @@ -1172,7 +1229,7 @@ library Ec_ZZ { } - /* validating signatures using a precomputed table of multiples of P and Q stored in contract at address Shamir8*/ + /* validating signatures using a precomputed table of multiples of P and Q stored in sstore2 at address Shamir8*/ function validateSignature_Precomputed_ss2( bytes32 message, @@ -1180,10 +1237,7 @@ library Ec_ZZ { address Shamir8 ) internal returns (bool) { - - - if (rs[0] == 0 || rs[0] >= n || rs[1] == 0) { return false; } @@ -1192,16 +1246,13 @@ library Ec_ZZ { return false; } */ - - + uint sInv = inverseMod(rs[1], n); uint scalar_u=mulmod(uint(message), sInv, n); uint scalar_v= mulmod(rs[0], sInv, n); uint[2] memory P; - //test_ecZZ_formulae(); - //Shamir 8 dimensions P=ecZZ_mulmuladd_S8_ss2(scalar_u, scalar_v, Shamir8); console.log("res Shamir 8dim precomputed XYZZ mulmuladd:",P[0]); @@ -1211,5 +1262,41 @@ library Ec_ZZ { return P[0] % n == rs[0]; } - + + /* validating signatures using a precomputed table of multiples of P and Q stored in contract at address Shamir8*/ + function validateSignature_Precomputed_extcode( + bytes32 message, + uint[2] memory rs, + address Shamir8 + ) internal returns (bool) { + + + if (rs[0] == 0 || rs[0] >= n || rs[1] == 0) { + return false; + } + /* tbd or not: check Q + if (!isOnCurve(Q[0], Q[1])) { + return false; + } + */ + + uint sInv = inverseMod(rs[1], n); + uint scalar_u=mulmod(uint(message), sInv, n); + uint scalar_v= mulmod(rs[0], sInv, n); + uint[2] memory P; + + + //Shamir 8 dimensions + P=ecZZ_mulmuladd_S8_extcode(scalar_u, scalar_v, Shamir8); + console.log("res Shamir 8dim precomputed XYZZ mulmuladd:",P[0]); + //uint[3] memory P = ec_mulmuladd_W(gx, gy, Q[0], Q[1],scalar_u ,scalar_v ); + //uint Px=NormalizedX(P[0], P[1], P[2]); + + return P[0] % n == rs[0]; + } + + + + + } diff --git a/contracts/Webauthn_prec.sol b/contracts/Webauthn_prec.sol index 1535d77..9a567b9 100644 --- a/contracts/Webauthn_prec.sol +++ b/contracts/Webauthn_prec.sol @@ -13,42 +13,15 @@ error InvalidAuthenticatorData(); error InvalidClientData(); error InvalidSignature(); - +contract replaceable{ + string constant x="replace me please"; +} contract BytecodeTable { //precomputations - uint[2][256] Shamir8; - - uint immutable a1=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - uint immutable a2=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - uint immutable a3=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - uint immutable a4=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - uint immutable a5=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - uint immutable a6=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - uint immutable a7=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - uint immutable a8=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - uint immutable a9=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - - - - constructor() { - for(uint i=0;i<256;i++) - { - //Shamir8[i][0]=0xcacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacaca; - //Shamir8[i][1]=0x9595959595959595959595959595959595959595959595959595959595959595; - - Shamir8[i][0]=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - Shamir8[i][1]=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - - - } - - - - } - function show_myself(address selfe) public + function Reader(address selfe) public { uint[2] memory px; @@ -62,14 +35,133 @@ contract BytecodeTable { assembly{ extcodecopy(selfe, px, offset, 64) } - console.log("read=",i, px[0], px[1]); + console.log("Test on curve of point ",i, px[0], px[1]); + console.log(Ec_ZZ.ecAff_isOnCurve(px[0], px[1])); + } } +} + + +contract Webauthn_prec3{ + + address dataPointer; + uint256 public counter; + constructor( address Precompute_contract) + { + dataPointer=Precompute_contract; + } - + + function checkSignature_prec( + bytes memory authenticatorData, + bytes1 authenticatorDataFlagMask, + bytes memory clientData, + bytes32 clientChallenge, + uint clientChallengeDataOffset, + uint[2] memory rs + ) public returns (bool) { + // Let the caller check if User Presence (0x01) or User Verification (0x04) are set + if ( + (authenticatorData[32] & authenticatorDataFlagMask) != + authenticatorDataFlagMask + ) { + revert InvalidAuthenticatorData(); + } + // Verify that clientData commits to the expected client challenge + string memory challengeEncoded = Base64URL.encode32( + abi.encodePacked(clientChallenge) + ); + bytes memory challengeExtracted = new bytes( + bytes(challengeEncoded).length + ); + copyBytes( + clientData, + clientChallengeDataOffset, + challengeExtracted.length, + challengeExtracted, + 0 + ); + if ( + keccak256(abi.encodePacked(bytes(challengeEncoded))) != + keccak256(abi.encodePacked(challengeExtracted)) + ) { + revert InvalidClientData(); + } + // Verify the signature over sha256(authenticatorData || sha256(clientData)) + bytes memory verifyData = new bytes(authenticatorData.length + 32); + copyBytes( + authenticatorData, + 0, + authenticatorData.length, + verifyData, + 0 + ); + copyBytes( + abi.encodePacked(sha256(clientData)), + 0, + 32, + verifyData, + authenticatorData.length + ); + bytes32 message = sha256(verifyData); + bool result=Ec_ZZ.validateSignature_Precomputed_extcode(message, rs, dataPointer); + console.log("result= %s", result); + + return result; + } - } + function validate_prec( + bytes memory authenticatorData, + bytes1 authenticatorDataFlagMask, + bytes memory clientData, + bytes32 clientChallenge, + uint clientChallengeDataOffset, + uint[2] memory rs + + ) public { + if ( + !checkSignature_prec( + authenticatorData, + authenticatorDataFlagMask, + clientData, + clientChallenge, + clientChallengeDataOffset, + rs + ) + ) { + revert InvalidSignature(); + } + counter++; + } + + + function copyBytes( + bytes memory _from, + uint _fromOffset, + uint _length, + bytes memory _to, + uint _toOffset + ) internal pure returns (bytes memory _copiedBytes) { + uint minLength = _length + _toOffset; + require(_to.length >= minLength); // Buffer too small. Should be a better way? + uint i = 32 + _fromOffset; // NOTE: the offset 32 is added to skip the `size` field of both bytes variables + uint j = 32 + _toOffset; + while (i < (32 + _fromOffset + _length)) { + assembly { + let tmp := mload(add(_from, i)) + mstore(add(_to, j), tmp) + } + i += 32; + j += 32; + } + return _to; + } + + + + } diff --git a/test/Webauthn.js b/test/Webauthn.js index ff2bdd5..98a7274 100644 --- a/test/Webauthn.js +++ b/test/Webauthn.js @@ -50,14 +50,22 @@ console.log("\n***************************************** \n Direct table adressi const wo_table = await ethers.getContractFactory("BytecodeTable"); const deployed = await wo_table.deploy(); -await deployed.deployed(); + +const replaceable = await ethers.getContractFactory("BytecodeTable"); +const deployed_2 = await replaceable.deploy(); + +await deployed_2.deployed(); console.log( - `table contract deployed to ${deployed.address}` + `replaceable contract deployed to ${deployed_2.address}` ); - const result_show = await deployed.show_myself(deployed.address); +await network.provider.send("hardhat_setCode", [deployed_2.address, "0x"+precompute]); + +const result_show = await deployed.Reader(deployed_2.address); + + console.log("\n***************************************** \n Validating ECDSA Core verification \n*****************************************" ); /* I Validation of Core ecdsa verification (no webauthn encoding) without precomputations */ @@ -181,7 +189,7 @@ console.log("\n***************************************** \n Validating WebAuthn */ -console.log("\n***************************************** \n Validating WebAuthn with XYZZ coordinates and Precomputations in precomputed contract \n*****************************************" ); +console.log("\n***************************************** \n Validating WebAuthn with XYZZ coordinates and Precomputations in precomputed contract with sstore2 \n*****************************************" ); /* III Validation of Webauthn verification with precomputations */ @@ -193,19 +201,35 @@ console.log("length:",precomputations_buff.length); const wo_core2 = await ethers.getContractFactory("Webauthn_prec2"); -const wo2 = await wo_core.deploy(precomputations_buff); +const wo2 = await wo_core2.deploy(precomputations_buff); - const result3 = await wo.validate_prec(authenticatorData, 0x01, clientData, clientChallenge, challengeOffset, + const result3 = await wo2.validate_prec(authenticatorData, 0x01, clientData, clientChallenge, challengeOffset, [ ethers.BigNumber.from("0x" + signatureParsed[0].toString('hex')), ethers.BigNumber.from("0x" + signatureParsed[1].toString('hex'))] ); await result3.wait(); + +console.log("\n***************************************** \n Validating WebAuthn with XYZZ coordinates and Precomputations in precomputed contract with extcodecopy \n*****************************************" ); + /* IV Validation of Webauthn verification with precomputations */ + + +const wo_core3 = await ethers.getContractFactory("Webauthn_prec3"); + + +const wo3 = await wo_core3.deploy(deployed_2.address); + + + const result4 = await wo3.validate_prec(authenticatorData, 0x01, clientData, clientChallenge, challengeOffset, + [ ethers.BigNumber.from("0x" + signatureParsed[0].toString('hex')), ethers.BigNumber.from("0x" + signatureParsed[1].toString('hex'))] + + ); + await result4.wait(); }) }); From 87ba9430ac134ac2584945bafcf10a02adb4006a Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Wed, 8 Mar 2023 21:27:37 +0100 Subject: [PATCH 12/25] optimizing register for gas cost --- contracts/Elliptic_ZZ.sol | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/contracts/Elliptic_ZZ.sol b/contracts/Elliptic_ZZ.sol index cc73fab..eca26c9 100644 --- a/contracts/Elliptic_ZZ.sol +++ b/contracts/Elliptic_ZZ.sol @@ -998,39 +998,52 @@ library Ec_ZZ { // the external tool to generate tables from public key is in the /sage directory function ecZZ_mulmuladd_S8_extcode(uint scalar_u, uint scalar_v, address dataPointer) internal returns(uint[2] memory P) { - uint octobit;uint index;uint py; + uint index;uint zzz;uint zz; index=255; - uint[2] memory R;//R[2] is used as intermediary to avoid too deep stack + uint[4] memory R;//R[0] store zz coordinates, R[2] is used as intermediary to avoid too deep stack unchecked{ //tbd case of msb octobit is null - octobit=128*((scalar_v>>index)&1)+64*((scalar_v>>(index-64))&1)+32*((scalar_v>>(index-128))&1)+16*((scalar_v>>(index-192))&1)+ + R[1]=128*((scalar_v>>index)&1)+64*((scalar_v>>(index-64))&1)+32*((scalar_v>>(index-128))&1)+16*((scalar_v>>(index-192))&1)+ 8*((scalar_u>>index)&1)+4*((scalar_u>>(index-64))&1)+2*((scalar_u>>(index-128))&1)+1*((scalar_u>>(index-192))&1); - + R[1]=R[1]*64; //(x,y,R[0], R[1])= (Shamir8[octobit][0], Shamir8[octobit][1],1,1); - (P[0],P[1])=ecZZ_ReadExt(dataPointer, octobit); - - (R[0], R[1])= (1,1); + //(P[0],P[1])=ecZZ_ReadExt(dataPointer, octobit); + assembly{ + extcodecopy(dataPointer, P, mload(add(R,32)), 64) + } + (zz, zzz)= (1,1); //loop over 1/4 of scalars for(index=254; index>=192; index--) { - (P[0],P[1],R[0], R[1])=ecZZ_Dbl( P[0],P[1],R[0], R[1]); + (P[0],P[1],zz, zzz)=ecZZ_Dbl( P[0],P[1],zz, zzz); - octobit=128*((scalar_v>>index)&1)+64*((scalar_v>>(index-64))&1)+32*((scalar_v>>(index-128))&1)+16*((scalar_v>>(index-192))&1)+ + + R[1]=128*((scalar_v>>index)&1)+64*((scalar_v>>(index-64))&1)+32*((scalar_v>>(index-128))&1)+16*((scalar_v>>(index-192))&1)+ 8*((scalar_u>>index)&1)+4*((scalar_u>>(index-64))&1)+2*((scalar_u>>(index-128))&1)+1*((scalar_u>>(index-192))&1); - - + + + + R[1]=R[1]*64; + assembly{ + extcodecopy(dataPointer, add(R, 64), mload(add(R,32)), 64) + + } + (P[0],P[1],zz, zzz)=ecZZ_AddN( P[0],P[1],zz, zzz, R[2], R[3] ); + + /* (octobit,py)=ecZZ_ReadExt(dataPointer, octobit); - (P[0],P[1],R[0], R[1])=ecZZ_AddN( P[0],P[1],R[0], R[1], octobit, py ); + (P[0],P[1],R[0], R[1])=ecZZ_AddN( P[0],P[1],R[0], R[1], octobit, py ); */ + } - (P[0],P[1])=ecZZ_SetAff(P[0],P[1],R[0], R[1]); + (P[0],P[1])=ecZZ_SetAff(P[0],P[1],zz, zzz); } } From 49a23f7a94e157ad70013846c129eb74944ebd8b Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Wed, 8 Mar 2023 22:50:23 +0100 Subject: [PATCH 13/25] Optimizing with assembly --- contracts/Elliptic_ZZ.sol | 84 +++++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/contracts/Elliptic_ZZ.sol b/contracts/Elliptic_ZZ.sol index eca26c9..55fc322 100644 --- a/contracts/Elliptic_ZZ.sol +++ b/contracts/Elliptic_ZZ.sol @@ -126,18 +126,32 @@ library Ec_ZZ { unchecked{ //use output as temporary to reduce RAM usage P0=mulmod(2, y, p); //U = 2*Y1 - P2=mulmod(P0,P0,p); // V=U^2 - P3=mulmod(x, P2,p); // S = X1*V - P1=mulmod(P0, P2,p); // W=UV + + + //P2=mulmod(P0,P0,p); // V=U^2 + assembly{ + P2:=mulmod(P0,P0,p) + P3:=mulmod(x, P2,p)// S = X1*V + P1:=mulmod(P0, P2,p) // W=UV + P2:=mulmod(P2, zz, p) //zz3=V*ZZ1 + + } + + //P3=mulmod(x, P2,p); // S = X1*V + //P1=mulmod(P0, P2,p); // W=UV - P2=mulmod(P2, zz, p); //zz3=V*ZZ1 + // P2=mulmod(P2, zz, p); //zz3=V*ZZ1 zz=mulmod(addmod(x,p-zz,p), addmod(x,zz,p),p);//M=3*(X1-ZZ1)*(X1+ZZ1), use zz to reduce RAM usage - zz=mulmod(3,zz, p);//M + + + assembly{ + zz:=mulmod(3,zz, p)//M + } P0=addmod(mulmod(zz,zz,p), mulmod(minus_2, P3,p),p);//X3=M^2-2S - P3=addmod(P3, p-P0,p);//S-X3 - x=mulmod(zz,P3,p);//M(S-X3) + // P3=addmod(P3, p-P0,p);//S-X3 + x=mulmod(zz,addmod(P3, p-P0,p),p);//M(S-X3) P3=mulmod(P1,zzz,p);//zzz3=W*zzz1 P1=addmod(x, p-mulmod(P1, y,p),p );//Y3= M(S-X3)-W*Y1 @@ -160,21 +174,34 @@ library Ec_ZZ { uint x2, uint y2) internal pure returns (uint P0, uint P1,uint P2,uint P3) { + unchecked{ if(y1==0){ return (x2,y2,1,1); } - unchecked{ - y1=p-y1;//-Y1 + + // y1=p-y1;//-Y1 //U2 = X2*ZZ1 - x2=mulmod(x2, zz1,p); + //x2=mulmod(x2, zz1,p); //S2 = Y2*ZZZ1, y2 free - y2=mulmod(y2, zzz1,p); + //y2=mulmod(y2, zzz1,p); //R = S2-Y1 - y2=addmod(y2,y1,p); + //y2=addmod(mulmod(y2, zzz1,p),y1,p); //P = U2-X1 - x2=addmod(x2,p-x1,p); + //x2=addmod(mulmod(x2, zz1,p),p-x1,p); - + assembly{ + y1:=sub(p, y1) + y2:=addmod(mulmod(y2, zzz1,p),y1,p) + x2:=addmod(mulmod(x2, zz1,p),sub(p,x1),p) + P0:=mulmod(x2, x2, p) + P1:=mulmod(P0,x2,p) + P2:=mulmod(zz1,P0,p) // W=UV + P3:= mulmod(zzz1,P1,p) //zz3=V*ZZ1 + zz1:=mulmod(x1, P0, p) + P0:=addmod(addmod(mulmod(y2,y2, p), sub(p,P1),p ), mulmod(minus_2, zz1,p) ,p ) + P1:=addmod(mulmod(addmod(zz1, sub(p,P0),p), y2, p), mulmod(y1, P1,p),p) + } + /* P0=mulmod(x2, x2, p);//PP = P^2 P1=mulmod(P0,x2,p); //PPP = P*PP @@ -182,15 +209,16 @@ library Ec_ZZ { P2=mulmod(zz1,P0,p); //ZZ3 = ZZ1*PP P3= mulmod(zzz1,P1,p); //ZZZ3 = ZZZ1*PPP - + zz1=mulmod(x1, P0, p); //Q = X1*PP, x1 free + */ //X3 = R^2-PPP-2*Q - P0= addmod(mulmod(y2,y2, p), p-P1,p ); //R^2-PPP - zzz1=mulmod(minus_2, zz1,p);//-2*Q - P0=addmod(P0, zzz1 ,p );//R^2-PPP-2*Q + //P0= addmod(mulmod(y2,y2, p), p-P1,p ); //R^2-PPP + //zzz1=mulmod(minus_2, zz1,p);//-2*Q + //P0=addmod(addmod(mulmod(y2,y2, p), p-P1,p ), mulmod(minus_2, zz1,p) ,p );//R^2-PPP-2*Q //Y3 = R*(Q-X3)-Y1*PPP - x1= mulmod(addmod(zz1, p-P0,p), y2, p);//R*(Q-X3) - P1=addmod(x1, mulmod(y1, P1,p),p) ; + //x1= mulmod(addmod(zz1, p-P0,p), y2, p);//R*(Q-X3) + //P1=addmod(mulmod(addmod(zz1, p-P0,p), y2, p), mulmod(y1, P1,p),p) ; } return (P0, P1, P2, P3); } @@ -998,7 +1026,8 @@ library Ec_ZZ { // the external tool to generate tables from public key is in the /sage directory function ecZZ_mulmuladd_S8_extcode(uint scalar_u, uint scalar_v, address dataPointer) internal returns(uint[2] memory P) { - uint index;uint zzz;uint zz; + uint index; + uint zzz;uint zz; // third and fourth coordinates of the point index=255; uint[4] memory R;//R[0] store zz coordinates, R[2] is used as intermediary to avoid too deep stack @@ -1023,13 +1052,16 @@ library Ec_ZZ { { (P[0],P[1],zz, zzz)=ecZZ_Dbl( P[0],P[1],zz, zzz); + //the outer x64 is to obtain the offset, code is ugly but we want to spare gas at maximum + /*R[1]=64*(128*((scalar_v>>index)&1)+64*((scalar_v>>(index-64))&1)+32*((scalar_v>>(index-128))&1)+16*((scalar_v>>(index-192))&1)+ + 8*((scalar_u>>index)&1)+4*((scalar_u>>(index-64))&1)+2*((scalar_u>>(index-128))&1)+1*((scalar_u>>(index-192))&1)); + */ - R[1]=128*((scalar_v>>index)&1)+64*((scalar_v>>(index-64))&1)+32*((scalar_v>>(index-128))&1)+16*((scalar_v>>(index-192))&1)+ - 8*((scalar_u>>index)&1)+4*((scalar_u>>(index-64))&1)+2*((scalar_u>>(index-128))&1)+1*((scalar_u>>(index-192))&1); - - + //incorporating offset with the chunk number + R[1]=(8192*((scalar_v>>index)&1)+4096*((scalar_v>>(index-64))&1)+2048*((scalar_v>>(index-128))&1)+1024*((scalar_v>>(index-192))&1)+ + 512*((scalar_u>>index)&1)+256*((scalar_u>>(index-64))&1)+128*((scalar_u>>(index-128))&1)+64*((scalar_u>>(index-192))&1)); + - R[1]=R[1]*64; assembly{ extcodecopy(dataPointer, add(R, 64), mload(add(R,32)), 64) From 7a4a6b73bace810b352d5c93634356334b62af68 Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Wed, 8 Mar 2023 23:29:06 +0100 Subject: [PATCH 14/25] more assembly --- contracts/Elliptic_ZZ.sol | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/contracts/Elliptic_ZZ.sol b/contracts/Elliptic_ZZ.sol index 55fc322..f7451ba 100644 --- a/contracts/Elliptic_ZZ.sol +++ b/contracts/Elliptic_ZZ.sol @@ -134,7 +134,12 @@ library Ec_ZZ { P3:=mulmod(x, P2,p)// S = X1*V P1:=mulmod(P0, P2,p) // W=UV P2:=mulmod(P2, zz, p) //zz3=V*ZZ1 - + zz:=mulmod(3, mulmod(addmod(x,sub(p,zz),p), addmod(x,zz,p),p) ,p) //M=3*(X1-ZZ1)*(X1+ZZ1), use zz to reduce RAM usage + P0:=addmod(mulmod(zz,zz,p), mulmod(minus_2, P3,p),p) //X3=M^2-2S + x:=mulmod(zz,addmod(P3, sub(p,P0),p),p)//M(S-X3) + P3:=mulmod(P1,zzz,p)//zzz3=W*zzz1 + P1:=addmod(x, sub(p, mulmod(P1, y,p)),p )//Y3= M(S-X3)-W*Y1 + } //P3=mulmod(x, P2,p); // S = X1*V @@ -142,19 +147,17 @@ library Ec_ZZ { // P2=mulmod(P2, zz, p); //zz3=V*ZZ1 - zz=mulmod(addmod(x,p-zz,p), addmod(x,zz,p),p);//M=3*(X1-ZZ1)*(X1+ZZ1), use zz to reduce RAM usage + //zz=mulmod(addmod(x,p-zz,p), addmod(x,zz,p),p);//M=3*(X1-ZZ1)*(X1+ZZ1), use zz to reduce RAM usage - assembly{ - zz:=mulmod(3,zz, p)//M - } - P0=addmod(mulmod(zz,zz,p), mulmod(minus_2, P3,p),p);//X3=M^2-2S + + // P0=addmod(mulmod(zz,zz,p), mulmod(minus_2, P3,p),p);//X3=M^2-2S // P3=addmod(P3, p-P0,p);//S-X3 - x=mulmod(zz,addmod(P3, p-P0,p),p);//M(S-X3) - P3=mulmod(P1,zzz,p);//zzz3=W*zzz1 + // x=mulmod(zz,addmod(P3, p-P0,p),p);//M(S-X3) + // P3=mulmod(P1,zzz,p);//zzz3=W*zzz1 - P1=addmod(x, p-mulmod(P1, y,p),p );//Y3= M(S-X3)-W*Y1 + // P1=addmod(x, p-mulmod(P1, y,p),p );//Y3= M(S-X3)-W*Y1 } return (P0, P1, P2, P3); From c031999afa247a18e044792130ba86504d4e7df6 Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Thu, 9 Mar 2023 18:28:29 +0100 Subject: [PATCH 15/25] Inlining assembly in main ec_mulmuladd loop --- contracts/Elliptic_ZZ.sol | 45 +++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/contracts/Elliptic_ZZ.sol b/contracts/Elliptic_ZZ.sol index f7451ba..b01783a 100644 --- a/contracts/Elliptic_ZZ.sol +++ b/contracts/Elliptic_ZZ.sol @@ -125,12 +125,13 @@ library Ec_ZZ { { unchecked{ //use output as temporary to reduce RAM usage - P0=mulmod(2, y, p); //U = 2*Y1 + //P0=mulmod(2, y, p); //U = 2*Y1 //P2=mulmod(P0,P0,p); // V=U^2 assembly{ - P2:=mulmod(P0,P0,p) + P0:=mulmod(2, y, p) //U = 2*Y1 + P2:=mulmod(P0,P0,p) // V=U^2 P3:=mulmod(x, P2,p)// S = X1*V P1:=mulmod(P0, P2,p) // W=UV P2:=mulmod(P2, zz, p) //zz3=V*ZZ1 @@ -1023,6 +1024,10 @@ library Ec_ZZ { } } + + + + //8 dimensions Shamir's trick, using precomputations stored in Shamir8, stored as Bytecode of an external //contract at given address dataPointer //(thx to Lakhdar https://github.com/Kelvyne for EVM storage explanations and tricks) @@ -1031,29 +1036,47 @@ library Ec_ZZ { { uint index; uint zzz;uint zz; // third and fourth coordinates of the point - + index=255; - uint[4] memory R;//R[0] store zz coordinates, R[2] is used as intermediary to avoid too deep stack + uint[5] memory T; + unchecked{ //tbd case of msb octobit is null - R[1]=128*((scalar_v>>index)&1)+64*((scalar_v>>(index-64))&1)+32*((scalar_v>>(index-128))&1)+16*((scalar_v>>(index-192))&1)+ + T[0]=128*((scalar_v>>index)&1)+64*((scalar_v>>(index-64))&1)+32*((scalar_v>>(index-128))&1)+16*((scalar_v>>(index-192))&1)+ 8*((scalar_u>>index)&1)+4*((scalar_u>>(index-64))&1)+2*((scalar_u>>(index-128))&1)+1*((scalar_u>>(index-192))&1); - R[1]=R[1]*64; + T[0]=T[0]*64; //(x,y,R[0], R[1])= (Shamir8[octobit][0], Shamir8[octobit][1],1,1); //(P[0],P[1])=ecZZ_ReadExt(dataPointer, octobit); assembly{ - extcodecopy(dataPointer, P, mload(add(R,32)), 64) + extcodecopy(dataPointer, P, mload(T), 64) } (zz, zzz)= (1,1); //loop over 1/4 of scalars for(index=254; index>=192; index--) { - (P[0],P[1],zz, zzz)=ecZZ_Dbl( P[0],P[1],zz, zzz); + //(P[0],P[1],zz, zzz)=ecZZ_Dbl( P[0],P[1],zz, zzz); + + //inlining the ecZZ_Dbl , welcome to the carroussel + + assembly{ + + let y:=mulmod(2, mload(add(P,32)), p) //U = 2*Y1, y free + let T2:=mulmod(y,y,p) // V=U^2 + let T3:=mulmod(mload(P), T2,p)// S = X1*V + let T1:=mulmod(y, T2,p) // W=UV + + let T4:=mulmod(3, mulmod(addmod(mload(P),sub(p,zz),p), addmod(mload(P),zz,p),p) ,p) //M=3*(X1-ZZ1)*(X1+ZZ1), use zz to reduce RAM usage, x free + mstore(P, addmod(mulmod(T4,T4,p), mulmod(minus_2, T3,p),p)) //X3=M^2-2S + y:=mulmod(T4,addmod(T3, sub(p, mload(P)),p),p)//M(S-X3) + zzz:=mulmod(T1,zzz,p)//zzz3=W*zzz1 + mstore(add(P,32) , addmod(y, sub(p, mulmod(T1, mload(add(P,32)) ,p)),p ))//Y3= M(S-X3)-W*Y1 + zz:=mulmod(T2, zz, p) //zz3=V*ZZ1 + } //the outer x64 is to obtain the offset, code is ugly but we want to spare gas at maximum /*R[1]=64*(128*((scalar_v>>index)&1)+64*((scalar_v>>(index-64))&1)+32*((scalar_v>>(index-128))&1)+16*((scalar_v>>(index-192))&1)+ @@ -1061,15 +1084,15 @@ library Ec_ZZ { */ //incorporating offset with the chunk number - R[1]=(8192*((scalar_v>>index)&1)+4096*((scalar_v>>(index-64))&1)+2048*((scalar_v>>(index-128))&1)+1024*((scalar_v>>(index-192))&1)+ + T[0]=(8192*((scalar_v>>index)&1)+4096*((scalar_v>>(index-64))&1)+2048*((scalar_v>>(index-128))&1)+1024*((scalar_v>>(index-192))&1)+ 512*((scalar_u>>index)&1)+256*((scalar_u>>(index-64))&1)+128*((scalar_u>>(index-128))&1)+64*((scalar_u>>(index-192))&1)); assembly{ - extcodecopy(dataPointer, add(R, 64), mload(add(R,32)), 64) + extcodecopy(dataPointer, T,mload(T), 64) } - (P[0],P[1],zz, zzz)=ecZZ_AddN( P[0],P[1],zz, zzz, R[2], R[3] ); + (P[0],P[1],zz, zzz)=ecZZ_AddN( P[0],P[1],zz, zzz, T[0], T[1] ); /* (octobit,py)=ecZZ_ReadExt(dataPointer, octobit); From 776c9daa590981056d0844417a682b98d094c468 Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Fri, 10 Mar 2023 11:10:00 +0100 Subject: [PATCH 16/25] Full ASM inlining of main loop --- contracts/Elliptic_ZZ.sol | 92 ++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 39 deletions(-) diff --git a/contracts/Elliptic_ZZ.sol b/contracts/Elliptic_ZZ.sol index b01783a..18a19d7 100644 --- a/contracts/Elliptic_ZZ.sol +++ b/contracts/Elliptic_ZZ.sol @@ -1034,18 +1034,16 @@ library Ec_ZZ { // the external tool to generate tables from public key is in the /sage directory function ecZZ_mulmuladd_S8_extcode(uint scalar_u, uint scalar_v, address dataPointer) internal returns(uint[2] memory P) { - uint index; - uint zzz;uint zz; // third and fourth coordinates of the point - - index=255; - uint[5] memory T; +uint zzz;uint zz; // third and fourth coordinates of the point + + zz=255; + uint[2] memory T; unchecked{ //tbd case of msb octobit is null - - T[0]=128*((scalar_v>>index)&1)+64*((scalar_v>>(index-64))&1)+32*((scalar_v>>(index-128))&1)+16*((scalar_v>>(index-192))&1)+ - 8*((scalar_u>>index)&1)+4*((scalar_u>>(index-64))&1)+2*((scalar_u>>(index-128))&1)+1*((scalar_u>>(index-192))&1); + T[0]=128*((scalar_v>>zz)&1)+64*((scalar_v>>(zz-64))&1)+32*((scalar_v>>(zz-128))&1)+16*((scalar_v>>(zz-192))&1)+ + 8*((scalar_u>>zz)&1)+4*((scalar_u>>(zz-64))&1)+2*((scalar_u>>(zz-128))&1)+1*((scalar_u>>(zz-192))&1); T[0]=T[0]*64; @@ -1057,49 +1055,65 @@ library Ec_ZZ { (zz, zzz)= (1,1); //loop over 1/4 of scalars - for(index=254; index>=192; index--) + // for(index=254; index>=192; index--) { //(P[0],P[1],zz, zzz)=ecZZ_Dbl( P[0],P[1],zz, zzz); //inlining the ecZZ_Dbl , welcome to the carroussel + // + assembly{ + for { let index := 254 } gt(index, 191) { index := sub(index, 1) } + { + + // inlined Ec_Dbl + let y:=mulmod(2, mload(add(P,32)), p) //U = 2*Y1, y free + let T2:=mulmod(y,y,p) // V=U^2 + let T3:=mulmod(mload(P), T2,p)// S = X1*V + let T1:=mulmod(y, T2,p) // W=UV - let y:=mulmod(2, mload(add(P,32)), p) //U = 2*Y1, y free - let T2:=mulmod(y,y,p) // V=U^2 - let T3:=mulmod(mload(P), T2,p)// S = X1*V - let T1:=mulmod(y, T2,p) // W=UV - - let T4:=mulmod(3, mulmod(addmod(mload(P),sub(p,zz),p), addmod(mload(P),zz,p),p) ,p) //M=3*(X1-ZZ1)*(X1+ZZ1), use zz to reduce RAM usage, x free - mstore(P, addmod(mulmod(T4,T4,p), mulmod(minus_2, T3,p),p)) //X3=M^2-2S - y:=mulmod(T4,addmod(T3, sub(p, mload(P)),p),p)//M(S-X3) - zzz:=mulmod(T1,zzz,p)//zzz3=W*zzz1 - mstore(add(P,32) , addmod(y, sub(p, mulmod(T1, mload(add(P,32)) ,p)),p ))//Y3= M(S-X3)-W*Y1 - zz:=mulmod(T2, zz, p) //zz3=V*ZZ1 - } + let T4:=mulmod(3, mulmod(addmod(mload(P),sub(p,zz),p), addmod(mload(P),zz,p),p) ,p) //M=3*(X1-ZZ1)*(X1+ZZ1), use zz to reduce RAM usage, x free + mstore(P, addmod(mulmod(T4,T4,p), mulmod(minus_2, T3,p),p)) //X3=M^2-2S + y:=mulmod(T4,addmod(T3, sub(p, mload(P)),p),p)//M(S-X3) + zzz:=mulmod(T1,zzz,p)//zzz3=W*zzz1 + mstore(add(P,32) , addmod(y, sub(p, mulmod(T1, mload(add(P,32)) ,p)),p ))//Y3= M(S-X3)-W*Y1 + zz:=mulmod(T2, zz, p) //zz3=V*ZZ1 - //the outer x64 is to obtain the offset, code is ugly but we want to spare gas at maximum - /*R[1]=64*(128*((scalar_v>>index)&1)+64*((scalar_v>>(index-64))&1)+32*((scalar_v>>(index-128))&1)+16*((scalar_v>>(index-192))&1)+ - 8*((scalar_u>>index)&1)+4*((scalar_u>>(index-64))&1)+2*((scalar_u>>(index-128))&1)+1*((scalar_u>>(index-192))&1)); - */ - //incorporating offset with the chunk number - T[0]=(8192*((scalar_v>>index)&1)+4096*((scalar_v>>(index-64))&1)+2048*((scalar_v>>(index-128))&1)+1024*((scalar_v>>(index-192))&1)+ - 512*((scalar_u>>index)&1)+256*((scalar_u>>(index-64))&1)+128*((scalar_u>>(index-128))&1)+64*((scalar_u>>(index-192))&1)); - - assembly{ - extcodecopy(dataPointer, T,mload(T), 64) - - } - (P[0],P[1],zz, zzz)=ecZZ_AddN( P[0],P[1],zz, zzz, T[0], T[1] ); - - /* - (octobit,py)=ecZZ_ReadExt(dataPointer, octobit); + /* compute element to access in precomputed table */ + let ind:=index + let T0:= add( shl(13, and(shr(ind, scalar_v),1)), shl(9, and(shr(ind, scalar_u),1)) ) + ind:=sub(index, 64) + T0:=add(T0, add( shl(12, and(shr(ind, scalar_v),1)), shl(8, and(shr(ind, scalar_u),1)) )) + ind:=sub(index, 128) + T0:=add(T0,add( shl(11, and(shr(ind, scalar_v),1)), shl(7, and(shr(ind, scalar_u),1)) )) + ind:=sub(index, 192) + T0:=add(T0,add( shl(10, and(shr(ind, scalar_v),1)), shl(6, and(shr(ind, scalar_u),1)) )) - (P[0],P[1],R[0], R[1])=ecZZ_AddN( P[0],P[1],R[0], R[1], octobit, py ); */ - + + mstore(T,T0) + /* Access to precomputed table using extcodecopy hack */ + extcodecopy(dataPointer, T,mload(T), 64) + + // inlined Ec_AddN + y:=sub(p, mload(add(P,32))) + let y2:=addmod(mulmod(mload(add(T,32)), zzz,p),y,p) + T2:=addmod(mulmod(mload(T), zz,p),sub(p,mload(P)),p) + T0:=mulmod(T2, T2, p) + T1:=mulmod(T0,T2,p) + T2:=mulmod(zz,T0,p) // W=UV + zzz:= mulmod(zzz,T1,p) //zz3=V*ZZ1 + let zz1:=mulmod(mload(P), T0, p) + T0:=addmod(addmod(mulmod(y2,y2, p), sub(p,T1),p ), mulmod(minus_2, zz1,p) ,p ) + mstore(add(P,32),addmod(mulmod(addmod(zz1, sub(p,T0),p), y2, p), mulmod(y, T1,p),p)) + + zz:=T2 + + mstore(P,T0) + }} } (P[0],P[1])=ecZZ_SetAff(P[0],P[1],zz, zzz); From 83855d2fecf6f5cfcbbd68aef32d77bfce7cb285 Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Fri, 10 Mar 2023 14:26:13 +0100 Subject: [PATCH 17/25] asm ordering --- contracts/Elliptic_ZZ.sol | 149 +++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 81 deletions(-) diff --git a/contracts/Elliptic_ZZ.sol b/contracts/Elliptic_ZZ.sol index 18a19d7..4c8ee8e 100644 --- a/contracts/Elliptic_ZZ.sol +++ b/contracts/Elliptic_ZZ.sol @@ -50,7 +50,7 @@ library Ec_ZZ { /** * @dev Inverse of u in the field of modulo m. */ - function inverseMod(uint u, uint m) internal pure returns (uint) { + function inverseMod(uint u, uint m) internal returns (uint) { if (u == 0 || u == m || m == 0) return 0; if (u > m) u = u % m; @@ -59,18 +59,22 @@ library Ec_ZZ { uint r1 = m; uint r2 = u; uint q; + unchecked { while (r2 != 0) { + q = r1 / r2; (t1, t2, r1, r2) = (t2, t1 - int(q) * t2, r2, r1 - q * r2); + } if (t1 < 0) return (m - uint(-t1)); - + return uint(t1); } } + /** * @dev Transform affine coordinates into projective coordinates. */ @@ -102,7 +106,7 @@ library Ec_ZZ { function ecZZ_SetAff( uint x, uint y, uint zz, - uint zzz) internal pure returns (uint x1, uint y1) + uint zzz) internal returns (uint x1, uint y1) { uint zzzInv = inverseMod(zzz, p); //1/zzz y1=mulmod(y,zzzInv,p);//Y/zzz @@ -235,7 +239,7 @@ library Ec_ZZ { uint y1, uint x2, uint y2 - ) internal pure returns (uint[3] memory P) { + ) internal returns (uint[3] memory P) { uint x; uint y; unchecked { @@ -251,7 +255,7 @@ library Ec_ZZ { uint x0, uint y0, uint z0 - ) internal pure returns (uint x1, uint y1) { + ) internal returns (uint x1, uint y1) { uint z0Inv; unchecked { z0Inv = inverseMod(z0, p); @@ -260,23 +264,6 @@ library Ec_ZZ { } } - /** - * @dev Transform from jacobian to affine coordinates. - */ - function ecJAc_SetAff( - uint x0, - uint y0, - uint z0 - ) internal pure returns (uint x1, uint y1) { - uint z0Inv; - unchecked { - z0Inv = inverseMod(z0, p); - uint z0Inv2 = mulmod(z0Inv,z0Inv, p); - x1 = mulmod(x0, z0Inv2, p); //x=X/Z^2 - z0Inv = mulmod(z0Inv2,z0Inv, p); - y1 = mulmod(y0, z0Inv, p);//y=X/Z^3 - } - } /** @@ -492,7 +479,7 @@ library Ec_ZZ { uint y0, uint x1, uint y1 - ) internal pure returns (uint, uint) { + ) internal returns (uint, uint) { uint z0; (x0, y0, z0) = addProj(x0, y0, 1, x1, y1, 1); @@ -503,7 +490,7 @@ library Ec_ZZ { /** * @dev Double an elliptic curve point in affine coordinates. */ - function twice(uint x0, uint y0) internal pure returns (uint, uint) { + function twice(uint x0, uint y0) internal returns (uint, uint) { uint z0; (x0, y0, z0) = twiceProj(x0, y0, 1); @@ -518,7 +505,7 @@ library Ec_ZZ { uint x0, uint y0, uint exp - ) internal pure returns (uint, uint) { + ) internal returns (uint, uint) { uint base2X = x0; uint base2Y = y0; uint base2Z = 1; @@ -581,7 +568,7 @@ library Ec_ZZ { uint x0, uint y0, uint scalar - ) internal pure returns (uint x1, uint y1) { + ) internal returns (uint x1, uint y1) { if (scalar == 0) { return ecAff_SetZero(); } @@ -624,7 +611,7 @@ library Ec_ZZ { return ecZZ_SetAff(x1, y1, zzZZ, zzZZZ); } - function NormalizedX( uint Gx0, uint Gy0, uint Gz0) internal pure returns(uint px) + function NormalizedX( uint Gx0, uint Gy0, uint Gz0) internal returns(uint px) { if (Gz0 == 0) { return 0; @@ -712,7 +699,7 @@ library Ec_ZZ { uint Qy0, uint scalar_u, uint scalar_v - ) internal pure returns (uint[3] memory R) { + ) internal returns (uint[3] memory R) { uint[3] memory H;//G+Q (H[0],H[1], H[2] )=addProj(Gx0, Gy0, 1, Qx0, Qy0, 1); @@ -767,7 +754,7 @@ library Ec_ZZ { uint Qy0, uint scalar_u, uint scalar_v - ) internal pure returns (uint R0, uint R1) { + ) internal returns (uint R0, uint R1) { Indexing_t memory Ind; uint[2] memory R; @@ -1032,91 +1019,90 @@ library Ec_ZZ { //contract at given address dataPointer //(thx to Lakhdar https://github.com/Kelvyne for EVM storage explanations and tricks) // the external tool to generate tables from public key is in the /sage directory - function ecZZ_mulmuladd_S8_extcode(uint scalar_u, uint scalar_v, address dataPointer) internal returns(uint[2] memory P) + function ecZZ_mulmuladd_S8_extcode(uint scalar_u, uint scalar_v, address dataPointer) internal returns(uint X/*, uint Y*/) { -uint zzz;uint zz; // third and fourth coordinates of the point - - zz=255; + uint zz; // third and fourth coordinates of the point + uint[2] memory T; + zz=255;//start index unchecked{ //tbd case of msb octobit is null T[0]=128*((scalar_v>>zz)&1)+64*((scalar_v>>(zz-64))&1)+32*((scalar_v>>(zz-128))&1)+16*((scalar_v>>(zz-192))&1)+ - 8*((scalar_u>>zz)&1)+4*((scalar_u>>(zz-64))&1)+2*((scalar_u>>(zz-128))&1)+1*((scalar_u>>(zz-192))&1); + 8*((scalar_u>>zz)&1)+4*((scalar_u>>(zz-64))&1)+2*((scalar_u>>(zz-128))&1)+((scalar_u>>(zz-192))&1); T[0]=T[0]*64; //(x,y,R[0], R[1])= (Shamir8[octobit][0], Shamir8[octobit][1],1,1); //(P[0],P[1])=ecZZ_ReadExt(dataPointer, octobit); assembly{ - extcodecopy(dataPointer, P, mload(T), 64) - } - (zz, zzz)= (1,1); - - //loop over 1/4 of scalars - // for(index=254; index>=192; index--) - { - //(P[0],P[1],zz, zzz)=ecZZ_Dbl( P[0],P[1],zz, zzz); - - //inlining the ecZZ_Dbl , welcome to the carroussel - - // - - assembly{ + + extcodecopy(dataPointer, T, mload(T), 64) + X:= mload(T) + let Y:= mload(add(T,32)) + let zzz:=1 + zz:=1 + + //loop over 1/4 of scalars thx to Shamir's trick over 8 points for { let index := 254 } gt(index, 191) { index := sub(index, 1) } { - + let ind:=index // inlined Ec_Dbl - let y:=mulmod(2, mload(add(P,32)), p) //U = 2*Y1, y free + let y:=mulmod(2, Y, p) //U = 2*Y1, y free let T2:=mulmod(y,y,p) // V=U^2 - let T3:=mulmod(mload(P), T2,p)// S = X1*V + let T3:=mulmod(X, T2,p)// S = X1*V let T1:=mulmod(y, T2,p) // W=UV - - let T4:=mulmod(3, mulmod(addmod(mload(P),sub(p,zz),p), addmod(mload(P),zz,p),p) ,p) //M=3*(X1-ZZ1)*(X1+ZZ1), use zz to reduce RAM usage, x free - mstore(P, addmod(mulmod(T4,T4,p), mulmod(minus_2, T3,p),p)) //X3=M^2-2S - y:=mulmod(T4,addmod(T3, sub(p, mload(P)),p),p)//M(S-X3) + let T4:=mulmod(3, mulmod(addmod(X,sub(p,zz),p), addmod(X,zz,p),p) ,p) //M=3*(X1-ZZ1)*(X1+ZZ1), use zz to reduce RAM usage, x free zzz:=mulmod(T1,zzz,p)//zzz3=W*zzz1 - mstore(add(P,32) , addmod(y, sub(p, mulmod(T1, mload(add(P,32)) ,p)),p ))//Y3= M(S-X3)-W*Y1 + + X:=addmod(mulmod(T4,T4,p), mulmod(minus_2, T3,p),p) //X3=M^2-2S + y:=mulmod(T4,addmod(T3, sub(p, X),p),p)//M(S-X3) + Y:= addmod(y, sub(p, mulmod(T1, Y ,p)),p )//Y3= M(S-X3)-W*Y1 zz:=mulmod(T2, zz, p) //zz3=V*ZZ1 - - - /* compute element to access in precomputed table */ - let ind:=index - let T0:= add( shl(13, and(shr(ind, scalar_v),1)), shl(9, and(shr(ind, scalar_u),1)) ) + /* compute element to access in precomputed table */ + + T4:= add( shl(13, and(shr(ind, scalar_v),1)), shl(9, and(shr(ind, scalar_u),1)) ) ind:=sub(index, 64) - T0:=add(T0, add( shl(12, and(shr(ind, scalar_v),1)), shl(8, and(shr(ind, scalar_u),1)) )) + T4:=add(T4, add( shl(12, and(shr(ind, scalar_v),1)), shl(8, and(shr(ind, scalar_u),1)) )) ind:=sub(index, 128) - T0:=add(T0,add( shl(11, and(shr(ind, scalar_v),1)), shl(7, and(shr(ind, scalar_u),1)) )) + T4:=add(T4,add( shl(11, and(shr(ind, scalar_v),1)), shl(7, and(shr(ind, scalar_u),1)) )) ind:=sub(index, 192) - T0:=add(T0,add( shl(10, and(shr(ind, scalar_v),1)), shl(6, and(shr(ind, scalar_u),1)) )) + T4:=add(T4,add( shl(10, and(shr(ind, scalar_v),1)), shl(6, and(shr(ind, scalar_u),1)) )) - - - mstore(T,T0) + mstore(T,T4) /* Access to precomputed table using extcodecopy hack */ extcodecopy(dataPointer, T,mload(T), 64) // inlined Ec_AddN - y:=sub(p, mload(add(P,32))) + y:=sub(p, Y) let y2:=addmod(mulmod(mload(add(T,32)), zzz,p),y,p) - T2:=addmod(mulmod(mload(T), zz,p),sub(p,mload(P)),p) - T0:=mulmod(T2, T2, p) - T1:=mulmod(T0,T2,p) - T2:=mulmod(zz,T0,p) // W=UV + T2:=addmod(mulmod(mload(T), zz,p),sub(p,X),p) + T4:=mulmod(T2, T2, p) + T1:=mulmod(T4,T2,p) + T2:=mulmod(zz,T4,p) // W=UV zzz:= mulmod(zzz,T1,p) //zz3=V*ZZ1 - let zz1:=mulmod(mload(P), T0, p) - T0:=addmod(addmod(mulmod(y2,y2, p), sub(p,T1),p ), mulmod(minus_2, zz1,p) ,p ) - mstore(add(P,32),addmod(mulmod(addmod(zz1, sub(p,T0),p), y2, p), mulmod(y, T1,p),p)) + let zz1:=mulmod(X, T4, p) + T4:=addmod(addmod(mulmod(y2,y2, p), sub(p,T1),p ), mulmod(minus_2, zz1,p) ,p ) + Y:=addmod(mulmod(addmod(zz1, sub(p,T4),p), y2, p), mulmod(y, T1,p),p) zz:=T2 - mstore(P,T0) - }} - } - (P[0],P[1])=ecZZ_SetAff(P[0],P[1],zz, zzz); - + X:=T4 + }//end loop + mstore(T,zzz) + } + + //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz); + + T[0] = inverseMod(T[0], p); //1/zzz + assembly{ + //Y:=mulmod(Y,zzz,p)//Y/zzz + zz :=mulmod(zz, mload(T),p) //1/z + zz:= mulmod(zz,zz,p) //1/zz + X:=mulmod(X,zz,p)//X/zz + } } } @@ -1372,11 +1358,12 @@ uint zzz;uint zz; // third and fourth coordinates of the point //Shamir 8 dimensions - P=ecZZ_mulmuladd_S8_extcode(scalar_u, scalar_v, Shamir8); + P[0]=ecZZ_mulmuladd_S8_extcode(scalar_u, scalar_v, Shamir8); console.log("res Shamir 8dim precomputed XYZZ mulmuladd:",P[0]); //uint[3] memory P = ec_mulmuladd_W(gx, gy, Q[0], Q[1],scalar_u ,scalar_v ); //uint Px=NormalizedX(P[0], P[1], P[2]); + return P[0] % n == rs[0]; } From 27e3fbd40a44e5545013168d69ed7b758d4ce3c4 Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Fri, 10 Mar 2023 14:54:22 +0100 Subject: [PATCH 18/25] remove console log --- contracts/Elliptic_ZZ.sol | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/contracts/Elliptic_ZZ.sol b/contracts/Elliptic_ZZ.sol index 4c8ee8e..0c762a4 100644 --- a/contracts/Elliptic_ZZ.sol +++ b/contracts/Elliptic_ZZ.sol @@ -1032,7 +1032,6 @@ library Ec_ZZ { T[0]=128*((scalar_v>>zz)&1)+64*((scalar_v>>(zz-64))&1)+32*((scalar_v>>(zz-128))&1)+16*((scalar_v>>(zz-192))&1)+ 8*((scalar_u>>zz)&1)+4*((scalar_u>>(zz-64))&1)+2*((scalar_u>>(zz-128))&1)+((scalar_u>>(zz-192))&1); - T[0]=T[0]*64; //(x,y,R[0], R[1])= (Shamir8[octobit][0], Shamir8[octobit][1],1,1); //(P[0],P[1])=ecZZ_ReadExt(dataPointer, octobit); @@ -1048,7 +1047,7 @@ library Ec_ZZ { for { let index := 254 } gt(index, 191) { index := sub(index, 1) } { let ind:=index - // inlined Ec_Dbl + // inlined EcZZ_Dbl let y:=mulmod(2, Y, p) //U = 2*Y1, y free let T2:=mulmod(y,y,p) // V=U^2 let T3:=mulmod(X, T2,p)// S = X1*V @@ -1071,11 +1070,13 @@ library Ec_ZZ { ind:=sub(index, 192) T4:=add(T4,add( shl(10, and(shr(ind, scalar_v),1)), shl(6, and(shr(ind, scalar_u),1)) )) + + mstore(T,T4) /* Access to precomputed table using extcodecopy hack */ extcodecopy(dataPointer, T,mload(T), 64) - // inlined Ec_AddN + // inlined EcZZ_AddN y:=sub(p, Y) let y2:=addmod(mulmod(mload(add(T,32)), zzz,p),y,p) T2:=addmod(mulmod(mload(T), zz,p),sub(p,X),p) @@ -1086,16 +1087,13 @@ library Ec_ZZ { let zz1:=mulmod(X, T4, p) T4:=addmod(addmod(mulmod(y2,y2, p), sub(p,T1),p ), mulmod(minus_2, zz1,p) ,p ) Y:=addmod(mulmod(addmod(zz1, sub(p,T4),p), y2, p), mulmod(y, T1,p),p) - zz:=T2 - X:=T4 }//end loop mstore(T,zzz) } //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz); - T[0] = inverseMod(T[0], p); //1/zzz assembly{ //Y:=mulmod(Y,zzz,p)//Y/zzz @@ -1354,17 +1352,21 @@ library Ec_ZZ { uint sInv = inverseMod(rs[1], n); uint scalar_u=mulmod(uint(message), sInv, n); uint scalar_v= mulmod(rs[0], sInv, n); - uint[2] memory P; + uint X; //Shamir 8 dimensions - P[0]=ecZZ_mulmuladd_S8_extcode(scalar_u, scalar_v, Shamir8); - console.log("res Shamir 8dim precomputed XYZZ mulmuladd:",P[0]); + X=ecZZ_mulmuladd_S8_extcode(scalar_u, scalar_v, Shamir8); + // console.log("res Shamir 8dim precomputed XYZZ mulmuladd:",X); //uint[3] memory P = ec_mulmuladd_W(gx, gy, Q[0], Q[1],scalar_u ,scalar_v ); //uint Px=NormalizedX(P[0], P[1], P[2]); - - return P[0] % n == rs[0]; + assembly{ + X:=addmod(X,sub(n,mload(rs)), n) + } + + return X == 0; + } From 3a0a39bb1aabb8eb0c886e87f0b5086c9f0e0e32 Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Mon, 13 Mar 2023 16:53:29 +0100 Subject: [PATCH 19/25] modular inversion using modexp precompiles --- contracts/Elliptic_ZZ.sol | 121 ++++++++++++++++++++++++++++++-------- 1 file changed, 98 insertions(+), 23 deletions(-) diff --git a/contracts/Elliptic_ZZ.sol b/contracts/Elliptic_ZZ.sol index 0c762a4..ab8761e 100644 --- a/contracts/Elliptic_ZZ.sol +++ b/contracts/Elliptic_ZZ.sol @@ -42,9 +42,73 @@ library Ec_ZZ { //curve order (number of points) uint constant n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551; - /* -2 constant, used to speed up doubling (avoid negation)*/ + /* -2 mod p constant, used to speed up doubling (avoid negation)*/ uint constant minus_2 = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFD; + uint constant minus_2modn = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F; + + //inversion mod n via a^(n-2) + function inverseModn_Hard_back(uint u, uint m) public returns (uint res){ + unchecked{ + res=u; + + for(uint index=0;index<255;index++){ + + assembly{ + res:=mulmod(res,res,n) + let bit:=and(shr(sub(254,index),minus_2modn),1) + res:=add(mul(res,sub(1,bit)), mul(bit, mulmod(res,u,m))) + } + } + }} + + //inversion mod n via a^(n-2), use of precompiled + function inverseModn_Hard(uint256 u, uint256 m) public returns (uint256 result) { + uint[6] memory pointer; + assembly { + + // Define length of base, exponent and modulus. 0x20 == 32 bytes + mstore(pointer, 0x20) + mstore(add(pointer, 0x20), 0x20) + mstore(add(pointer, 0x40), 0x20) + // Define variables base, exponent and modulus + mstore(add(pointer, 0x60), u) + mstore(add(pointer, 0x80), minus_2modn) + mstore(add(pointer, 0xa0), m) + + + // Call the precompiled contract 0x05 = ModExp + if iszero(call(not(0), 0x05, 0, pointer, 0xc0, pointer, 0x20)) { + revert(0, 0) + } + result:=mload(pointer) + } + + } + + //inversion mod n via a^(n-2), use of precompiled + function inverseModp_Hard(uint256 u, uint256 m) public returns (uint256 result) { + uint[6] memory pointer; + assembly { + + // Define length of base, exponent and modulus. 0x20 == 32 bytes + mstore(pointer, 0x20) + mstore(add(pointer, 0x20), 0x20) + mstore(add(pointer, 0x40), 0x20) + // Define variables base, exponent and modulus + mstore(add(pointer, 0x60), u) + mstore(add(pointer, 0x80), minus_2) + mstore(add(pointer, 0xa0), m) + + + // Call the precompiled contract 0x05 = ModExp + if iszero(call(not(0), 0x05, 0, pointer, 0xc0, pointer, 0x20)) { + revert(0, 0) + } + result:=mload(pointer) + } + + } /** @@ -108,7 +172,7 @@ library Ec_ZZ { uint zz, uint zzz) internal returns (uint x1, uint y1) { - uint zzzInv = inverseMod(zzz, p); //1/zzz + uint zzzInv = inverseModp_Hard(zzz, p); //1/zzz y1=mulmod(y,zzzInv,p);//Y/zzz uint b=mulmod(zz, zzzInv,p); //1/z zzzInv= mulmod(b,b,p); //1/zz @@ -1021,21 +1085,20 @@ library Ec_ZZ { // the external tool to generate tables from public key is in the /sage directory function ecZZ_mulmuladd_S8_extcode(uint scalar_u, uint scalar_v, address dataPointer) internal returns(uint X/*, uint Y*/) { - uint zz; // third and fourth coordinates of the point + uint zz; // third and coordinates of the point - uint[2] memory T; + uint[6] memory T; zz=255;//start index unchecked{ //tbd case of msb octobit is null - T[0]=128*((scalar_v>>zz)&1)+64*((scalar_v>>(zz-64))&1)+32*((scalar_v>>(zz-128))&1)+16*((scalar_v>>(zz-192))&1)+ - 8*((scalar_u>>zz)&1)+4*((scalar_u>>(zz-64))&1)+2*((scalar_u>>(zz-128))&1)+((scalar_u>>(zz-192))&1); + T[0]=64*(128*((scalar_v>>zz)&1)+64*((scalar_v>>(zz-64))&1)+32*((scalar_v>>(zz-128))&1)+16*((scalar_v>>(zz-192))&1)+ + 8*((scalar_u>>zz)&1)+4*((scalar_u>>(zz-64))&1)+2*((scalar_u>>(zz-128))&1)+((scalar_u>>(zz-192))&1)); - T[0]=T[0]*64; //(x,y,R[0], R[1])= (Shamir8[octobit][0], Shamir8[octobit][1],1,1); //(P[0],P[1])=ecZZ_ReadExt(dataPointer, octobit); - assembly{ + assembly{ extcodecopy(dataPointer, T, mload(T), 64) X:= mload(T) @@ -1061,7 +1124,6 @@ library Ec_ZZ { zz:=mulmod(T2, zz, p) //zz3=V*ZZ1 /* compute element to access in precomputed table */ - T4:= add( shl(13, and(shr(ind, scalar_v),1)), shl(9, and(shr(ind, scalar_u),1)) ) ind:=sub(index, 64) T4:=add(T4, add( shl(12, and(shr(ind, scalar_v),1)), shl(8, and(shr(ind, scalar_u),1)) )) @@ -1070,8 +1132,6 @@ library Ec_ZZ { ind:=sub(index, 192) T4:=add(T4,add( shl(10, and(shr(ind, scalar_v),1)), shl(6, and(shr(ind, scalar_u),1)) )) - - mstore(T,T4) /* Access to precomputed table using extcodecopy hack */ extcodecopy(dataPointer, T,mload(T), 64) @@ -1090,12 +1150,25 @@ library Ec_ZZ { zz:=T2 X:=T4 }//end loop - mstore(T,zzz) - } + mstore(add(T, 0x60),zzz) + //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz); - T[0] = inverseMod(T[0], p); //1/zzz - assembly{ + //T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile: + // Define length of base, exponent and modulus. 0x20 == 32 bytes + mstore(T, 0x20) + mstore(add(T, 0x20), 0x20) + mstore(add(T, 0x40), 0x20) + // Define variables base, exponent and modulus + //mstore(add(pointer, 0x60), u) + mstore(add(T, 0x80), minus_2) + mstore(add(T, 0xa0), p) + + // Call the precompiled contract 0x05 = ModExp + if iszero(call(not(0), 0x05, 0, T, 0xc0, T, 0x20)) { + revert(0, 0) + } + //Y:=mulmod(Y,zzz,p)//Y/zzz zz :=mulmod(zz, mload(T),p) //1/z zz:= mulmod(zz,zz,p) //1/zz @@ -1219,7 +1292,7 @@ library Ec_ZZ { - uint sInv = inverseMod(rs[1], n); + uint sInv = inverseModn_Hard(rs[1], n); uint scalar_u=mulmod(uint(message), sInv, n); uint scalar_v= mulmod(rs[0], sInv, n); @@ -1277,7 +1350,7 @@ library Ec_ZZ { */ - uint sInv = inverseMod(rs[1], n); + uint sInv = inverseModn_Hard(rs[1], n); uint scalar_u=mulmod(uint(message), sInv, n); uint scalar_v= mulmod(rs[0], sInv, n); uint x1; @@ -1349,14 +1422,16 @@ library Ec_ZZ { } */ - uint sInv = inverseMod(rs[1], n); - uint scalar_u=mulmod(uint(message), sInv, n); - uint scalar_v= mulmod(rs[0], sInv, n); +// uint sInv = inverseMod(rs[1], n); + uint sInv =inverseModn_Hard(rs[1], n); + // console.log("******************************inv=",sInv); + //uint scalar_u=mulmod(uint(message), sInv, n); + //uint scalar_v= mulmod(rs[0], sInv, n); uint X; - //Shamir 8 dimensions - X=ecZZ_mulmuladd_S8_extcode(scalar_u, scalar_v, Shamir8); + //Shamir 8 dimensions + X=ecZZ_mulmuladd_S8_extcode(mulmod(uint(message), sInv, n), mulmod(rs[0], sInv, n), Shamir8); // console.log("res Shamir 8dim precomputed XYZZ mulmuladd:",X); //uint[3] memory P = ec_mulmuladd_W(gx, gy, Q[0], Q[1],scalar_u ,scalar_v ); //uint Px=NormalizedX(P[0], P[1], P[2]); @@ -1364,7 +1439,7 @@ library Ec_ZZ { assembly{ X:=addmod(X,sub(n,mload(rs)), n) } - + //return true; return X == 0; } From 22713f61dcc66520747ea79bd39a84b7bef9d62e Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Tue, 14 Mar 2023 13:20:58 +0100 Subject: [PATCH 20/25] Inline assembly for the non precomputed validate --- contracts/Elliptic_ZZ.sol | 162 +++++++++++++++++++++++++++++++++++--- 1 file changed, 149 insertions(+), 13 deletions(-) diff --git a/contracts/Elliptic_ZZ.sol b/contracts/Elliptic_ZZ.sol index ab8761e..89288f1 100644 --- a/contracts/Elliptic_ZZ.sol +++ b/contracts/Elliptic_ZZ.sol @@ -45,7 +45,7 @@ library Ec_ZZ { /* -2 mod p constant, used to speed up doubling (avoid negation)*/ uint constant minus_2 = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFD; uint constant minus_2modn = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F; - + uint constant minus_1= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; //inversion mod n via a^(n-2) function inverseModn_Hard_back(uint u, uint m) public returns (uint res){ @@ -114,6 +114,7 @@ library Ec_ZZ { /** * @dev Inverse of u in the field of modulo m. */ + /* function inverseMod(uint u, uint m) internal returns (uint) { if (u == 0 || u == m || m == 0) return 0; if (u > m) u = u % m; @@ -137,7 +138,7 @@ library Ec_ZZ { return uint(t1); } } - +*/ /** * @dev Transform affine coordinates into projective coordinates. @@ -322,7 +323,7 @@ library Ec_ZZ { ) internal returns (uint x1, uint y1) { uint z0Inv; unchecked { - z0Inv = inverseMod(z0, p); + z0Inv = inverseModp_Hard(z0, p); x1 = mulmod(x0, z0Inv, p); y1 = mulmod(y0, z0Inv, p); } @@ -681,7 +682,7 @@ library Ec_ZZ { return 0; } - uint Px = inverseMod(Gz0, p); + uint Px = inverseModp_Hard(Gz0, p); unchecked { Px = mulmod(Gx0, Px, p); } @@ -811,6 +812,7 @@ library Ec_ZZ { } + function ecZZ_mulmuladd_S( uint Gx0, uint Gy0, @@ -877,6 +879,132 @@ library Ec_ZZ { return (R0 ,R1); } + function ecZZ_mulmuladd_S_asm( + uint Q0, uint Q1,// Point G and Q stored in one memory for stack optimization + uint scalar_u, + uint scalar_v + ) internal returns (uint X) { + uint zz; + uint zzz; + uint Y; + uint index=255; + uint[6] memory T; + + + if(scalar_u==0 && scalar_v==0) return 0; + + (T[3], T[4] )=add(gx,gy,Q0, Q1); + + + + while( ( ((scalar_u>>index)&1)+2*((scalar_v>>index)&1) ) ==0){ + index=index-1; + } + + zz =((scalar_u>>index)&1)+2*((scalar_v>>index)&1); + if(zz==1){ + (X,Y) = (gx, gy); + } + if(zz==2){ + (X,Y) = (Q0, Q1); + } + if(zz==3){ + (X,Y) = (T[3], T[4]); + } + + index=index-1; + console.log("index=", index); + //index=1<= n || rs[1] == 0) { return false; } + + if (!ecAff_isOnCurve(Q[0], Q[1])) { return false; } @@ -1319,11 +1451,15 @@ library Ec_ZZ { //Shamir 2 dimensions //(x1, y1)=ecZZ_mulmuladd_S(gx, gy, Q[0], Q[1],scalar_u, scalar_v); - (x1, y1)=ecZZ_mulmuladd_S(gx, gy, Q[0], Q[1],scalar_u, scalar_v); + x1=ecZZ_mulmuladd_S_asm(Q[0], Q[1],scalar_u, scalar_v); - console.log("res Shamir monobit XYZZ mulmuladd:",x1); + //console.log("res Shamir monobit XYZZ mulmuladd:",x1); - return x1 % n == rs[0]; + assembly{ + x1:=addmod(x1,sub(n,mload(rs)), n) + } + //return true; + return x1 == 0; } @@ -1389,7 +1525,7 @@ library Ec_ZZ { } */ - uint sInv = inverseMod(rs[1], n); + uint sInv = inverseModn_Hard(rs[1], n); uint scalar_u=mulmod(uint(message), sInv, n); uint scalar_v= mulmod(rs[0], sInv, n); uint[2] memory P; From 990b992478c2ca5f1c7788f78a8e0749be61beec Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Tue, 14 Mar 2023 16:53:46 +0100 Subject: [PATCH 21/25] Separating FCL functions from Drerup library --- contracts/Elliptic_ZZ.sol | 60 +++++++++++++++++++++++++++---------- contracts/Webauthn.sol | 7 +++-- contracts/Webauthn_prec.sol | 9 ++++-- 3 files changed, 55 insertions(+), 21 deletions(-) diff --git a/contracts/Elliptic_ZZ.sol b/contracts/Elliptic_ZZ.sol index 89288f1..222fc53 100644 --- a/contracts/Elliptic_ZZ.sol +++ b/contracts/Elliptic_ZZ.sol @@ -4,18 +4,40 @@ // | _| '_/ -_|_-< ' \ | (__| '_| || | '_ \ _/ _ \ | |__| | '_ \ // |_||_| \___/__/_||_| \___|_| \_, | .__/\__\___/ |____|_|_.__/ // |__/|_| -///* Copyright (C) 2022 - Renaud Dubois - This file is part of FCL (Fresh CryptoLib) project */ -///* License: This software is licensed under MIT License */ -///* See LICENSE file at the root folder of the project. */ -///* FILE: Elliptic_ZZ.sol */ -///* */ -///* */ +///* Copyright (C) 2022 - Renaud Dubois - This file is part of FCL (Fresh CryptoLib) project +///* License: This software is licensed under MIT License +///* This Code may be reused including license and copyright notice. +///* See LICENSE file at the root folder of the project. +///* FILE: Elliptic_ZZ.sol +///* +///* ///* DESCRIPTION: modified XYZZ system coordinates for EVM elliptic point multiplication ///* optimization ///* //**************************************************************************************/ - +//* WARNING: this code SHALL not be used for non prime order curves for security reasons. // SPDX-License-Identifier: MIT +//Also contain for validating and benchmarking purpose projective curve implementation from +/** + * @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) + * + * @dev NOTE: To disambiguate public keys when verifying signatures, activate + * condition 'rs[1] > lowSmax' in validateSignature(). + */ + + // Benchmark results: + // ECDSA verification using Tilman Drerup : 1.1M gas/verification + // ECDSA verification using FCL, no precomputation: 290K gas + //ECDSA verificaiton using FCL with precomputations: 151K gas + pragma solidity ^0.8.0; import {Base64URL} from "./Base64URL.sol"; @@ -47,7 +69,7 @@ library Ec_ZZ { uint constant minus_2modn = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F; uint constant minus_1= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; - //inversion mod n via a^(n-2) + //inversion mod n via a^(n-2) using little Fermat theorem function inverseModn_Hard_back(uint u, uint m) public returns (uint res){ unchecked{ res=u; @@ -62,7 +84,7 @@ library Ec_ZZ { } }} - //inversion mod n via a^(n-2), use of precompiled + //inversion mod n via a^(n-2), use of precompiled using little Fermat theorem function inverseModn_Hard(uint256 u, uint256 m) public returns (uint256 result) { uint[6] memory pointer; assembly { @@ -76,7 +98,6 @@ library Ec_ZZ { mstore(add(pointer, 0x80), minus_2modn) mstore(add(pointer, 0xa0), m) - // Call the precompiled contract 0x05 = ModExp if iszero(call(not(0), 0x05, 0, pointer, 0xc0, pointer, 0x20)) { revert(0, 0) @@ -86,7 +107,7 @@ library Ec_ZZ { } - //inversion mod n via a^(n-2), use of precompiled + //inversion mod nusing little Fermat theorem via a^(n-2), use of precompiled function inverseModp_Hard(uint256 u, uint256 m) public returns (uint256 result) { uint[6] memory pointer; assembly { @@ -359,6 +380,11 @@ library Ec_ZZ { return (0, 0); } + function ecAff_IsZero(uint x, uint y) internal pure returns (bool flag) { + return (y==0); + } + + /** * @dev Check if the curve is the zero curve in affine rep. */ @@ -545,11 +571,15 @@ library Ec_ZZ { uint x1, uint y1 ) internal returns (uint, uint) { - uint z0; - - (x0, y0, z0) = addProj(x0, y0, 1, x1, y1, 1); + uint zz0; + uint zzz0; + + if(ecAff_IsZero(x0,y0)) return (x1,y1); + if(ecAff_IsZero(x1,y1)) return (x1,y1); + + (x0, y0, zz0, zzz0) = ecZZ_AddN(x0, y0, 1,1, x1, y1); - return toAffinePoint(x0, y0, z0); + return ecZZ_SetAff(x0, y0, zz0, zzz0); } /** diff --git a/contracts/Webauthn.sol b/contracts/Webauthn.sol index 6e0a71b..5ffb6e0 100644 --- a/contracts/Webauthn.sol +++ b/contracts/Webauthn.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; import {Base64URL} from "./Base64URL.sol"; -import {Ec_ZZ} from "./Elliptic_ZZ.sol"; +import {FCL_Elliptic_ZZ} from "./FCL_elliptic.sol"; import "hardhat/console.sol"; @@ -22,7 +22,7 @@ contract Webauthn { console.log("hash=", uint(hash)); console.log("rs0=", rs[0]); - bool result=Ec_ZZ.validateSignature(bytes32(hash), rs, Q); + bool result=FCL_Elliptic_ZZ.ecdsa_verify(bytes32(hash), rs, Q); console.log("result= %s", result); } @@ -85,7 +85,8 @@ contract Webauthn { console.log("verifyData:", tmp); */ bytes32 message = sha256(verifyData); - bool result=Ec_ZZ.validateSignature(message, rs, Q); + //bool result=Ec_ZZ.validateSignature(message, rs, Q); + bool result=FCL_Elliptic_ZZ.ecdsa_verify(message, rs, Q); console.log("result= %s", result); return result; diff --git a/contracts/Webauthn_prec.sol b/contracts/Webauthn_prec.sol index 9a567b9..a049f30 100644 --- a/contracts/Webauthn_prec.sol +++ b/contracts/Webauthn_prec.sol @@ -3,6 +3,9 @@ pragma solidity ^0.8.0; import {Base64URL} from "./Base64URL.sol"; import {Ec_ZZ} from "./Elliptic_ZZ.sol"; + +import {FCL_Elliptic_ZZ} from "./FCL_elliptic.sol"; + import "hardhat/console.sol"; import "solmate/src/utils/SSTORE2.sol"; @@ -35,8 +38,8 @@ contract BytecodeTable { assembly{ extcodecopy(selfe, px, offset, 64) } - console.log("Test on curve of point ",i, px[0], px[1]); - console.log(Ec_ZZ.ecAff_isOnCurve(px[0], px[1])); + //console.log("Test on curve of point ",i, px[0], px[1]); + //console.log(Ec_ZZ.ecAff_isOnCurve(px[0], px[1])); } } } @@ -106,7 +109,7 @@ contract Webauthn_prec3{ authenticatorData.length ); bytes32 message = sha256(verifyData); - bool result=Ec_ZZ.validateSignature_Precomputed_extcode(message, rs, dataPointer); + bool result=FCL_Elliptic_ZZ.ecdsa_precomputed_verify(message, rs, dataPointer); console.log("result= %s", result); return result; From ce2faf165ce77ee924bfb04bf61de8aa00b5555c Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Tue, 14 Mar 2023 16:55:02 +0100 Subject: [PATCH 22/25] Adding FCL elliptic Sutherland XYZZ representation in assembly --- contracts/FCL_elliptic.sol | 534 +++++++++++++++++++++++++++++++++++++ 1 file changed, 534 insertions(+) create mode 100644 contracts/FCL_elliptic.sol diff --git a/contracts/FCL_elliptic.sol b/contracts/FCL_elliptic.sol new file mode 100644 index 0000000..33d31f2 --- /dev/null +++ b/contracts/FCL_elliptic.sol @@ -0,0 +1,534 @@ +//********************************************************************************************/ +// ___ _ ___ _ _ _ _ +// | __| _ ___ __| |_ / __|_ _ _ _ _ __| |_ ___ | | (_) |__ +// | _| '_/ -_|_-< ' \ | (__| '_| || | '_ \ _/ _ \ | |__| | '_ \ +// |_||_| \___/__/_||_| \___|_| \_, | .__/\__\___/ |____|_|_.__/ +// |__/|_| +///* Copyright (C) 2022 - Renaud Dubois - This file is part of FCL (Fresh CryptoLib) project +///* License: This software is licensed under MIT License +///* This Code may be reused including license and copyright notice. +///* See LICENSE file at the root folder of the project. +///* FILE: FCL_elliptic.sol +///* +///* +///* DESCRIPTION: modified XYZZ system coordinates for EVM elliptic point multiplication +///* optimization +///* +//**************************************************************************************/ +//* WARNING: this code SHALL not be used for non prime order curves for security reasons. +// Code is optimized for a=-3 only curves with prime order, constant like -1, -2 shall be replaced +// if ever used for other curve than sec256R1 +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Base64URL} from "./Base64URL.sol"; +import "solmate/src/utils/SSTORE2.sol"; +import "hardhat/console.sol"; + +library FCL_Elliptic_ZZ { + // Set parameters for curve sec256r1. + + //curve prime field modulus + uint constant p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF; + //short weierstrass first coefficient + uint constant a = + 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC; + //short weierstrass second coefficient + uint constant b = + 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B; + //generating point affine coordinates + uint constant gx = + 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296; + uint constant gy = + 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5; + //curve order (number of points) + uint constant n = + 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551; + /* -2 mod p constant, used to speed up inversion and doubling (avoid negation)*/ + uint constant minus_2 = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFD; + /* -2 mod n constant, used to speed up inversion*/ + uint constant minus_2modn = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F; + + uint constant minus_1= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + + /** + /* inversion mod n via a^(n-2), use of precompiled using little Fermat theorem*/ + function FCL_nModInv(uint256 u, uint256 m) public returns (uint256 result) { + uint[6] memory pointer; + assembly { + + // Define length of base, exponent and modulus. 0x20 == 32 bytes + mstore(pointer, 0x20) + mstore(add(pointer, 0x20), 0x20) + mstore(add(pointer, 0x40), 0x20) + // Define variables base, exponent and modulus + mstore(add(pointer, 0x60), u) + mstore(add(pointer, 0x80), minus_2modn) + mstore(add(pointer, 0xa0), m) + + // Call the precompiled contract 0x05 = ModExp + if iszero(call(not(0), 0x05, 0, pointer, 0xc0, pointer, 0x20)) { + revert(0, 0) + } + result:=mload(pointer) + } + + } + /** + /* @dev inversion mod nusing little Fermat theorem via a^(n-2), use of precompiled*/ + function FCL_pModInv(uint256 u, uint256 m) public returns (uint256 result) { + uint[6] memory pointer; + assembly { + // Define length of base, exponent and modulus. 0x20 == 32 bytes + mstore(pointer, 0x20) + mstore(add(pointer, 0x20), 0x20) + mstore(add(pointer, 0x40), 0x20) + // Define variables base, exponent and modulus + mstore(add(pointer, 0x60), u) + mstore(add(pointer, 0x80), minus_2) + mstore(add(pointer, 0xa0), m) + + // Call the precompiled contract 0x05 = ModExp + if iszero(call(not(0), 0x05, 0, pointer, 0xc0, pointer, 0x20)) { + revert(0, 0) + } + result:=mload(pointer) + } + } + + /** + /* @dev Convert from affine rep to XYZZ rep*/ + function ecAff_SetZZ( + uint x0, + uint y0 + ) internal pure returns (uint[4] memory P) { + unchecked { + P[2] = 1; //ZZ + P[3] = 1; //ZZZ + P[0] = x0; + P[1] = y0; + } + } + + /** + /* @dev Convert from XYZZ rep to affine rep*/ + /* https://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz-3.html#addition-add-2008-s*/ + function ecZZ_SetAff( uint x, + uint y, + uint zz, + uint zzz) internal returns (uint x1, uint y1) + { + uint zzzInv = FCL_pModInv(zzz, p); //1/zzz + y1=mulmod(y,zzzInv,p);//Y/zzz + uint b=mulmod(zz, zzzInv,p); //1/z + zzzInv= mulmod(b,b,p); //1/zz + x1=mulmod(x,zzzInv,p);//X/zz + } + + + + /** + /* @dev Sutherland2008 doubling*/ + /* The "dbl-2008-s-1" doubling formulas */ + function ecZZ_Dbl( + uint x, + uint y, + uint zz, + uint zzz + ) internal pure returns (uint P0, uint P1,uint P2,uint P3) + { + unchecked{ + assembly{ + P0:=mulmod(2, y, p) //U = 2*Y1 + P2:=mulmod(P0,P0,p) // V=U^2 + P3:=mulmod(x, P2,p)// S = X1*V + P1:=mulmod(P0, P2,p) // W=UV + P2:=mulmod(P2, zz, p) //zz3=V*ZZ1 + zz:=mulmod(3, mulmod(addmod(x,sub(p,zz),p), addmod(x,zz,p),p) ,p) //M=3*(X1-ZZ1)*(X1+ZZ1), use zz to reduce RAM usage + P0:=addmod(mulmod(zz,zz,p), mulmod(minus_2, P3,p),p) //X3=M^2-2S + x:=mulmod(zz,addmod(P3, sub(p,P0),p),p)//M(S-X3) + P3:=mulmod(P1,zzz,p)//zzz3=W*zzz1 + P1:=addmod(x, sub(p, mulmod(P1, y,p)),p )//Y3= M(S-X3)-W*Y1 + } + } + return (P0, P1, P2, P3); + } + + /** + * @dev Sutherland2008 add a ZZ point with a normalized point and greedy formulae + * warning: assume that P1(x1,y1)!=P2(x2,y2), true in multiplication loop with prime order (cofactor 1) + */ + + //tbd: return -x1 and -Y1 in double to avoid two substractions + function ecZZ_AddN( + uint x1, + uint y1, + uint zz1, + uint zzz1, + uint x2, + uint y2) internal pure returns (uint P0, uint P1,uint P2,uint P3) + { + unchecked{ + if(y1==0){ + return (x2,y2,1,1); + } + + assembly{ + y1:=sub(p, y1) + y2:=addmod(mulmod(y2, zzz1,p),y1,p) + x2:=addmod(mulmod(x2, zz1,p),sub(p,x1),p) + P0:=mulmod(x2, x2, p)//PP = P^2 + P1:=mulmod(P0,x2,p)//PPP = P*PP + P2:=mulmod(zz1,P0,p) ////ZZ3 = ZZ1*PP + P3:= mulmod(zzz1,P1,p) ////ZZZ3 = ZZZ1*PPP + zz1:=mulmod(x1, P0, p)//Q = X1*PP + P0:=addmod(addmod(mulmod(y2,y2, p), sub(p,P1),p ), mulmod(minus_2, zz1,p) ,p )//R^2-PPP-2*Q + P1:=addmod(mulmod(addmod(zz1, sub(p,P0),p), y2, p), mulmod(y1, P1,p),p)//R*(Q-X3) + } + //end assembly + }//end unchecked + return (P0, P1, P2, P3); + } + + /** + * @dev Return the zero curve in XYZZ coordinates. + */ + function ecZZ_SetZero() internal pure returns (uint x, uint y, uint zz, uint zzz) { + return (0, 0, 0, 0); + } + + function ecZZ_IsZero (uint x0, uint y0, uint zz0, uint zzz0) internal pure returns (bool) + { + if ( (y0 == 0) ) { + return true; + } + return false; + } + /** + * @dev Return the zero curve in affine coordinates. Compatible with the double formulae (no special case) + */ + function ecAff_SetZero() internal pure returns (uint x, uint y) { + return (0, 0); + } + + /** + * @dev Check if the curve is the zero curve in affine rep. + */ + function ecAff_IsZero(uint x, uint y) internal pure returns (bool flag) { + return (y==0); + } + + /** + * @dev Check if a point in affine coordinates is on the curve. + */ + function ecAff_isOnCurve(uint x, uint y) internal pure returns (bool) { + if (0 == x || x == p || 0 == y || y == p) { + return false; + } + unchecked { + uint LHS = mulmod(y, y, p); // y^2 + uint 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 Add two elliptic curve points in affine coordinates. + */ + function ecAff_add( + uint x0, + uint y0, + uint x1, + uint y1 + ) internal returns (uint, uint) { + uint zz0; + uint zzz0; + + if(ecAff_IsZero(x0,y0)) return (x1,y1); + if(ecAff_IsZero(x1,y1)) return (x1,y1); + + (x0, y0, zz0, zzz0) = ecZZ_AddN(x0, y0, 1,1, x1, y1); + + return ecZZ_SetAff(x0, y0, zz0, zzz0); + } + + /** + * @dev Computation of uG+vQ using Strauss-Shamir's trick, G basepoint, Q public key + */ + function ecZZ_mulmuladd_S_asm( + uint Q0, uint Q1,// Point G and Q stored in one memory for stack optimization + uint scalar_u, + uint scalar_v + ) internal returns (uint X) { + uint zz; + uint zzz; + uint Y; + uint index=255; + uint[6] memory T; + + if(scalar_u==0 && scalar_v==0) return 0; + + (T[3], T[4] )=ecAff_add(gx,gy,Q0, Q1); + + while( ( ((scalar_u>>index)&1)+2*((scalar_v>>index)&1) ) ==0){ + index=index-1; + } + + zz =((scalar_u>>index)&1)+2*((scalar_v>>index)&1); + if(zz==1){ + (X,Y) = (gx, gy); + } + if(zz==2){ + (X,Y) = (Q0, Q1); + } + if(zz==3){ + (X,Y) = (T[3], T[4]); + } + + index=index-1; + + zz=1; + zzz=1; + unchecked { + + assembly{ + for { index := index } gt( minus_1, index) { index := sub(index, 1) } + { + // inlined EcZZ_Dbl + let y:=mulmod(2, Y, p) //U = 2*Y1, y free + let T2:=mulmod(y,y,p) // V=U^2 + let T3:=mulmod(X, T2,p)// S = X1*V + let T1:=mulmod(y, T2,p) // W=UV + let T4:=mulmod(3, mulmod(addmod(X,sub(p,zz),p), addmod(X,zz,p),p) ,p) //M=3*(X1-ZZ1)*(X1+ZZ1), use zz to reduce RAM usage, x free + zzz:=mulmod(T1,zzz,p)//zzz3=W*zzz1 + + X:=addmod(mulmod(T4,T4,p), mulmod(minus_2, T3,p),p) //X3=M^2-2S + y:=mulmod(T4,addmod(T3, sub(p, X),p),p)//M(S-X3) + Y:= addmod(y, sub(p, mulmod(T1, Y ,p)),p )//Y3= M(S-X3)-W*Y1 + zz:=mulmod(T2, zz, p) //zz3=V*ZZ1 + + //value of dibit + T4:=add( shl(1, and(shr(index, scalar_v),1)), and(shr(index, scalar_u),1) ) + + if eq(T4,1) { + mstore(T, gx) + mstore(add(T,32) , gy) + } + if eq(T4,2) { + mstore(T, Q0) + mstore(add(T,32) , Q1) + } + if eq(T4,3) { + mstore(T, mload(add(T,96))) + mstore(add(T,32) , mload(add(T,128))) + } + if gt(T4,0){ + // inlined EcZZ_AddN + y:=sub(p, Y) + let y2:=addmod(mulmod(mload(add(T,32)), zzz,p),y,p) + T2:=addmod(mulmod(mload(T), zz,p),sub(p,X),p) + T4:=mulmod(T2, T2, p) + T1:=mulmod(T4,T2,p) + T2:=mulmod(zz,T4,p) // W=UV + zzz:= mulmod(zzz,T1,p) //zz3=V*ZZ1 + let zz1:=mulmod(X, T4, p) + T4:=addmod(addmod(mulmod(y2,y2, p), sub(p,T1),p ), mulmod(minus_2, zz1,p) ,p ) + Y:=addmod(mulmod(addmod(zz1, sub(p,T4),p), y2, p), mulmod(y, T1,p),p) + zz:=T2 + X:=T4 + } + }//end loop + mstore(add(T, 0x60),zzz) + //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz); + //T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile: + // Define length of base, exponent and modulus. 0x20 == 32 bytes + mstore(T, 0x20) + mstore(add(T, 0x20), 0x20) + mstore(add(T, 0x40), 0x20) + // Define variables base, exponent and modulus + //mstore(add(pointer, 0x60), u) + mstore(add(T, 0x80), minus_2) + mstore(add(T, 0xa0), p) + + // Call the precompiled contract 0x05 = ModExp + if iszero(call(not(0), 0x05, 0, T, 0xc0, T, 0x20)) { + revert(0, 0) + } + + //Y:=mulmod(Y,zzz,p)//Y/zzz + zz :=mulmod(zz, mload(T),p) //1/z + zz:= mulmod(zz,zz,p) //1/zz + X:=mulmod(X,zz,p)//X/zz + } //end assembly + }//end unchecked + + return X; + } + + + //8 dimensions Shamir's trick, using precomputations stored in Shamir8, stored as Bytecode of an external + //contract at given address dataPointer + //(thx to Lakhdar https://github.com/Kelvyne for EVM storage explanations and tricks) + // the external tool to generate tables from public key is in the /sage directory + function ecZZ_mulmuladd_S8_extcode(uint scalar_u, uint scalar_v, address dataPointer) internal returns(uint X/*, uint Y*/) + { + uint zz; // third and coordinates of the point + + uint[6] memory T; + zz=255;//start index + + unchecked{ + + //tbd case of msb octobit is null + T[0]=64*(128*((scalar_v>>zz)&1)+64*((scalar_v>>(zz-64))&1)+32*((scalar_v>>(zz-128))&1)+16*((scalar_v>>(zz-192))&1)+ + 8*((scalar_u>>zz)&1)+4*((scalar_u>>(zz-64))&1)+2*((scalar_u>>(zz-128))&1)+((scalar_u>>(zz-192))&1)); + + //(x,y,R[0], R[1])= (Shamir8[octobit][0], Shamir8[octobit][1],1,1); + //(P[0],P[1])=ecZZ_ReadExt(dataPointer, octobit); + assembly{ + + extcodecopy(dataPointer, T, mload(T), 64) + X:= mload(T) + let Y:= mload(add(T,32)) + let zzz:=1 + zz:=1 + + //loop over 1/4 of scalars thx to Shamir's trick over 8 points + for { let index := 254 } gt(index, 191) { index := sub(index, 1) } + { + let ind:=index + // inlined EcZZ_Dbl + let y:=mulmod(2, Y, p) //U = 2*Y1, y free + let T2:=mulmod(y,y,p) // V=U^2 + let T3:=mulmod(X, T2,p)// S = X1*V + let T1:=mulmod(y, T2,p) // W=UV + let T4:=mulmod(3, mulmod(addmod(X,sub(p,zz),p), addmod(X,zz,p),p) ,p) //M=3*(X1-ZZ1)*(X1+ZZ1), use zz to reduce RAM usage, x free + zzz:=mulmod(T1,zzz,p)//zzz3=W*zzz1 + + X:=addmod(mulmod(T4,T4,p), mulmod(minus_2, T3,p),p) //X3=M^2-2S + y:=mulmod(T4,addmod(T3, sub(p, X),p),p)//M(S-X3) + Y:= addmod(y, sub(p, mulmod(T1, Y ,p)),p )//Y3= M(S-X3)-W*Y1 + zz:=mulmod(T2, zz, p) //zz3=V*ZZ1 + + /* compute element to access in precomputed table */ + T4:= add( shl(13, and(shr(ind, scalar_v),1)), shl(9, and(shr(ind, scalar_u),1)) ) + ind:=sub(index, 64) + T4:=add(T4, add( shl(12, and(shr(ind, scalar_v),1)), shl(8, and(shr(ind, scalar_u),1)) )) + ind:=sub(index, 128) + T4:=add(T4,add( shl(11, and(shr(ind, scalar_v),1)), shl(7, and(shr(ind, scalar_u),1)) )) + ind:=sub(index, 192) + T4:=add(T4,add( shl(10, and(shr(ind, scalar_v),1)), shl(6, and(shr(ind, scalar_u),1)) )) + + mstore(T,T4) + /* Access to precomputed table using extcodecopy hack */ + extcodecopy(dataPointer, T,mload(T), 64) + + // inlined EcZZ_AddN + y:=sub(p, Y) + let y2:=addmod(mulmod(mload(add(T,32)), zzz,p),y,p) + T2:=addmod(mulmod(mload(T), zz,p),sub(p,X),p) + T4:=mulmod(T2, T2, p) + T1:=mulmod(T4,T2,p) + T2:=mulmod(zz,T4,p) // W=UV + zzz:= mulmod(zzz,T1,p) //zz3=V*ZZ1 + let zz1:=mulmod(X, T4, p) + T4:=addmod(addmod(mulmod(y2,y2, p), sub(p,T1),p ), mulmod(minus_2, zz1,p) ,p ) + Y:=addmod(mulmod(addmod(zz1, sub(p,T4),p), y2, p), mulmod(y, T1,p),p) + zz:=T2 + X:=T4 + }//end loop + mstore(add(T, 0x60),zz) + + //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz); + //T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile: + // Define length of base, exponent and modulus. 0x20 == 32 bytes + mstore(T, 0x20) + mstore(add(T, 0x20), 0x20) + mstore(add(T, 0x40), 0x20) + // Define variables base, exponent and modulus + //mstore(add(pointer, 0x60), u) + mstore(add(T, 0x80), minus_2) + mstore(add(T, 0xa0), p) + + // Call the precompiled contract 0x05 = ModExp + if iszero(call(not(0), 0x05, 0, T, 0xc0, T, 0x20)) { + revert(0, 0) + } + + zz:=mload(T) + X:=mulmod(X,zz,p)//X/zz + } + }//end unchecked + } + + /** + * @dev Validate combination of message, signature, and public key. + */ + function ecdsa_verify( + bytes32 message, + uint[2] memory rs, + uint[2] memory Q + ) internal returns (bool) { + if (rs[0] == 0 || rs[0] >= n || rs[1] == 0) { + return false; + } + + + if (!ecAff_isOnCurve(Q[0], Q[1])) { + return false; + } + + uint sInv = FCL_nModInv(rs[1], n); + uint scalar_u=mulmod(uint(message), sInv, n); + uint scalar_v= mulmod(rs[0], sInv, n); + uint x1; + + x1=ecZZ_mulmuladd_S_asm(Q[0], Q[1],scalar_u, scalar_v); + + assembly{ + x1:=addmod(x1,sub(n,mload(rs)), n) + } + //return true; + return x1 == 0; + + } + + /* validating signatures using a precomputed table of multiples of P and Q stored in contract at address Shamir8*/ + function ecdsa_precomputed_verify( + bytes32 message, + uint[2] memory rs, + address Shamir8 + ) internal returns (bool) { + if (rs[0] == 0 || rs[0] >= n || rs[1] == 0) { + return false; + } + /* tbd or not: check Q + if (!isOnCurve(Q[0], Q[1])) { + return false; + }*/ + + uint sInv =FCL_nModInv(rs[1], n); + uint X; + + //Shamir 8 dimensions + X=ecZZ_mulmuladd_S8_extcode(mulmod(uint(message), sInv, n), mulmod(rs[0], sInv, n), Shamir8); + + assembly{ + X:=addmod(X,sub(n,mload(rs)), n) + } + //return true; + return X == 0; + + }//end ecdsa_precomputed_verify() +}//EOF + + From b94782aee31780830f3adfc75890cb21969b3102 Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Wed, 15 Mar 2023 11:08:29 +0100 Subject: [PATCH 23/25] Removing unused debug dependencies --- contracts/FCL_elliptic.sol | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/contracts/FCL_elliptic.sol b/contracts/FCL_elliptic.sol index 33d31f2..6529664 100644 --- a/contracts/FCL_elliptic.sol +++ b/contracts/FCL_elliptic.sol @@ -21,9 +21,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {Base64URL} from "./Base64URL.sol"; -import "solmate/src/utils/SSTORE2.sol"; -import "hardhat/console.sol"; +//import "hardhat/console.sol"; library FCL_Elliptic_ZZ { // Set parameters for curve sec256r1. @@ -196,7 +194,9 @@ library FCL_Elliptic_ZZ { function ecZZ_SetZero() internal pure returns (uint x, uint y, uint zz, uint zzz) { return (0, 0, 0, 0); } - + /** + * @dev Check if point is the neutral of the curve + */ function ecZZ_IsZero (uint x0, uint y0, uint zz0, uint zzz0) internal pure returns (bool) { if ( (y0 == 0) ) { @@ -219,7 +219,7 @@ library FCL_Elliptic_ZZ { } /** - * @dev Check if a point in affine coordinates is on the curve. + * @dev Check if a point in affine coordinates is on the curve (reject Neutral that is indeed on the curve). */ function ecAff_isOnCurve(uint x, uint y) internal pure returns (bool) { if (0 == x || x == p || 0 == y || y == p) { @@ -227,15 +227,9 @@ library FCL_Elliptic_ZZ { } unchecked { uint LHS = mulmod(y, y, p); // y^2 - uint 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 - } - + uint RHS = addmod(mulmod(mulmod(x, x, p), x, p), mulmod(x, a, p), p); // x^3+ax + RHS = addmod(RHS, b, p); // x^3 + a*x + b + return LHS == RHS; } } @@ -384,17 +378,18 @@ library FCL_Elliptic_ZZ { uint zz; // third and coordinates of the point uint[6] memory T; - zz=255;//start index + zz=256;//start index unchecked{ + while(T[0]==0) + { + zz=zz-1; //tbd case of msb octobit is null T[0]=64*(128*((scalar_v>>zz)&1)+64*((scalar_v>>(zz-64))&1)+32*((scalar_v>>(zz-128))&1)+16*((scalar_v>>(zz-192))&1)+ 8*((scalar_u>>zz)&1)+4*((scalar_u>>(zz-64))&1)+2*((scalar_u>>(zz-128))&1)+((scalar_u>>(zz-192))&1)); - - //(x,y,R[0], R[1])= (Shamir8[octobit][0], Shamir8[octobit][1],1,1); - //(P[0],P[1])=ecZZ_ReadExt(dataPointer, octobit); - assembly{ + } + assembly{ extcodecopy(dataPointer, T, mload(T), 64) X:= mload(T) @@ -511,7 +506,7 @@ library FCL_Elliptic_ZZ { if (rs[0] == 0 || rs[0] >= n || rs[1] == 0) { return false; } - /* tbd or not: check Q + /* Q is pushed via bytecode assumed to be correct if (!isOnCurve(Q[0], Q[1])) { return false; }*/ From 01c90181cd0f7ab97df7c9e48f50e961a9b8979f Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Thu, 16 Mar 2023 14:32:18 +0100 Subject: [PATCH 24/25] one register spared --- contracts/FCL_elliptic.sol | 84 ++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/contracts/FCL_elliptic.sol b/contracts/FCL_elliptic.sol index 6529664..7c95400 100644 --- a/contracts/FCL_elliptic.sol +++ b/contracts/FCL_elliptic.sol @@ -51,7 +51,7 @@ library FCL_Elliptic_ZZ { /** /* inversion mod n via a^(n-2), use of precompiled using little Fermat theorem*/ - function FCL_nModInv(uint256 u, uint256 m) public returns (uint256 result) { + function FCL_nModInv(uint256 u) public returns (uint256 result) { uint[6] memory pointer; assembly { @@ -62,7 +62,7 @@ library FCL_Elliptic_ZZ { // Define variables base, exponent and modulus mstore(add(pointer, 0x60), u) mstore(add(pointer, 0x80), minus_2modn) - mstore(add(pointer, 0xa0), m) + mstore(add(pointer, 0xa0), n) // Call the precompiled contract 0x05 = ModExp if iszero(call(not(0), 0x05, 0, pointer, 0xc0, pointer, 0x20)) { @@ -74,7 +74,7 @@ library FCL_Elliptic_ZZ { } /** /* @dev inversion mod nusing little Fermat theorem via a^(n-2), use of precompiled*/ - function FCL_pModInv(uint256 u, uint256 m) public returns (uint256 result) { + function FCL_pModInv(uint256 u) public returns (uint256 result) { uint[6] memory pointer; assembly { // Define length of base, exponent and modulus. 0x20 == 32 bytes @@ -84,7 +84,7 @@ library FCL_Elliptic_ZZ { // Define variables base, exponent and modulus mstore(add(pointer, 0x60), u) mstore(add(pointer, 0x80), minus_2) - mstore(add(pointer, 0xa0), m) + mstore(add(pointer, 0xa0), p) // Call the precompiled contract 0x05 = ModExp if iszero(call(not(0), 0x05, 0, pointer, 0xc0, pointer, 0x20)) { @@ -116,7 +116,7 @@ library FCL_Elliptic_ZZ { uint zz, uint zzz) internal returns (uint x1, uint y1) { - uint zzzInv = FCL_pModInv(zzz, p); //1/zzz + uint zzzInv = FCL_pModInv(zzz); //1/zzz y1=mulmod(y,zzzInv,p);//Y/zzz uint b=mulmod(zz, zzzInv,p); //1/z zzzInv= mulmod(b,b,p); //1/zz @@ -268,10 +268,11 @@ library FCL_Elliptic_ZZ { uint Y; uint index=255; uint[6] memory T; - + uint H0; + uint H1; if(scalar_u==0 && scalar_v==0) return 0; - (T[3], T[4] )=ecAff_add(gx,gy,Q0, Q1); + (H0,H1 )=ecAff_add(gx,gy,Q0, Q1); while( ( ((scalar_u>>index)&1)+2*((scalar_v>>index)&1) ) ==0){ index=index-1; @@ -285,58 +286,67 @@ library FCL_Elliptic_ZZ { (X,Y) = (Q0, Q1); } if(zz==3){ - (X,Y) = (T[3], T[4]); + (X,Y) = (H0, H1); } index=index-1; - zz=1; - zzz=1; unchecked { assembly{ + zz:=1 + zzz:=1 + for { index := index } gt( minus_1, index) { index := sub(index, 1) } - { + { + // inlined EcZZ_Dbl - let y:=mulmod(2, Y, p) //U = 2*Y1, y free - let T2:=mulmod(y,y,p) // V=U^2 + let T1:=mulmod(2, Y, p) //U = 2*Y1, y free + let T2:=mulmod(T1,T1,p) // V=U^2 let T3:=mulmod(X, T2,p)// S = X1*V - let T1:=mulmod(y, T2,p) // W=UV + T1:=mulmod(T1, T2,p) // W=UV let T4:=mulmod(3, mulmod(addmod(X,sub(p,zz),p), addmod(X,zz,p),p) ,p) //M=3*(X1-ZZ1)*(X1+ZZ1), use zz to reduce RAM usage, x free zzz:=mulmod(T1,zzz,p)//zzz3=W*zzz1 - - X:=addmod(mulmod(T4,T4,p), mulmod(minus_2, T3,p),p) //X3=M^2-2S - y:=mulmod(T4,addmod(T3, sub(p, X),p),p)//M(S-X3) - Y:= addmod(y, sub(p, mulmod(T1, Y ,p)),p )//Y3= M(S-X3)-W*Y1 - zz:=mulmod(T2, zz, p) //zz3=V*ZZ1 + zz:=mulmod(T2, zz, p) //zz3=V*ZZ1, V free + X:=addmod(mulmod(T4,T4,p), mulmod(minus_2, T3,p),p) //X3=M^2-2S + T2:=mulmod(T4,addmod(T3, sub(p, X),p),p)//M(S-X3) + //T2:=mulmod(T4,addmod(X, sub(p, T3),p),p)//-M(S-X3)=M(X3-S) + + Y:= addmod(T2, sub(p, mulmod(T1, Y ,p)),p )//Y3= M(S-X3)-W*Y1 + //Y:= addmod(mulmod(T1, Y ,p), y ,p ) //-Y3=W*Y1-M(S-X3)=W*Y1+M(X3-S) + //Y:= addmod(mulmod(T1, Y ,p), sub(p, T2),p )//-Y3= W*Y1-M(S-X3) + + //value of dibit T4:=add( shl(1, and(shr(index, scalar_v),1)), and(shr(index, scalar_u),1) ) if eq(T4,1) { - mstore(T, gx) - mstore(add(T,32) , gy) + T1:=gx + T2:=gy } if eq(T4,2) { - mstore(T, Q0) - mstore(add(T,32) , Q1) + T1:=Q0 + T2:=Q1 } if eq(T4,3) { - mstore(T, mload(add(T,96))) - mstore(add(T,32) , mload(add(T,128))) + T1:=H0 + T2:= H1 } if gt(T4,0){ // inlined EcZZ_AddN - y:=sub(p, Y) - let y2:=addmod(mulmod(mload(add(T,32)), zzz,p),y,p) - T2:=addmod(mulmod(mload(T), zz,p),sub(p,X),p) - T4:=mulmod(T2, T2, p) - T1:=mulmod(T4,T2,p) + T3:=sub(p, Y) + //T3:=Y + let y2:=addmod(mulmod(T2, zzz,p),T3,p) + T2:=addmod(mulmod(T1, zz,p),sub(p,X),p) + + T4:=mulmod(T2, T2, p)//PP + T1:=mulmod(T4,T2,p)//PPP T2:=mulmod(zz,T4,p) // W=UV zzz:= mulmod(zzz,T1,p) //zz3=V*ZZ1 let zz1:=mulmod(X, T4, p) T4:=addmod(addmod(mulmod(y2,y2, p), sub(p,T1),p ), mulmod(minus_2, zz1,p) ,p ) - Y:=addmod(mulmod(addmod(zz1, sub(p,T4),p), y2, p), mulmod(y, T1,p),p) + Y:=addmod(mulmod(addmod(zz1, sub(p,T4),p), y2, p), mulmod(T3, T1,p),p) zz:=T2 X:=T4 } @@ -466,7 +476,7 @@ library FCL_Elliptic_ZZ { } /** - * @dev Validate combination of message, signature, and public key. + * @dev ECDSA verification, given , signature, and public key. */ function ecdsa_verify( bytes32 message, @@ -482,7 +492,7 @@ library FCL_Elliptic_ZZ { return false; } - uint sInv = FCL_nModInv(rs[1], n); + uint sInv = FCL_nModInv(rs[1]); uint scalar_u=mulmod(uint(message), sInv, n); uint scalar_v= mulmod(rs[0], sInv, n); uint x1; @@ -497,7 +507,11 @@ library FCL_Elliptic_ZZ { } - /* validating signatures using a precomputed table of multiples of P and Q stored in contract at address Shamir8*/ + /** + * @dev ECDSA verification using a precomputed table of multiples of P and Q stored in contract at address Shamir8 + generation of contract bytecode for precomputations is done using sagemath code (see sage directory, WebAuthn_precompute.sage) + */ + function ecdsa_precomputed_verify( bytes32 message, uint[2] memory rs, @@ -511,7 +525,7 @@ library FCL_Elliptic_ZZ { return false; }*/ - uint sInv =FCL_nModInv(rs[1], n); + uint sInv =FCL_nModInv(rs[1]); uint X; //Shamir 8 dimensions From fccce0f2da3b0744049becbb160069d74865f8b1 Mon Sep 17 00:00:00 2001 From: rdubois-crypto Date: Fri, 17 Mar 2023 15:32:08 +0100 Subject: [PATCH 25/25] inverting y trick - Inverting y in dbl loop to spare sub - Using continue to jump the 0 case of mul loop --- contracts/FCL_elliptic.sol | 109 +++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 47 deletions(-) diff --git a/contracts/FCL_elliptic.sol b/contracts/FCL_elliptic.sol index 7c95400..a8979a9 100644 --- a/contracts/FCL_elliptic.sol +++ b/contracts/FCL_elliptic.sol @@ -142,7 +142,7 @@ library FCL_Elliptic_ZZ { P3:=mulmod(x, P2,p)// S = X1*V P1:=mulmod(P0, P2,p) // W=UV P2:=mulmod(P2, zz, p) //zz3=V*ZZ1 - zz:=mulmod(3, mulmod(addmod(x,sub(p,zz),p), addmod(x,zz,p),p) ,p) //M=3*(X1-ZZ1)*(X1+ZZ1), use zz to reduce RAM usage + zz:=mulmod(3, mulmod(addmod(x,sub(p,zz),p), addmod(x,zz,p),p) ,p) //M=3*(X1-ZZ1)*(X1+ZZ1) P0:=addmod(mulmod(zz,zz,p), mulmod(minus_2, P3,p),p) //X3=M^2-2S x:=mulmod(zz,addmod(P3, sub(p,P0),p),p)//M(S-X3) P3:=mulmod(P1,zzz,p)//zzz3=W*zzz1 @@ -272,58 +272,64 @@ library FCL_Elliptic_ZZ { uint H1; if(scalar_u==0 && scalar_v==0) return 0; - (H0,H1 )=ecAff_add(gx,gy,Q0, Q1); + (H0,H1 )=ecAff_add(gx,gy,Q0, Q1);//will not work if Q=P, obvious forbidden private key while( ( ((scalar_u>>index)&1)+2*((scalar_v>>index)&1) ) ==0){ index=index-1; } - - zz =((scalar_u>>index)&1)+2*((scalar_v>>index)&1); - if(zz==1){ - (X,Y) = (gx, gy); - } - if(zz==2){ - (X,Y) = (Q0, Q1); - } - if(zz==3){ - (X,Y) = (H0, H1); - } - - index=index-1; - + unchecked { + assembly{ + zz:=add( shl(1, and(shr(index, scalar_v),1)), and(shr(index, scalar_u),1) ) + + if eq(zz,1) { + X:=gx + Y:=gy + } + if eq(zz,2) { + X:=Q0 + Y:=Q1 + } + if eq(zz,3) { + X:=H0 + Y:= H1 + } - assembly{ + index:=sub(index,1) zz:=1 zzz:=1 - for { index := index } gt( minus_1, index) { index := sub(index, 1) } + for { index := index } gt( minus_1, index) { index := sub(index, 1) } { - - // inlined EcZZ_Dbl + // inlined EcZZ_Dbl let T1:=mulmod(2, Y, p) //U = 2*Y1, y free let T2:=mulmod(T1,T1,p) // V=U^2 let T3:=mulmod(X, T2,p)// S = X1*V T1:=mulmod(T1, T2,p) // W=UV - let T4:=mulmod(3, mulmod(addmod(X,sub(p,zz),p), addmod(X,zz,p),p) ,p) //M=3*(X1-ZZ1)*(X1+ZZ1), use zz to reduce RAM usage, x free + let T4:=mulmod(3, mulmod(addmod(X,sub(p,zz),p), addmod(X,zz,p),p) ,p) //M=3*(X1-ZZ1)*(X1+ZZ1) zzz:=mulmod(T1,zzz,p)//zzz3=W*zzz1 zz:=mulmod(T2, zz, p) //zz3=V*ZZ1, V free X:=addmod(mulmod(T4,T4,p), mulmod(minus_2, T3,p),p) //X3=M^2-2S - T2:=mulmod(T4,addmod(T3, sub(p, X),p),p)//M(S-X3) - //T2:=mulmod(T4,addmod(X, sub(p, T3),p),p)//-M(S-X3)=M(X3-S) + //T2:=mulmod(T4,addmod(T3, sub(p, X),p),p)//M(S-X3) + T2:=mulmod(T4,addmod(X, sub(p, T3),p),p)//-M(S-X3)=M(X3-S) - Y:= addmod(T2, sub(p, mulmod(T1, Y ,p)),p )//Y3= M(S-X3)-W*Y1 - //Y:= addmod(mulmod(T1, Y ,p), y ,p ) //-Y3=W*Y1-M(S-X3)=W*Y1+M(X3-S) - //Y:= addmod(mulmod(T1, Y ,p), sub(p, T2),p )//-Y3= W*Y1-M(S-X3) + //Y:= addmod(T2, sub(p, mulmod(T1, Y ,p)),p )//Y3= M(S-X3)-W*Y1 + Y:= addmod(mulmod(T1, Y ,p), T2,p )//-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd //value of dibit T4:=add( shl(1, and(shr(index, scalar_v),1)), and(shr(index, scalar_u),1) ) + if iszero(T4){ + Y:=sub(p,Y)//restore the -Y inversion + continue + }// if T4!=0 + { if eq(T4,1) { T1:=gx T2:=gy + } if eq(T4,2) { T1:=Q0 @@ -333,25 +339,26 @@ library FCL_Elliptic_ZZ { T1:=H0 T2:= H1 } - if gt(T4,0){ + // inlined EcZZ_AddN - T3:=sub(p, Y) + //T3:=sub(p, Y) //T3:=Y - let y2:=addmod(mulmod(T2, zzz,p),T3,p) + let y2:=addmod(mulmod(T2, zzz,p),Y,p) T2:=addmod(mulmod(T1, zz,p),sub(p,X),p) T4:=mulmod(T2, T2, p)//PP - T1:=mulmod(T4,T2,p)//PPP - T2:=mulmod(zz,T4,p) // W=UV - zzz:= mulmod(zzz,T1,p) //zz3=V*ZZ1 - let zz1:=mulmod(X, T4, p) - T4:=addmod(addmod(mulmod(y2,y2, p), sub(p,T1),p ), mulmod(minus_2, zz1,p) ,p ) - Y:=addmod(mulmod(addmod(zz1, sub(p,T4),p), y2, p), mulmod(T3, T1,p),p) - zz:=T2 + let TT1:=mulmod(T4,T2,p)//PPP, this one could be spared, but adding this register spare gas + zz:=mulmod(zz,T4,p) + zzz:= mulmod(zzz,TT1,p) //zz3=V*ZZ1 + T2:=mulmod(X, T4, p) + T4:=addmod(addmod(mulmod(y2,y2, p), sub(p,TT1),p ), mulmod(minus_2, T2,p) ,p ) + Y:=addmod(mulmod(addmod(T2, sub(p,T4),p), y2, p), mulmod(Y, TT1,p),p) + X:=T4 - } + } + }//end loop - mstore(add(T, 0x60),zzz) + mstore(add(T, 0x60),zz) //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz); //T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile: // Define length of base, exponent and modulus. 0x20 == 32 bytes @@ -369,9 +376,9 @@ library FCL_Elliptic_ZZ { } //Y:=mulmod(Y,zzz,p)//Y/zzz - zz :=mulmod(zz, mload(T),p) //1/z - zz:= mulmod(zz,zz,p) //1/zz - X:=mulmod(X,zz,p)//X/zz + //zz :=mulmod(zz, mload(T),p) //1/z + //zz:= mulmod(zz,zz,p) //1/zz + X:=mulmod(X,mload(T),p)//X/zz } //end assembly }//end unchecked @@ -383,7 +390,8 @@ library FCL_Elliptic_ZZ { //contract at given address dataPointer //(thx to Lakhdar https://github.com/Kelvyne for EVM storage explanations and tricks) // the external tool to generate tables from public key is in the /sage directory - function ecZZ_mulmuladd_S8_extcode(uint scalar_u, uint scalar_v, address dataPointer) internal returns(uint X/*, uint Y*/) + function ecZZ_mulmuladd_S8_extcode(uint scalar_u, uint scalar_v, address dataPointer) + internal returns(uint X/*, uint Y*/) { uint zz; // third and coordinates of the point @@ -396,7 +404,8 @@ library FCL_Elliptic_ZZ { { zz=zz-1; //tbd case of msb octobit is null - T[0]=64*(128*((scalar_v>>zz)&1)+64*((scalar_v>>(zz-64))&1)+32*((scalar_v>>(zz-128))&1)+16*((scalar_v>>(zz-192))&1)+ + T[0]=64*(128*((scalar_v>>zz)&1)+64*((scalar_v>>(zz-64))&1)+ + 32*((scalar_v>>(zz-128))&1)+16*((scalar_v>>(zz-192))&1)+ 8*((scalar_u>>zz)&1)+4*((scalar_u>>(zz-64))&1)+2*((scalar_u>>(zz-128))&1)+((scalar_u>>(zz-192))&1)); } assembly{ @@ -416,7 +425,7 @@ library FCL_Elliptic_ZZ { let T2:=mulmod(y,y,p) // V=U^2 let T3:=mulmod(X, T2,p)// S = X1*V let T1:=mulmod(y, T2,p) // W=UV - let T4:=mulmod(3, mulmod(addmod(X,sub(p,zz),p), addmod(X,zz,p),p) ,p) //M=3*(X1-ZZ1)*(X1+ZZ1), use zz to reduce RAM usage, x free + let T4:=mulmod(3, mulmod(addmod(X,sub(p,zz),p), addmod(X,zz,p),p) ,p) //M=3*(X1-ZZ1)*(X1+ZZ1) zzz:=mulmod(T1,zzz,p)//zzz3=W*zzz1 X:=addmod(mulmod(T4,T4,p), mulmod(minus_2, T3,p),p) //X3=M^2-2S @@ -433,9 +442,13 @@ library FCL_Elliptic_ZZ { ind:=sub(index, 192) T4:=add(T4,add( shl(10, and(shr(ind, scalar_v),1)), shl(6, and(shr(ind, scalar_u),1)) )) - mstore(T,T4) + //tbd: check validity of formulae with (0,1) to remove conditional jump + if iszero(T4){ + continue + } + { /* Access to precomputed table using extcodecopy hack */ - extcodecopy(dataPointer, T,mload(T), 64) + extcodecopy(dataPointer, T,T4, 64) // inlined EcZZ_AddN y:=sub(p, Y) @@ -450,6 +463,7 @@ library FCL_Elliptic_ZZ { Y:=addmod(mulmod(addmod(zz1, sub(p,T4),p), y2, p), mulmod(y, T1,p),p) zz:=T2 X:=T4 + } }//end loop mstore(add(T, 0x60),zz) @@ -509,7 +523,8 @@ library FCL_Elliptic_ZZ { /** * @dev ECDSA verification using a precomputed table of multiples of P and Q stored in contract at address Shamir8 - generation of contract bytecode for precomputations is done using sagemath code (see sage directory, WebAuthn_precompute.sage) + generation of contract bytecode for precomputations is done using sagemath code + (see sage directory, WebAuthn_precompute.sage) */ function ecdsa_precomputed_verify(