From 9773861b4effcee9e4b94e3966aef046966c799f Mon Sep 17 00:00:00 2001 From: querolita Date: Tue, 26 Nov 2024 19:12:49 +0100 Subject: [PATCH 1/6] add parameters of ed25519 twisted edwards curve --- .DS_Store | Bin 0 -> 8196 bytes crypto/elliptic-curve-examples.ts | 25 +++++++++++++++++++++-- crypto/elliptic-curve.ts | 32 ++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..ee2b7aaac54ba742836882ef5f4931887a347615 GIT binary patch literal 8196 zcmeHMJ#Q015S{fIY(arbmsfy5NJKh??jc zTjwz>XxgrOrPbWbMW8}FrF*M8aW)uLJY_-@5Cud5Q9u+B1^yQW@SCk$bII?%waO?8 zhytfl0ewD%=o%x3xkYnyU@#>Bu#RcdaNW`uz|djjFt-Q`nzErl8>(E1p=>zjL+2Mc z%q`k*Qm*(=F0*nKiZa>p{Lr|QiY&?~3Wx$#1$6CRrxn_xg!Xy;zV_k5A-4^(PN$n@ zUCcGt^Y+TO>Bqaiy?3I0)Gyu*tbMwVP-@KbQlF;O<=J69u6l3|M&2{HLd$$O532vrJZkiqCSHEJ^KLfJ^S=3-&U7#_ zJYU)7<)K}jCxH4%X+NlQ2Ze>_y171Iql@F&s#Y(bx~}uJ@R=txq=$433Xg$9nqZ7^ zmtaQ>y)~;ZFaIQlb9{em&hfXoIQzee^VU_1K#|UXdyI7qDD_wHLhl-8|NL}2 zF?+`ID0c5+4?n;AeoxHKP0Y?M?VPXy4h`t6_qOxkJT7k@UU6qk#jD`5+{@zw0@6eQ zQNU9muy<`;|8L_Ml=lFhJaQunoC*b0y|dHV!ctvrU0JlQwL|n>bltRHZqbBbFy%PV gl;gnpABMON;i_XIhq*<}pvj8>BZCa0z&};s7b8h12mk;8 literal 0 HcmV?d00001 diff --git a/crypto/elliptic-curve-examples.ts b/crypto/elliptic-curve-examples.ts index 105e7947..1839482c 100644 --- a/crypto/elliptic-curve-examples.ts +++ b/crypto/elliptic-curve-examples.ts @@ -1,7 +1,12 @@ -import { CurveParams, Pallas, Vesta } from './elliptic-curve.js'; +import { + CurveParams, + Pallas, + Vesta, + TwistedCurveParams, +} from './elliptic-curve.js'; import { exampleFields } from './finite-field-examples.js'; -export { CurveParams }; +export { CurveParams, TwistedCurveParams }; const secp256k1Params: CurveParams = { name: 'secp256k1', @@ -55,3 +60,19 @@ const CurveParams = { Pallas: pallasParams, Vesta: vestaParams, }; + +const ed25519Params: TwistedCurveParams = { + name: 'Ed25519', + modulus: exampleFields.f25519.modulus, // 2^255 - 19 + order: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn, //2^252 + 27742317777372353535851937790883648493, + generator: { + x: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an, + y: 0x6666666666666666666666666666666666666666666666666666666666666658n, // 4/5 mod p + }, + a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn, // -1 mod p + d: 0x2dfc9311d490018c7338bf8688861767ff8ff5b2bebe27548a14b235eca6874an, // 121665/121666 mod p +}; + +const TwistedCurveParams = { + Ed25519: ed25519Params, +}; diff --git a/crypto/elliptic-curve.ts b/crypto/elliptic-curve.ts index 4e823e9d..3a24734f 100644 --- a/crypto/elliptic-curve.ts +++ b/crypto/elliptic-curve.ts @@ -30,6 +30,7 @@ export { projectiveAdd, getProjectiveDouble, projectiveNeg, + TwistedCurveParams, }; // TODO: constants, like generator points and cube roots for endomorphisms, should be drawn from @@ -703,3 +704,34 @@ function createCurveAffine({ }, }; } + +/** + * Parameters defining an elliptic curve in twisted Edwards form + * ax^2 + y^2 = 1 + dx^2y^2 + */ +type TwistedCurveParams = { + /** + * Human-friendly name for the curve + */ + name: string; + /** + * Base field modulus + */ + modulus: bigint; + /** + * Scalar field modulus = group order + */ + order: bigint; + /** + * Generator point + */ + generator: { x: bigint; y: bigint }; + /** + * The `a` parameter in the curve equation ax^2 + y^2 = 1 + dx^2y^2 + */ + a: bigint; + /** + * The `d` parameter in the curve equation ax^2 + y^2 = 1 + dx^2y^2 + */ + d: bigint; +}; From 9b2da94648610eaf7f9918e95e134fe7d50b8463 Mon Sep 17 00:00:00 2001 From: querolita Date: Wed, 27 Nov 2024 15:01:31 +0100 Subject: [PATCH 2/6] fix sign in parameters and include source --- crypto/elliptic-curve-examples.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crypto/elliptic-curve-examples.ts b/crypto/elliptic-curve-examples.ts index 1839482c..2943fe1f 100644 --- a/crypto/elliptic-curve-examples.ts +++ b/crypto/elliptic-curve-examples.ts @@ -61,16 +61,17 @@ const CurveParams = { Vesta: vestaParams, }; +// https://datatracker.ietf.org/doc/html/rfc8032#section-5.1 const ed25519Params: TwistedCurveParams = { name: 'Ed25519', modulus: exampleFields.f25519.modulus, // 2^255 - 19 order: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn, //2^252 + 27742317777372353535851937790883648493, generator: { - x: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an, - y: 0x6666666666666666666666666666666666666666666666666666666666666658n, // 4/5 mod p + x: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an, // <=> 15112221349535400772501151409588531511454012693041857206046113283949847762202 + y: 0x6666666666666666666666666666666666666666666666666666666666666658n, // <=> 4/5 mod p <=> 46316835694926478169428394003475163141307993866256225615783033603165251855960 }, a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn, // -1 mod p - d: 0x2dfc9311d490018c7338bf8688861767ff8ff5b2bebe27548a14b235eca6874an, // 121665/121666 mod p + d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n, // -121665/121666 mod p <=> 37095705934669439343138083508754565189542113879843219016388785533085940283555 }; const TwistedCurveParams = { From 4e0555a379f3bbdcb137ac472ece920ea0403ebd Mon Sep 17 00:00:00 2001 From: querolita Date: Tue, 10 Dec 2024 20:42:13 +0100 Subject: [PATCH 3/6] twisted curve operations --- crypto/elliptic-curve.ts | 315 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 315 insertions(+) diff --git a/crypto/elliptic-curve.ts b/crypto/elliptic-curve.ts index 3a24734f..8e23191a 100644 --- a/crypto/elliptic-curve.ts +++ b/crypto/elliptic-curve.ts @@ -9,17 +9,21 @@ import { q, } from './finite-field.js'; import { Endomorphism } from './elliptic-curve-endomorphism.js'; +import assert from 'assert'; export { Pallas, PallasAffine, Vesta, CurveParams, GroupAffine, + GroupTwisted, GroupProjective, GroupMapPallas, createCurveProjective, createCurveAffine, + createCurveTwisted, CurveAffine, + CurveTwisted, ProjectiveCurve, affineAdd, affineDouble, @@ -31,6 +35,10 @@ export { getProjectiveDouble, projectiveNeg, TwistedCurveParams, + twistedAdd, + twistedDouble, + twistedScale, + projectiveZeroTwisted, }; // TODO: constants, like generator points and cube roots for endomorphisms, should be drawn from @@ -57,11 +65,15 @@ const b = 5n; const a = 0n; const projectiveZero = { x: 1n, y: 1n, z: 0n }; +const projectiveZeroTwisted = { x: 0n, y: 1n, z: 1n }; type GroupProjective = { x: bigint; y: bigint; z: bigint }; type PointAtInfinity = { x: bigint; y: bigint; infinity: true }; type FinitePoint = { x: bigint; y: bigint; infinity: false }; type GroupAffine = PointAtInfinity | FinitePoint; +type GroupTwisted = PointAtInfinity | FinitePoint; + +const twistedZero: PointAtInfinity = { x: 0n, y: 1n, infinity: true }; /** * Parameters defining an elliptic curve in short Weierstraß form @@ -735,3 +747,306 @@ type TwistedCurveParams = { */ d: bigint; }; + +function twistedOnCurve( + { x, y, infinity }: GroupTwisted, + p: bigint, + a: bigint, + d: bigint +) { + if (infinity) return true; + // a * x^2 + y^2 = 1 + d * x^2 * y^2 + let x2 = mod(x * x, p); + let y2 = mod(y * y, p); + return mod(a * x2 + y2 - 1n - d * x2 * y2, p) === 0n; +} + +function twistedAdd( + g: GroupTwisted, + h: GroupTwisted, + p: bigint, + a: bigint, + d: bigint +): GroupTwisted { + if (g.infinity) return h; + if (h.infinity) return g; + + let { x: x1, y: y1 } = g; + let { x: x2, y: y2 } = h; + + if (x1 === x2) { + // g + g --> we double + if (y1 === y2) return twistedDouble(g, p, d); + // g - g --> return zero + return affineZero; + } + + // x3 = (x1 * y2 + y1 * x2) / (1 + d * x1 * x2 * y1 * y2) + // y3 = (y1 * y2 - a * x1 * x2) / (1 - d * x1 * x2 * y1 * y2) + let x1x2 = mod(x1 * x2, p); + let y1y2 = mod(y1 * y2, p); + let x1y2 = mod(x1 * y2, p); + let y1x2 = mod(y1 * x2, p); + let ax1x2 = mod(a * x1x2, p); + + let x3Num = mod(x1y2 + y1x2, p); + let y3Num = mod(y1y2 + ax1x2, p); + + let dx1x2y1y2 = mod(d * x1x2 * y1y2, p); + + let x3Denom = inverse(mod(1n + dx1x2y1y2, p), p); + if (x3Denom === undefined) throw Error('impossible'); + + let y3Denom = inverse(mod(1n - dx1x2y1y2, p), p); + if (y3Denom === undefined) throw Error('impossible'); + + let x3 = mod(x3Num * x3Denom, p); + let y3 = mod(y3Num * y3Denom, p); + + return { x: x3, y: y3, infinity: false }; +} + +function twistedDouble(g: GroupTwisted, p: bigint, d: bigint): GroupTwisted { + let { x: x1, y: y1 } = g; + + // x3 = 2*x1*y1 / (1 + d * x1^2 * y1^2) + // y3 = (y1^2 - x1^2) / (1 - d * x1^2 * y1^2) + let x1x1 = x1 * x1; + let y1y1 = y1 * y1; + let x1y1 = x1 * y1; + + let x3Num = mod(2n * x1y1, p); + let y3Num = mod(y1y1 - x1x1, p); + + let dx1x1y1y1 = mod(d * x1x1 * y1y1, p); + + let x3Den = inverse(1n + dx1x1y1y1, p); + if (x3Den === undefined) throw Error('impossible'); + let y3Den = inverse(1n - dx1x1y1y1, p); + if (y3Den === undefined) throw Error('impossible'); + + let x3 = mod(x3Num * x3Den, p); + let y3 = mod(y3Num * y3Den, p); + + return { x: x3, y: y3, infinity: false }; +} + +function twistedNegate( + { x, y, infinity }: GroupTwisted, + p: bigint +): GroupTwisted { + if (infinity) return twistedZero; + return { x: x === 0n ? 0n : p - x, y, infinity }; +} + +function twistedScale( + g: GroupAffine, + s: bigint | boolean[], + p: bigint, + a: bigint, + d: bigint +) { + let gProj = projectiveFromTwisted(g); + let sgProj = projectiveScaleTwisted(gProj, s, p, a, d); + return projectiveToTwisted(sgProj, p); +} + +// https://www.hyperelliptic.org/EFD/g1p/auto-twisted-projective.html +// https://eprint.iacr.org/2008/013.pdf Section 6 +function projectiveAddTwisted( + g: GroupProjective, + h: GroupProjective, + p: bigint, + a: bigint, + d: bigint +): GroupProjective { + let { x: X1, y: Y1, z: Z1 } = g; + let { x: X2, y: Y2, z: Z2 } = h; + + // A = Z1 * Z2 + let A = mod(Z1 * Z2, p); + // B = A^2 + let B = mod(A * A, p); + // C = X1 * X2 + let C = mod(X1 * X2, p); + // D = Y1 * Y2 + let D = mod(Y1 * Y2, p); + // E = d * C * D + let E = mod(d * C * D, p); + // F = B - E + let F = mod(B - E, p); + // G = B + E + let G = mod(B + E, p); + + return { + x: mod(A * F * ((X1 + Y1) * (X2 + Y2) - C - D), p), + y: mod(A * G * (D - a * C), p), + z: mod(F * G, p), + }; +} + +// https://www.hyperelliptic.org/EFD/g1p/auto-twisted-projective.html +// https://eprint.iacr.org/2008/013.pdf Section 6 +function projectiveDoubleTwisted( + g: GroupProjective, + p: bigint, + a: bigint +): GroupProjective { + let { x: X, y: Y, z: Z } = g; + + // B = (X + Y)^2 + let B = mod((X + Y) ** 2n, p); + // C = X^2 + let C = mod(X * X, p); + // D = Y^2 + let D = mod(Y * Y, p); + // E = a * C + let E = mod(a * C, p); + // F = E + D + let F = mod(E + D, p); + // H = Z^2 + let H = mod(Z * Z, p); + // J = F - 2 * H + let J = mod(F - 2n * H, p); + + return { + x: mod((B - C - D) * J, p), + y: mod(F * (E - D), p), + z: mod(F * J, p), + }; +} + +function projectiveScaleTwisted( + g: GroupProjective, + x: bigint | boolean[], + p: bigint, + a: bigint, + d: bigint +) { + let bits = typeof x === 'bigint' ? bigIntToBits(x) : x; + let h = projectiveZeroTwisted; + for (let bit of bits) { + if (bit) h = projectiveAddTwisted(h, g, p, a, d); + g = projectiveDoubleTwisted(g, p, a); + } + return h; +} + +function projectiveFromTwisted({ + x, + y, + infinity, +}: GroupTwisted): GroupProjective { + if (infinity) return projectiveZeroTwisted; + return { x, y, z: 1n }; +} + +// The twisted curve with equation +// a * x^2 + y^2 = 1 + d * x^2 * y^2 +// in projective coordinates is represented as +// a * X^2 * Z^2 + Y^2 Z^2 = Z^4 + d * X^2 * Y^2 +// where x = X/Z, y = Y/Z, and Z ≠ 0 +function projectiveToTwisted(g: GroupProjective, p: bigint): GroupTwisted { + let z = g.z; + if (z === 0n) { + // infinity + return twistedZero; + } else if (z === 1n) { + // already normalized affine form + return { x: g.x, y: g.y, infinity: false }; + } else { + let zinv = inverse(z, p)!; // we checked for z === 0, so inverse exists + // x/z + let x = mod(g.x * zinv, p); + // y/z + let y = mod(g.y * zinv, p); + return { x: x, y: y, infinity: false }; + } +} + +type CurveTwisted = ReturnType; + +// Creates twisted Edwards curves in the form +// a * x^2 + y^2 = 1 + d * x^2 * y^2 +// with a ≠ 0, d ≠ 0 and a ≠ d +function createCurveTwisted({ + name, + modulus: p, + order, + generator, + a, + d, +}: TwistedCurveParams) { + const Field = createField(p); + const Scalar = createField(order); + const one = { ...generator, infinity: false }; + + assert(a !== 0n, 'a must not be zero'); + assert(d !== 0n, 'd must not be zero'); + assert(a !== d, 'a must not be equal to d'); + + return { + name, + /** + * Arithmetic over the base field + */ + Field, + /** + * Arithmetic over the scalar field + */ + Scalar, + + modulus: p, + order, + a, + b, + + zero: twistedZero, + one, + + from(g: { x: bigint; y: bigint }): GroupTwisted { + if (g.x === 0n && g.y === 1n) return twistedZero; + return { ...g, infinity: false }; + }, + + fromNonzero(g: { x: bigint; y: bigint }): GroupTwisted { + if (g.x === 0n && g.y === 1n) { + throw Error( + 'fromNonzero: got (0, 1), which is reserved for the zero point' + ); + } + return { ...g, infinity: false }; + }, + + equal(g: GroupTwisted, h: GroupTwisted) { + if (g.infinity && h.infinity) { + return true; + } else if (g.infinity || h.infinity) { + return false; + } else { + return mod(g.x - h.x, p) === 0n && mod(g.y - h.y, p) === 0n; + } + }, + isOnCurve(g: GroupTwisted) { + return twistedOnCurve(g, p, a, b); + }, + isInSubgroup(g: GroupAffine) { + return projectiveInSubgroup(projectiveFromAffine(g), p, order, a); + }, + add(g: GroupTwisted, h: GroupTwisted) { + return twistedAdd(g, h, p, a, d); + }, + double(g: GroupTwisted) { + return twistedDouble(g, p, a); + }, + negate(g: GroupTwisted) { + return twistedNegate(g, p); + }, + sub(g: GroupTwisted, h: GroupTwisted) { + return twistedAdd(g, twistedNegate(h, p), p, a, d); + }, + scale(g: GroupTwisted, s: bigint | boolean[]) { + return twistedScale(g, s, p, a); + }, + }; +} From 0cc506ed99543f88df993467a445b461e48682b6 Mon Sep 17 00:00:00 2001 From: querolita Date: Tue, 10 Dec 2024 21:14:33 +0100 Subject: [PATCH 4/6] add missing parameter in scale function --- crypto/elliptic-curve.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/elliptic-curve.ts b/crypto/elliptic-curve.ts index 8e23191a..77b1cf6c 100644 --- a/crypto/elliptic-curve.ts +++ b/crypto/elliptic-curve.ts @@ -1046,7 +1046,7 @@ function createCurveTwisted({ return twistedAdd(g, twistedNegate(h, p), p, a, d); }, scale(g: GroupTwisted, s: bigint | boolean[]) { - return twistedScale(g, s, p, a); + return twistedScale(g, s, p, a, d); }, }; } From df65376cbd3d4a075b4bc4fbf1d814e463a9c047 Mon Sep 17 00:00:00 2001 From: querolita Date: Tue, 10 Dec 2024 21:25:40 +0100 Subject: [PATCH 5/6] complete the function to create twisted curves with cofactor and endo --- crypto/elliptic-curve.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/crypto/elliptic-curve.ts b/crypto/elliptic-curve.ts index 77b1cf6c..357c82fa 100644 --- a/crypto/elliptic-curve.ts +++ b/crypto/elliptic-curve.ts @@ -734,6 +734,12 @@ type TwistedCurveParams = { * Scalar field modulus = group order */ order: bigint; + /** + * Cofactor = size of EC / order + * + * This can be left undefined if the cofactor is 1. + */ + cofactor?: bigint; /** * Generator point */ @@ -746,6 +752,8 @@ type TwistedCurveParams = { * The `d` parameter in the curve equation ax^2 + y^2 = 1 + dx^2y^2 */ d: bigint; + endoBase?: bigint; + endoScalar?: bigint; }; function twistedOnCurve( @@ -973,13 +981,17 @@ function createCurveTwisted({ name, modulus: p, order, + cofactor, generator, a, d, }: TwistedCurveParams) { + let hasCofactor = cofactor !== undefined && cofactor !== 1n; + const Field = createField(p); const Scalar = createField(order); const one = { ...generator, infinity: false }; + const Endo = undefined; // for Ed25519 assert(a !== 0n, 'a must not be zero'); assert(d !== 0n, 'd must not be zero'); @@ -999,11 +1011,19 @@ function createCurveTwisted({ modulus: p, order, a, - b, + d, + cofactor, + hasCofactor, zero: twistedZero, one, + hasEndomorphism: Endo !== undefined, + get Endo() { + if (Endo === undefined) throw Error(`no endomorphism defined on ${name}`); + return Endo; + }, + from(g: { x: bigint; y: bigint }): GroupTwisted { if (g.x === 0n && g.y === 1n) return twistedZero; return { ...g, infinity: false }; From 66d49caf3f89f4e559aad7d75514e57f8b6b63bf Mon Sep 17 00:00:00 2001 From: querolita Date: Wed, 11 Dec 2024 13:55:53 +0100 Subject: [PATCH 6/6] fix typo in sign of twisted add --- crypto/elliptic-curve.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/elliptic-curve.ts b/crypto/elliptic-curve.ts index 357c82fa..376859aa 100644 --- a/crypto/elliptic-curve.ts +++ b/crypto/elliptic-curve.ts @@ -798,7 +798,7 @@ function twistedAdd( let ax1x2 = mod(a * x1x2, p); let x3Num = mod(x1y2 + y1x2, p); - let y3Num = mod(y1y2 + ax1x2, p); + let y3Num = mod(y1y2 - ax1x2, p); let dx1x2y1y2 = mod(d * x1x2 * y1y2, p);