From 69b3ab5a577a8804108b28f8aa205564a7d58ad9 Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Thu, 26 Jan 2023 04:46:14 +0000 Subject: [PATCH] Shuffle code --- benchmark/index.js | 42 ++++---- src/abstract/weierstrass.ts | 12 ++- src/ed25519.ts | 115 +++++++++++----------- src/ed448.ts | 190 ++++++++++++++++++------------------ src/stark.ts | 2 +- 5 files changed, 181 insertions(+), 180 deletions(-) diff --git a/benchmark/index.js b/benchmark/index.js index c2fb8a2..5f8b5a3 100644 --- a/benchmark/index.js +++ b/benchmark/index.js @@ -82,7 +82,7 @@ export const CURVES = { secp256k1_old: ({ sig, msg, pub }) => { return old_secp.verify(new old_secp.Signature(sig.r, sig.s), msg, pub); }, - secp256k1: ({ sig, msg, pub }) => secp256k1.verify(sig.toCompactRawBytes(), msg, pub), + secp256k1: ({ sig, msg, pub }) => secp256k1.verify(sig, msg, pub), }, getSharedSecret: { samples: 1000, @@ -152,10 +152,10 @@ export const CURVES = { samples: 500, noble: ({ sig, msg, pub }) => ed448.verify(sig, msg, pub), }, - hashToCurve: { - samples: 500, - noble: () => ed448.Point.hashToCurve('abcd'), - }, + // hashToCurve: { + // samples: 500, + // noble: () => ed448.Point.hashToCurve('abcd'), + // }, }, nist: { data: () => { @@ -179,12 +179,12 @@ export const CURVES = { P384: ({ p384: { sig, msg, pub } }) => P384.verify(sig, msg, pub), P521: ({ p521: { sig, msg, pub } }) => P521.verify(sig, msg, pub), }, - hashToCurve: { - samples: 500, - P256: () => P256.Point.hashToCurve('abcd'), - P384: () => P384.Point.hashToCurve('abcd'), - P521: () => P521.Point.hashToCurve('abcd'), - }, + // hashToCurve: { + // samples: 500, + // P256: () => P256.Point.hashToCurve('abcd'), + // P384: () => P384.Point.hashToCurve('abcd'), + // P521: () => P521.Point.hashToCurve('abcd'), + // }, }, stark: { data: () => { @@ -330,16 +330,16 @@ export const CURVES = { old: () => old_bls.pairing(oldp1, oldp2), noble: () => bls.pairing(p1, p2), }, - 'hashToCurve/G1': { - samples: 500, - old: () => old_bls.PointG1.hashToCurve('abcd'), - noble: () => bls.hashToCurve.G1.hashToCurve('abcd'), - }, - 'hashToCurve/G2': { - samples: 200, - old: () => old_bls.PointG2.hashToCurve('abcd'), - noble: () => bls.hashToCurve.G2.hashToCurve('abcd'), - }, + // 'hashToCurve/G1': { + // samples: 500, + // old: () => old_bls.PointG1.hashToCurve('abcd'), + // noble: () => bls.hashToCurve.G1.hashToCurve('abcd'), + // }, + // 'hashToCurve/G2': { + // samples: 200, + // old: () => old_bls.PointG2.hashToCurve('abcd'), + // noble: () => bls.hashToCurve.G2.hashToCurve('abcd'), + // }, // SLOW PART // Requires points which we cannot init before (data fn same for all) // await mark('sign/nc', 30, () => bls.sign(msgp, priv)); diff --git a/src/abstract/weierstrass.ts b/src/abstract/weierstrass.ts index 2dd3e8b..10b4afa 100644 --- a/src/abstract/weierstrass.ts +++ b/src/abstract/weierstrass.ts @@ -604,6 +604,7 @@ export type SignatureConstructor = { fromCompact(hex: Hex): SignatureType; fromDER(hex: Hex): SignatureType; }; +type SignatureLike = { r: bigint; s: bigint }; export type PubKey = Hex | ProjPointType; @@ -634,7 +635,7 @@ export type CurveFn = { getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array; getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array; sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType; - verify: (signature: Hex | SignatureType, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean; + verify: (signature: Hex | SignatureLike, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean; ProjectivePoint: ProjConstructor; Signature: SignatureConstructor; utils: { @@ -955,6 +956,7 @@ export function weierstrass(curveDef: CurveType): CurveFn { // works with order, can have different size than numToField! return ut.numberToBytesBE(num, CURVE.nByteLength); } + // Steps A, D of RFC6979 3.2 // Creates RFC6979 seed; converts msg/privKey to numbers. // Used only in sign, not in verify. @@ -1048,7 +1050,7 @@ export function weierstrass(curveDef: CurveType): CurveFn { * ``` */ function verify( - signature: Hex | SignatureType, + signature: Hex | { r: bigint; s: bigint }, msgHash: Hex, publicKey: Hex, opts = defaultVerOpts @@ -1057,9 +1059,9 @@ export function weierstrass(curveDef: CurveType): CurveFn { let _sig: Signature | undefined = undefined; if (publicKey instanceof Point) throw new Error('publicKey must be hex'); try { - if (signature instanceof Signature) { - signature.assertValidity(); - _sig = signature; + if (signature && typeof signature === 'object' && !(signature instanceof Uint8Array)) { + const { r, s } = signature; + _sig = new Signature(r, s); // assertValidity() is executed on creation } else { // Signature can be represented in 2 ways: compact (2*nByteLength) & DER (variable-length). // Since DER can also be 2*nByteLength bytes, we check for it first. diff --git a/src/ed25519.ts b/src/ed25519.ts index a2f1812..57b20f0 100644 --- a/src/ed25519.ts +++ b/src/ed25519.ts @@ -94,6 +94,63 @@ export const ED25519_TORSION_SUBGROUP = [ const Fp = Field(ED25519_P, undefined, true); +const ED25519_DEF = { + // Param: a + a: BigInt(-1), + // Equal to -121665/121666 over finite field. + // Negative number is P - number, and division is invert(number, P) + d: BigInt('37095705934669439343138083508754565189542113879843219016388785533085940283555'), + // Finite field 𝔽p over which we'll do calculations; 2n ** 255n - 19n + Fp, + // Subgroup order: how many points ed25519 has + // 2n ** 252n + 27742317777372353535851937790883648493n; + n: BigInt('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + // Cofactor + h: BigInt(8), + // Base point (x, y) aka generator point + Gx: BigInt('15112221349535400772501151409588531511454012693041857206046113283949847762202'), + Gy: BigInt('46316835694926478169428394003475163141307993866256225615783033603165251855960'), + hash: sha512, + randomBytes, + adjustScalarBytes, + // dom2 + // Ratio of u to v. Allows us to combine inversion and square root. Uses algo from RFC8032 5.1.3. + // Constant-time, u/√v + uvRatio, +} as const; + +export const ed25519 = twistedEdwards(ED25519_DEF); +function ed25519_domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) { + if (ctx.length > 255) throw new Error('Context is too big'); + return concatBytes( + utf8ToBytes('SigEd25519 no Ed25519 collisions'), + new Uint8Array([phflag ? 1 : 0, ctx.length]), + ctx, + data + ); +} +export const ed25519ctx = twistedEdwards({ ...ED25519_DEF, domain: ed25519_domain }); +export const ed25519ph = twistedEdwards({ + ...ED25519_DEF, + domain: ed25519_domain, + preHash: sha512, +}); + +export const x25519 = montgomery({ + P: ED25519_P, + a24: BigInt('121665'), + montgomeryBits: 255, // n is 253 bits + nByteLength: 32, + Gu: '0900000000000000000000000000000000000000000000000000000000000000', + powPminus2: (x: bigint): bigint => { + const P = ED25519_P; + // x^(p-2) aka x^(2^255-21) + const { pow_p_5_8, b2 } = ed25519_pow_2_252_3(x); + return mod(pow2(pow_p_5_8, BigInt(3), P) * b2, P); + }, + adjustScalarBytes, +}); + // Hash To Curve Elligator2 Map (NOTE: different from ristretto255 elligator) // NOTE: very important part is usage of FpSqrtEven for ELL2_C1_EDWARDS, since // SageMath returns different root first and everything falls apart @@ -166,49 +223,6 @@ function map_to_curve_elligator2_edwards25519(u: bigint) { const inv = Fp.invertBatch([xd, yd]); // batch division return { x: Fp.mul(xn, inv[0]), y: Fp.mul(yn, inv[1]) }; // 13. return (xn, xd, yn, yd) } - -const ED25519_DEF = { - // Param: a - a: BigInt(-1), - // Equal to -121665/121666 over finite field. - // Negative number is P - number, and division is invert(number, P) - d: BigInt('37095705934669439343138083508754565189542113879843219016388785533085940283555'), - // Finite field 𝔽p over which we'll do calculations; 2n ** 255n - 19n - Fp, - // Subgroup order: how many points ed25519 has - // 2n ** 252n + 27742317777372353535851937790883648493n; - n: BigInt('7237005577332262213973186563042994240857116359379907606001950938285454250989'), - // Cofactor - h: BigInt(8), - // Base point (x, y) aka generator point - Gx: BigInt('15112221349535400772501151409588531511454012693041857206046113283949847762202'), - Gy: BigInt('46316835694926478169428394003475163141307993866256225615783033603165251855960'), - hash: sha512, - randomBytes, - adjustScalarBytes, - // dom2 - // Ratio of u to v. Allows us to combine inversion and square root. Uses algo from RFC8032 5.1.3. - // Constant-time, u/√v - uvRatio, -} as const; - -export const ed25519 = twistedEdwards(ED25519_DEF); -function ed25519_domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) { - if (ctx.length > 255) throw new Error('Context is too big'); - return concatBytes( - utf8ToBytes('SigEd25519 no Ed25519 collisions'), - new Uint8Array([phflag ? 1 : 0, ctx.length]), - ctx, - data - ); -} -export const ed25519ctx = twistedEdwards({ ...ED25519_DEF, domain: ed25519_domain }); -export const ed25519ph = twistedEdwards({ - ...ED25519_DEF, - domain: ed25519_domain, - preHash: sha512, -}); - const { hashToCurve, encodeToCurve } = htf.hashToCurve( ed25519.ExtendedPoint, (scalars: bigint[]) => map_to_curve_elligator2_edwards25519(scalars[0]), @@ -224,21 +238,6 @@ const { hashToCurve, encodeToCurve } = htf.hashToCurve( ); export { hashToCurve, encodeToCurve }; -export const x25519 = montgomery({ - P: ED25519_P, - a24: BigInt('121665'), - montgomeryBits: 255, // n is 253 bits - nByteLength: 32, - Gu: '0900000000000000000000000000000000000000000000000000000000000000', - powPminus2: (x: bigint): bigint => { - const P = ED25519_P; - // x^(p-2) aka x^(2^255-21) - const { pow_p_5_8, b2 } = ed25519_pow_2_252_3(x); - return mod(pow2(pow_p_5_8, BigInt(3), P) * b2, P); - }, - adjustScalarBytes, -}); - function assertRstPoint(other: unknown) { if (!(other instanceof RistrettoPoint)) throw new Error('RistrettoPoint expected'); } diff --git a/src/ed448.ts b/src/ed448.ts index bfe2301..66399cd 100644 --- a/src/ed448.ts +++ b/src/ed448.ts @@ -55,6 +55,101 @@ function adjustScalarBytes(bytes: Uint8Array): Uint8Array { const Fp = Field(ed448P, 456, true); +const ED448_DEF = { + // Param: a + a: BigInt(1), + // -39081. Negative number is P - number + d: BigInt( + '726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018326358' + ), + // Finite field 𝔽p over which we'll do calculations; 2n ** 448n - 2n ** 224n - 1n + Fp, + // Subgroup order: how many points curve has; + // 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n + n: BigInt( + '181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779' + ), + nBitLength: 456, + // Cofactor + h: BigInt(4), + // Base point (x, y) aka generator point + Gx: BigInt( + '224580040295924300187604334099896036246789641632564134246125461686950415467406032909029192869357953282578032075146446173674602635247710' + ), + Gy: BigInt( + '298819210078481492676017930443930673437544040154080242095928241372331506189835876003536878655418784733982303233503462500531545062832660' + ), + // SHAKE256(dom4(phflag,context)||x, 114) + hash: shake256_114, + randomBytes, + adjustScalarBytes, + // dom4 + domain: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => { + if (ctx.length > 255) throw new Error(`Context is too big: ${ctx.length}`); + return concatBytes( + utf8ToBytes('SigEd448'), + new Uint8Array([phflag ? 1 : 0, ctx.length]), + ctx, + data + ); + }, + + // Constant-time ratio of u to v. Allows to combine inversion and square root u/√v. + // Uses algo from RFC8032 5.1.3. + uvRatio: (u: bigint, v: bigint): { isValid: boolean; value: bigint } => { + const P = ed448P; + // https://datatracker.ietf.org/doc/html/rfc8032#section-5.2.3 + // To compute the square root of (u/v), the first step is to compute the + // candidate root x = (u/v)^((p+1)/4). This can be done using the + // following trick, to use a single modular powering for both the + // inversion of v and the square root: + // x = (u/v)^((p+1)/4) = u³v(u⁵v³)^((p-3)/4) (mod p) + const u2v = mod(u * u * v, P); // u²v + const u3v = mod(u2v * u, P); // u³v + const u5v3 = mod(u3v * u2v * v, P); // u⁵v³ + const root = ed448_pow_Pminus3div4(u5v3); + const x = mod(u3v * root, P); + // Verify that root is exists + const x2 = mod(x * x, P); // x² + // If vx² = u, the recovered x-coordinate is x. Otherwise, no + // square root exists, and the decoding fails. + return { isValid: mod(x2 * v, P) === u, value: x }; + }, +} as const; + +export const ed448 = twistedEdwards(ED448_DEF); +// NOTE: there is no ed448ctx, since ed448 supports ctx by default +export const ed448ph = twistedEdwards({ ...ED448_DEF, preHash: shake256_64 }); + +export const x448 = montgomery({ + a24: BigInt(39081), + montgomeryBits: 448, + nByteLength: 57, + P: ed448P, + Gu: '0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + powPminus2: (x: bigint): bigint => { + const P = ed448P; + const Pminus3div4 = ed448_pow_Pminus3div4(x); + const Pminus3 = pow2(Pminus3div4, BigInt(2), P); + return mod(Pminus3 * x, P); // Pminus3 * x = Pminus2 + }, + adjustScalarBytes, + // The 4-isogeny maps between the Montgomery curve and this Edwards + // curve are: + // (u, v) = (y^2/x^2, (2 - x^2 - y^2)*y/x^3) + // (x, y) = (4*v*(u^2 - 1)/(u^4 - 2*u^2 + 4*v^2 + 1), + // -(u^5 - 2*u^3 - 4*u*v^2 + u)/ + // (u^5 - 2*u^2*v^2 - 2*u^3 - 2*v^2 + u)) + // xyToU: (p: PointType) => { + // const P = ed448P; + // const { x, y } = p; + // if (x === _0n) throw new Error(`Point with x=0 doesn't have mapping`); + // const invX = invert(x * x, P); // x^2 + // const u = mod(y * y * invX, P); // (y^2/x^2) + // return numberToBytesLE(u, 56); + // }, +}); + // Hash To Curve Elligator2 Map const ELL2_C1 = (Fp.ORDER - BigInt(3)) / BigInt(4); // 1. c1 = (q - 3) / 4 # Integer arithmetic const ELL2_J = BigInt(156326); @@ -130,72 +225,6 @@ function map_to_curve_elligator2_edwards448(u: bigint) { return { x: Fp.mul(xEn, inv[0]), y: Fp.mul(yEn, inv[1]) }; // 38. return (xEn, xEd, yEn, yEd) } -const ED448_DEF = { - // Param: a - a: BigInt(1), - // -39081. Negative number is P - number - d: BigInt( - '726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018326358' - ), - // Finite field 𝔽p over which we'll do calculations; 2n ** 448n - 2n ** 224n - 1n - Fp, - // Subgroup order: how many points curve has; - // 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n - n: BigInt( - '181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779' - ), - nBitLength: 456, - // Cofactor - h: BigInt(4), - // Base point (x, y) aka generator point - Gx: BigInt( - '224580040295924300187604334099896036246789641632564134246125461686950415467406032909029192869357953282578032075146446173674602635247710' - ), - Gy: BigInt( - '298819210078481492676017930443930673437544040154080242095928241372331506189835876003536878655418784733982303233503462500531545062832660' - ), - // SHAKE256(dom4(phflag,context)||x, 114) - hash: shake256_114, - randomBytes, - adjustScalarBytes, - // dom4 - domain: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => { - if (ctx.length > 255) throw new Error(`Context is too big: ${ctx.length}`); - return concatBytes( - utf8ToBytes('SigEd448'), - new Uint8Array([phflag ? 1 : 0, ctx.length]), - ctx, - data - ); - }, - - // Constant-time ratio of u to v. Allows to combine inversion and square root u/√v. - // Uses algo from RFC8032 5.1.3. - uvRatio: (u: bigint, v: bigint): { isValid: boolean; value: bigint } => { - const P = ed448P; - // https://datatracker.ietf.org/doc/html/rfc8032#section-5.2.3 - // To compute the square root of (u/v), the first step is to compute the - // candidate root x = (u/v)^((p+1)/4). This can be done using the - // following trick, to use a single modular powering for both the - // inversion of v and the square root: - // x = (u/v)^((p+1)/4) = u³v(u⁵v³)^((p-3)/4) (mod p) - const u2v = mod(u * u * v, P); // u²v - const u3v = mod(u2v * u, P); // u³v - const u5v3 = mod(u3v * u2v * v, P); // u⁵v³ - const root = ed448_pow_Pminus3div4(u5v3); - const x = mod(u3v * root, P); - // Verify that root is exists - const x2 = mod(x * x, P); // x² - // If vx² = u, the recovered x-coordinate is x. Otherwise, no - // square root exists, and the decoding fails. - return { isValid: mod(x2 * v, P) === u, value: x }; - }, -} as const; - -export const ed448 = twistedEdwards(ED448_DEF); -// NOTE: there is no ed448ctx, since ed448 supports ctx by default -export const ed448ph = twistedEdwards({ ...ED448_DEF, preHash: shake256_64 }); - const { hashToCurve, encodeToCurve } = htf.hashToCurve( ed448.ExtendedPoint, (scalars: bigint[]) => map_to_curve_elligator2_edwards448(scalars[0]), @@ -210,32 +239,3 @@ const { hashToCurve, encodeToCurve } = htf.hashToCurve( } ); export { hashToCurve, encodeToCurve }; - -export const x448 = montgomery({ - a24: BigInt(39081), - montgomeryBits: 448, - nByteLength: 57, - P: ed448P, - Gu: '0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', - powPminus2: (x: bigint): bigint => { - const P = ed448P; - const Pminus3div4 = ed448_pow_Pminus3div4(x); - const Pminus3 = pow2(Pminus3div4, BigInt(2), P); - return mod(Pminus3 * x, P); // Pminus3 * x = Pminus2 - }, - adjustScalarBytes, - // The 4-isogeny maps between the Montgomery curve and this Edwards - // curve are: - // (u, v) = (y^2/x^2, (2 - x^2 - y^2)*y/x^3) - // (x, y) = (4*v*(u^2 - 1)/(u^4 - 2*u^2 + 4*v^2 + 1), - // -(u^5 - 2*u^3 - 4*u*v^2 + u)/ - // (u^5 - 2*u^2*v^2 - 2*u^3 - 2*v^2 + u)) - // xyToU: (p: PointType) => { - // const P = ed448P; - // const { x, y } = p; - // if (x === _0n) throw new Error(`Point with x=0 doesn't have mapping`); - // const invX = invert(x * x, P); // x^2 - // const u = mod(y * y * invX, P); // (y^2/x^2) - // return numberToBytesLE(u, 56); - // }, -}); diff --git a/src/stark.ts b/src/stark.ts index e747b4c..cd15290 100644 --- a/src/stark.ts +++ b/src/stark.ts @@ -95,7 +95,7 @@ function ensureBytes0x(hex: Hex): Uint8Array { } function normalizePrivateKey(privKey: Hex) { - return cutils.bytesToHex(ensureBytes0x(privKey)).padStart(32 * 2, '0'); + return cutils.bytesToHex(ensureBytes0x(privKey)).padStart(64, '0'); } function getPublicKey0x(privKey: Hex, isCompressed?: boolean) { return starkCurve.getPublicKey(normalizePrivateKey(privKey), isCompressed);