@noble/curves 1.9.0 → 1.9.1
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 +22 -9
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +13 -4
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +14 -1
- package/abstract/edwards.js.map +1 -1
- package/abstract/fft.d.ts +120 -0
- package/abstract/fft.d.ts.map +1 -0
- package/abstract/fft.js +439 -0
- package/abstract/fft.js.map +1 -0
- package/abstract/modular.d.ts +4 -6
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +107 -119
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +4 -9
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +70 -91
- package/abstract/montgomery.js.map +1 -1
- package/abstract/tower.js +1 -1
- package/abstract/tower.js.map +1 -1
- package/abstract/weierstrass.d.ts +27 -4
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +61 -42
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +9 -23
- package/bls12-381.js.map +1 -1
- package/ed25519.d.ts +17 -4
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +23 -12
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +19 -4
- package/ed448.d.ts.map +1 -1
- package/ed448.js +41 -29
- package/ed448.js.map +1 -1
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +13 -4
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +14 -1
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/fft.d.ts +120 -0
- package/esm/abstract/fft.d.ts.map +1 -0
- package/esm/abstract/fft.js +426 -0
- package/esm/abstract/fft.js.map +1 -0
- package/esm/abstract/modular.d.ts +4 -6
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +107 -119
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts +4 -9
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +71 -92
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/tower.js +1 -1
- package/esm/abstract/tower.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +27 -4
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +61 -42
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +9 -23
- package/esm/bls12-381.js.map +1 -1
- package/esm/ed25519.d.ts +17 -4
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +23 -12
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +19 -4
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +42 -30
- package/esm/ed448.js.map +1 -1
- package/esm/jubjub.d.ts +4 -0
- package/esm/jubjub.d.ts.map +1 -1
- package/esm/jubjub.js +4 -0
- package/esm/jubjub.js.map +1 -1
- package/esm/nist.d.ts +1 -0
- package/esm/nist.d.ts.map +1 -1
- package/esm/nist.js +1 -0
- package/esm/nist.js.map +1 -1
- package/esm/pasta.d.ts +4 -0
- package/esm/pasta.d.ts.map +1 -1
- package/esm/pasta.js +4 -0
- package/esm/pasta.js.map +1 -1
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +3 -3
- package/esm/secp256k1.js.map +1 -1
- package/jubjub.d.ts +4 -0
- package/jubjub.d.ts.map +1 -1
- package/jubjub.js +4 -0
- package/jubjub.js.map +1 -1
- package/nist.d.ts +1 -0
- package/nist.d.ts.map +1 -1
- package/nist.js +1 -0
- package/nist.js.map +1 -1
- package/package.json +13 -4
- package/pasta.d.ts +4 -0
- package/pasta.d.ts.map +1 -1
- package/pasta.js +4 -0
- package/pasta.js.map +1 -1
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +3 -3
- package/secp256k1.js.map +1 -1
- package/src/abstract/curve.ts +10 -5
- package/src/abstract/edwards.ts +15 -1
- package/src/abstract/fft.ts +508 -0
- package/src/abstract/modular.ts +107 -115
- package/src/abstract/montgomery.ts +78 -110
- package/src/abstract/tower.ts +1 -1
- package/src/abstract/weierstrass.ts +93 -49
- package/src/bls12-381.ts +11 -27
- package/src/ed25519.ts +24 -12
- package/src/ed448.ts +84 -70
- package/src/jubjub.ts +4 -0
- package/src/nist.ts +1 -0
- package/src/pasta.ts +5 -1
- package/src/secp256k1.ts +3 -3
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
* * a: formula param
|
|
9
9
|
* * b: formula param
|
|
10
|
-
* * Fp: finite
|
|
11
|
-
* * n:
|
|
12
|
-
* * Gx: Base point (x, y) aka generator point x coordinate
|
|
10
|
+
* * Fp: finite field of prime characteristic P; may be complex (Fp2). Arithmetics is done in field
|
|
11
|
+
* * n: order of prime subgroup a.k.a total amount of valid curve points
|
|
12
|
+
* * Gx: Base point (x, y) aka generator point. Gx = x coordinate
|
|
13
13
|
* * Gy: ...y coordinate
|
|
14
14
|
* * h: cofactor, usually 1. h*n = curve group order (n is only subgroup order)
|
|
15
15
|
* * lowS: whether to enable (default) or disable "low-s" non-malleable signatures
|
|
@@ -40,27 +40,51 @@
|
|
|
40
40
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
41
41
|
// prettier-ignore
|
|
42
42
|
import {
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
pippenger, validateBasic, wNAF,
|
|
44
|
+
type AffinePoint, type BasicCurve, type Group, type GroupConstructor
|
|
45
45
|
} from './curve.ts';
|
|
46
46
|
// prettier-ignore
|
|
47
47
|
import {
|
|
48
48
|
Field,
|
|
49
49
|
FpInvertBatch,
|
|
50
|
-
|
|
50
|
+
getMinHashLength, invert, mapHashToField, mod, validateField,
|
|
51
|
+
type IField
|
|
51
52
|
} from './modular.ts';
|
|
52
53
|
// prettier-ignore
|
|
53
54
|
import {
|
|
54
|
-
type CHash, type Hex, type PrivKey,
|
|
55
55
|
aInRange, abool,
|
|
56
56
|
bitMask,
|
|
57
57
|
bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, ensureBytes, hexToBytes,
|
|
58
|
-
inRange, isBytes, memoized, numberToBytesBE, numberToHexUnpadded, validateObject
|
|
58
|
+
inRange, isBytes, memoized, numberToBytesBE, numberToHexUnpadded, validateObject,
|
|
59
|
+
type CHash, type Hex, type PrivKey
|
|
59
60
|
} from './utils.ts';
|
|
60
61
|
|
|
61
62
|
export type { AffinePoint };
|
|
62
63
|
type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
|
|
63
|
-
|
|
64
|
+
/**
|
|
65
|
+
* When Weierstrass curve has `a=0`, it becomes Koblitz curve.
|
|
66
|
+
* Koblitz curves allow using **efficiently-computable GLV endomorphism ψ**.
|
|
67
|
+
* Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%.
|
|
68
|
+
* For precomputed wNAF it trades off 1/2 init time & 1/3 ram for 20% perf hit.
|
|
69
|
+
*
|
|
70
|
+
* Endomorphism consists of beta, lambda and splitScalar:
|
|
71
|
+
*
|
|
72
|
+
* 1. GLV endomorphism ψ transforms a point: `P = (x, y) ↦ ψ(P) = (β·x mod p, y)`
|
|
73
|
+
* 2. GLV scalar decomposition transforms a scalar: `k ≡ k₁ + k₂·λ (mod n)`
|
|
74
|
+
* 3. Then these are combined: `k·P = k₁·P + k₂·ψ(P)`
|
|
75
|
+
* 4. Two 128-bit point-by-scalar multiplications + one point addition is faster than
|
|
76
|
+
* one 256-bit multiplication.
|
|
77
|
+
*
|
|
78
|
+
* where
|
|
79
|
+
* * beta: β ∈ Fₚ with β³ = 1, β ≠ 1
|
|
80
|
+
* * lambda: λ ∈ Fₙ with λ³ = 1, λ ≠ 1
|
|
81
|
+
* * splitScalar decomposes k ↦ k₁, k₂, by using reduced basis vectors.
|
|
82
|
+
* Gauss lattice reduction calculates them from initial basis vectors `(n, 0), (-λ, 0)`
|
|
83
|
+
*
|
|
84
|
+
* Check out `test/misc/endomorphism.js` and
|
|
85
|
+
* [gist](https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066).
|
|
86
|
+
*/
|
|
87
|
+
export type EndomorphismOpts = {
|
|
64
88
|
beta: bigint;
|
|
65
89
|
splitScalar: (k: bigint) => { k1neg: boolean; k1: bigint; k2neg: boolean; k2: bigint };
|
|
66
90
|
};
|
|
@@ -72,7 +96,7 @@ export type BasicWCurve<T> = BasicCurve<T> & {
|
|
|
72
96
|
// Optional params
|
|
73
97
|
allowedPrivateKeyLengths?: readonly number[]; // for P521
|
|
74
98
|
wrapPrivateKey?: boolean; // bls12-381 requires mod(n) instead of rejecting keys >= n
|
|
75
|
-
endo?: EndomorphismOpts;
|
|
99
|
+
endo?: EndomorphismOpts;
|
|
76
100
|
// When a cofactor != 1, there can be an effective methods to:
|
|
77
101
|
// 1. Determine whether a point is torsion-free
|
|
78
102
|
isTorsionFree?: (c: ProjConstructor<T>, point: ProjPointType<T>) => boolean;
|
|
@@ -137,26 +161,26 @@ function validatePointOpts<T>(curve: CurvePointsType<T>): CurvePointsTypeWithLen
|
|
|
137
161
|
b: 'field',
|
|
138
162
|
},
|
|
139
163
|
{
|
|
164
|
+
allowInfinityPoint: 'boolean',
|
|
140
165
|
allowedPrivateKeyLengths: 'array',
|
|
141
|
-
wrapPrivateKey: 'boolean',
|
|
142
|
-
isTorsionFree: 'function',
|
|
143
166
|
clearCofactor: 'function',
|
|
144
|
-
allowInfinityPoint: 'boolean',
|
|
145
167
|
fromBytes: 'function',
|
|
168
|
+
isTorsionFree: 'function',
|
|
146
169
|
toBytes: 'function',
|
|
170
|
+
wrapPrivateKey: 'boolean',
|
|
147
171
|
}
|
|
148
172
|
);
|
|
149
173
|
const { endo, Fp, a } = opts;
|
|
150
174
|
if (endo) {
|
|
151
175
|
if (!Fp.eql(a, Fp.ZERO)) {
|
|
152
|
-
throw new Error('invalid
|
|
176
|
+
throw new Error('invalid endo: CURVE.a must be 0');
|
|
153
177
|
}
|
|
154
178
|
if (
|
|
155
179
|
typeof endo !== 'object' ||
|
|
156
180
|
typeof endo.beta !== 'bigint' ||
|
|
157
181
|
typeof endo.splitScalar !== 'function'
|
|
158
182
|
) {
|
|
159
|
-
throw new Error('invalid
|
|
183
|
+
throw new Error('invalid endo: expected "beta": bigint and "splitScalar": function');
|
|
160
184
|
}
|
|
161
185
|
}
|
|
162
186
|
return Object.freeze({ ...opts } as const);
|
|
@@ -288,6 +312,10 @@ export const DER: IDER = {
|
|
|
288
312
|
},
|
|
289
313
|
};
|
|
290
314
|
|
|
315
|
+
function numToSizedHex(num: bigint, size: number): string {
|
|
316
|
+
return bytesToHex(numberToBytesBE(num, size));
|
|
317
|
+
}
|
|
318
|
+
|
|
291
319
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
292
320
|
// prettier-ignore
|
|
293
321
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
|
@@ -321,15 +349,25 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
321
349
|
function weierstrassEquation(x: T): T {
|
|
322
350
|
const { a, b } = CURVE;
|
|
323
351
|
const x2 = Fp.sqr(x); // x * x
|
|
324
|
-
const x3 = Fp.mul(x2, x); //
|
|
325
|
-
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); //
|
|
352
|
+
const x3 = Fp.mul(x2, x); // x² * x
|
|
353
|
+
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function isValidXY(x: T, y: T): boolean {
|
|
357
|
+
const left = Fp.sqr(y); // y²
|
|
358
|
+
const right = weierstrassEquation(x); // x³ + ax + b
|
|
359
|
+
return Fp.eql(left, right);
|
|
326
360
|
}
|
|
361
|
+
|
|
327
362
|
// Validate whether the passed curve params are valid.
|
|
328
|
-
//
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
363
|
+
// Test 1: equation y² = x³ + ax + b should work for generator point.
|
|
364
|
+
if (!isValidXY(CURVE.Gx, CURVE.Gy)) throw new Error('bad curve params: generator point');
|
|
365
|
+
|
|
366
|
+
// Test 2: discriminant Δ part should be non-zero: 4a³ + 27b² != 0.
|
|
367
|
+
// Guarantees curve is genus-1, smooth (non-singular).
|
|
368
|
+
const _4a3 = Fp.mul(Fp.pow(CURVE.a, _3n), _4n);
|
|
369
|
+
const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27));
|
|
370
|
+
if (Fp.is0(Fp.add(_4a3, _27b2))) throw new Error('bad curve params: a or b');
|
|
333
371
|
|
|
334
372
|
// Valid group elements reside in range 1..n-1
|
|
335
373
|
function isWithinCurveOrder(num: bigint): boolean {
|
|
@@ -370,7 +408,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
370
408
|
|
|
371
409
|
// Converts Projective point to affine (x, y) coordinates.
|
|
372
410
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
373
|
-
// (
|
|
411
|
+
// (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
|
|
374
412
|
const toAffineMemo = memoized((p: Point, iz?: T): AffinePoint<T> => {
|
|
375
413
|
const { px: x, py: y, pz: z } = p;
|
|
376
414
|
// Fast-path for normalized points
|
|
@@ -400,20 +438,20 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
400
438
|
const { x, y } = p.toAffine();
|
|
401
439
|
// Check if x, y are valid field elements
|
|
402
440
|
if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not FE');
|
|
403
|
-
|
|
404
|
-
const right = weierstrassEquation(x); // x³ + ax + b
|
|
405
|
-
if (!Fp.eql(left, right)) throw new Error('bad point: equation left != right');
|
|
441
|
+
if (!isValidXY(x, y)) throw new Error('bad point: equation left != right');
|
|
406
442
|
if (!p.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
|
|
407
443
|
return true;
|
|
408
444
|
});
|
|
409
445
|
|
|
410
446
|
/**
|
|
411
|
-
* Projective Point works in 3d / projective (homogeneous) coordinates: (
|
|
447
|
+
* Projective Point works in 3d / projective (homogeneous) coordinates: (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
|
|
412
448
|
* Default Point works in 2d / affine coordinates: (x, y)
|
|
413
449
|
* We're doing calculations in projective, because its operations don't require costly inversion.
|
|
414
450
|
*/
|
|
415
451
|
class Point implements ProjPointType<T> {
|
|
452
|
+
// base / generator point
|
|
416
453
|
static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
454
|
+
// zero / infinity / identity point
|
|
417
455
|
static readonly ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
|
|
418
456
|
readonly px: T;
|
|
419
457
|
readonly py: T;
|
|
@@ -643,6 +681,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
643
681
|
return wnaf.wNAFCachedUnsafe(this, sc, Point.normalizeZ);
|
|
644
682
|
|
|
645
683
|
// Case c: endomorphism
|
|
684
|
+
/** See docs for {@link EndomorphismOpts} */
|
|
646
685
|
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
|
|
647
686
|
let k1p = I;
|
|
648
687
|
let k2p = I;
|
|
@@ -673,6 +712,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
673
712
|
const { endo, n: N } = CURVE;
|
|
674
713
|
aInRange('scalar', scalar, _1n, N);
|
|
675
714
|
let point: Point, fake: Point; // Fake point is used to const-time mult
|
|
715
|
+
/** See docs for {@link EndomorphismOpts} */
|
|
676
716
|
if (endo) {
|
|
677
717
|
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
|
|
678
718
|
let { p: k1p, f: f1p } = this.wNAF(k1);
|
|
@@ -737,8 +777,8 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
737
777
|
return bytesToHex(this.toRawBytes(isCompressed));
|
|
738
778
|
}
|
|
739
779
|
}
|
|
740
|
-
const
|
|
741
|
-
const wnaf = wNAF(Point,
|
|
780
|
+
const { endo, nBitLength } = CURVE;
|
|
781
|
+
const wnaf = wNAF(Point, endo ? Math.ceil(nBitLength / 2) : nBitLength);
|
|
742
782
|
return {
|
|
743
783
|
CURVE,
|
|
744
784
|
ProjectivePoint: Point as ProjConstructor<T>,
|
|
@@ -830,7 +870,7 @@ export type CurveFn = {
|
|
|
830
870
|
*/
|
|
831
871
|
export function weierstrass(curveDef: CurveType): CurveFn {
|
|
832
872
|
const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
|
|
833
|
-
const { Fp, n: CURVE_ORDER } = CURVE;
|
|
873
|
+
const { Fp, n: CURVE_ORDER, nByteLength, nBitLength } = CURVE;
|
|
834
874
|
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
|
|
835
875
|
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
|
|
836
876
|
|
|
@@ -893,8 +933,6 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
893
933
|
}
|
|
894
934
|
},
|
|
895
935
|
});
|
|
896
|
-
const numToNByteHex = (num: bigint): string =>
|
|
897
|
-
bytesToHex(numberToBytesBE(num, CURVE.nByteLength));
|
|
898
936
|
|
|
899
937
|
function isBiggerThanHalfOrder(number: bigint) {
|
|
900
938
|
const HALF = CURVE_ORDER >> _1n;
|
|
@@ -925,7 +963,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
925
963
|
|
|
926
964
|
// pair (bytes of r, bytes of s)
|
|
927
965
|
static fromCompact(hex: Hex) {
|
|
928
|
-
const l =
|
|
966
|
+
const l = nByteLength;
|
|
929
967
|
hex = ensureBytes('compactSignature', hex, l * 2);
|
|
930
968
|
return new Signature(slcNum(hex, 0, l), slcNum(hex, l, 2 * l));
|
|
931
969
|
}
|
|
@@ -954,7 +992,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
954
992
|
const radj = rec === 2 || rec === 3 ? r + CURVE.n : r;
|
|
955
993
|
if (radj >= Fp.ORDER) throw new Error('recovery id 2 or 3 invalid');
|
|
956
994
|
const prefix = (rec & 1) === 0 ? '02' : '03';
|
|
957
|
-
const R = Point.fromHex(prefix +
|
|
995
|
+
const R = Point.fromHex(prefix + numToSizedHex(radj, Fp.BYTES));
|
|
958
996
|
const ir = invN(radj); // r^-1
|
|
959
997
|
const u1 = modN(-h * ir); // -hr^-1
|
|
960
998
|
const u2 = modN(s * ir); // sr^-1
|
|
@@ -986,7 +1024,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
986
1024
|
return hexToBytes(this.toCompactHex());
|
|
987
1025
|
}
|
|
988
1026
|
toCompactHex() {
|
|
989
|
-
|
|
1027
|
+
const l = nByteLength;
|
|
1028
|
+
return numToSizedHex(this.r, l) + numToSizedHex(this.s, l);
|
|
990
1029
|
}
|
|
991
1030
|
}
|
|
992
1031
|
type RecoveredSignature = Signature & { recovery: number };
|
|
@@ -1039,14 +1078,19 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1039
1078
|
/**
|
|
1040
1079
|
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
1041
1080
|
*/
|
|
1042
|
-
function isProbPub(item: PrivKey | PubKey): boolean {
|
|
1043
|
-
|
|
1044
|
-
const str = typeof item === 'string';
|
|
1045
|
-
const len = (arr || str) && (item as Hex).length;
|
|
1046
|
-
if (arr) return len === compressedLen || len === uncompressedLen;
|
|
1047
|
-
if (str) return len === 2 * compressedLen || len === 2 * uncompressedLen;
|
|
1081
|
+
function isProbPub(item: PrivKey | PubKey): boolean | undefined {
|
|
1082
|
+
if (typeof item === 'bigint') return false;
|
|
1048
1083
|
if (item instanceof Point) return true;
|
|
1049
|
-
|
|
1084
|
+
const arr = ensureBytes('key', item);
|
|
1085
|
+
const len = arr.length;
|
|
1086
|
+
const fpl = Fp.BYTES;
|
|
1087
|
+
const compLen = fpl + 1; // e.g. 33 for 32
|
|
1088
|
+
const uncompLen = 2 * fpl + 1; // e.g. 65 for 32
|
|
1089
|
+
if (CURVE.allowedPrivateKeyLengths || nByteLength === compLen) {
|
|
1090
|
+
return undefined;
|
|
1091
|
+
} else {
|
|
1092
|
+
return len === compLen || len === uncompLen;
|
|
1093
|
+
}
|
|
1050
1094
|
}
|
|
1051
1095
|
|
|
1052
1096
|
/**
|
|
@@ -1060,8 +1104,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1060
1104
|
* @returns shared public key
|
|
1061
1105
|
*/
|
|
1062
1106
|
function getSharedSecret(privateA: PrivKey, publicB: Hex, isCompressed = true): Uint8Array {
|
|
1063
|
-
if (isProbPub(privateA)) throw new Error('first arg must be private key');
|
|
1064
|
-
if (
|
|
1107
|
+
if (isProbPub(privateA) === true) throw new Error('first arg must be private key');
|
|
1108
|
+
if (isProbPub(publicB) === false) throw new Error('second arg must be public key');
|
|
1065
1109
|
const b = Point.fromHex(publicB); // check for being on-curve
|
|
1066
1110
|
return b.multiply(normPrivateKeyToScalar(privateA)).toRawBytes(isCompressed);
|
|
1067
1111
|
}
|
|
@@ -1073,12 +1117,12 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1073
1117
|
const bits2int =
|
|
1074
1118
|
CURVE.bits2int ||
|
|
1075
1119
|
function (bytes: Uint8Array): bigint {
|
|
1076
|
-
// Our custom check "just in case"
|
|
1120
|
+
// Our custom check "just in case", for protection against DoS
|
|
1077
1121
|
if (bytes.length > 8192) throw new Error('input is too large');
|
|
1078
1122
|
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
|
1079
1123
|
// for some cases, since bytes.length * 8 is not actual bitLength.
|
|
1080
1124
|
const num = bytesToNumberBE(bytes); // check for == u8 done here
|
|
1081
|
-
const delta = bytes.length * 8 -
|
|
1125
|
+
const delta = bytes.length * 8 - nBitLength; // truncate to nBitLength leftmost bits
|
|
1082
1126
|
return delta > 0 ? num >> BigInt(delta) : num;
|
|
1083
1127
|
};
|
|
1084
1128
|
const bits2int_modN =
|
|
@@ -1087,14 +1131,14 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1087
1131
|
return modN(bits2int(bytes)); // can't use bytesToNumberBE here
|
|
1088
1132
|
};
|
|
1089
1133
|
// NOTE: pads output with zero as per spec
|
|
1090
|
-
const ORDER_MASK = bitMask(
|
|
1134
|
+
const ORDER_MASK = bitMask(nBitLength);
|
|
1091
1135
|
/**
|
|
1092
1136
|
* Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
|
|
1093
1137
|
*/
|
|
1094
1138
|
function int2octets(num: bigint): Uint8Array {
|
|
1095
|
-
aInRange('num < 2^' +
|
|
1139
|
+
aInRange('num < 2^' + nBitLength, num, _0n, ORDER_MASK);
|
|
1096
1140
|
// works with order, can have different size than numToField!
|
|
1097
|
-
return numberToBytesBE(num,
|
|
1141
|
+
return numberToBytesBE(num, nByteLength);
|
|
1098
1142
|
}
|
|
1099
1143
|
|
|
1100
1144
|
// Steps A, D of RFC6979 3.2
|
package/src/bls12-381.ts
CHANGED
|
@@ -28,12 +28,12 @@
|
|
|
28
28
|
* 3. Curve security level is about 120 bits as per [Barbulescu-Duquesne 2017](https://hal.science/hal-01534101/file/main.pdf)
|
|
29
29
|
* 4. Compatible with specs:
|
|
30
30
|
* [cfrg-pairing-friendly-curves-11](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11),
|
|
31
|
-
* [cfrg-bls-signature-05](https://
|
|
31
|
+
* [cfrg-bls-signature-05](https://www.rfc-editor.org/rfc/draft-irtf-cfrg-bls-signature-05),
|
|
32
32
|
* RFC 9380.
|
|
33
33
|
*
|
|
34
34
|
* ### Params
|
|
35
35
|
* To verify curve parameters, see
|
|
36
|
-
* [pairing-friendly-curves spec](https://
|
|
36
|
+
* [pairing-friendly-curves spec](https://www.rfc-editor.org/rfc/draft-irtf-cfrg-pairing-friendly-curves-11).
|
|
37
37
|
* Basic math is done over finite fields over p.
|
|
38
38
|
* More complicated math is done over polynominal extension fields.
|
|
39
39
|
* To simplify calculations in Fp12, we construct extension tower:
|
|
@@ -70,16 +70,16 @@ import {
|
|
|
70
70
|
bytesToNumberBE,
|
|
71
71
|
concatBytes as concatB,
|
|
72
72
|
ensureBytes,
|
|
73
|
-
type Hex,
|
|
74
73
|
numberToBytesBE,
|
|
74
|
+
type Hex,
|
|
75
75
|
} from './abstract/utils.ts';
|
|
76
76
|
// Types
|
|
77
77
|
import { isogenyMap } from './abstract/hash-to-curve.ts';
|
|
78
78
|
import type { Fp, Fp12, Fp2, Fp6 } from './abstract/tower.ts';
|
|
79
79
|
import { psiFrobenius, tower12 } from './abstract/tower.ts';
|
|
80
80
|
import {
|
|
81
|
-
type AffinePoint,
|
|
82
81
|
mapToCurveSimpleSWU,
|
|
82
|
+
type AffinePoint,
|
|
83
83
|
type ProjPointType,
|
|
84
84
|
} from './abstract/weierstrass.ts';
|
|
85
85
|
|
|
@@ -337,8 +337,7 @@ const G1_SWU = mapToCurveSimpleSWU(Fp, {
|
|
|
337
337
|
Z: Fp.create(BigInt(11)),
|
|
338
338
|
});
|
|
339
339
|
|
|
340
|
-
//
|
|
341
|
-
// Ψ(P) endomorphism
|
|
340
|
+
// GLV endomorphism Ψ(P), for fast cofactor clearing
|
|
342
341
|
const { G2psi, G2psi2 } = psiFrobenius(Fp, Fp2, Fp2.div(Fp2.ONE, Fp2.NONRESIDUE)); // 1/(u+1)
|
|
343
342
|
|
|
344
343
|
// Default hash_to_field options are for hash to G2.
|
|
@@ -476,28 +475,15 @@ export const bls12_381: CurveFn = bls({
|
|
|
476
475
|
// It returns false for shitty points.
|
|
477
476
|
// https://eprint.iacr.org/2021/1130.pdf
|
|
478
477
|
isTorsionFree: (c, point): boolean => {
|
|
479
|
-
//
|
|
480
|
-
const
|
|
478
|
+
// GLV endomorphism ψ(P)
|
|
479
|
+
const beta = BigInt(
|
|
481
480
|
'0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
|
|
482
481
|
);
|
|
483
|
-
const phi = new c(Fp.mul(point.px,
|
|
484
|
-
|
|
485
|
-
// todo: unroll
|
|
482
|
+
const phi = new c(Fp.mul(point.px, beta), point.py, point.pz);
|
|
483
|
+
// TODO: unroll
|
|
486
484
|
const xP = point.multiplyUnsafe(BLS_X).negate(); // [x]P
|
|
487
485
|
const u2P = xP.multiplyUnsafe(BLS_X); // [u2]P
|
|
488
486
|
return u2P.equals(phi);
|
|
489
|
-
|
|
490
|
-
// https://eprint.iacr.org/2019/814.pdf
|
|
491
|
-
// (z² − 1)/3
|
|
492
|
-
// const c1 = BigInt('0x396c8c005555e1560000000055555555');
|
|
493
|
-
// const P = this;
|
|
494
|
-
// const S = P.sigma();
|
|
495
|
-
// const Q = S.double();
|
|
496
|
-
// const S2 = S.sigma();
|
|
497
|
-
// // [(z² − 1)/3](2σ(P) − P − σ²(P)) − σ²(P) = O
|
|
498
|
-
// const left = Q.subtract(P).subtract(S2).multiplyUnsafe(c1);
|
|
499
|
-
// const C = left.subtract(S2);
|
|
500
|
-
// return C.isZero();
|
|
501
487
|
},
|
|
502
488
|
// Clear cofactor of G1
|
|
503
489
|
// https://eprint.iacr.org/2019/403
|
|
@@ -627,14 +613,12 @@ export const bls12_381: CurveFn = bls({
|
|
|
627
613
|
// point.isTorsionFree() should return true for valid points
|
|
628
614
|
// It returns false for shitty points.
|
|
629
615
|
// https://eprint.iacr.org/2021/1130.pdf
|
|
616
|
+
// Older version: https://eprint.iacr.org/2019/814.pdf
|
|
630
617
|
isTorsionFree: (c, P): boolean => {
|
|
631
618
|
return P.multiplyUnsafe(BLS_X).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
|
|
632
|
-
// Older version: https://eprint.iacr.org/2019/814.pdf
|
|
633
|
-
// Ψ²(P) => Ψ³(P) => [z]Ψ³(P) where z = -x => [z]Ψ³(P) - Ψ²(P) + P == O
|
|
634
|
-
// return P.psi2().psi().mulNegX().subtract(psi2).add(P).isZero();
|
|
635
619
|
},
|
|
636
620
|
// Maps the point into the prime-order subgroup G2.
|
|
637
|
-
// clear_cofactor_bls12381_g2 from
|
|
621
|
+
// clear_cofactor_bls12381_g2 from RFC 9380.
|
|
638
622
|
// https://eprint.iacr.org/2017/419.pdf
|
|
639
623
|
// prettier-ignore
|
|
640
624
|
clearCofactor: (c, P) => {
|
package/src/ed25519.ts
CHANGED
|
@@ -179,10 +179,7 @@ export const ed25519ph: CurveFn = /* @__PURE__ */ (() =>
|
|
|
179
179
|
export const x25519: XCurveFn = /* @__PURE__ */ (() =>
|
|
180
180
|
montgomery({
|
|
181
181
|
P: ED25519_P,
|
|
182
|
-
|
|
183
|
-
montgomeryBits: 255, // n is 253 bits
|
|
184
|
-
nByteLength: 32,
|
|
185
|
-
Gu: BigInt(9),
|
|
182
|
+
type: 'x25519',
|
|
186
183
|
powPminus2: (x: bigint): bigint => {
|
|
187
184
|
const P = ED25519_P;
|
|
188
185
|
// x^(p-2) aka x^(2^255-21)
|
|
@@ -345,8 +342,11 @@ const bytes255ToNumberLE = (bytes: Uint8Array) =>
|
|
|
345
342
|
|
|
346
343
|
type ExtendedPoint = ExtPointType;
|
|
347
344
|
|
|
348
|
-
|
|
349
|
-
|
|
345
|
+
/**
|
|
346
|
+
* Computes Elligator map for Ristretto255.
|
|
347
|
+
* Described in [RFC9380](https://www.rfc-editor.org/rfc/rfc9380#appendix-B) and on
|
|
348
|
+
* the [website](https://ristretto.group/formulas/elligator.html).
|
|
349
|
+
*/
|
|
350
350
|
function calcElligatorRistrettoMap(r0: bigint): ExtendedPoint {
|
|
351
351
|
const { d } = ed25519.CURVE;
|
|
352
352
|
const P = ed25519.CURVE.Fp.ORDER;
|
|
@@ -374,7 +374,7 @@ function calcElligatorRistrettoMap(r0: bigint): ExtendedPoint {
|
|
|
374
374
|
* a source of bugs for protocols like ring signatures. Ristretto was created to solve this.
|
|
375
375
|
* Ristretto point operates in X:Y:Z:T extended coordinates like ExtendedPoint,
|
|
376
376
|
* but it should work in its own namespace: do not combine those two.
|
|
377
|
-
* https://
|
|
377
|
+
* See [RFC9496](https://www.rfc-editor.org/rfc/rfc9496).
|
|
378
378
|
*/
|
|
379
379
|
class RistPoint implements Group<RistPoint> {
|
|
380
380
|
static BASE: RistPoint;
|
|
@@ -394,7 +394,8 @@ class RistPoint implements Group<RistPoint> {
|
|
|
394
394
|
* Takes uniform output of 64-byte hash function like sha512 and converts it to `RistrettoPoint`.
|
|
395
395
|
* The hash-to-group operation applies Elligator twice and adds the results.
|
|
396
396
|
* **Note:** this is one-way map, there is no conversion from point to hash.
|
|
397
|
-
* https://
|
|
397
|
+
* Described in [RFC9380](https://www.rfc-editor.org/rfc/rfc9380#appendix-B) and on
|
|
398
|
+
* the [website](https://ristretto.group/formulas/elligator.html).
|
|
398
399
|
* @param hex 64-byte output of a hash function
|
|
399
400
|
*/
|
|
400
401
|
static hashToCurve(hex: Hex): RistPoint {
|
|
@@ -408,7 +409,7 @@ class RistPoint implements Group<RistPoint> {
|
|
|
408
409
|
|
|
409
410
|
/**
|
|
410
411
|
* Converts ristretto-encoded string to ristretto point.
|
|
411
|
-
* https://
|
|
412
|
+
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-decode).
|
|
412
413
|
* @param hex Ristretto-encoded 32 bytes. Not every 32-byte string is valid ristretto encoding
|
|
413
414
|
*/
|
|
414
415
|
static fromHex(hex: Hex): RistPoint {
|
|
@@ -445,7 +446,7 @@ class RistPoint implements Group<RistPoint> {
|
|
|
445
446
|
|
|
446
447
|
/**
|
|
447
448
|
* Encodes ristretto point to Uint8Array.
|
|
448
|
-
* https://
|
|
449
|
+
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-encode).
|
|
449
450
|
*/
|
|
450
451
|
toRawBytes(): Uint8Array {
|
|
451
452
|
let { ex: x, ey: y, ez: z, et: t } = this.ep;
|
|
@@ -483,7 +484,10 @@ class RistPoint implements Group<RistPoint> {
|
|
|
483
484
|
return this.toHex();
|
|
484
485
|
}
|
|
485
486
|
|
|
486
|
-
|
|
487
|
+
/**
|
|
488
|
+
* Compares two Ristretto points.
|
|
489
|
+
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-equals).
|
|
490
|
+
*/
|
|
487
491
|
equals(other: RistPoint): boolean {
|
|
488
492
|
aristp(other);
|
|
489
493
|
const { ex: X1, ey: Y1 } = this.ep;
|
|
@@ -521,13 +525,21 @@ class RistPoint implements Group<RistPoint> {
|
|
|
521
525
|
return new RistPoint(this.ep.negate());
|
|
522
526
|
}
|
|
523
527
|
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Wrapper over Edwards Point for ristretto255 from
|
|
531
|
+
* [RFC9496](https://www.rfc-editor.org/rfc/rfc9496).
|
|
532
|
+
*/
|
|
524
533
|
export const RistrettoPoint: typeof RistPoint = /* @__PURE__ */ (() => {
|
|
525
534
|
if (!RistPoint.BASE) RistPoint.BASE = new RistPoint(ed25519.ExtendedPoint.BASE);
|
|
526
535
|
if (!RistPoint.ZERO) RistPoint.ZERO = new RistPoint(ed25519.ExtendedPoint.ZERO);
|
|
527
536
|
return RistPoint;
|
|
528
537
|
})();
|
|
529
538
|
|
|
530
|
-
|
|
539
|
+
/**
|
|
540
|
+
* hash-to-curve for ristretto255.
|
|
541
|
+
* Described in [RFC9380](https://www.rfc-editor.org/rfc/rfc9380#appendix-B).
|
|
542
|
+
*/
|
|
531
543
|
export const hashToRistretto255 = (msg: Uint8Array, options: htfBasicOpts): RistPoint => {
|
|
532
544
|
const d = options.DST;
|
|
533
545
|
const DST = typeof d === 'string' ? utf8ToBytes(d) : d;
|