Shuffle code

This commit is contained in:
Paul Miller 2023-01-26 04:46:14 +00:00
parent 9465e60d30
commit 69b3ab5a57
No known key found for this signature in database
GPG Key ID: 697079DA6878B89B
5 changed files with 181 additions and 180 deletions

@ -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));

@ -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<bigint>;
@ -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<bigint>;
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.

@ -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');
}

@ -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);
// },
});

@ -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);