@noble/curves 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +267 -164
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +2 -2
- package/abstract/bls.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +1 -1
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +14 -8
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +51 -11
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +79 -21
- package/abstract/modular.js.map +1 -1
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +39 -41
- package/abstract/poseidon.js.map +1 -1
- package/abstract/utils.d.ts +1 -0
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +2 -1
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +2 -1
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +13 -11
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +7 -8
- package/bls12-381.js.map +1 -1
- package/ed25519.d.ts +1 -0
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +9 -6
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +51 -2
- package/ed448.d.ts.map +1 -1
- package/ed448.js +206 -28
- package/ed448.js.map +1 -1
- package/esm/abstract/bls.js +3 -3
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/hash-to-curve.js +14 -8
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.js +75 -20
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/poseidon.js +39 -41
- package/esm/abstract/poseidon.js.map +1 -1
- package/esm/abstract/utils.js +2 -1
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.js +13 -11
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.js +7 -8
- package/esm/bls12-381.js.map +1 -1
- package/esm/ed25519.js +9 -6
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.js +208 -31
- package/esm/ed448.js.map +1 -1
- package/esm/jubjub.js +1 -1
- package/esm/jubjub.js.map +1 -1
- package/esm/package.json +1 -4
- package/jubjub.js.map +1 -1
- package/package.json +4 -3
- package/src/abstract/bls.ts +3 -3
- package/src/abstract/hash-to-curve.ts +14 -8
- package/src/abstract/modular.ts +81 -22
- package/src/abstract/poseidon.ts +39 -40
- package/src/abstract/utils.ts +4 -1
- package/src/abstract/weierstrass.ts +13 -11
- package/src/bls12-381.ts +7 -8
- package/src/ed25519.ts +9 -6
- package/src/ed448.ts +251 -33
- package/src/jubjub.ts +1 -1
package/src/abstract/modular.ts
CHANGED
|
@@ -75,9 +75,14 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
|
|
75
75
|
return mod(x, modulo);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Tonelli-Shanks square root search algorithm.
|
|
80
|
+
* 1. https://eprint.iacr.org/2012/685.pdf (page 12)
|
|
81
|
+
* 2. Square Roots from 1; 24, 51, 10 to Dan Shanks
|
|
82
|
+
* Will start an infinite loop if field order P is not prime.
|
|
83
|
+
* @param P field order
|
|
84
|
+
* @returns function that takes field Fp (created from P) and number n
|
|
85
|
+
*/
|
|
81
86
|
export function tonelliShanks(P: bigint) {
|
|
82
87
|
// Legendre constant: used to calculate Legendre symbol (a | p),
|
|
83
88
|
// which denotes the value of a^((p-1)/2) (mod p).
|
|
@@ -198,7 +203,7 @@ export function FpSqrt(P: bigint) {
|
|
|
198
203
|
// Little-endian check for first LE bit (last BE bit);
|
|
199
204
|
export const isNegativeLE = (num: bigint, modulo: bigint) => (mod(num, modulo) & _1n) === _1n;
|
|
200
205
|
|
|
201
|
-
// Field is not always over prime
|
|
206
|
+
// Field is not always over prime: for example, Fp2 has ORDER(q)=p^m
|
|
202
207
|
export interface IField<T> {
|
|
203
208
|
ORDER: bigint;
|
|
204
209
|
BYTES: number;
|
|
@@ -228,7 +233,8 @@ export interface IField<T> {
|
|
|
228
233
|
sqrN(num: T): T;
|
|
229
234
|
|
|
230
235
|
// Optional
|
|
231
|
-
// Should be same as sgn0 function in
|
|
236
|
+
// Should be same as sgn0 function in
|
|
237
|
+
// [RFC9380](https://www.rfc-editor.org/rfc/rfc9380#section-4.1).
|
|
232
238
|
// NOTE: sgn0 is 'negative in LE', which is same as odd. And negative in LE is kinda strange definition anyway.
|
|
233
239
|
isOdd?(num: T): boolean; // Odd instead of even since we have it for Fp2
|
|
234
240
|
// legendre?(num: T): T;
|
|
@@ -260,6 +266,11 @@ export function validateField<T>(field: IField<T>) {
|
|
|
260
266
|
}
|
|
261
267
|
|
|
262
268
|
// Generic field functions
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Same as `pow` but for Fp: non-constant-time.
|
|
272
|
+
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
|
|
273
|
+
*/
|
|
263
274
|
export function FpPow<T>(f: IField<T>, num: T, power: bigint): T {
|
|
264
275
|
// Should have same speed as pow for bigints
|
|
265
276
|
// TODO: benchmark!
|
|
@@ -276,7 +287,10 @@ export function FpPow<T>(f: IField<T>, num: T, power: bigint): T {
|
|
|
276
287
|
return p;
|
|
277
288
|
}
|
|
278
289
|
|
|
279
|
-
|
|
290
|
+
/**
|
|
291
|
+
* Efficiently invert an array of Field elements.
|
|
292
|
+
* `inv(0)` will return `undefined` here: make sure to throw an error.
|
|
293
|
+
*/
|
|
280
294
|
export function FpInvertBatch<T>(f: IField<T>, nums: T[]): T[] {
|
|
281
295
|
const tmp = new Array(nums.length);
|
|
282
296
|
// Walk from first to last, multiply them by each other MOD p
|
|
@@ -319,12 +333,12 @@ export function nLength(n: bigint, nBitLength?: number) {
|
|
|
319
333
|
|
|
320
334
|
type FpField = IField<bigint> & Required<Pick<IField<bigint>, 'isOdd'>>;
|
|
321
335
|
/**
|
|
322
|
-
* Initializes a
|
|
323
|
-
* Do not init in loop: slow. Very fragile: always run a benchmark on change.
|
|
324
|
-
* Major performance
|
|
325
|
-
* a)
|
|
326
|
-
* b)
|
|
327
|
-
* c)
|
|
336
|
+
* Initializes a finite field over prime. **Non-primes are not supported.**
|
|
337
|
+
* Do not init in loop: slow. Very fragile: always run a benchmark on a change.
|
|
338
|
+
* Major performance optimizations:
|
|
339
|
+
* * a) denormalized operations like mulN instead of mul
|
|
340
|
+
* * b) same object shape: never add or remove keys
|
|
341
|
+
* * c) Object.freeze
|
|
328
342
|
* @param ORDER prime positive bigint
|
|
329
343
|
* @param bitLen how many bits the field consumes
|
|
330
344
|
* @param isLE (def: false) if encoding / decoding should be in little-endian
|
|
@@ -336,7 +350,7 @@ export function Field(
|
|
|
336
350
|
isLE = false,
|
|
337
351
|
redef: Partial<IField<bigint>> = {}
|
|
338
352
|
): Readonly<FpField> {
|
|
339
|
-
if (ORDER <= _0n) throw new Error(`Expected
|
|
353
|
+
if (ORDER <= _0n) throw new Error(`Expected Field ORDER > 0, got ${ORDER}`);
|
|
340
354
|
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
|
|
341
355
|
if (BYTES > 2048) throw new Error('Field lengths over 2048 bytes are not supported');
|
|
342
356
|
const sqrtP = FpSqrt(ORDER);
|
|
@@ -400,15 +414,10 @@ export function FpSqrtEven<T>(Fp: IField<T>, elm: T) {
|
|
|
400
414
|
}
|
|
401
415
|
|
|
402
416
|
/**
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
|
408
|
-
* @param hash hash output from SHA3 or a similar function
|
|
409
|
-
* @param groupOrder size of subgroup - (e.g. curveFn.CURVE.n)
|
|
410
|
-
* @param isLE interpret hash bytes as LE num
|
|
411
|
-
* @returns valid private scalar
|
|
417
|
+
* "Constant-time" private key generation utility.
|
|
418
|
+
* Same as mapKeyToField, but accepts less bytes (40 instead of 48 for 32-byte field).
|
|
419
|
+
* Which makes it slightly more biased, less secure.
|
|
420
|
+
* @deprecated use mapKeyToField instead
|
|
412
421
|
*/
|
|
413
422
|
export function hashToPrivateScalar(
|
|
414
423
|
hash: string | Uint8Array,
|
|
@@ -423,3 +432,53 @@ export function hashToPrivateScalar(
|
|
|
423
432
|
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
|
|
424
433
|
return mod(num, groupOrder - _1n) + _1n;
|
|
425
434
|
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Returns total number of bytes consumed by the field element.
|
|
438
|
+
* For example, 32 bytes for usual 256-bit weierstrass curve.
|
|
439
|
+
* @param fieldOrder number of field elements, usually CURVE.n
|
|
440
|
+
* @returns byte length of field
|
|
441
|
+
*/
|
|
442
|
+
export function getFieldBytesLength(fieldOrder: bigint): number {
|
|
443
|
+
if (typeof fieldOrder !== 'bigint') throw new Error('field order must be bigint');
|
|
444
|
+
const bitLength = fieldOrder.toString(2).length;
|
|
445
|
+
return Math.ceil(bitLength / 8);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Returns minimal amount of bytes that can be safely reduced
|
|
450
|
+
* by field order.
|
|
451
|
+
* Should be 2^-128 for 128-bit curve such as P256.
|
|
452
|
+
* @param fieldOrder number of field elements, usually CURVE.n
|
|
453
|
+
* @returns byte length of target hash
|
|
454
|
+
*/
|
|
455
|
+
export function getMinHashLength(fieldOrder: bigint): number {
|
|
456
|
+
const length = getFieldBytesLength(fieldOrder);
|
|
457
|
+
return length + Math.ceil(length / 2);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* "Constant-time" private key generation utility.
|
|
462
|
+
* Can take (n + n/2) or more bytes of uniform input e.g. from CSPRNG or KDF
|
|
463
|
+
* and convert them into private scalar, with the modulo bias being negligible.
|
|
464
|
+
* Needs at least 48 bytes of input for 32-byte private key.
|
|
465
|
+
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
|
466
|
+
* FIPS 186-5, A.2 https://csrc.nist.gov/publications/detail/fips/186/5/final
|
|
467
|
+
* RFC 9380, https://www.rfc-editor.org/rfc/rfc9380#section-5
|
|
468
|
+
* @param hash hash output from SHA3 or a similar function
|
|
469
|
+
* @param groupOrder size of subgroup - (e.g. secp256k1.CURVE.n)
|
|
470
|
+
* @param isLE interpret hash bytes as LE num
|
|
471
|
+
* @returns valid private scalar
|
|
472
|
+
*/
|
|
473
|
+
export function mapHashToField(key: Uint8Array, fieldOrder: bigint, isLE = false): Uint8Array {
|
|
474
|
+
const len = key.length;
|
|
475
|
+
const fieldLen = getFieldBytesLength(fieldOrder);
|
|
476
|
+
const minLen = getMinHashLength(fieldOrder);
|
|
477
|
+
// No small numbers: need to understand bias story. No huge numbers: easier to detect JS timings.
|
|
478
|
+
if (len < 16 || len < minLen || len > 1024)
|
|
479
|
+
throw new Error(`expected ${minLen}-1024 bytes of input, got ${len}`);
|
|
480
|
+
const num = isLE ? bytesToNumberBE(key) : bytesToNumberLE(key);
|
|
481
|
+
// `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0
|
|
482
|
+
const reduced = mod(num, fieldOrder - _1n) + _1n;
|
|
483
|
+
return isLE ? numberToBytesLE(reduced, fieldLen) : numberToBytesBE(reduced, fieldLen);
|
|
484
|
+
}
|
package/src/abstract/poseidon.ts
CHANGED
|
@@ -15,34 +15,36 @@ export type PoseidonOpts = {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
export function validateOpts(opts: PoseidonOpts) {
|
|
18
|
-
const { Fp } = opts;
|
|
18
|
+
const { Fp, mds, reversePartialPowIdx: rev, roundConstants: rc } = opts;
|
|
19
|
+
const { roundsFull, roundsPartial, sboxPower, t } = opts;
|
|
20
|
+
|
|
19
21
|
validateField(Fp);
|
|
20
22
|
for (const i of ['t', 'roundsFull', 'roundsPartial'] as const) {
|
|
21
23
|
if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
|
|
22
24
|
throw new Error(`Poseidon: invalid param ${i}=${opts[i]} (${typeof opts[i]})`);
|
|
23
25
|
}
|
|
24
|
-
if (opts.reversePartialPowIdx !== undefined && typeof opts.reversePartialPowIdx !== 'boolean')
|
|
25
|
-
throw new Error(`Poseidon: invalid param reversePartialPowIdx=${opts.reversePartialPowIdx}`);
|
|
26
|
-
// Default is 5, but by some reasons stark uses 3
|
|
27
|
-
let sboxPower = opts.sboxPower;
|
|
28
|
-
if (sboxPower === undefined) sboxPower = 5;
|
|
29
|
-
if (typeof sboxPower !== 'number' || !Number.isSafeInteger(sboxPower))
|
|
30
|
-
throw new Error(`Poseidon wrong sboxPower=${sboxPower}`);
|
|
31
26
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
// MDS is TxT matrix
|
|
28
|
+
if (!Array.isArray(mds) || mds.length !== t) throw new Error('Poseidon: wrong MDS matrix');
|
|
29
|
+
const _mds = mds.map((mdsRow) => {
|
|
30
|
+
if (!Array.isArray(mdsRow) || mdsRow.length !== t)
|
|
31
|
+
throw new Error(`Poseidon MDS matrix row: ${mdsRow}`);
|
|
32
|
+
return mdsRow.map((i) => {
|
|
33
|
+
if (typeof i !== 'bigint') throw new Error(`Poseidon MDS matrix value=${i}`);
|
|
34
|
+
return Fp.create(i);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (rev !== undefined && typeof rev !== 'boolean')
|
|
39
|
+
throw new Error(`Poseidon: invalid param reversePartialPowIdx=${rev}`);
|
|
37
40
|
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
const rounds = opts.roundsFull + opts.roundsPartial;
|
|
41
|
+
if (roundsFull % 2 !== 0) throw new Error(`Poseidon roundsFull is not even: ${roundsFull}`);
|
|
42
|
+
const rounds = roundsFull + roundsPartial;
|
|
41
43
|
|
|
42
|
-
if (!Array.isArray(
|
|
44
|
+
if (!Array.isArray(rc) || rc.length !== rounds)
|
|
43
45
|
throw new Error('Poseidon: wrong round constants');
|
|
44
|
-
const roundConstants =
|
|
45
|
-
if (!Array.isArray(rc) || rc.length !==
|
|
46
|
+
const roundConstants = rc.map((rc) => {
|
|
47
|
+
if (!Array.isArray(rc) || rc.length !== t)
|
|
46
48
|
throw new Error(`Poseidon wrong round constants: ${rc}`);
|
|
47
49
|
return rc.map((i) => {
|
|
48
50
|
if (typeof i !== 'bigint' || !Fp.isValid(i))
|
|
@@ -50,18 +52,16 @@ export function validateOpts(opts: PoseidonOpts) {
|
|
|
50
52
|
return Fp.create(i);
|
|
51
53
|
});
|
|
52
54
|
});
|
|
53
|
-
|
|
54
|
-
if (!
|
|
55
|
-
throw new Error(
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
});
|
|
64
|
-
return Object.freeze({ ...opts, rounds, sboxFn, roundConstants, mds });
|
|
55
|
+
|
|
56
|
+
if (!sboxPower || ![3, 5, 7].includes(sboxPower))
|
|
57
|
+
throw new Error(`Poseidon wrong sboxPower=${sboxPower}`);
|
|
58
|
+
const _sboxPower = BigInt(sboxPower);
|
|
59
|
+
let sboxFn = (n: bigint) => FpPow(Fp, n, _sboxPower);
|
|
60
|
+
// Unwrapped sbox power for common cases (195->142μs)
|
|
61
|
+
if (sboxPower === 3) sboxFn = (n: bigint) => Fp.mul(Fp.sqrN(n), n);
|
|
62
|
+
else if (sboxPower === 5) sboxFn = (n: bigint) => Fp.mul(Fp.sqrN(Fp.sqrN(n)), n);
|
|
63
|
+
|
|
64
|
+
return Object.freeze({ ...opts, rounds, sboxFn, roundConstants, mds: _mds });
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
export function splitConstants(rc: bigint[], t: number) {
|
|
@@ -80,18 +80,17 @@ export function splitConstants(rc: bigint[], t: number) {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
export function poseidon(opts: PoseidonOpts) {
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
const
|
|
83
|
+
const _opts = validateOpts(opts);
|
|
84
|
+
const { Fp, mds, roundConstants, rounds, roundsPartial, sboxFn, t } = _opts;
|
|
85
|
+
const halfRoundsFull = _opts.roundsFull / 2;
|
|
86
|
+
const partialIdx = _opts.reversePartialPowIdx ? t - 1 : 0;
|
|
86
87
|
const poseidonRound = (values: bigint[], isFull: boolean, idx: number) => {
|
|
87
|
-
values = values.map((i, j) => Fp.add(i,
|
|
88
|
+
values = values.map((i, j) => Fp.add(i, roundConstants[idx][j]));
|
|
88
89
|
|
|
89
90
|
if (isFull) values = values.map((i) => sboxFn(i));
|
|
90
91
|
else values[partialIdx] = sboxFn(values[partialIdx]);
|
|
91
92
|
// Matrix multiplication
|
|
92
|
-
values =
|
|
93
|
-
i.reduce((acc, i, j) => Fp.add(acc, Fp.mulN(i, values[j])), Fp.ZERO)
|
|
94
|
-
);
|
|
93
|
+
values = mds.map((i) => i.reduce((acc, i, j) => Fp.add(acc, Fp.mulN(i, values[j])), Fp.ZERO));
|
|
95
94
|
return values;
|
|
96
95
|
};
|
|
97
96
|
const poseidonHash = function poseidonHash(values: bigint[]) {
|
|
@@ -105,7 +104,7 @@ export function poseidon(opts: PoseidonOpts) {
|
|
|
105
104
|
// Apply r_f/2 full rounds.
|
|
106
105
|
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, round++);
|
|
107
106
|
// Apply r_p partial rounds.
|
|
108
|
-
for (let i = 0; i <
|
|
107
|
+
for (let i = 0; i < roundsPartial; i++) values = poseidonRound(values, false, round++);
|
|
109
108
|
// Apply r_f/2 full rounds.
|
|
110
109
|
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, round++);
|
|
111
110
|
|
|
@@ -114,6 +113,6 @@ export function poseidon(opts: PoseidonOpts) {
|
|
|
114
113
|
return values;
|
|
115
114
|
};
|
|
116
115
|
// For verification in tests
|
|
117
|
-
poseidonHash.roundConstants =
|
|
116
|
+
poseidonHash.roundConstants = roundConstants;
|
|
118
117
|
return poseidonHash;
|
|
119
118
|
}
|
package/src/abstract/utils.ts
CHANGED
|
@@ -17,7 +17,9 @@ export type CHash = {
|
|
|
17
17
|
};
|
|
18
18
|
export type FHash = (message: Uint8Array | string) => Uint8Array;
|
|
19
19
|
|
|
20
|
-
const hexes = Array.from({ length: 256 }, (
|
|
20
|
+
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>
|
|
21
|
+
i.toString(16).padStart(2, '0')
|
|
22
|
+
);
|
|
21
23
|
/**
|
|
22
24
|
* @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
|
|
23
25
|
*/
|
|
@@ -246,6 +248,7 @@ const validatorFns = {
|
|
|
246
248
|
function: (val: any) => typeof val === 'function',
|
|
247
249
|
boolean: (val: any) => typeof val === 'boolean',
|
|
248
250
|
string: (val: any) => typeof val === 'string',
|
|
251
|
+
stringOrUint8Array: (val: any) => typeof val === 'string' || val instanceof Uint8Array,
|
|
249
252
|
isSafeInteger: (val: any) => Number.isSafeInteger(val),
|
|
250
253
|
array: (val: any) => Array.isArray(val),
|
|
251
254
|
field: (val: any, object: any) => (object as any).Fp.isValid(val),
|
|
@@ -193,7 +193,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|
|
193
193
|
|
|
194
194
|
const toBytes =
|
|
195
195
|
CURVE.toBytes ||
|
|
196
|
-
((
|
|
196
|
+
((_c: ProjConstructor<T>, point: ProjPointType<T>, _isCompressed: boolean) => {
|
|
197
197
|
const a = point.toAffine();
|
|
198
198
|
return ut.concatBytes(Uint8Array.from([0x04]), Fp.toBytes(a.x), Fp.toBytes(a.y));
|
|
199
199
|
});
|
|
@@ -333,9 +333,11 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|
|
333
333
|
|
|
334
334
|
// A point on curve is valid if it conforms to equation.
|
|
335
335
|
assertValidity(): void {
|
|
336
|
-
// Zero is valid point too!
|
|
337
336
|
if (this.is0()) {
|
|
338
|
-
|
|
337
|
+
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
338
|
+
// In BLS, ZERO can be serialized, so we allow it.
|
|
339
|
+
// (0, 0, 0) is wrong representation of ZERO and is always invalid.
|
|
340
|
+
if (CURVE.allowInfinityPoint && !Fp.is0(this.py)) return;
|
|
339
341
|
throw new Error('bad point: ZERO');
|
|
340
342
|
}
|
|
341
343
|
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
@@ -707,7 +709,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
707
709
|
isWithinCurveOrder,
|
|
708
710
|
} = weierstrassPoints({
|
|
709
711
|
...CURVE,
|
|
710
|
-
toBytes(
|
|
712
|
+
toBytes(_c, point, isCompressed: boolean): Uint8Array {
|
|
711
713
|
const a = point.toAffine();
|
|
712
714
|
const x = Fp.toBytes(a.x);
|
|
713
715
|
const cat = ut.concatBytes;
|
|
@@ -845,13 +847,12 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
845
847
|
normPrivateKeyToScalar: normPrivateKeyToScalar,
|
|
846
848
|
|
|
847
849
|
/**
|
|
848
|
-
* Produces cryptographically secure private key from random of size
|
|
849
|
-
*
|
|
850
|
+
* Produces cryptographically secure private key from random of size
|
|
851
|
+
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
850
852
|
*/
|
|
851
853
|
randomPrivateKey: (): Uint8Array => {
|
|
852
|
-
const
|
|
853
|
-
|
|
854
|
-
return ut.numberToBytesBE(num, CURVE.nByteLength);
|
|
854
|
+
const length = mod.getMinHashLength(CURVE.n);
|
|
855
|
+
return mod.mapHashToField(CURVE.randomBytes(length), CURVE.n);
|
|
855
856
|
},
|
|
856
857
|
|
|
857
858
|
/**
|
|
@@ -964,7 +965,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
964
965
|
if (ent != null) {
|
|
965
966
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
966
967
|
const e = ent === true ? randomBytes(Fp.BYTES) : ent; // generate random bytes OR pass as-is
|
|
967
|
-
seedArgs.push(ensureBytes('extraEntropy', e
|
|
968
|
+
seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
|
|
968
969
|
}
|
|
969
970
|
const seed = ut.concatBytes(...seedArgs); // Step D of RFC6979 3.2
|
|
970
971
|
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
|
@@ -1170,7 +1171,8 @@ export function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T) {
|
|
|
1170
1171
|
return sqrtRatio;
|
|
1171
1172
|
}
|
|
1172
1173
|
/**
|
|
1173
|
-
*
|
|
1174
|
+
* Simplified Shallue-van de Woestijne-Ulas Method
|
|
1175
|
+
* https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2
|
|
1174
1176
|
*/
|
|
1175
1177
|
export function mapToCurveSimpleSWU<T>(
|
|
1176
1178
|
Fp: mod.IField<T>,
|
package/src/bls12-381.ts
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
// The library uses G1 for public keys and G2 for signatures. Support for G1 signatures is planned.
|
|
9
9
|
// Compatible with Algorand, Chia, Dfinity, Ethereum, FIL, Zcash. Matches specs
|
|
10
10
|
// [pairing-curves-11](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11),
|
|
11
|
-
// [bls-sigs-04](https
|
|
12
|
-
// [hash-to-curve-12](https://tools.ietf.org/html/draft-irtf
|
|
11
|
+
// [bls-sigs-04](https:/cfrg-hash-to/tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04),
|
|
12
|
+
// [hash-to-curve-12](https://tools.ietf.org/html/draft-irtf--curve-12).
|
|
13
13
|
//
|
|
14
14
|
// ### Summary
|
|
15
15
|
// 1. BLS Relies on Bilinear Pairing (expensive)
|
|
@@ -177,7 +177,7 @@ const Fp2: mod.IField<Fp2> & Fp2Utils = {
|
|
|
177
177
|
if (im1 > im2 || (im1 === im2 && re1 > re2)) return x1;
|
|
178
178
|
return x2;
|
|
179
179
|
},
|
|
180
|
-
// Same as
|
|
180
|
+
// Same as sgn0_m_eq_2 in RFC 9380
|
|
181
181
|
isOdd: (x: Fp2) => {
|
|
182
182
|
const { re: x0, im: x1 } = Fp2.reim(x);
|
|
183
183
|
const sign_0 = x0 % _2n;
|
|
@@ -780,8 +780,7 @@ const FP12_FROBENIUS_COEFFICIENTS = [
|
|
|
780
780
|
|
|
781
781
|
// HashToCurve
|
|
782
782
|
|
|
783
|
-
// 3-isogeny map from E' to E
|
|
784
|
-
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-E.3
|
|
783
|
+
// 3-isogeny map from E' to E https://www.rfc-editor.org/rfc/rfc9380#appendix-E.3
|
|
785
784
|
const isogenyMapG2 = isogenyMap(
|
|
786
785
|
Fp2,
|
|
787
786
|
[
|
|
@@ -985,7 +984,7 @@ function G2psi2(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
|
|
|
985
984
|
//
|
|
986
985
|
// Parameter definitions are in section 5.3 of the spec unless otherwise noted.
|
|
987
986
|
// Parameter values come from section 8.8.2 of the spec.
|
|
988
|
-
// https://
|
|
987
|
+
// https://www.rfc-editor.org/rfc/rfc9380#section-8.8.2
|
|
989
988
|
//
|
|
990
989
|
// Base field F is GF(p^m)
|
|
991
990
|
// p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
|
|
@@ -1040,7 +1039,7 @@ function signatureG2ToRawBytes(point: ProjPointType<Fp2>) {
|
|
|
1040
1039
|
}
|
|
1041
1040
|
|
|
1042
1041
|
// To verify curve parameters, see pairing-friendly-curves spec:
|
|
1043
|
-
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-
|
|
1042
|
+
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11
|
|
1044
1043
|
// Basic math is done over finite fields over p.
|
|
1045
1044
|
// More complicated math is done over polynominal extension fields.
|
|
1046
1045
|
// To simplify calculations in Fp12, we construct extension tower:
|
|
@@ -1108,7 +1107,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|
|
1108
1107
|
},
|
|
1109
1108
|
// Clear cofactor of G1
|
|
1110
1109
|
// https://eprint.iacr.org/2019/403
|
|
1111
|
-
clearCofactor: (
|
|
1110
|
+
clearCofactor: (_c, point) => {
|
|
1112
1111
|
// return this.multiplyUnsafe(CURVE.h);
|
|
1113
1112
|
return point.multiplyUnsafe(bls12_381.params.x).add(point); // x*P + P
|
|
1114
1113
|
},
|
package/src/ed25519.ts
CHANGED
|
@@ -123,7 +123,7 @@ const ed25519Defaults = {
|
|
|
123
123
|
uvRatio,
|
|
124
124
|
} as const;
|
|
125
125
|
|
|
126
|
-
export const ed25519 = twistedEdwards(ed25519Defaults);
|
|
126
|
+
export const ed25519 = /* @__PURE__ */ twistedEdwards(ed25519Defaults);
|
|
127
127
|
|
|
128
128
|
function ed25519_domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {
|
|
129
129
|
if (ctx.length > 255) throw new Error('Context is too big');
|
|
@@ -135,8 +135,11 @@ function ed25519_domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {
|
|
|
135
135
|
);
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
export const ed25519ctx = twistedEdwards({
|
|
139
|
-
|
|
138
|
+
export const ed25519ctx = /* @__PURE__ */ twistedEdwards({
|
|
139
|
+
...ed25519Defaults,
|
|
140
|
+
domain: ed25519_domain,
|
|
141
|
+
});
|
|
142
|
+
export const ed25519ph = /* @__PURE__ */ twistedEdwards({
|
|
140
143
|
...ed25519Defaults,
|
|
141
144
|
domain: ed25519_domain,
|
|
142
145
|
prehash: sha512,
|
|
@@ -475,12 +478,12 @@ export const RistrettoPoint = /* @__PURE__ */ (() => {
|
|
|
475
478
|
return RistPoint;
|
|
476
479
|
})();
|
|
477
480
|
|
|
478
|
-
// https://
|
|
479
|
-
|
|
480
|
-
export const hash_to_ristretto255 = (msg: Uint8Array, options: htfBasicOpts) => {
|
|
481
|
+
// Hashing to ristretto255. https://www.rfc-editor.org/rfc/rfc9380#appendix-B
|
|
482
|
+
export const hashToRistretto255 = (msg: Uint8Array, options: htfBasicOpts) => {
|
|
481
483
|
const d = options.DST;
|
|
482
484
|
const DST = typeof d === 'string' ? utf8ToBytes(d) : d;
|
|
483
485
|
const uniform_bytes = expand_message_xmd(msg, DST, 64, sha512);
|
|
484
486
|
const P = RistPoint.hashToCurve(uniform_bytes);
|
|
485
487
|
return P;
|
|
486
488
|
};
|
|
489
|
+
export const hash_to_ristretto255 = hashToRistretto255; // legacy
|