forked from tornado-packages/noble-curves
Refactor: weierstrass assertValidity and others
This commit is contained in:
parent
23cc2aa5d1
commit
83960d445d
@ -108,11 +108,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>
|
||||
): CurveFn<Fp, Fp2, Fp6, Fp12> {
|
||||
// Fields looks pretty specific for curve, so for now we need to pass them with options
|
||||
const Fp = CURVE.Fp;
|
||||
const Fr = CURVE.Fr;
|
||||
const Fp2 = CURVE.Fp2;
|
||||
const Fp6 = CURVE.Fp6;
|
||||
const Fp12 = CURVE.Fp12;
|
||||
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE;
|
||||
const BLS_X_LEN = ut.bitLen(CURVE.x);
|
||||
const groupLen = 32; // TODO: calculate; hardcoded for now
|
||||
|
||||
@ -161,13 +157,14 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
}
|
||||
|
||||
function millerLoop(ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]): Fp12 {
|
||||
const { x } = CURVE;
|
||||
const Px = g1[0];
|
||||
const Py = g1[1];
|
||||
let f12 = Fp12.ONE;
|
||||
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
|
||||
const E = ell[j];
|
||||
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
|
||||
if (ut.bitGet(CURVE.x, i)) {
|
||||
if (ut.bitGet(x, i)) {
|
||||
j += 1;
|
||||
const F = ell[j];
|
||||
f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
|
||||
@ -177,64 +174,32 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
return Fp12.conjugate(f12);
|
||||
}
|
||||
|
||||
const htfDefaults = { ...CURVE.htfDefaults };
|
||||
|
||||
// TODO: not needed?
|
||||
function isWithinCurveOrder(num: bigint): boolean {
|
||||
return 0 < num && num < CURVE.r;
|
||||
}
|
||||
|
||||
const utils = {
|
||||
hexToBytes: ut.hexToBytes,
|
||||
bytesToHex: ut.bytesToHex,
|
||||
mod: mod.mod,
|
||||
stringToBytes,
|
||||
stringToBytes: stringToBytes,
|
||||
// TODO: do we need to export it here?
|
||||
hashToField: (msg: Uint8Array, count: number, options: Partial<typeof htfDefaults> = {}) =>
|
||||
hashToField(msg, count, { ...CURVE.htfDefaults, ...options }),
|
||||
hashToField: (
|
||||
msg: Uint8Array,
|
||||
count: number,
|
||||
options: Partial<typeof CURVE.htfDefaults> = {}
|
||||
) => hashToField(msg, count, { ...CURVE.htfDefaults, ...options }),
|
||||
expandMessageXMD: (msg: Uint8Array, DST: Uint8Array, lenInBytes: number, H = CURVE.hash) =>
|
||||
expandMessageXMD(msg, DST, lenInBytes, H),
|
||||
|
||||
/**
|
||||
* Creates FIPS 186 B.4.1 compliant private keys without modulo bias.
|
||||
*/
|
||||
hashToPrivateKey: (hash: Hex): Uint8Array => {
|
||||
hash = ut.ensureBytes(hash);
|
||||
if (hash.length < 40 || hash.length > 1024)
|
||||
throw new Error('Expected 40-1024 bytes of private key as per FIPS 186');
|
||||
// hashToPrivateScalar(hash, CURVE.r)
|
||||
// NOTE: doesn't add +/-1
|
||||
const num = mod.mod(ut.bytesToNumberBE(hash), CURVE.r);
|
||||
// This should never happen
|
||||
if (num === 0n || num === 1n) throw new Error('Invalid private key');
|
||||
return ut.numberToBytesBE(num, groupLen);
|
||||
},
|
||||
|
||||
hashToPrivateKey: (hash: Hex): Uint8Array => Fr.toBytes(ut.hashToPrivateScalar(hash, CURVE.r)),
|
||||
randomBytes: (bytesLength: number = groupLen): Uint8Array => CURVE.randomBytes(bytesLength),
|
||||
randomPrivateKey: (): Uint8Array => utils.hashToPrivateKey(utils.randomBytes(groupLen + 8)),
|
||||
getDSTLabel: () => htfDefaults.DST,
|
||||
getDSTLabel: () => CURVE.htfDefaults.DST,
|
||||
setDSTLabel(newLabel: string) {
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3.1
|
||||
if (typeof newLabel !== 'string' || newLabel.length > 2048 || newLabel.length === 0) {
|
||||
throw new TypeError('Invalid DST');
|
||||
}
|
||||
htfDefaults.DST = newLabel;
|
||||
CURVE.htfDefaults.DST = newLabel;
|
||||
},
|
||||
};
|
||||
|
||||
// TODO: reuse CURVE.G1.utils.normalizePrivateKey() ?
|
||||
function normalizePrivKey(key: PrivKey): bigint {
|
||||
let int: bigint;
|
||||
if (key instanceof Uint8Array && key.length === groupLen) int = ut.bytesToNumberBE(key);
|
||||
else if (typeof key === 'string' && key.length === 2 * groupLen) int = BigInt(`0x${key}`);
|
||||
else if (ut.isPositiveInt(key)) int = BigInt(key);
|
||||
else if (typeof key === 'bigint' && key > 0n) int = key;
|
||||
else throw new TypeError('Expected valid private key');
|
||||
int = mod.mod(int, CURVE.r);
|
||||
if (!isWithinCurveOrder(int)) throw new Error('Private key must be 0 < key < CURVE.r');
|
||||
return int;
|
||||
}
|
||||
|
||||
// Point on G1 curve: (x, y)
|
||||
const G1 = weierstrassPoints({
|
||||
n: Fr.ORDER,
|
||||
@ -306,7 +271,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
function sign(message: G2Hex, privateKey: PrivKey): Uint8Array | G2 {
|
||||
const msgPoint = normP2Hash(message);
|
||||
msgPoint.assertValidity();
|
||||
const sigPoint = msgPoint.multiply(normalizePrivKey(privateKey));
|
||||
const sigPoint = msgPoint.multiply(G1.normalizePrivateKey(privateKey));
|
||||
if (message instanceof G2.Point) return sigPoint;
|
||||
return Signature.encode(sigPoint);
|
||||
}
|
||||
|
@ -149,21 +149,22 @@ export function nLength(n: bigint, nBitLength?: number) {
|
||||
}
|
||||
|
||||
/**
|
||||
* FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
|
||||
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
|
||||
* and convert them into private scalar, with the modulo bias being neglible.
|
||||
* As per FIPS 186 B.4.1.
|
||||
* Needs at least 40 bytes of input for 32-byte private key.
|
||||
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
||||
* @param hash hash output from sha512, or a similar function
|
||||
* @param hash hash output from SHA3 or a similar function
|
||||
* @returns valid private scalar
|
||||
*/
|
||||
export function hashToPrivateScalar(hash: Hex, CURVE_ORDER: bigint, isLE = false): bigint {
|
||||
export function hashToPrivateScalar(hash: Hex, groupOrder: bigint, isLE = false): bigint {
|
||||
hash = ensureBytes(hash);
|
||||
const orderLen = nLength(CURVE_ORDER).nByteLength;
|
||||
const minLen = orderLen + 8;
|
||||
if (orderLen < 16 || hash.length < minLen || hash.length > 1024)
|
||||
throw new Error('Expected valid bytes of private key as per FIPS 186');
|
||||
const hashLen = hash.length;
|
||||
const minLen = nLength(groupOrder).nByteLength + 8;
|
||||
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
|
||||
throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
|
||||
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
|
||||
return mod.mod(num, CURVE_ORDER - _1n) + _1n;
|
||||
return mod.mod(num, groupOrder - _1n) + _1n;
|
||||
}
|
||||
|
||||
export function equalBytes(b1: Uint8Array, b2: Uint8Array) {
|
||||
|
@ -601,15 +601,19 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
||||
// Zero is valid point too!
|
||||
if (this.equals(Point.ZERO)) {
|
||||
if (CURVE.allowInfinityPoint) return;
|
||||
throw new Error('Point is infinity');
|
||||
throw new Error('Point at infinity');
|
||||
}
|
||||
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
||||
const msg = 'Point is not on elliptic curve';
|
||||
const { x, y } = this;
|
||||
// Check if x, y are valid field elements
|
||||
if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error(msg);
|
||||
const left = Fp.square(y);
|
||||
const right = weierstrassEquation(x);
|
||||
if (!Fp.equals(left, right)) throw new Error(msg);
|
||||
// We subtract instead of comparing: it's safer
|
||||
// (y²) - (x³ + ax + b) == 0
|
||||
if (!Fp.isZero(Fp.sub(left, right))) throw new Error(msg);
|
||||
// if (!Fp.equals(left, right))
|
||||
// TODO: flag to disable this?
|
||||
if (!this.isTorsionFree()) throw new Error('Point must be of prime-order subgroup');
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import { Fp } from './abstract/modular.js';
|
||||
/**
|
||||
* jubjub Twisted Edwards curve.
|
||||
* https://neuromancer.sk/std/other/JubJub
|
||||
* jubjub does not use EdDSA, so `hash`/sha512 params are passed because interface expects them.
|
||||
*/
|
||||
|
||||
export const jubjub = twistedEdwards({
|
||||
@ -15,9 +16,9 @@ export const jubjub = twistedEdwards({
|
||||
a: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000'),
|
||||
d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'),
|
||||
// Finite field 𝔽p over which we'll do calculations
|
||||
// Same value as bls12-381 Fr (not Fp)
|
||||
Fp: Fp(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')),
|
||||
// Subgroup order: how many points curve has
|
||||
// 2n ** 252n + 27742317777372353535851937790883648493n;
|
||||
n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'),
|
||||
// Cofactor
|
||||
h: BigInt(8),
|
||||
|
Loading…
Reference in New Issue
Block a user