forked from tornado-packages/noble-curves
Shuffle code
This commit is contained in:
parent
9465e60d30
commit
69b3ab5a57
@ -82,7 +82,7 @@ export const CURVES = {
|
|||||||
secp256k1_old: ({ sig, msg, pub }) => {
|
secp256k1_old: ({ sig, msg, pub }) => {
|
||||||
return old_secp.verify(new old_secp.Signature(sig.r, sig.s), 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: {
|
getSharedSecret: {
|
||||||
samples: 1000,
|
samples: 1000,
|
||||||
@ -152,10 +152,10 @@ export const CURVES = {
|
|||||||
samples: 500,
|
samples: 500,
|
||||||
noble: ({ sig, msg, pub }) => ed448.verify(sig, msg, pub),
|
noble: ({ sig, msg, pub }) => ed448.verify(sig, msg, pub),
|
||||||
},
|
},
|
||||||
hashToCurve: {
|
// hashToCurve: {
|
||||||
samples: 500,
|
// samples: 500,
|
||||||
noble: () => ed448.Point.hashToCurve('abcd'),
|
// noble: () => ed448.Point.hashToCurve('abcd'),
|
||||||
},
|
// },
|
||||||
},
|
},
|
||||||
nist: {
|
nist: {
|
||||||
data: () => {
|
data: () => {
|
||||||
@ -179,12 +179,12 @@ export const CURVES = {
|
|||||||
P384: ({ p384: { sig, msg, pub } }) => P384.verify(sig, msg, pub),
|
P384: ({ p384: { sig, msg, pub } }) => P384.verify(sig, msg, pub),
|
||||||
P521: ({ p521: { sig, msg, pub } }) => P521.verify(sig, msg, pub),
|
P521: ({ p521: { sig, msg, pub } }) => P521.verify(sig, msg, pub),
|
||||||
},
|
},
|
||||||
hashToCurve: {
|
// hashToCurve: {
|
||||||
samples: 500,
|
// samples: 500,
|
||||||
P256: () => P256.Point.hashToCurve('abcd'),
|
// P256: () => P256.Point.hashToCurve('abcd'),
|
||||||
P384: () => P384.Point.hashToCurve('abcd'),
|
// P384: () => P384.Point.hashToCurve('abcd'),
|
||||||
P521: () => P521.Point.hashToCurve('abcd'),
|
// P521: () => P521.Point.hashToCurve('abcd'),
|
||||||
},
|
// },
|
||||||
},
|
},
|
||||||
stark: {
|
stark: {
|
||||||
data: () => {
|
data: () => {
|
||||||
@ -330,16 +330,16 @@ export const CURVES = {
|
|||||||
old: () => old_bls.pairing(oldp1, oldp2),
|
old: () => old_bls.pairing(oldp1, oldp2),
|
||||||
noble: () => bls.pairing(p1, p2),
|
noble: () => bls.pairing(p1, p2),
|
||||||
},
|
},
|
||||||
'hashToCurve/G1': {
|
// 'hashToCurve/G1': {
|
||||||
samples: 500,
|
// samples: 500,
|
||||||
old: () => old_bls.PointG1.hashToCurve('abcd'),
|
// old: () => old_bls.PointG1.hashToCurve('abcd'),
|
||||||
noble: () => bls.hashToCurve.G1.hashToCurve('abcd'),
|
// noble: () => bls.hashToCurve.G1.hashToCurve('abcd'),
|
||||||
},
|
// },
|
||||||
'hashToCurve/G2': {
|
// 'hashToCurve/G2': {
|
||||||
samples: 200,
|
// samples: 200,
|
||||||
old: () => old_bls.PointG2.hashToCurve('abcd'),
|
// old: () => old_bls.PointG2.hashToCurve('abcd'),
|
||||||
noble: () => bls.hashToCurve.G2.hashToCurve('abcd'),
|
// noble: () => bls.hashToCurve.G2.hashToCurve('abcd'),
|
||||||
},
|
// },
|
||||||
// SLOW PART
|
// SLOW PART
|
||||||
// Requires points which we cannot init before (data fn same for all)
|
// Requires points which we cannot init before (data fn same for all)
|
||||||
// await mark('sign/nc', 30, () => bls.sign(msgp, priv));
|
// await mark('sign/nc', 30, () => bls.sign(msgp, priv));
|
||||||
|
@ -604,6 +604,7 @@ export type SignatureConstructor = {
|
|||||||
fromCompact(hex: Hex): SignatureType;
|
fromCompact(hex: Hex): SignatureType;
|
||||||
fromDER(hex: Hex): SignatureType;
|
fromDER(hex: Hex): SignatureType;
|
||||||
};
|
};
|
||||||
|
type SignatureLike = { r: bigint; s: bigint };
|
||||||
|
|
||||||
export type PubKey = Hex | ProjPointType<bigint>;
|
export type PubKey = Hex | ProjPointType<bigint>;
|
||||||
|
|
||||||
@ -634,7 +635,7 @@ export type CurveFn = {
|
|||||||
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
||||||
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
|
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
|
||||||
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType;
|
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<bigint>;
|
ProjectivePoint: ProjConstructor<bigint>;
|
||||||
Signature: SignatureConstructor;
|
Signature: SignatureConstructor;
|
||||||
utils: {
|
utils: {
|
||||||
@ -955,6 +956,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
// works with order, can have different size than numToField!
|
// works with order, can have different size than numToField!
|
||||||
return ut.numberToBytesBE(num, CURVE.nByteLength);
|
return ut.numberToBytesBE(num, CURVE.nByteLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Steps A, D of RFC6979 3.2
|
// Steps A, D of RFC6979 3.2
|
||||||
// Creates RFC6979 seed; converts msg/privKey to numbers.
|
// Creates RFC6979 seed; converts msg/privKey to numbers.
|
||||||
// Used only in sign, not in verify.
|
// Used only in sign, not in verify.
|
||||||
@ -1048,7 +1050,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
function verify(
|
function verify(
|
||||||
signature: Hex | SignatureType,
|
signature: Hex | { r: bigint; s: bigint },
|
||||||
msgHash: Hex,
|
msgHash: Hex,
|
||||||
publicKey: Hex,
|
publicKey: Hex,
|
||||||
opts = defaultVerOpts
|
opts = defaultVerOpts
|
||||||
@ -1057,9 +1059,9 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
let _sig: Signature | undefined = undefined;
|
let _sig: Signature | undefined = undefined;
|
||||||
if (publicKey instanceof Point) throw new Error('publicKey must be hex');
|
if (publicKey instanceof Point) throw new Error('publicKey must be hex');
|
||||||
try {
|
try {
|
||||||
if (signature instanceof Signature) {
|
if (signature && typeof signature === 'object' && !(signature instanceof Uint8Array)) {
|
||||||
signature.assertValidity();
|
const { r, s } = signature;
|
||||||
_sig = signature;
|
_sig = new Signature(r, s); // assertValidity() is executed on creation
|
||||||
} else {
|
} else {
|
||||||
// Signature can be represented in 2 ways: compact (2*nByteLength) & DER (variable-length).
|
// 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.
|
// Since DER can also be 2*nByteLength bytes, we check for it first.
|
||||||
|
115
src/ed25519.ts
115
src/ed25519.ts
@ -94,6 +94,63 @@ export const ED25519_TORSION_SUBGROUP = [
|
|||||||
|
|
||||||
const Fp = Field(ED25519_P, undefined, true);
|
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)
|
// Hash To Curve Elligator2 Map (NOTE: different from ristretto255 elligator)
|
||||||
// NOTE: very important part is usage of FpSqrtEven for ELL2_C1_EDWARDS, since
|
// NOTE: very important part is usage of FpSqrtEven for ELL2_C1_EDWARDS, since
|
||||||
// SageMath returns different root first and everything falls apart
|
// 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
|
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)
|
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(
|
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
||||||
ed25519.ExtendedPoint,
|
ed25519.ExtendedPoint,
|
||||||
(scalars: bigint[]) => map_to_curve_elligator2_edwards25519(scalars[0]),
|
(scalars: bigint[]) => map_to_curve_elligator2_edwards25519(scalars[0]),
|
||||||
@ -224,21 +238,6 @@ const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
|||||||
);
|
);
|
||||||
export { hashToCurve, encodeToCurve };
|
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) {
|
function assertRstPoint(other: unknown) {
|
||||||
if (!(other instanceof RistrettoPoint)) throw new Error('RistrettoPoint expected');
|
if (!(other instanceof RistrettoPoint)) throw new Error('RistrettoPoint expected');
|
||||||
}
|
}
|
||||||
|
190
src/ed448.ts
190
src/ed448.ts
@ -55,6 +55,101 @@ function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
|
|||||||
|
|
||||||
const Fp = Field(ed448P, 456, true);
|
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
|
// Hash To Curve Elligator2 Map
|
||||||
const ELL2_C1 = (Fp.ORDER - BigInt(3)) / BigInt(4); // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
const ELL2_C1 = (Fp.ORDER - BigInt(3)) / BigInt(4); // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
||||||
const ELL2_J = BigInt(156326);
|
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)
|
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(
|
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
||||||
ed448.ExtendedPoint,
|
ed448.ExtendedPoint,
|
||||||
(scalars: bigint[]) => map_to_curve_elligator2_edwards448(scalars[0]),
|
(scalars: bigint[]) => map_to_curve_elligator2_edwards448(scalars[0]),
|
||||||
@ -210,32 +239,3 @@ const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
export { hashToCurve, encodeToCurve };
|
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);
|
|
||||||
// },
|
|
||||||
});
|
|
||||||
|
@ -95,7 +95,7 @@ function ensureBytes0x(hex: Hex): Uint8Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizePrivateKey(privKey: Hex) {
|
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) {
|
function getPublicKey0x(privKey: Hex, isCompressed?: boolean) {
|
||||||
return starkCurve.getPublicKey(normalizePrivateKey(privKey), isCompressed);
|
return starkCurve.getPublicKey(normalizePrivateKey(privKey), isCompressed);
|
||||||
|
Loading…
Reference in New Issue
Block a user