forked from tornado-packages/noble-curves
Rename group to curve. More refactoring
This commit is contained in:
parent
be0b2a32a5
commit
0fb78b7097
@ -73,10 +73,10 @@
|
|||||||
"import": "./lib/esm/abstract/hash-to-curve.js",
|
"import": "./lib/esm/abstract/hash-to-curve.js",
|
||||||
"default": "./lib/abstract/hash-to-curve.js"
|
"default": "./lib/abstract/hash-to-curve.js"
|
||||||
},
|
},
|
||||||
"./abstract/group": {
|
"./abstract/curve": {
|
||||||
"types": "./lib/abstract/group.d.ts",
|
"types": "./lib/abstract/curve.d.ts",
|
||||||
"import": "./lib/esm/abstract/group.js",
|
"import": "./lib/esm/abstract/curve.js",
|
||||||
"default": "./lib/abstract/group.js"
|
"default": "./lib/abstract/curve.js"
|
||||||
},
|
},
|
||||||
"./abstract/utils": {
|
"./abstract/utils": {
|
||||||
"types": "./lib/abstract/utils.d.ts",
|
"types": "./lib/abstract/utils.d.ts",
|
||||||
|
@ -193,7 +193,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
) => htf.hash_to_field(msg, count, { ...CURVE.htfDefaults, ...options }),
|
) => htf.hash_to_field(msg, count, { ...CURVE.htfDefaults, ...options }),
|
||||||
expandMessageXMD: (msg: Uint8Array, DST: Uint8Array, lenInBytes: number, H = CURVE.hash) =>
|
expandMessageXMD: (msg: Uint8Array, DST: Uint8Array, lenInBytes: number, H = CURVE.hash) =>
|
||||||
htf.expand_message_xmd(msg, DST, lenInBytes, H),
|
htf.expand_message_xmd(msg, DST, lenInBytes, H),
|
||||||
hashToPrivateKey: (hash: Hex): Uint8Array => Fr.toBytes(ut.hashToPrivateScalar(hash, CURVE.r)),
|
hashToPrivateKey: (hash: Hex): Uint8Array => Fr.toBytes(mod.hashToPrivateScalar(hash, CURVE.r)),
|
||||||
randomBytes: (bytesLength: number = groupLen): Uint8Array => CURVE.randomBytes(bytesLength),
|
randomBytes: (bytesLength: number = groupLen): Uint8Array => CURVE.randomBytes(bytesLength),
|
||||||
randomPrivateKey: (): Uint8Array => utils.hashToPrivateKey(utils.randomBytes(groupLen + 8)),
|
randomPrivateKey: (): Uint8Array => utils.hashToPrivateKey(utils.randomBytes(groupLen + 8)),
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
// Abelian group utilities
|
// Abelian group utilities
|
||||||
|
import { Field, validateField, nLength } from './modular.js';
|
||||||
const _0n = BigInt(0);
|
const _0n = BigInt(0);
|
||||||
const _1n = BigInt(1);
|
const _1n = BigInt(1);
|
||||||
|
|
||||||
@ -144,3 +145,36 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generic BasicCurve interface: works even for polynomial fields (BLS): P, n, h would be ok.
|
||||||
|
// Though generator can be different (Fp2 / Fp6 for BLS).
|
||||||
|
export type BasicCurve<T> = {
|
||||||
|
Fp: Field<T>; // Field over which we'll do calculations (Fp)
|
||||||
|
n: bigint; // Curve order, total count of valid points in the field
|
||||||
|
nBitLength?: number; // bit length of curve order
|
||||||
|
nByteLength?: number; // byte length of curve order
|
||||||
|
h: bigint; // cofactor. we can assign default=1, but users will just ignore it w/o validation
|
||||||
|
hEff?: bigint; // Number to multiply to clear cofactor
|
||||||
|
Gx: T; // base point X coordinate
|
||||||
|
Gy: T; // base point Y coordinate
|
||||||
|
wrapPrivateKey?: boolean; // bls12-381 requires mod(n) instead of rejecting keys >= n
|
||||||
|
allowInfinityPoint?: boolean; // bls12-381 requires it. ZERO point is valid, but invalid pubkey
|
||||||
|
};
|
||||||
|
|
||||||
|
export function validateBasicCurveOpts<FP, T>(curve: BasicCurve<FP> & T) {
|
||||||
|
validateField(curve.Fp);
|
||||||
|
for (const i of ['n', 'h'] as const) {
|
||||||
|
const val = curve[i];
|
||||||
|
if (typeof val !== 'bigint') throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
|
||||||
|
}
|
||||||
|
if (!curve.Fp.isValid(curve.Gx)) throw new Error('Invalid generator X coordinate Fp element');
|
||||||
|
if (!curve.Fp.isValid(curve.Gy)) throw new Error('Invalid generator Y coordinate Fp element');
|
||||||
|
|
||||||
|
for (const i of ['nBitLength', 'nByteLength'] as const) {
|
||||||
|
const val = curve[i];
|
||||||
|
if (val === undefined) continue; // Optional
|
||||||
|
if (!Number.isSafeInteger(val)) throw new Error(`Invalid param ${i}=${val} (${typeof val})`);
|
||||||
|
}
|
||||||
|
// Set defaults
|
||||||
|
return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve } as const);
|
||||||
|
}
|
@ -1,9 +1,16 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
// Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
|
// Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
|
||||||
import * as mod from './modular.js';
|
import { mod } from './modular.js';
|
||||||
import * as ut from './utils.js';
|
import {
|
||||||
import { ensureBytes, Hex } from './utils.js';
|
bytesToHex,
|
||||||
import { Group, GroupConstructor, wNAF } from './group.js';
|
bytesToNumberLE,
|
||||||
|
concatBytes,
|
||||||
|
ensureBytes,
|
||||||
|
FHash,
|
||||||
|
Hex,
|
||||||
|
numberToBytesLE,
|
||||||
|
} from './utils.js';
|
||||||
|
import { Group, GroupConstructor, wNAF, BasicCurve, validateBasicCurveOpts } from './curve.js';
|
||||||
|
|
||||||
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
||||||
const _0n = BigInt(0);
|
const _0n = BigInt(0);
|
||||||
@ -12,20 +19,20 @@ const _2n = BigInt(2);
|
|||||||
const _8n = BigInt(8);
|
const _8n = BigInt(8);
|
||||||
|
|
||||||
// Edwards curves must declare params a & d.
|
// Edwards curves must declare params a & d.
|
||||||
export type CurveType = ut.BasicCurve<bigint> & {
|
export type CurveType = BasicCurve<bigint> & {
|
||||||
a: bigint; // curve param a
|
a: bigint; // curve param a
|
||||||
d: bigint; // curve param d
|
d: bigint; // curve param d
|
||||||
hash: ut.FHash; // Hashing
|
hash: FHash; // Hashing
|
||||||
randomBytes: (bytesLength?: number) => Uint8Array; // CSPRNG
|
randomBytes: (bytesLength?: number) => Uint8Array; // CSPRNG
|
||||||
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array; // clears bits to get valid field elemtn
|
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array; // clears bits to get valid field elemtn
|
||||||
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array; // Used for hashing
|
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array; // Used for hashing
|
||||||
uvRatio?: (u: bigint, v: bigint) => { isValid: boolean; value: bigint }; // Ratio √(u/v)
|
uvRatio?: (u: bigint, v: bigint) => { isValid: boolean; value: bigint }; // Ratio √(u/v)
|
||||||
preHash?: ut.FHash; // RFC 8032 pre-hashing of messages to sign() / verify()
|
preHash?: FHash; // RFC 8032 pre-hashing of messages to sign() / verify()
|
||||||
mapToCurve?: (scalar: bigint[]) => AffinePoint; // for hash-to-curve standard
|
mapToCurve?: (scalar: bigint[]) => AffinePoint; // for hash-to-curve standard
|
||||||
};
|
};
|
||||||
|
|
||||||
function validateOpts(curve: CurveType) {
|
function validateOpts(curve: CurveType) {
|
||||||
const opts = ut.validateOpts(curve);
|
const opts = validateBasicCurveOpts(curve);
|
||||||
if (typeof opts.hash !== 'function') throw new Error('Invalid hash function');
|
if (typeof opts.hash !== 'function') throw new Error('Invalid hash function');
|
||||||
for (const i of ['a', 'd'] as const) {
|
for (const i of ['a', 'd'] as const) {
|
||||||
const val = opts[i];
|
const val = opts[i];
|
||||||
@ -329,7 +336,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
const normed = hex.slice(); // copy again, we'll manipulate it
|
const normed = hex.slice(); // copy again, we'll manipulate it
|
||||||
const lastByte = hex[len - 1]; // select last byte
|
const lastByte = hex[len - 1]; // select last byte
|
||||||
normed[len - 1] = lastByte & ~0x80; // clear last bit
|
normed[len - 1] = lastByte & ~0x80; // clear last bit
|
||||||
const y = ut.bytesToNumberLE(normed);
|
const y = bytesToNumberLE(normed);
|
||||||
if (y === _0n) {
|
if (y === _0n) {
|
||||||
// y=0 is allowed
|
// y=0 is allowed
|
||||||
} else {
|
} else {
|
||||||
@ -355,20 +362,23 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
}
|
}
|
||||||
toRawBytes(): Uint8Array {
|
toRawBytes(): Uint8Array {
|
||||||
const { x, y } = this.toAffine();
|
const { x, y } = this.toAffine();
|
||||||
const bytes = ut.numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
|
const bytes = numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
|
||||||
bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
|
bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
|
||||||
return bytes; // and use the last byte to encode sign of x
|
return bytes; // and use the last byte to encode sign of x
|
||||||
}
|
}
|
||||||
toHex(): string {
|
toHex(): string {
|
||||||
return ut.bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
|
return bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const { BASE: G, ZERO: I } = Point;
|
const { BASE: G, ZERO: I } = Point;
|
||||||
const wnaf = wNAF(Point, nByteLength * 8);
|
const wnaf = wNAF(Point, nByteLength * 8);
|
||||||
|
|
||||||
|
function modN(a: bigint) {
|
||||||
|
return mod(a, CURVE_ORDER);
|
||||||
|
}
|
||||||
// Little-endian SHA512 with modulo n
|
// Little-endian SHA512 with modulo n
|
||||||
function modnLE(hash: Uint8Array): bigint {
|
function modN_LE(hash: Uint8Array): bigint {
|
||||||
return mod.mod(ut.bytesToNumberLE(hash), CURVE_ORDER);
|
return modN(bytesToNumberLE(hash));
|
||||||
}
|
}
|
||||||
function isHex(item: Hex, err: string) {
|
function isHex(item: Hex, err: string) {
|
||||||
if (typeof item !== 'string' && !(item instanceof Uint8Array))
|
if (typeof item !== 'string' && !(item instanceof Uint8Array))
|
||||||
@ -384,7 +394,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
const hashed = ensureBytes(cHash(ensureBytes(key, len)), 2 * len);
|
const hashed = ensureBytes(cHash(ensureBytes(key, len)), 2 * len);
|
||||||
const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
|
const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
|
||||||
const prefix = hashed.slice(len, 2 * len); // second half is called key prefix (5.1.6)
|
const prefix = hashed.slice(len, 2 * len); // second half is called key prefix (5.1.6)
|
||||||
const scalar = modnLE(head); // The actual private scalar
|
const scalar = modN_LE(head); // The actual private scalar
|
||||||
const point = G.multiply(scalar); // Point on Edwards curve aka public key
|
const point = G.multiply(scalar); // Point on Edwards curve aka public key
|
||||||
const pointBytes = point.toRawBytes(); // Uint8Array representation
|
const pointBytes = point.toRawBytes(); // Uint8Array representation
|
||||||
return { head, prefix, scalar, point, pointBytes };
|
return { head, prefix, scalar, point, pointBytes };
|
||||||
@ -397,8 +407,8 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
|
|
||||||
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
|
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
|
||||||
function hashDomainToScalar(context: Hex = new Uint8Array(), ...msgs: Uint8Array[]) {
|
function hashDomainToScalar(context: Hex = new Uint8Array(), ...msgs: Uint8Array[]) {
|
||||||
const msg = ut.concatBytes(...msgs);
|
const msg = concatBytes(...msgs);
|
||||||
return modnLE(cHash(domain(msg, ensureBytes(context), !!preHash)));
|
return modN_LE(cHash(domain(msg, ensureBytes(context), !!preHash)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Signs message with privateKey. RFC8032 5.1.6 */
|
/** Signs message with privateKey. RFC8032 5.1.6 */
|
||||||
@ -410,9 +420,9 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
const r = hashDomainToScalar(context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
|
const r = hashDomainToScalar(context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
|
||||||
const R = G.multiply(r).toRawBytes(); // R = rG
|
const R = G.multiply(r).toRawBytes(); // R = rG
|
||||||
const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
|
const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
|
||||||
const s = mod.mod(r + k * scalar, CURVE_ORDER); // S = (r + k * s) mod L
|
const s = modN(r + k * scalar); // S = (r + k * s) mod L
|
||||||
assertGE0(s); // 0 <= s < l
|
assertGE0(s); // 0 <= s < l
|
||||||
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
|
const res = concatBytes(R, numberToBytesLE(s, Fp.BYTES));
|
||||||
return ensureBytes(res, nByteLength * 2); // 64-byte signature
|
return ensureBytes(res, nByteLength * 2); // 64-byte signature
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,7 +435,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
if (preHash) msg = preHash(msg); // for ed25519ph, etc
|
if (preHash) msg = preHash(msg); // for ed25519ph, etc
|
||||||
const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
|
const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
|
||||||
const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
|
const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
|
||||||
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
|
const s = bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
|
||||||
const SB = G.multiplyUnsafe(s);
|
const SB = G.multiplyUnsafe(s);
|
||||||
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
|
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
|
||||||
const RkA = R.add(A.multiplyUnsafe(k));
|
const RkA = R.add(A.multiplyUnsafe(k));
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import * as ut from './utils.js';
|
import * as ut from './utils.js';
|
||||||
import * as mod from './modular.js';
|
import * as mod from './modular.js';
|
||||||
import type { Group, GroupConstructor } from './group.js';
|
import type { Group, GroupConstructor } from './curve.js';
|
||||||
|
|
||||||
export type Opts = {
|
export type Opts = {
|
||||||
// DST: a domain separation tag
|
// DST: a domain separation tag
|
||||||
@ -38,13 +38,16 @@ export function validateOpts(opts: Opts) {
|
|||||||
throw new Error('Invalid htf/hash function');
|
throw new Error('Invalid htf/hash function');
|
||||||
}
|
}
|
||||||
|
|
||||||
// UTF8 to ui8a
|
// Global symbols in both browsers and Node.js since v11
|
||||||
// TODO: looks broken, ASCII only, why not TextEncoder/TextDecoder? it is in hashes anyway
|
// See https://github.com/microsoft/TypeScript/issues/31535
|
||||||
export function stringToBytes(str: string) {
|
declare const TextEncoder: any;
|
||||||
// return new TextEncoder().encode(str);
|
declare const TextDecoder: any;
|
||||||
const bytes = new Uint8Array(str.length);
|
|
||||||
for (let i = 0; i < str.length; i++) bytes[i] = str.charCodeAt(i);
|
export function stringToBytes(str: string): Uint8Array {
|
||||||
return bytes;
|
if (typeof str !== 'string') {
|
||||||
|
throw new TypeError(`utf8ToBytes expected string, got ${typeof str}`);
|
||||||
|
}
|
||||||
|
return new TextEncoder().encode(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Octet Stream to Integer (bytesToNumberBE)
|
// Octet Stream to Integer (bytesToNumberBE)
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
// TODO: remove circular imports
|
|
||||||
import * as utils from './utils.js';
|
|
||||||
// Utilities for modular arithmetics and finite fields
|
// Utilities for modular arithmetics and finite fields
|
||||||
|
import {
|
||||||
|
bitMask,
|
||||||
|
numberToBytesBE,
|
||||||
|
numberToBytesLE,
|
||||||
|
bytesToNumberBE,
|
||||||
|
bytesToNumberLE,
|
||||||
|
ensureBytes,
|
||||||
|
} from './utils.js';
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@ -306,6 +312,14 @@ export function FpIsSquare<T>(f: Field<T>) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CURVE.n lengths
|
||||||
|
export function nLength(n: bigint, nBitLength?: number) {
|
||||||
|
// Bit size, byte size of CURVE.n
|
||||||
|
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
|
||||||
|
const nByteLength = Math.ceil(_nBitLength / 8);
|
||||||
|
return { nBitLength: _nBitLength, nByteLength };
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: very fragile, always bench. Major performance points:
|
// NOTE: very fragile, always bench. Major performance points:
|
||||||
// - NonNormalized ops
|
// - NonNormalized ops
|
||||||
// - Object.freeze
|
// - Object.freeze
|
||||||
@ -318,14 +332,14 @@ export function Fp(
|
|||||||
redef: Partial<Field<bigint>> = {}
|
redef: Partial<Field<bigint>> = {}
|
||||||
): Readonly<FpField> {
|
): Readonly<FpField> {
|
||||||
if (ORDER <= _0n) throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
|
if (ORDER <= _0n) throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
|
||||||
const { nBitLength: BITS, nByteLength: BYTES } = utils.nLength(ORDER, bitLen);
|
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
|
||||||
if (BYTES > 2048) throw new Error('Field lengths over 2048 bytes are not supported');
|
if (BYTES > 2048) throw new Error('Field lengths over 2048 bytes are not supported');
|
||||||
const sqrtP = FpSqrt(ORDER);
|
const sqrtP = FpSqrt(ORDER);
|
||||||
const f: Readonly<FpField> = Object.freeze({
|
const f: Readonly<FpField> = Object.freeze({
|
||||||
ORDER,
|
ORDER,
|
||||||
BITS,
|
BITS,
|
||||||
BYTES,
|
BYTES,
|
||||||
MASK: utils.bitMask(BITS),
|
MASK: bitMask(BITS),
|
||||||
ZERO: _0n,
|
ZERO: _0n,
|
||||||
ONE: _1n,
|
ONE: _1n,
|
||||||
create: (num) => mod(num, ORDER),
|
create: (num) => mod(num, ORDER),
|
||||||
@ -358,12 +372,11 @@ export function Fp(
|
|||||||
// TODO: do we really need constant cmov?
|
// TODO: do we really need constant cmov?
|
||||||
// We don't have const-time bigints anyway, so probably will be not very useful
|
// We don't have const-time bigints anyway, so probably will be not very useful
|
||||||
cmov: (a, b, c) => (c ? b : a),
|
cmov: (a, b, c) => (c ? b : a),
|
||||||
toBytes: (num) =>
|
toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
|
||||||
isLE ? utils.numberToBytesLE(num, BYTES) : utils.numberToBytesBE(num, BYTES),
|
|
||||||
fromBytes: (bytes) => {
|
fromBytes: (bytes) => {
|
||||||
if (bytes.length !== BYTES)
|
if (bytes.length !== BYTES)
|
||||||
throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
|
throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
|
||||||
return isLE ? utils.bytesToNumberLE(bytes) : utils.bytesToNumberBE(bytes);
|
return isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
||||||
},
|
},
|
||||||
} as FpField);
|
} as FpField);
|
||||||
return Object.freeze(f);
|
return Object.freeze(f);
|
||||||
@ -380,3 +393,26 @@ export function FpSqrtEven<T>(Fp: Field<T>, elm: T) {
|
|||||||
const root = Fp.sqrt(elm);
|
const root = Fp.sqrt(elm);
|
||||||
return Fp.isOdd(root) ? Fp.neg(root) : root;
|
return Fp.isOdd(root) ? Fp.neg(root) : root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* 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 SHA3 or a similar function
|
||||||
|
* @returns valid private scalar
|
||||||
|
*/
|
||||||
|
export function hashToPrivateScalar(
|
||||||
|
hash: string | Uint8Array,
|
||||||
|
groupOrder: bigint,
|
||||||
|
isLE = false
|
||||||
|
): bigint {
|
||||||
|
hash = ensureBytes(hash);
|
||||||
|
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(num, groupOrder - _1n) + _1n;
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import * as mod from './modular.js';
|
import * as mod from './modular.js';
|
||||||
import { ensureBytes, numberToBytesLE, bytesToNumberLE, isPositiveInt } from './utils.js';
|
import { ensureBytes, numberToBytesLE, bytesToNumberLE } from './utils.js';
|
||||||
|
|
||||||
const _0n = BigInt(0);
|
const _0n = BigInt(0);
|
||||||
const _1n = BigInt(1);
|
const _1n = BigInt(1);
|
||||||
@ -33,7 +33,7 @@ function validateOpts(curve: CurveType) {
|
|||||||
}
|
}
|
||||||
for (const i of ['montgomeryBits', 'nByteLength'] as const) {
|
for (const i of ['montgomeryBits', 'nByteLength'] as const) {
|
||||||
if (curve[i] === undefined) continue; // Optional
|
if (curve[i] === undefined) continue; // Optional
|
||||||
if (!isPositiveInt(curve[i]))
|
if (!Number.isSafeInteger(curve[i]))
|
||||||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
||||||
}
|
}
|
||||||
for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2'] as const) {
|
for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2'] as const) {
|
||||||
@ -46,7 +46,6 @@ function validateOpts(curve: CurveType) {
|
|||||||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
||||||
}
|
}
|
||||||
// Set defaults
|
// Set defaults
|
||||||
// ...nLength(curve.n, curve.nBitLength),
|
|
||||||
return Object.freeze({ ...curve } as const);
|
return Object.freeze({ ...curve } as const);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
// Poseidon Hash (https://eprint.iacr.org/2019/458.pdf)
|
// Poseidon Hash (https://eprint.iacr.org/2019/458.pdf)
|
||||||
// Website: https://www.poseidon-hash.info
|
// Website: https://www.poseidon-hash.info
|
||||||
|
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import * as mod from './modular.js';
|
|
||||||
const _0n = BigInt(0);
|
const _0n = BigInt(0);
|
||||||
const _1n = BigInt(1);
|
const _1n = BigInt(1);
|
||||||
const _2n = BigInt(2);
|
const _2n = BigInt(2);
|
||||||
|
|
||||||
const str = (a: any): a is string => typeof a === 'string';
|
const str = (a: any): a is string => typeof a === 'string';
|
||||||
export const big = (a: any): a is bigint => typeof a === 'bigint';
|
|
||||||
const u8a = (a: any): a is Uint8Array => a instanceof Uint8Array;
|
const u8a = (a: any): a is Uint8Array => a instanceof Uint8Array;
|
||||||
|
|
||||||
// We accept hex strings besides Uint8Array for simplicity
|
// We accept hex strings besides Uint8Array for simplicity
|
||||||
@ -20,54 +18,6 @@ export type CHash = {
|
|||||||
};
|
};
|
||||||
export type FHash = (message: Uint8Array | string) => Uint8Array;
|
export type FHash = (message: Uint8Array | string) => Uint8Array;
|
||||||
|
|
||||||
// NOTE: these are generic, even if curve is on some polynominal field (bls), it will still have P/n/h
|
|
||||||
// But generator can be different (Fp2/Fp6 for bls?)
|
|
||||||
export type BasicCurve<T> = {
|
|
||||||
// Field over which we'll do calculations (Fp)
|
|
||||||
Fp: mod.Field<T>;
|
|
||||||
// Curve order, total count of valid points in the field
|
|
||||||
n: bigint;
|
|
||||||
// Bit/byte length of curve order
|
|
||||||
nBitLength?: number;
|
|
||||||
nByteLength?: number;
|
|
||||||
// Cofactor
|
|
||||||
// NOTE: we can assign default value of 1, but then users will just ignore it, without validating with spec
|
|
||||||
// Has not use for now, but nice to have in API
|
|
||||||
h: bigint;
|
|
||||||
hEff?: bigint; // Number to multiply to clear cofactor
|
|
||||||
// Base point (x, y) aka generator point
|
|
||||||
Gx: T;
|
|
||||||
Gy: T;
|
|
||||||
// Wrap private key by curve order (% CURVE.n instead of throwing error)
|
|
||||||
wrapPrivateKey?: boolean;
|
|
||||||
// Point at infinity is perfectly valid point, but not valid public key.
|
|
||||||
// Disabled by default because of compatibility reasons with @noble/secp256k1
|
|
||||||
allowInfinityPoint?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Bans floats and integers above 2^53-1
|
|
||||||
export function isPositiveInt(num: any): num is number {
|
|
||||||
return typeof num === 'number' && Number.isSafeInteger(num) && num > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateOpts<FP, T>(curve: BasicCurve<FP> & T) {
|
|
||||||
mod.validateField(curve.Fp);
|
|
||||||
for (const i of ['n', 'h'] as const) {
|
|
||||||
const val = curve[i];
|
|
||||||
if (!big(val)) throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
|
|
||||||
}
|
|
||||||
if (!curve.Fp.isValid(curve.Gx)) throw new Error('Invalid generator X coordinate Fp element');
|
|
||||||
if (!curve.Fp.isValid(curve.Gy)) throw new Error('Invalid generator Y coordinate Fp element');
|
|
||||||
|
|
||||||
for (const i of ['nBitLength', 'nByteLength'] as const) {
|
|
||||||
const val = curve[i];
|
|
||||||
if (val === undefined) continue; // Optional
|
|
||||||
if (!isPositiveInt(val)) throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
|
|
||||||
}
|
|
||||||
// Set defaults
|
|
||||||
return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve } as const);
|
|
||||||
}
|
|
||||||
|
|
||||||
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
|
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
|
||||||
export function bytesToHex(bytes: Uint8Array): string {
|
export function bytesToHex(bytes: Uint8Array): string {
|
||||||
if (!u8a(bytes)) throw new Error('Expected Uint8Array');
|
if (!u8a(bytes)) throw new Error('Expected Uint8Array');
|
||||||
@ -147,33 +97,6 @@ export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CURVE.n lengths
|
|
||||||
export function nLength(n: bigint, nBitLength?: number) {
|
|
||||||
// Bit size, byte size of CURVE.n
|
|
||||||
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
|
|
||||||
const nByteLength = Math.ceil(_nBitLength / 8);
|
|
||||||
return { nBitLength: _nBitLength, nByteLength };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
* 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 SHA3 or a similar function
|
|
||||||
* @returns valid private scalar
|
|
||||||
*/
|
|
||||||
export function hashToPrivateScalar(hash: Hex, groupOrder: bigint, isLE = false): bigint {
|
|
||||||
hash = ensureBytes(hash);
|
|
||||||
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, groupOrder - _1n) + _1n;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function equalBytes(b1: Uint8Array, b2: Uint8Array) {
|
export function equalBytes(b1: Uint8Array, b2: Uint8Array) {
|
||||||
// We don't care about timing attacks here
|
// We don't care about timing attacks here
|
||||||
if (b1.length !== b2.length) return false;
|
if (b1.length !== b2.length) return false;
|
||||||
|
@ -1,16 +1,34 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
// Short Weierstrass curve. The formula is: y² = x³ + ax + b
|
// Short Weierstrass curve. The formula is: y² = x³ + ax + b
|
||||||
import * as mod from './modular.js';
|
import * as mod from './modular.js';
|
||||||
import * as ut from './utils.js';
|
// import * as ut from './utils.js';
|
||||||
import { Hex, PrivKey } from './utils.js';
|
import {
|
||||||
import { Group, GroupConstructor, wNAF } from './group.js';
|
Hex,
|
||||||
|
PrivKey,
|
||||||
|
bytesToHex,
|
||||||
|
bytesToNumberBE,
|
||||||
|
ensureBytes,
|
||||||
|
hexToBytes,
|
||||||
|
concatBytes,
|
||||||
|
bitMask,
|
||||||
|
numberToBytesBE,
|
||||||
|
CHash,
|
||||||
|
numberToHexUnpadded,
|
||||||
|
} from './utils.js';
|
||||||
|
import {
|
||||||
|
Group,
|
||||||
|
GroupConstructor,
|
||||||
|
wNAF,
|
||||||
|
BasicCurve as CBasicCurve,
|
||||||
|
validateBasicCurveOpts,
|
||||||
|
} from './curve.js';
|
||||||
|
|
||||||
type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
|
type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
|
||||||
type EndomorphismOpts = {
|
type EndomorphismOpts = {
|
||||||
beta: bigint;
|
beta: bigint;
|
||||||
splitScalar: (k: bigint) => { k1neg: boolean; k1: bigint; k2neg: boolean; k2: bigint };
|
splitScalar: (k: bigint) => { k1neg: boolean; k1: bigint; k2neg: boolean; k2: bigint };
|
||||||
};
|
};
|
||||||
export type BasicCurve<T> = ut.BasicCurve<T> & {
|
export type BasicCurve<T> = CBasicCurve<T> & {
|
||||||
// Params: a, b
|
// Params: a, b
|
||||||
a: T;
|
a: T;
|
||||||
b: T;
|
b: T;
|
||||||
@ -44,7 +62,7 @@ const DER = {
|
|||||||
},
|
},
|
||||||
parseInt(data: Uint8Array): { data: bigint; left: Uint8Array } {
|
parseInt(data: Uint8Array): { data: bigint; left: Uint8Array } {
|
||||||
if (data.length < 2 || data[0] !== 0x02) {
|
if (data.length < 2 || data[0] !== 0x02) {
|
||||||
throw new DERError(`Invalid signature integer tag: ${ut.bytesToHex(data)}`);
|
throw new DERError(`Invalid signature integer tag: ${bytesToHex(data)}`);
|
||||||
}
|
}
|
||||||
const len = data[1];
|
const len = data[1];
|
||||||
const res = data.subarray(2, len + 2);
|
const res = data.subarray(2, len + 2);
|
||||||
@ -55,11 +73,11 @@ const DER = {
|
|||||||
if (res[0] === 0x00 && res[1] <= 0x7f) {
|
if (res[0] === 0x00 && res[1] <= 0x7f) {
|
||||||
throw new DERError('Invalid signature integer: trailing length');
|
throw new DERError('Invalid signature integer: trailing length');
|
||||||
}
|
}
|
||||||
return { data: ut.bytesToNumberBE(res), left: data.subarray(len + 2) };
|
return { data: bytesToNumberBE(res), left: data.subarray(len + 2) };
|
||||||
},
|
},
|
||||||
parseSig(data: Uint8Array): { r: bigint; s: bigint } {
|
parseSig(data: Uint8Array): { r: bigint; s: bigint } {
|
||||||
if (data.length < 2 || data[0] != 0x30) {
|
if (data.length < 2 || data[0] != 0x30) {
|
||||||
throw new DERError(`Invalid signature tag: ${ut.bytesToHex(data)}`);
|
throw new DERError(`Invalid signature tag: ${bytesToHex(data)}`);
|
||||||
}
|
}
|
||||||
if (data[1] !== data.length - 2) {
|
if (data[1] !== data.length - 2) {
|
||||||
throw new DERError('Invalid signature: incorrect length');
|
throw new DERError('Invalid signature: incorrect length');
|
||||||
@ -67,9 +85,7 @@ const DER = {
|
|||||||
const { data: r, left: sBytes } = DER.parseInt(data.subarray(2));
|
const { data: r, left: sBytes } = DER.parseInt(data.subarray(2));
|
||||||
const { data: s, left: rBytesLeft } = DER.parseInt(sBytes);
|
const { data: s, left: rBytesLeft } = DER.parseInt(sBytes);
|
||||||
if (rBytesLeft.length) {
|
if (rBytesLeft.length) {
|
||||||
throw new DERError(
|
throw new DERError(`Invalid signature: left bytes after parsing: ${bytesToHex(rBytesLeft)}`);
|
||||||
`Invalid signature: left bytes after parsing: ${ut.bytesToHex(rBytesLeft)}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return { r, s };
|
return { r, s };
|
||||||
},
|
},
|
||||||
@ -138,7 +154,7 @@ export type CurvePointsType<T> = BasicCurve<T> & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function validatePointOpts<T>(curve: CurvePointsType<T>) {
|
function validatePointOpts<T>(curve: CurvePointsType<T>) {
|
||||||
const opts = ut.validateOpts(curve);
|
const opts = validateBasicCurveOpts(curve);
|
||||||
const Fp = opts.Fp;
|
const Fp = opts.Fp;
|
||||||
for (const i of ['a', 'b'] as const) {
|
for (const i of ['a', 'b'] as const) {
|
||||||
if (!Fp.isValid(curve[i]))
|
if (!Fp.isValid(curve[i]))
|
||||||
@ -216,10 +232,10 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
} else if (typeof key === 'string') {
|
} else if (typeof key === 'string') {
|
||||||
if (key.length !== 2 * groupLen) throw new Error(`must be ${groupLen} bytes`);
|
if (key.length !== 2 * groupLen) throw new Error(`must be ${groupLen} bytes`);
|
||||||
// Validates individual octets
|
// Validates individual octets
|
||||||
num = ut.hexToNumber(key);
|
num = bytesToNumberBE(ensureBytes(key));
|
||||||
} else if (key instanceof Uint8Array) {
|
} else if (key instanceof Uint8Array) {
|
||||||
if (key.length !== groupLen) throw new Error(`must be ${groupLen} bytes`);
|
if (key.length !== groupLen) throw new Error(`must be ${groupLen} bytes`);
|
||||||
num = ut.bytesToNumberBE(key);
|
num = bytesToNumberBE(key);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('private key must be bytes, hex or bigint, not ' + typeof key);
|
throw new Error('private key must be bytes, hex or bigint, not ' + typeof key);
|
||||||
}
|
}
|
||||||
@ -281,7 +297,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
* @param hex short/long ECDSA hex
|
* @param hex short/long ECDSA hex
|
||||||
*/
|
*/
|
||||||
static fromHex(hex: Hex): ProjectivePoint {
|
static fromHex(hex: Hex): ProjectivePoint {
|
||||||
const P = ProjectivePoint.fromAffine(CURVE.fromBytes(ut.ensureBytes(hex)));
|
const P = ProjectivePoint.fromAffine(CURVE.fromBytes(ensureBytes(hex)));
|
||||||
P.assertValidity();
|
P.assertValidity();
|
||||||
return P;
|
return P;
|
||||||
}
|
}
|
||||||
@ -565,7 +581,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toHex(isCompressed = true): string {
|
toHex(isCompressed = true): string {
|
||||||
return ut.bytesToHex(this.toRawBytes(isCompressed));
|
return bytesToHex(this.toRawBytes(isCompressed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const _bits = CURVE.nBitLength;
|
const _bits = CURVE.nBitLength;
|
||||||
@ -608,7 +624,7 @@ export type CurveType = BasicCurve<bigint> & {
|
|||||||
// Default options
|
// Default options
|
||||||
lowS?: boolean;
|
lowS?: boolean;
|
||||||
// Hashes
|
// Hashes
|
||||||
hash: ut.CHash; // Because we need outputLen for DRBG
|
hash: CHash; // Because we need outputLen for DRBG
|
||||||
hmac: HmacFnSync;
|
hmac: HmacFnSync;
|
||||||
randomBytes: (bytesLength?: number) => Uint8Array;
|
randomBytes: (bytesLength?: number) => Uint8Array;
|
||||||
// truncateHash?: (hash: Uint8Array, truncateOnly?: boolean) => Uint8Array;
|
// truncateHash?: (hash: Uint8Array, truncateOnly?: boolean) => Uint8Array;
|
||||||
@ -617,8 +633,8 @@ export type CurveType = BasicCurve<bigint> & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function validateOpts(curve: CurveType) {
|
function validateOpts(curve: CurveType) {
|
||||||
const opts = ut.validateOpts(curve);
|
const opts = validateBasicCurveOpts(curve);
|
||||||
if (typeof opts.hash !== 'function' || !ut.isPositiveInt(opts.hash.outputLen))
|
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
|
||||||
throw new Error('Invalid hash function');
|
throw new Error('Invalid hash function');
|
||||||
if (typeof opts.hmac !== 'function') throw new Error('Invalid hmac function');
|
if (typeof opts.hmac !== 'function') throw new Error('Invalid hmac function');
|
||||||
if (typeof opts.randomBytes !== 'function') throw new Error('Invalid randomBytes function');
|
if (typeof opts.randomBytes !== 'function') throw new Error('Invalid randomBytes function');
|
||||||
@ -683,7 +699,7 @@ function hmacDrbg<T>(
|
|||||||
out.push(sl);
|
out.push(sl);
|
||||||
len += v.length;
|
len += v.length;
|
||||||
}
|
}
|
||||||
return ut.concatBytes(...out);
|
return concatBytes(...out);
|
||||||
};
|
};
|
||||||
const genUntil = (seed: Uint8Array, pred: Pred<T>): T => {
|
const genUntil = (seed: Uint8Array, pred: Pred<T>): T => {
|
||||||
reset();
|
reset();
|
||||||
@ -716,7 +732,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
toBytes(c, point, isCompressed: boolean): Uint8Array {
|
toBytes(c, point, isCompressed: boolean): Uint8Array {
|
||||||
const a = point.toAffine();
|
const a = point.toAffine();
|
||||||
const x = Fp.toBytes(a.x);
|
const x = Fp.toBytes(a.x);
|
||||||
const cat = ut.concatBytes;
|
const cat = concatBytes;
|
||||||
if (isCompressed) {
|
if (isCompressed) {
|
||||||
// TODO: hasEvenY
|
// TODO: hasEvenY
|
||||||
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
||||||
@ -730,7 +746,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
const tail = bytes.subarray(1);
|
const tail = bytes.subarray(1);
|
||||||
// this.assertValidity() is done inside of fromHex
|
// this.assertValidity() is done inside of fromHex
|
||||||
if (len === compressedLen && (head === 0x02 || head === 0x03)) {
|
if (len === compressedLen && (head === 0x02 || head === 0x03)) {
|
||||||
const x = ut.bytesToNumberBE(tail);
|
const x = bytesToNumberBE(tail);
|
||||||
if (!isValidFieldElement(x)) throw new Error('Point is not on curve');
|
if (!isValidFieldElement(x)) throw new Error('Point is not on curve');
|
||||||
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
||||||
let y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
let y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
||||||
@ -751,7 +767,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const numToNByteStr = (num: bigint): string =>
|
const numToNByteStr = (num: bigint): string =>
|
||||||
ut.bytesToHex(ut.numberToBytesBE(num, CURVE.nByteLength));
|
bytesToHex(numberToBytesBE(num, CURVE.nByteLength));
|
||||||
|
|
||||||
function isBiggerThanHalfOrder(number: bigint) {
|
function isBiggerThanHalfOrder(number: bigint) {
|
||||||
const HALF = CURVE_ORDER >> _1n;
|
const HALF = CURVE_ORDER >> _1n;
|
||||||
@ -762,7 +778,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
return isBiggerThanHalfOrder(s) ? mod.mod(-s, CURVE_ORDER) : s;
|
return isBiggerThanHalfOrder(s) ? mod.mod(-s, CURVE_ORDER) : s;
|
||||||
}
|
}
|
||||||
// slice bytes num
|
// slice bytes num
|
||||||
const slcNum = (b: Uint8Array, from: number, to: number) => ut.bytesToNumberBE(b.slice(from, to));
|
const slcNum = (b: Uint8Array, from: number, to: number) => bytesToNumberBE(b.slice(from, to));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ECDSA signature with its (r, s) properties. Supports DER & compact representations.
|
* ECDSA signature with its (r, s) properties. Supports DER & compact representations.
|
||||||
@ -775,7 +791,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
// pair (bytes of r, bytes of s)
|
// pair (bytes of r, bytes of s)
|
||||||
static fromCompact(hex: Hex) {
|
static fromCompact(hex: Hex) {
|
||||||
const gl = CURVE.nByteLength;
|
const gl = CURVE.nByteLength;
|
||||||
hex = ut.ensureBytes(hex, gl * 2);
|
hex = ensureBytes(hex, gl * 2);
|
||||||
return new Signature(slcNum(hex, 0, gl), slcNum(hex, gl, 2 * gl));
|
return new Signature(slcNum(hex, 0, gl), slcNum(hex, gl, 2 * gl));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -785,7 +801,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
const arr = hex instanceof Uint8Array;
|
const arr = hex instanceof Uint8Array;
|
||||||
if (typeof hex !== 'string' && !arr)
|
if (typeof hex !== 'string' && !arr)
|
||||||
throw new Error(`Signature.fromDER: Expected string or Uint8Array`);
|
throw new Error(`Signature.fromDER: Expected string or Uint8Array`);
|
||||||
const { r, s } = DER.parseSig(arr ? hex : ut.hexToBytes(hex));
|
const { r, s } = DER.parseSig(ensureBytes(hex));
|
||||||
return new Signature(r, s);
|
return new Signature(r, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -802,7 +818,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
recoverPublicKey(msgHash: Hex): typeof Point.BASE {
|
recoverPublicKey(msgHash: Hex): typeof Point.BASE {
|
||||||
const { n: N } = CURVE; // ECDSA public key recovery secg.org/sec1-v2.pdf 4.1.6
|
const { n: N } = CURVE; // ECDSA public key recovery secg.org/sec1-v2.pdf 4.1.6
|
||||||
const { r, s, recovery: rec } = this;
|
const { r, s, recovery: rec } = this;
|
||||||
const h = bits2int_modN(ut.ensureBytes(msgHash)); // Truncate hash
|
const h = bits2int_modN(ensureBytes(msgHash)); // Truncate hash
|
||||||
if (rec == null || ![0, 1, 2, 3].includes(rec)) throw new Error('recovery id invalid');
|
if (rec == null || ![0, 1, 2, 3].includes(rec)) throw new Error('recovery id invalid');
|
||||||
const radj = rec === 2 || rec === 3 ? r + N : r;
|
const radj = rec === 2 || rec === 3 ? r + N : r;
|
||||||
if (radj >= Fp.ORDER) throw new Error('recovery id 2 or 3 invalid');
|
if (radj >= Fp.ORDER) throw new Error('recovery id 2 or 3 invalid');
|
||||||
@ -831,10 +847,10 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
|
|
||||||
// DER-encoded
|
// DER-encoded
|
||||||
toDERRawBytes() {
|
toDERRawBytes() {
|
||||||
return ut.hexToBytes(this.toDERHex());
|
return hexToBytes(this.toDERHex());
|
||||||
}
|
}
|
||||||
toDERHex() {
|
toDERHex() {
|
||||||
const { numberToHexUnpadded: toHex } = ut;
|
const toHex = numberToHexUnpadded;
|
||||||
const sHex = DER.slice(toHex(this.s));
|
const sHex = DER.slice(toHex(this.s));
|
||||||
const rHex = DER.slice(toHex(this.r));
|
const rHex = DER.slice(toHex(this.r));
|
||||||
const sHexL = sHex.length / 2;
|
const sHexL = sHex.length / 2;
|
||||||
@ -847,9 +863,15 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
|
|
||||||
// padded bytes of r, then padded bytes of s
|
// padded bytes of r, then padded bytes of s
|
||||||
toCompactRawBytes() {
|
toCompactRawBytes() {
|
||||||
return ut.hexToBytes(this.toCompactHex());
|
// const l = CURVE.nByteLength;
|
||||||
|
// const a = new Uint8Array(2 * l);
|
||||||
|
// a.set(numberToBytesBE(this.r, l), 0);
|
||||||
|
// a.set(numberToBytesBE(this.s, l), l);
|
||||||
|
// return a;
|
||||||
|
return hexToBytes(this.toCompactHex());
|
||||||
}
|
}
|
||||||
toCompactHex() {
|
toCompactHex() {
|
||||||
|
// return bytesToHex(this.toCompactRawBytes());
|
||||||
return numToNByteStr(this.r) + numToNByteStr(this.s);
|
return numToNByteStr(this.r) + numToNByteStr(this.s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -869,7 +891,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
* Converts some bytes to a valid private key. Needs at least (nBitLength+64) bytes.
|
* Converts some bytes to a valid private key. Needs at least (nBitLength+64) bytes.
|
||||||
*/
|
*/
|
||||||
hashToPrivateKey: (hash: Hex): Uint8Array =>
|
hashToPrivateKey: (hash: Hex): Uint8Array =>
|
||||||
ut.numberToBytesBE(ut.hashToPrivateScalar(hash, CURVE_ORDER), CURVE.nByteLength),
|
numberToBytesBE(mod.hashToPrivateScalar(hash, CURVE_ORDER), CURVE.nByteLength),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces cryptographically secure private key from random of size (nBitLength+64)
|
* Produces cryptographically secure private key from random of size (nBitLength+64)
|
||||||
@ -941,7 +963,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
||||||
// for some cases, since bytes.length * 8 is not actual bitLength.
|
// for some cases, since bytes.length * 8 is not actual bitLength.
|
||||||
const delta = bytes.length * 8 - CURVE.nBitLength; // truncate to nBitLength leftmost bits
|
const delta = bytes.length * 8 - CURVE.nBitLength; // truncate to nBitLength leftmost bits
|
||||||
const num = ut.bytesToNumberBE(bytes); // check for == u8 done here
|
const num = bytesToNumberBE(bytes); // check for == u8 done here
|
||||||
return delta > 0 ? num >> BigInt(delta) : num;
|
return delta > 0 ? num >> BigInt(delta) : num;
|
||||||
};
|
};
|
||||||
const bits2int_modN =
|
const bits2int_modN =
|
||||||
@ -950,13 +972,13 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
return mod.mod(bits2int(bytes), CURVE_ORDER); // can't use bytesToNumberBE here
|
return mod.mod(bits2int(bytes), CURVE_ORDER); // can't use bytesToNumberBE here
|
||||||
};
|
};
|
||||||
// NOTE: pads output with zero as per spec
|
// NOTE: pads output with zero as per spec
|
||||||
const ORDER_MASK = ut.bitMask(CURVE.nBitLength);
|
const ORDER_MASK = bitMask(CURVE.nBitLength);
|
||||||
function int2octets(num: bigint): Uint8Array {
|
function int2octets(num: bigint): Uint8Array {
|
||||||
if (typeof num !== 'bigint') throw new Error('Expected bigint');
|
if (typeof num !== 'bigint') throw new Error('Expected bigint');
|
||||||
if (!(_0n <= num && num < ORDER_MASK))
|
if (!(_0n <= num && num < ORDER_MASK))
|
||||||
throw new Error(`Expected number < 2^${CURVE.nBitLength}`);
|
throw new Error(`Expected number < 2^${CURVE.nBitLength}`);
|
||||||
// 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 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.
|
||||||
@ -969,7 +991,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
// Ban legacy options
|
// Ban legacy options
|
||||||
throw new Error('sign() legacy options not supported');
|
throw new Error('sign() legacy options not supported');
|
||||||
let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
|
let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
|
||||||
if (prehash) msgHash = CURVE.hash(ut.ensureBytes(msgHash));
|
if (prehash) msgHash = CURVE.hash(ensureBytes(msgHash));
|
||||||
if (lowS == null) lowS = true; // RFC6979 3.2: we skip step A, because
|
if (lowS == null) lowS = true; // RFC6979 3.2: we skip step A, because
|
||||||
// Step A is ignored, since we already provide hash instead of msg
|
// Step A is ignored, since we already provide hash instead of msg
|
||||||
|
|
||||||
@ -977,8 +999,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
// custom truncation for stark. For other curves it is essentially same as calling bits2int + mod
|
// custom truncation for stark. For other curves it is essentially same as calling bits2int + mod
|
||||||
// However, we cannot later call bits2octets (which is truncateHash + int2octets), since nested bits2int is broken
|
// However, we cannot later call bits2octets (which is truncateHash + int2octets), since nested bits2int is broken
|
||||||
// for curves where nBitLength % 8 !== 0, so we unwrap it here as int2octets call.
|
// for curves where nBitLength % 8 !== 0, so we unwrap it here as int2octets call.
|
||||||
// const bits2octets = (bits)=>int2octets(ut.bytesToNumberBE(truncateHash(bits)))
|
// const bits2octets = (bits)=>int2octets(bytesToNumberBE(truncateHash(bits)))
|
||||||
const h1int = bits2int_modN(ut.ensureBytes(msgHash));
|
const h1int = bits2int_modN(ensureBytes(msgHash));
|
||||||
const h1octets = int2octets(h1int);
|
const h1octets = int2octets(h1int);
|
||||||
|
|
||||||
const d = normalizePrivateKey(privateKey);
|
const d = normalizePrivateKey(privateKey);
|
||||||
@ -987,14 +1009,14 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
if (ent != null) {
|
if (ent != null) {
|
||||||
// RFC6979 3.6: additional k' (optional)
|
// RFC6979 3.6: additional k' (optional)
|
||||||
if (ent === true) ent = CURVE.randomBytes(Fp.BYTES);
|
if (ent === true) ent = CURVE.randomBytes(Fp.BYTES);
|
||||||
const e = ut.ensureBytes(ent);
|
const e = ensureBytes(ent);
|
||||||
if (e.length !== Fp.BYTES) throw new Error(`sign: Expected ${Fp.BYTES} bytes of extra data`);
|
if (e.length !== Fp.BYTES) throw new Error(`sign: Expected ${Fp.BYTES} bytes of extra data`);
|
||||||
seedArgs.push(e);
|
seedArgs.push(e);
|
||||||
}
|
}
|
||||||
// seed is constructed from private key and message
|
// seed is constructed from private key and message
|
||||||
// Step D
|
// Step D
|
||||||
// V, 0x00 are done in HmacDRBG constructor.
|
// V, 0x00 are done in HmacDRBG constructor.
|
||||||
const seed = ut.concatBytes(...seedArgs);
|
const seed = concatBytes(...seedArgs);
|
||||||
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
||||||
// Converts signature params into point w r/s, checks result for validity.
|
// Converts signature params into point w r/s, checks result for validity.
|
||||||
function k2sig(kBytes: Uint8Array): Signature | undefined {
|
function k2sig(kBytes: Uint8Array): Signature | undefined {
|
||||||
@ -1080,7 +1102,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
_sig = Signature.fromCompact(signature as Hex);
|
_sig = Signature.fromCompact(signature as Hex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
msgHash = ut.ensureBytes(msgHash);
|
msgHash = ensureBytes(msgHash);
|
||||||
P = Point.fromHex(publicKey);
|
P = Point.fromHex(publicKey);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { weierstrass } from './abstract/weierstrass.js';
|
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
|
import { weierstrass } from './abstract/weierstrass.js';
|
||||||
import { getHash } from './_shortw_utils.js';
|
import { getHash } from './_shortw_utils.js';
|
||||||
import { Fp } from './abstract/modular.js';
|
import { Fp } from './abstract/modular.js';
|
||||||
/**
|
/**
|
||||||
|
@ -6,7 +6,7 @@ import * as fc from 'fast-check';
|
|||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import zkVectors from './bls12-381/zkcrypto/converted.json' assert { type: 'json' };
|
import zkVectors from './bls12-381/zkcrypto/converted.json' assert { type: 'json' };
|
||||||
import pairingVectors from './bls12-381/go_pairing_vectors/pairing.json' assert { type: 'json' };
|
import pairingVectors from './bls12-381/go_pairing_vectors/pairing.json' assert { type: 'json' };
|
||||||
import { wNAF } from '../lib/esm/abstract/group.js';
|
import { wNAF } from '../lib/esm/abstract/curve.js';
|
||||||
const bls = bls12_381;
|
const bls = bls12_381;
|
||||||
const { Fp2 } = bls;
|
const { Fp2 } = bls;
|
||||||
const G1Point = bls.G1.ProjectivePoint;
|
const G1Point = bls.G1.ProjectivePoint;
|
||||||
@ -970,352 +970,6 @@ describe('pairing', () => {
|
|||||||
});
|
});
|
||||||
// hashToCurve
|
// hashToCurve
|
||||||
describe('hash-to-curve', () => {
|
describe('hash-to-curve', () => {
|
||||||
const DST = 'QUUX-V01-CS02-with-expander-SHA256-128';
|
|
||||||
const VECTORS = [
|
|
||||||
{
|
|
||||||
msg: '',
|
|
||||||
len: 0x20,
|
|
||||||
expected: '68a985b87eb6b46952128911f2a4412bbc302a9d759667f87f7a21d803f07235',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: 'abc',
|
|
||||||
len: 0x20,
|
|
||||||
expected: 'd8ccab23b5985ccea865c6c97b6e5b8350e794e603b4b97902f53a8a0d605615',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: 'abcdef0123456789',
|
|
||||||
len: 0x20,
|
|
||||||
expected: 'eff31487c770a893cfb36f912fbfcbff40d5661771ca4b2cb4eafe524333f5c1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg:
|
|
||||||
'q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' +
|
|
||||||
'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq',
|
|
||||||
len: 0x20,
|
|
||||||
expected: 'b23a1d2b4d97b2ef7785562a7e8bac7eed54ed6e97e29aa51bfe3f12ddad1ff9',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg:
|
|
||||||
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
|
||||||
len: 0x20,
|
|
||||||
expected: '4623227bcc01293b8c130bf771da8c298dede7383243dc0993d2d94823958c4c',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: '',
|
|
||||||
len: 0x80,
|
|
||||||
expected:
|
|
||||||
'af84c27ccfd45d41914fdff5df25293e221afc53d8ad2ac0' +
|
|
||||||
'6d5e3e29485dadbee0d121587713a3e0dd4d5e69e93eb7cd4f5df4' +
|
|
||||||
'cd103e188cf60cb02edc3edf18eda8576c412b18ffb658e3dd6ec8' +
|
|
||||||
'49469b979d444cf7b26911a08e63cf31f9dcc541708d3491184472' +
|
|
||||||
'c2c29bb749d4286b004ceb5ee6b9a7fa5b646c993f0ced',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: 'abc',
|
|
||||||
len: 0x80,
|
|
||||||
expected:
|
|
||||||
'abba86a6129e366fc877aab32fc4ffc70120d8996c88aee2' +
|
|
||||||
'fe4b32d6c7b6437a647e6c3163d40b76a73cf6a5674ef1d890f95b' +
|
|
||||||
'664ee0afa5359a5c4e07985635bbecbac65d747d3d2da7ec2b8221' +
|
|
||||||
'b17b0ca9dc8a1ac1c07ea6a1e60583e2cb00058e77b7b72a298425' +
|
|
||||||
'cd1b941ad4ec65e8afc50303a22c0f99b0509b4c895f40',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: 'abcdef0123456789',
|
|
||||||
len: 0x80,
|
|
||||||
expected:
|
|
||||||
'ef904a29bffc4cf9ee82832451c946ac3c8f8058ae97d8d6' +
|
|
||||||
'29831a74c6572bd9ebd0df635cd1f208e2038e760c4994984ce73f' +
|
|
||||||
'0d55ea9f22af83ba4734569d4bc95e18350f740c07eef653cbb9f8' +
|
|
||||||
'7910d833751825f0ebefa1abe5420bb52be14cf489b37fe1a72f7d' +
|
|
||||||
'e2d10be453b2c9d9eb20c7e3f6edc5a60629178d9478df',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg:
|
|
||||||
'q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' +
|
|
||||||
'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq',
|
|
||||||
len: 0x80,
|
|
||||||
expected:
|
|
||||||
'80be107d0884f0d881bb460322f0443d38bd222db8bd0b0a' +
|
|
||||||
'5312a6fedb49c1bbd88fd75d8b9a09486c60123dfa1d73c1cc3169' +
|
|
||||||
'761b17476d3c6b7cbbd727acd0e2c942f4dd96ae3da5de368d26b3' +
|
|
||||||
'2286e32de7e5a8cb2949f866a0b80c58116b29fa7fabb3ea7d520e' +
|
|
||||||
'e603e0c25bcaf0b9a5e92ec6a1fe4e0391d1cdbce8c68a',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg:
|
|
||||||
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
|
||||||
len: 0x80,
|
|
||||||
expected:
|
|
||||||
'546aff5444b5b79aa6148bd81728704c32decb73a3ba76e9' +
|
|
||||||
'e75885cad9def1d06d6792f8a7d12794e90efed817d96920d72889' +
|
|
||||||
'6a4510864370c207f99bd4a608ea121700ef01ed879745ee3e4cee' +
|
|
||||||
'f777eda6d9e5e38b90c86ea6fb0b36504ba4a45d22e86f6db5dd43' +
|
|
||||||
'd98a294bebb9125d5b794e9d2a81181066eb954966a487',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
for (let i = 0; i < VECTORS.length; i++) {
|
|
||||||
const t = VECTORS[i];
|
|
||||||
should(`hash_to_field/expand_message_xmd(SHA-256) (${i})`, () => {
|
|
||||||
const p = bls.utils.expandMessageXMD(
|
|
||||||
bls.utils.stringToBytes(t.msg),
|
|
||||||
bls.utils.stringToBytes(DST),
|
|
||||||
t.len
|
|
||||||
);
|
|
||||||
deepStrictEqual(bls.utils.bytesToHex(p), t.expected);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const LONG_DST =
|
|
||||||
'QUUX-V01-CS02-with-expander-SHA256-128-long-DST-111111' +
|
|
||||||
'111111111111111111111111111111111111111111111111111111' +
|
|
||||||
'111111111111111111111111111111111111111111111111111111' +
|
|
||||||
'111111111111111111111111111111111111111111111111111111' +
|
|
||||||
'1111111111111111111111111111111111111111';
|
|
||||||
const VECTORS_BIG = [
|
|
||||||
{
|
|
||||||
msg: '',
|
|
||||||
len: 0x20,
|
|
||||||
expected: 'e8dc0c8b686b7ef2074086fbdd2f30e3f8bfbd3bdf177f73f04b97ce618a3ed3',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: 'abc',
|
|
||||||
len: 0x20,
|
|
||||||
expected: '52dbf4f36cf560fca57dedec2ad924ee9c266341d8f3d6afe5171733b16bbb12',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: 'abcdef0123456789',
|
|
||||||
len: 0x20,
|
|
||||||
expected: '35387dcf22618f3728e6c686490f8b431f76550b0b2c61cbc1ce7001536f4521',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg:
|
|
||||||
'q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' +
|
|
||||||
'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq',
|
|
||||||
len: 0x20,
|
|
||||||
expected: '01b637612bb18e840028be900a833a74414140dde0c4754c198532c3a0ba42bc',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg:
|
|
||||||
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
|
||||||
len: 0x20,
|
|
||||||
expected: '20cce7033cabc5460743180be6fa8aac5a103f56d481cf369a8accc0c374431b',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: '',
|
|
||||||
len: 0x80,
|
|
||||||
expected:
|
|
||||||
'14604d85432c68b757e485c8894db3117992fc57e0e136f7' +
|
|
||||||
'1ad987f789a0abc287c47876978e2388a02af86b1e8d1342e5ce4f' +
|
|
||||||
'7aaa07a87321e691f6fba7e0072eecc1218aebb89fb14a0662322d' +
|
|
||||||
'5edbd873f0eb35260145cd4e64f748c5dfe60567e126604bcab1a3' +
|
|
||||||
'ee2dc0778102ae8a5cfd1429ebc0fa6bf1a53c36f55dfc',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: 'abc',
|
|
||||||
len: 0x80,
|
|
||||||
expected:
|
|
||||||
'1a30a5e36fbdb87077552b9d18b9f0aee16e80181d5b951d' +
|
|
||||||
'0471d55b66684914aef87dbb3626eaabf5ded8cd0686567e503853' +
|
|
||||||
'e5c84c259ba0efc37f71c839da2129fe81afdaec7fbdc0ccd4c794' +
|
|
||||||
'727a17c0d20ff0ea55e1389d6982d1241cb8d165762dbc39fb0cee' +
|
|
||||||
'4474d2cbbd468a835ae5b2f20e4f959f56ab24cd6fe267',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: 'abcdef0123456789',
|
|
||||||
len: 0x80,
|
|
||||||
expected:
|
|
||||||
'd2ecef3635d2397f34a9f86438d772db19ffe9924e28a1ca' +
|
|
||||||
'f6f1c8f15603d4028f40891044e5c7e39ebb9b31339979ff33a424' +
|
|
||||||
'9206f67d4a1e7c765410bcd249ad78d407e303675918f20f26ce6d' +
|
|
||||||
'7027ed3774512ef5b00d816e51bfcc96c3539601fa48ef1c07e494' +
|
|
||||||
'bdc37054ba96ecb9dbd666417e3de289d4f424f502a982',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg:
|
|
||||||
'q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' +
|
|
||||||
'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq',
|
|
||||||
len: 0x80,
|
|
||||||
expected:
|
|
||||||
'ed6e8c036df90111410431431a232d41a32c86e296c05d42' +
|
|
||||||
'6e5f44e75b9a50d335b2412bc6c91e0a6dc131de09c43110d9180d' +
|
|
||||||
'0a70f0d6289cb4e43b05f7ee5e9b3f42a1fad0f31bac6a625b3b5c' +
|
|
||||||
'50e3a83316783b649e5ecc9d3b1d9471cb5024b7ccf40d41d1751a' +
|
|
||||||
'04ca0356548bc6e703fca02ab521b505e8e45600508d32',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg:
|
|
||||||
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
|
||||||
len: 0x80,
|
|
||||||
expected:
|
|
||||||
'78b53f2413f3c688f07732c10e5ced29a17c6a16f717179f' +
|
|
||||||
'fbe38d92d6c9ec296502eb9889af83a1928cd162e845b0d3c5424e' +
|
|
||||||
'83280fed3d10cffb2f8431f14e7a23f4c68819d40617589e4c4116' +
|
|
||||||
'9d0b56e0e3535be1fd71fbb08bb70c5b5ffed953d6c14bf7618b35' +
|
|
||||||
'fc1f4c4b30538236b4b08c9fbf90462447a8ada60be495',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
for (let i = 0; i < VECTORS_BIG.length; i++) {
|
|
||||||
const t = VECTORS_BIG[i];
|
|
||||||
should(`hash_to_field/expand_message_xmd(SHA-256) (long DST) (${i})`, () => {
|
|
||||||
const p = bls.utils.expandMessageXMD(
|
|
||||||
bls.utils.stringToBytes(t.msg),
|
|
||||||
bls.utils.stringToBytes(LONG_DST),
|
|
||||||
t.len
|
|
||||||
);
|
|
||||||
deepStrictEqual(bls.utils.bytesToHex(p), t.expected);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const DST_512 = 'QUUX-V01-CS02-with-expander-SHA512-256';
|
|
||||||
const VECTORS_SHA512 = [
|
|
||||||
{
|
|
||||||
msg: '',
|
|
||||||
len: 0x20,
|
|
||||||
expected: '6b9a7312411d92f921c6f68ca0b6380730a1a4d982c507211a90964c394179ba',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: 'abc',
|
|
||||||
len: 0x20,
|
|
||||||
expected: '0da749f12fbe5483eb066a5f595055679b976e93abe9be6f0f6318bce7aca8dc',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: 'abcdef0123456789',
|
|
||||||
len: 0x20,
|
|
||||||
expected: '087e45a86e2939ee8b91100af1583c4938e0f5fc6c9db4b107b83346bc967f58',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg:
|
|
||||||
'q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' +
|
|
||||||
'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq',
|
|
||||||
len: 0x20,
|
|
||||||
expected: '7336234ee9983902440f6bc35b348352013becd88938d2afec44311caf8356b3',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg:
|
|
||||||
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
|
||||||
len: 0x20,
|
|
||||||
expected: '57b5f7e766d5be68a6bfe1768e3c2b7f1228b3e4b3134956dd73a59b954c66f4',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: '',
|
|
||||||
len: 0x80,
|
|
||||||
expected:
|
|
||||||
'41b037d1734a5f8df225dd8c7de38f851efdb45c372887be' +
|
|
||||||
'655212d07251b921b052b62eaed99b46f72f2ef4cc96bfaf254ebb' +
|
|
||||||
'bec091e1a3b9e4fb5e5b619d2e0c5414800a1d882b62bb5cd1778f' +
|
|
||||||
'098b8eb6cb399d5d9d18f5d5842cf5d13d7eb00a7cff859b605da6' +
|
|
||||||
'78b318bd0e65ebff70bec88c753b159a805d2c89c55961',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: 'abc',
|
|
||||||
len: 0x80,
|
|
||||||
expected:
|
|
||||||
'7f1dddd13c08b543f2e2037b14cefb255b44c83cc397c178' +
|
|
||||||
'6d975653e36a6b11bdd7732d8b38adb4a0edc26a0cef4bb4521713' +
|
|
||||||
'5456e58fbca1703cd6032cb1347ee720b87972d63fbf232587043e' +
|
|
||||||
'd2901bce7f22610c0419751c065922b488431851041310ad659e4b' +
|
|
||||||
'23520e1772ab29dcdeb2002222a363f0c2b1c972b3efe1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: 'abcdef0123456789',
|
|
||||||
len: 0x80,
|
|
||||||
expected:
|
|
||||||
'3f721f208e6199fe903545abc26c837ce59ac6fa45733f1b' +
|
|
||||||
'aaf0222f8b7acb0424814fcb5eecf6c1d38f06e9d0a6ccfbf85ae6' +
|
|
||||||
'12ab8735dfdf9ce84c372a77c8f9e1c1e952c3a61b7567dd069301' +
|
|
||||||
'6af51d2745822663d0c2367e3f4f0bed827feecc2aaf98c949b5ed' +
|
|
||||||
'0d35c3f1023d64ad1407924288d366ea159f46287e61ac',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg:
|
|
||||||
'q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' +
|
|
||||||
'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq',
|
|
||||||
len: 0x80,
|
|
||||||
expected:
|
|
||||||
'b799b045a58c8d2b4334cf54b78260b45eec544f9f2fb5bd' +
|
|
||||||
'12fb603eaee70db7317bf807c406e26373922b7b8920fa29142703' +
|
|
||||||
'dd52bdf280084fb7ef69da78afdf80b3586395b433dc66cde048a2' +
|
|
||||||
'58e476a561e9deba7060af40adf30c64249ca7ddea79806ee5beb9' +
|
|
||||||
'a1422949471d267b21bc88e688e4014087a0b592b695ed',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg:
|
|
||||||
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
|
||||||
len: 0x80,
|
|
||||||
expected:
|
|
||||||
'05b0bfef265dcee87654372777b7c44177e2ae4c13a27f10' +
|
|
||||||
'3340d9cd11c86cb2426ffcad5bd964080c2aee97f03be1ca18e30a' +
|
|
||||||
'1f14e27bc11ebbd650f305269cc9fb1db08bf90bfc79b42a952b46' +
|
|
||||||
'daf810359e7bc36452684784a64952c343c52e5124cd1f71d474d5' +
|
|
||||||
'197fefc571a92929c9084ffe1112cf5eea5192ebff330b',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
for (let i = 0; i < VECTORS_SHA512.length; i++) {
|
|
||||||
const t = VECTORS_SHA512[i];
|
|
||||||
should(`hash_to_field/expand_message_xmd(SHA-256) (long DST) (${i})`, () => {
|
|
||||||
const p = bls.utils.expandMessageXMD(
|
|
||||||
bls.utils.stringToBytes(t.msg),
|
|
||||||
bls.utils.stringToBytes(DST_512),
|
|
||||||
t.len,
|
|
||||||
sha512
|
|
||||||
);
|
|
||||||
deepStrictEqual(bls.utils.bytesToHex(p), t.expected);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Point G1
|
// Point G1
|
||||||
const VECTORS_G1 = [
|
const VECTORS_G1 = [
|
||||||
{
|
{
|
||||||
@ -1354,107 +1008,7 @@ describe('hash-to-curve', () => {
|
|||||||
deepStrictEqual(p.toHex(false), t.expected);
|
deepStrictEqual(p.toHex(false), t.expected);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const VECTORS_G1_RO = [
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes(''),
|
|
||||||
expected:
|
|
||||||
'052926add2207b76ca4fa57a8734416c8dc95e24501772c814278700eed6d1e4e8cf62d9c09db0fac349612b759e79a1' +
|
|
||||||
'08ba738453bfed09cb546dbb0783dbb3a5f1f566ed67bb6be0e8c67e2e81a4cc68ee29813bb7994998f3eae0c9c6a265',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes('abc'),
|
|
||||||
expected:
|
|
||||||
'03567bc5ef9c690c2ab2ecdf6a96ef1c139cc0b2f284dca0a9a7943388a49a3aee664ba5379a7655d3c68900be2f6903' +
|
|
||||||
'0b9c15f3fe6e5cf4211f346271d7b01c8f3b28be689c8429c85b67af215533311f0b8dfaaa154fa6b88176c229f2885d',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes('abcdef0123456789'),
|
|
||||||
expected:
|
|
||||||
'11e0b079dea29a68f0383ee94fed1b940995272407e3bb916bbf268c263ddd57a6a27200a784cbc248e84f357ce82d98' +
|
|
||||||
'03a87ae2caf14e8ee52e51fa2ed8eefe80f02457004ba4d486d6aa1f517c0889501dc7413753f9599b099ebcbbd2d709',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes(
|
|
||||||
'q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' +
|
|
||||||
'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq'
|
|
||||||
),
|
|
||||||
expected:
|
|
||||||
'15f68eaa693b95ccb85215dc65fa81038d69629f70aeee0d0f677cf22285e7bf58d7cb86eefe8f2e9bc3f8cb84fac488' +
|
|
||||||
'1807a1d50c29f430b8cafc4f8638dfeeadf51211e1602a5f184443076715f91bb90a48ba1e370edce6ae1062f5e6dd38',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes(
|
|
||||||
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
|
||||||
),
|
|
||||||
expected:
|
|
||||||
'082aabae8b7dedb0e78aeb619ad3bfd9277a2f77ba7fad20ef6aabdc6c31d19ba5a6d12283553294c1825c4b3ca2dcfe' +
|
|
||||||
'05b84ae5a942248eea39e1d91030458c40153f3b654ab7872d779ad1e942856a20c438e8d99bc8abfbf74729ce1f7ac8',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
for (let i = 0; i < VECTORS_G1_RO.length; i++) {
|
|
||||||
const t = VECTORS_G1_RO[i];
|
|
||||||
should(`hashToCurve/G1 (BLS12381G1_XMD:SHA-256_SSWU_RO_) (${i})`, () => {
|
|
||||||
const p = bls.hashToCurve.G1.hashToCurve(t.msg, {
|
|
||||||
DST: 'QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_RO_',
|
|
||||||
});
|
|
||||||
deepStrictEqual(p.toHex(false), t.expected);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const VECTORS_G1_NU = [
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes(''),
|
|
||||||
expected:
|
|
||||||
'184bb665c37ff561a89ec2122dd343f20e0f4cbcaec84e3c3052ea81d1834e192c426074b02ed3dca4e7676ce4ce48ba' +
|
|
||||||
'04407b8d35af4dacc809927071fc0405218f1401a6d15af775810e4e460064bcc9468beeba82fdc751be70476c888bf3',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes('abc'),
|
|
||||||
expected:
|
|
||||||
'009769f3ab59bfd551d53a5f846b9984c59b97d6842b20a2c565baa167945e3d026a3755b6345df8ec7e6acb6868ae6d' +
|
|
||||||
'1532c00cf61aa3d0ce3e5aa20c3b531a2abd2c770a790a2613818303c6b830ffc0ecf6c357af3317b9575c567f11cd2c',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes('abcdef0123456789'),
|
|
||||||
expected:
|
|
||||||
'1974dbb8e6b5d20b84df7e625e2fbfecb2cdb5f77d5eae5fb2955e5ce7313cae8364bc2fff520a6c25619739c6bdcb6a' +
|
|
||||||
'15f9897e11c6441eaa676de141c8d83c37aab8667173cbe1dfd6de74d11861b961dccebcd9d289ac633455dfcc7013a3',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes(
|
|
||||||
'q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' +
|
|
||||||
'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq'
|
|
||||||
),
|
|
||||||
expected:
|
|
||||||
'0a7a047c4a8397b3446450642c2ac64d7239b61872c9ae7a59707a8f4f950f101e766afe58223b3bff3a19a7f754027c' +
|
|
||||||
'1383aebba1e4327ccff7cf9912bda0dbc77de048b71ef8c8a81111d71dc33c5e3aa6edee9cf6f5fe525d50cc50b77cc9',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes(
|
|
||||||
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
|
||||||
),
|
|
||||||
expected:
|
|
||||||
'0e7a16a975904f131682edbb03d9560d3e48214c9986bd50417a77108d13dc957500edf96462a3d01e62dc6cd468ef11' +
|
|
||||||
'0ae89e677711d05c30a48d6d75e76ca9fb70fe06c6dd6ff988683d89ccde29ac7d46c53bb97a59b1901abf1db66052db',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
for (let i = 0; i < VECTORS_G1_NU.length; i++) {
|
|
||||||
const t = VECTORS_G1_NU[i];
|
|
||||||
should(`hashToCurve/G1 (BLS12381G1_XMD:SHA-256_SSWU_NU_) (${i})`, () => {
|
|
||||||
const p = bls.hashToCurve.G1.encodeToCurve(t.msg, {
|
|
||||||
DST: 'QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_NU_',
|
|
||||||
});
|
|
||||||
deepStrictEqual(p.toHex(false), t.expected);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const VECTORS_ENCODE_G1 = [
|
const VECTORS_ENCODE_G1 = [
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes(''),
|
msg: bls.utils.stringToBytes(''),
|
||||||
@ -1538,136 +1092,7 @@ describe('hash-to-curve', () => {
|
|||||||
deepStrictEqual(p.toHex(false), t.expected);
|
deepStrictEqual(p.toHex(false), t.expected);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const VECTORS_G2_RO = [
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes(''),
|
|
||||||
expected:
|
|
||||||
'05cb8437535e20ecffaef7752baddf98034139c38452458baeefab379ba13dff5bf5dd71b72418717047f5b0f37da03d' +
|
|
||||||
'0141ebfbdca40eb85b87142e130ab689c673cf60f1a3e98d69335266f30d9b8d4ac44c1038e9dcdd5393faf5c41fb78a' +
|
|
||||||
'12424ac32561493f3fe3c260708a12b7c620e7be00099a974e259ddc7d1f6395c3c811cdd19f1e8dbf3e9ecfdcbab8d6' +
|
|
||||||
'0503921d7f6a12805e72940b963c0cf3471c7b2a524950ca195d11062ee75ec076daf2d4bc358c4b190c0c98064fdd92',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes('abc'),
|
|
||||||
expected:
|
|
||||||
'139cddbccdc5e91b9623efd38c49f81a6f83f175e80b06fc374de9eb4b41dfe4ca3a230ed250fbe3a2acf73a41177fd8' +
|
|
||||||
'02c2d18e033b960562aae3cab37a27ce00d80ccd5ba4b7fe0e7a210245129dbec7780ccc7954725f4168aff2787776e6' +
|
|
||||||
'00aa65dae3c8d732d10ecd2c50f8a1baf3001578f71c694e03866e9f3d49ac1e1ce70dd94a733534f106d4cec0eddd16' +
|
|
||||||
'1787327b68159716a37440985269cf584bcb1e621d3a7202be6ea05c4cfe244aeb197642555a0645fb87bf7466b2ba48',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes('abcdef0123456789'),
|
|
||||||
expected:
|
|
||||||
'190d119345b94fbd15497bcba94ecf7db2cbfd1e1fe7da034d26cbba169fb3968288b3fafb265f9ebd380512a71c3f2c' +
|
|
||||||
'121982811d2491fde9ba7ed31ef9ca474f0e1501297f68c298e9f4c0028add35aea8bb83d53c08cfc007c1e005723cd0' +
|
|
||||||
'0bb5e7572275c567462d91807de765611490205a941a5a6af3b1691bfe596c31225d3aabdf15faff860cb4ef17c7c3be' +
|
|
||||||
'05571a0f8d3c08d094576981f4a3b8eda0a8e771fcdcc8ecceaf1356a6acf17574518acb506e435b639353c2e14827c8',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes(
|
|
||||||
'q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' +
|
|
||||||
'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq'
|
|
||||||
),
|
|
||||||
expected:
|
|
||||||
'0934aba516a52d8ae479939a91998299c76d39cc0c035cd18813bec433f587e2d7a4fef038260eef0cef4d02aae3eb91' +
|
|
||||||
'19a84dd7248a1066f737cc34502ee5555bd3c19f2ecdb3c7d9e24dc65d4e25e50d83f0f77105e955d78f4762d33c17da' +
|
|
||||||
'09bcccfa036b4847c9950780733633f13619994394c23ff0b32fa6b795844f4a0673e20282d07bc69641cee04f5e5662' +
|
|
||||||
'14f81cd421617428bc3b9fe25afbb751d934a00493524bc4e065635b0555084dd54679df1536101b2c979c0152d09192',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes(
|
|
||||||
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
|
||||||
),
|
|
||||||
expected:
|
|
||||||
'11fca2ff525572795a801eed17eb12785887c7b63fb77a42be46ce4a34131d71f7a73e95fee3f812aea3de78b4d01569' +
|
|
||||||
'01a6ba2f9a11fa5598b2d8ace0fbe0a0eacb65deceb476fbbcb64fd24557c2f4b18ecfc5663e54ae16a84f5ab7f62534' +
|
|
||||||
'03a47f8e6d1763ba0cad63d6114c0accbef65707825a511b251a660a9b3994249ae4e63fac38b23da0c398689ee2ab52' +
|
|
||||||
'0b6798718c8aed24bc19cb27f866f1c9effcdbf92397ad6448b5c9db90d2b9da6cbabf48adc1adf59a1a28344e79d57e',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
for (let i = 0; i < VECTORS_G2_RO.length; i++) {
|
|
||||||
const t = VECTORS_G2_RO[i];
|
|
||||||
should(`hashToCurve/G2 (BLS12381G2_XMD:SHA-256_SSWU_RO_) (${i})`, () => {
|
|
||||||
const p = bls.hashToCurve.G2.hashToCurve(t.msg, {
|
|
||||||
DST: 'QUUX-V01-CS02-with-BLS12381G2_XMD:SHA-256_SSWU_RO_',
|
|
||||||
});
|
|
||||||
deepStrictEqual(p.toHex(false), t.expected);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const VECTORS_G2_NU = [
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes(''),
|
|
||||||
expected:
|
|
||||||
'126b855e9e69b1f691f816e48ac6977664d24d99f8724868a184186469ddfd4617367e94527d4b74fc86413483afb35b' +
|
|
||||||
'00e7f4568a82b4b7dc1f14c6aaa055edf51502319c723c4dc2688c7fe5944c213f510328082396515734b6612c4e7bb7' +
|
|
||||||
'1498aadcf7ae2b345243e281ae076df6de84455d766ab6fcdaad71fab60abb2e8b980a440043cd305db09d283c895e3d' +
|
|
||||||
'0caead0fd7b6176c01436833c79d305c78be307da5f6af6c133c47311def6ff1e0babf57a0fb5539fce7ee12407b0a42',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes('abc'),
|
|
||||||
expected:
|
|
||||||
'0296238ea82c6d4adb3c838ee3cb2346049c90b96d602d7bb1b469b905c9228be25c627bffee872def773d5b2a2eb57d' +
|
|
||||||
'108ed59fd9fae381abfd1d6bce2fd2fa220990f0f837fa30e0f27914ed6e1454db0d1ee957b219f61da6ff8be0d6441f' +
|
|
||||||
'153606c417e59fb331b7ae6bce4fbf7c5190c33ce9402b5ebe2b70e44fca614f3f1382a3625ed5493843d0b0a652fc3f' +
|
|
||||||
'033f90f6057aadacae7963b0a0b379dd46750c1c94a6357c99b65f63b79e321ff50fe3053330911c56b6ceea08fee656',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes('abcdef0123456789'),
|
|
||||||
expected:
|
|
||||||
'0da75be60fb6aa0e9e3143e40c42796edf15685cafe0279afd2a67c3dff1c82341f17effd402e4f1af240ea90f4b659b' +
|
|
||||||
'038af300ef34c7759a6caaa4e69363cafeed218a1f207e93b2c70d91a1263d375d6730bd6b6509dcac3ba5b567e85bf3' +
|
|
||||||
'0492f4fed741b073e5a82580f7c663f9b79e036b70ab3e51162359cec4e77c78086fe879b65ca7a47d34374c8315ac5e' +
|
|
||||||
'19b148cbdf163cf0894f29660d2e7bfb2b68e37d54cc83fd4e6e62c020eaa48709302ef8e746736c0e19342cc1ce3df4',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes(
|
|
||||||
'q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' +
|
|
||||||
'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq'
|
|
||||||
),
|
|
||||||
expected:
|
|
||||||
'12c8c05c1d5fc7bfa847f4d7d81e294e66b9a78bc9953990c358945e1f042eedafce608b67fdd3ab0cb2e6e263b9b1ad' +
|
|
||||||
'0c5ae723be00e6c3f0efe184fdc0702b64588fe77dda152ab13099a3bacd3876767fa7bbad6d6fd90b3642e902b208f9' +
|
|
||||||
'11c624c56dbe154d759d021eec60fab3d8b852395a89de497e48504366feedd4662d023af447d66926a28076813dd646' +
|
|
||||||
'04e77ddb3ede41b5ec4396b7421dd916efc68a358a0d7425bddd253547f2fb4830522358491827265dfc5bcc1928a569',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msg: bls.utils.stringToBytes(
|
|
||||||
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
|
|
||||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
|
||||||
),
|
|
||||||
expected:
|
|
||||||
'1565c2f625032d232f13121d3cfb476f45275c303a037faa255f9da62000c2c864ea881e2bcddd111edc4a3c0da3e88d' +
|
|
||||||
'0ea4e7c33d43e17cc516a72f76437c4bf81d8f4eac69ac355d3bf9b71b8138d55dc10fd458be115afa798b55dac34be1' +
|
|
||||||
'0f8991d2a1ad662e7b6f58ab787947f1fa607fce12dde171bc17903b012091b657e15333e11701edcf5b63ba2a561247' +
|
|
||||||
'043b6f5fe4e52c839148dc66f2b3751e69a0f6ebb3d056d6465d50d4108543ecd956e10fa1640dfd9bc0030cc2558d28',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
for (let i = 0; i < VECTORS_G2_NU.length; i++) {
|
|
||||||
const t = VECTORS_G2_NU[i];
|
|
||||||
should(`hashToCurve/G2 (BLS12381G2_XMD:SHA-256_SSWU_NU_) (${i})`, () => {
|
|
||||||
const p = bls.hashToCurve.G2.encodeToCurve(t.msg, {
|
|
||||||
DST: 'QUUX-V01-CS02-with-BLS12381G2_XMD:SHA-256_SSWU_NU_',
|
|
||||||
});
|
|
||||||
deepStrictEqual(p.toHex(false), t.expected);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const VECTORS_ENCODE_G2 = [
|
const VECTORS_ENCODE_G2 = [
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes(''),
|
msg: bls.utils.stringToBytes(''),
|
||||||
|
@ -140,7 +140,6 @@ function testCurve(curve, ro, nu) {
|
|||||||
testCurve(secp256r1, p256_ro, p256_nu);
|
testCurve(secp256r1, p256_ro, p256_nu);
|
||||||
testCurve(secp384r1, p384_ro, p384_nu);
|
testCurve(secp384r1, p384_ro, p384_nu);
|
||||||
testCurve(secp521r1, p521_ro, p521_nu);
|
testCurve(secp521r1, p521_ro, p521_nu);
|
||||||
// TODO: remove same tests from bls12
|
|
||||||
testCurve(bls12_381.hashToCurve.G1, g1_ro, g1_nu);
|
testCurve(bls12_381.hashToCurve.G1, g1_ro, g1_nu);
|
||||||
testCurve(bls12_381.hashToCurve.G2, g2_ro, g2_nu);
|
testCurve(bls12_381.hashToCurve.G2, g2_ro, g2_nu);
|
||||||
testCurve(secp256k1, secp256k1_ro, secp256k1_nu);
|
testCurve(secp256k1, secp256k1_ro, secp256k1_nu);
|
||||||
|
Loading…
Reference in New Issue
Block a user