@noble/curves 1.5.0 → 1.7.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 +76 -25
- package/_shortw_utils.d.ts.map +1 -1
- package/abstract/bls.d.ts +7 -5
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +9 -9
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +37 -2
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +220 -22
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +3 -0
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +25 -9
- package/abstract/edwards.js.map +1 -1
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +7 -6
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +32 -21
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +5 -3
- package/abstract/montgomery.js.map +1 -1
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +22 -22
- package/abstract/poseidon.js.map +1 -1
- package/abstract/tower.d.ts +2 -0
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js +7 -6
- package/abstract/tower.js.map +1 -1
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +21 -23
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +19 -3
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +149 -71
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.js +8 -8
- package/bn254.d.ts +2 -1
- package/bn254.d.ts.map +1 -1
- package/bn254.js +9 -7
- package/bn254.js.map +1 -1
- package/ed448.js +1 -1
- package/ed448.js.map +1 -1
- package/esm/_shortw_utils.d.ts.map +1 -1
- package/esm/abstract/bls.d.ts +7 -5
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +9 -9
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +37 -2
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +219 -23
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +3 -0
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +27 -11
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/hash-to-curve.d.ts.map +1 -1
- package/esm/abstract/hash-to-curve.js +7 -6
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +32 -21
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +5 -3
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/poseidon.d.ts.map +1 -1
- package/esm/abstract/poseidon.js +22 -22
- package/esm/abstract/poseidon.js.map +1 -1
- package/esm/abstract/tower.d.ts +2 -0
- package/esm/abstract/tower.d.ts.map +1 -1
- package/esm/abstract/tower.js +7 -6
- package/esm/abstract/tower.js.map +1 -1
- package/esm/abstract/utils.d.ts.map +1 -1
- package/esm/abstract/utils.js +21 -23
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +19 -3
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +150 -72
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.js +8 -8
- package/esm/bn254.d.ts +2 -1
- package/esm/bn254.d.ts.map +1 -1
- package/esm/bn254.js +7 -6
- package/esm/bn254.js.map +1 -1
- package/esm/ed448.js +1 -1
- package/esm/ed448.js.map +1 -1
- package/esm/jubjub.d.ts.map +1 -1
- package/esm/jubjub.js +8 -2
- package/esm/jubjub.js.map +1 -1
- package/esm/p256.d.ts.map +1 -1
- package/esm/p256.js +6 -6
- package/esm/p256.js.map +1 -1
- package/esm/p384.d.ts.map +1 -1
- package/esm/p384.js +6 -6
- package/esm/p384.js.map +1 -1
- package/esm/p521.d.ts.map +1 -1
- package/esm/p521.js +7 -7
- package/esm/p521.js.map +1 -1
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +8 -8
- package/esm/secp256k1.js.map +1 -1
- package/jubjub.d.ts.map +1 -1
- package/jubjub.js +8 -2
- package/jubjub.js.map +1 -1
- package/p256.d.ts.map +1 -1
- package/p256.js +6 -6
- package/p256.js.map +1 -1
- package/p384.d.ts.map +1 -1
- package/p384.js +6 -6
- package/p384.js.map +1 -1
- package/p521.d.ts.map +1 -1
- package/p521.js +7 -7
- package/p521.js.map +1 -1
- package/package.json +28 -20
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +8 -8
- package/secp256k1.js.map +1 -1
- package/src/abstract/bls.ts +25 -13
- package/src/abstract/curve.ts +228 -23
- package/src/abstract/edwards.ts +40 -11
- package/src/abstract/hash-to-curve.ts +5 -6
- package/src/abstract/modular.ts +29 -19
- package/src/abstract/montgomery.ts +5 -3
- package/src/abstract/poseidon.ts +20 -24
- package/src/abstract/tower.ts +8 -6
- package/src/abstract/utils.ts +18 -24
- package/src/abstract/weierstrass.ts +144 -64
- package/src/bls12-381.ts +9 -9
- package/src/bn254.ts +16 -7
- package/src/ed448.ts +1 -1
- package/src/jubjub.ts +7 -2
- package/src/p256.ts +6 -6
- package/src/p384.ts +6 -6
- package/src/p521.ts +7 -7
- package/src/secp256k1.ts +8 -8
package/src/abstract/curve.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
2
|
// Abelian group utilities
|
|
3
3
|
import { IField, validateField, nLength } from './modular.js';
|
|
4
|
-
import { validateObject } from './utils.js';
|
|
4
|
+
import { validateObject, bitLen } from './utils.js';
|
|
5
5
|
const _0n = BigInt(0);
|
|
6
6
|
const _1n = BigInt(1);
|
|
7
7
|
|
|
@@ -25,11 +25,45 @@ export type GroupConstructor<T> = {
|
|
|
25
25
|
};
|
|
26
26
|
export type Mapper<T> = (i: T[]) => T[];
|
|
27
27
|
|
|
28
|
+
function constTimeNegate<T extends Group<T>>(condition: boolean, item: T): T {
|
|
29
|
+
const neg = item.negate();
|
|
30
|
+
return condition ? neg : item;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function validateW(W: number, bits: number) {
|
|
34
|
+
if (!Number.isSafeInteger(W) || W <= 0 || W > bits)
|
|
35
|
+
throw new Error('invalid window size, expected [1..' + bits + '], got W=' + W);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function calcWOpts(W: number, bits: number) {
|
|
39
|
+
validateW(W, bits);
|
|
40
|
+
const windows = Math.ceil(bits / W) + 1; // +1, because
|
|
41
|
+
const windowSize = 2 ** (W - 1); // -1 because we skip zero
|
|
42
|
+
return { windows, windowSize };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function validateMSMPoints(points: any[], c: any) {
|
|
46
|
+
if (!Array.isArray(points)) throw new Error('array expected');
|
|
47
|
+
points.forEach((p, i) => {
|
|
48
|
+
if (!(p instanceof c)) throw new Error('invalid point at index ' + i);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function validateMSMScalars(scalars: any[], field: any) {
|
|
52
|
+
if (!Array.isArray(scalars)) throw new Error('array of scalars expected');
|
|
53
|
+
scalars.forEach((s, i) => {
|
|
54
|
+
if (!field.isValid(s)) throw new Error('invalid scalar at index ' + i);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
28
58
|
// Since points in different groups cannot be equal (different object constructor),
|
|
29
59
|
// we can have single place to store precomputes
|
|
30
60
|
const pointPrecomputes = new WeakMap<any, any[]>();
|
|
31
61
|
const pointWindowSizes = new WeakMap<any, number>(); // This allows use make points immutable (nothing changes inside)
|
|
32
62
|
|
|
63
|
+
function getW(P: any): number {
|
|
64
|
+
return pointWindowSizes.get(P) || 1;
|
|
65
|
+
}
|
|
66
|
+
|
|
33
67
|
// Elliptic curve multiplication of Point by scalar. Fragile.
|
|
34
68
|
// Scalars should always be less than curve order: this should be checked inside of a curve itself.
|
|
35
69
|
// Creates precomputation tables for fast multiplication:
|
|
@@ -42,25 +76,15 @@ const pointWindowSizes = new WeakMap<any, number>(); // This allows use make poi
|
|
|
42
76
|
// TODO: Research returning 2d JS array of windows, instead of a single window. This would allow
|
|
43
77
|
// windows to be in different memory locations
|
|
44
78
|
export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
|
45
|
-
const constTimeNegate = (condition: boolean, item: T): T => {
|
|
46
|
-
const neg = item.negate();
|
|
47
|
-
return condition ? neg : item;
|
|
48
|
-
};
|
|
49
|
-
const validateW = (W: number) => {
|
|
50
|
-
if (!Number.isSafeInteger(W) || W <= 0 || W > bits)
|
|
51
|
-
throw new Error(`Wrong window size=${W}, should be [1..${bits}]`);
|
|
52
|
-
};
|
|
53
|
-
const opts = (W: number) => {
|
|
54
|
-
validateW(W);
|
|
55
|
-
const windows = Math.ceil(bits / W) + 1; // +1, because
|
|
56
|
-
const windowSize = 2 ** (W - 1); // -1 because we skip zero
|
|
57
|
-
return { windows, windowSize };
|
|
58
|
-
};
|
|
59
79
|
return {
|
|
60
80
|
constTimeNegate,
|
|
81
|
+
|
|
82
|
+
hasPrecomputes(elm: T) {
|
|
83
|
+
return getW(elm) !== 1;
|
|
84
|
+
},
|
|
85
|
+
|
|
61
86
|
// non-const time multiplication ladder
|
|
62
|
-
unsafeLadder(elm: T, n: bigint) {
|
|
63
|
-
let p = c.ZERO;
|
|
87
|
+
unsafeLadder(elm: T, n: bigint, p = c.ZERO) {
|
|
64
88
|
let d: T = elm;
|
|
65
89
|
while (n > _0n) {
|
|
66
90
|
if (n & _1n) p = p.add(d);
|
|
@@ -78,10 +102,12 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
|
|
78
102
|
* - 𝑊 is the window size
|
|
79
103
|
* - 𝑛 is the bitlength of the curve order.
|
|
80
104
|
* For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224.
|
|
105
|
+
* @param elm Point instance
|
|
106
|
+
* @param W window size
|
|
81
107
|
* @returns precomputed point tables flattened to a single array
|
|
82
108
|
*/
|
|
83
109
|
precomputeWindow(elm: T, W: number): Group<T>[] {
|
|
84
|
-
const { windows, windowSize } =
|
|
110
|
+
const { windows, windowSize } = calcWOpts(W, bits);
|
|
85
111
|
const points: T[] = [];
|
|
86
112
|
let p: T = elm;
|
|
87
113
|
let base = p;
|
|
@@ -108,7 +134,7 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
|
|
108
134
|
wNAF(W: number, precomputes: T[], n: bigint): { p: T; f: T } {
|
|
109
135
|
// TODO: maybe check that scalar is less than group order? wNAF behavious is undefined otherwise
|
|
110
136
|
// But need to carefully remove other checks before wNAF. ORDER == bits here
|
|
111
|
-
const { windows, windowSize } =
|
|
137
|
+
const { windows, windowSize } = calcWOpts(W, bits);
|
|
112
138
|
|
|
113
139
|
let p = c.ZERO;
|
|
114
140
|
let f = c.BASE;
|
|
@@ -159,28 +185,207 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
|
|
159
185
|
return { p, f };
|
|
160
186
|
},
|
|
161
187
|
|
|
162
|
-
|
|
163
|
-
|
|
188
|
+
/**
|
|
189
|
+
* Implements ec unsafe (non const-time) multiplication using precomputed tables and w-ary non-adjacent form.
|
|
190
|
+
* @param W window size
|
|
191
|
+
* @param precomputes precomputed tables
|
|
192
|
+
* @param n scalar (we don't check here, but should be less than curve order)
|
|
193
|
+
* @param acc accumulator point to add result of multiplication
|
|
194
|
+
* @returns point
|
|
195
|
+
*/
|
|
196
|
+
wNAFUnsafe(W: number, precomputes: T[], n: bigint, acc: T = c.ZERO): T {
|
|
197
|
+
const { windows, windowSize } = calcWOpts(W, bits);
|
|
198
|
+
const mask = BigInt(2 ** W - 1); // Create mask with W ones: 0b1111 for W=4 etc.
|
|
199
|
+
const maxNumber = 2 ** W;
|
|
200
|
+
const shiftBy = BigInt(W);
|
|
201
|
+
for (let window = 0; window < windows; window++) {
|
|
202
|
+
const offset = window * windowSize;
|
|
203
|
+
if (n === _0n) break; // No need to go over empty scalar
|
|
204
|
+
// Extract W bits.
|
|
205
|
+
let wbits = Number(n & mask);
|
|
206
|
+
// Shift number by W bits.
|
|
207
|
+
n >>= shiftBy;
|
|
208
|
+
// If the bits are bigger than max size, we'll split those.
|
|
209
|
+
// +224 => 256 - 32
|
|
210
|
+
if (wbits > windowSize) {
|
|
211
|
+
wbits -= maxNumber;
|
|
212
|
+
n += _1n;
|
|
213
|
+
}
|
|
214
|
+
if (wbits === 0) continue;
|
|
215
|
+
let curr = precomputes[offset + Math.abs(wbits) - 1]; // -1 because we skip zero
|
|
216
|
+
if (wbits < 0) curr = curr.negate();
|
|
217
|
+
// NOTE: by re-using acc, we can save a lot of additions in case of MSM
|
|
218
|
+
acc = acc.add(curr);
|
|
219
|
+
}
|
|
220
|
+
return acc;
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
getPrecomputes(W: number, P: T, transform: Mapper<T>): T[] {
|
|
164
224
|
// Calculate precomputes on a first run, reuse them after
|
|
165
225
|
let comp = pointPrecomputes.get(P);
|
|
166
226
|
if (!comp) {
|
|
167
227
|
comp = this.precomputeWindow(P, W) as T[];
|
|
168
228
|
if (W !== 1) pointPrecomputes.set(P, transform(comp));
|
|
169
229
|
}
|
|
170
|
-
return
|
|
230
|
+
return comp;
|
|
171
231
|
},
|
|
232
|
+
|
|
233
|
+
wNAFCached(P: T, n: bigint, transform: Mapper<T>): { p: T; f: T } {
|
|
234
|
+
const W = getW(P);
|
|
235
|
+
return this.wNAF(W, this.getPrecomputes(W, P, transform), n);
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
wNAFCachedUnsafe(P: T, n: bigint, transform: Mapper<T>, prev?: T): T {
|
|
239
|
+
const W = getW(P);
|
|
240
|
+
if (W === 1) return this.unsafeLadder(P, n, prev); // For W=1 ladder is ~x2 faster
|
|
241
|
+
return this.wNAFUnsafe(W, this.getPrecomputes(W, P, transform), n, prev);
|
|
242
|
+
},
|
|
243
|
+
|
|
172
244
|
// We calculate precomputes for elliptic curve point multiplication
|
|
173
245
|
// using windowed method. This specifies window size and
|
|
174
246
|
// stores precomputed values. Usually only base point would be precomputed.
|
|
175
247
|
|
|
176
248
|
setWindowSize(P: T, W: number) {
|
|
177
|
-
validateW(W);
|
|
249
|
+
validateW(W, bits);
|
|
178
250
|
pointWindowSizes.set(P, W);
|
|
179
251
|
pointPrecomputes.delete(P);
|
|
180
252
|
},
|
|
181
253
|
};
|
|
182
254
|
}
|
|
183
255
|
|
|
256
|
+
/**
|
|
257
|
+
* Pippenger algorithm for multi-scalar multiplication (MSM, Pa + Qb + Rc + ...).
|
|
258
|
+
* 30x faster vs naive addition on L=4096, 10x faster with precomputes.
|
|
259
|
+
* For N=254bit, L=1, it does: 1024 ADD + 254 DBL. For L=5: 1536 ADD + 254 DBL.
|
|
260
|
+
* Algorithmically constant-time (for same L), even when 1 point + scalar, or when scalar = 0.
|
|
261
|
+
* @param c Curve Point constructor
|
|
262
|
+
* @param fieldN field over CURVE.N - important that it's not over CURVE.P
|
|
263
|
+
* @param points array of L curve points
|
|
264
|
+
* @param scalars array of L scalars (aka private keys / bigints)
|
|
265
|
+
*/
|
|
266
|
+
export function pippenger<T extends Group<T>>(
|
|
267
|
+
c: GroupConstructor<T>,
|
|
268
|
+
fieldN: IField<bigint>,
|
|
269
|
+
points: T[],
|
|
270
|
+
scalars: bigint[]
|
|
271
|
+
): T {
|
|
272
|
+
// If we split scalars by some window (let's say 8 bits), every chunk will only
|
|
273
|
+
// take 256 buckets even if there are 4096 scalars, also re-uses double.
|
|
274
|
+
// TODO:
|
|
275
|
+
// - https://eprint.iacr.org/2024/750.pdf
|
|
276
|
+
// - https://tches.iacr.org/index.php/TCHES/article/view/10287
|
|
277
|
+
// 0 is accepted in scalars
|
|
278
|
+
validateMSMPoints(points, c);
|
|
279
|
+
validateMSMScalars(scalars, fieldN);
|
|
280
|
+
if (points.length !== scalars.length)
|
|
281
|
+
throw new Error('arrays of points and scalars must have equal length');
|
|
282
|
+
const zero = c.ZERO;
|
|
283
|
+
const wbits = bitLen(BigInt(points.length));
|
|
284
|
+
const windowSize = wbits > 12 ? wbits - 3 : wbits > 4 ? wbits - 2 : wbits ? 2 : 1; // in bits
|
|
285
|
+
const MASK = (1 << windowSize) - 1;
|
|
286
|
+
const buckets = new Array(MASK + 1).fill(zero); // +1 for zero array
|
|
287
|
+
const lastBits = Math.floor((fieldN.BITS - 1) / windowSize) * windowSize;
|
|
288
|
+
let sum = zero;
|
|
289
|
+
for (let i = lastBits; i >= 0; i -= windowSize) {
|
|
290
|
+
buckets.fill(zero);
|
|
291
|
+
for (let j = 0; j < scalars.length; j++) {
|
|
292
|
+
const scalar = scalars[j];
|
|
293
|
+
const wbits = Number((scalar >> BigInt(i)) & BigInt(MASK));
|
|
294
|
+
buckets[wbits] = buckets[wbits].add(points[j]);
|
|
295
|
+
}
|
|
296
|
+
let resI = zero; // not using this will do small speed-up, but will lose ct
|
|
297
|
+
// Skip first bucket, because it is zero
|
|
298
|
+
for (let j = buckets.length - 1, sumI = zero; j > 0; j--) {
|
|
299
|
+
sumI = sumI.add(buckets[j]);
|
|
300
|
+
resI = resI.add(sumI);
|
|
301
|
+
}
|
|
302
|
+
sum = sum.add(resI);
|
|
303
|
+
if (i !== 0) for (let j = 0; j < windowSize; j++) sum = sum.double();
|
|
304
|
+
}
|
|
305
|
+
return sum as T;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Precomputed multi-scalar multiplication (MSM, Pa + Qb + Rc + ...).
|
|
309
|
+
* @param c Curve Point constructor
|
|
310
|
+
* @param fieldN field over CURVE.N - important that it's not over CURVE.P
|
|
311
|
+
* @param points array of L curve points
|
|
312
|
+
* @returns function which multiplies points with scaars
|
|
313
|
+
*/
|
|
314
|
+
export function precomputeMSMUnsafe<T extends Group<T>>(
|
|
315
|
+
c: GroupConstructor<T>,
|
|
316
|
+
fieldN: IField<bigint>,
|
|
317
|
+
points: T[],
|
|
318
|
+
windowSize: number
|
|
319
|
+
) {
|
|
320
|
+
/**
|
|
321
|
+
* Performance Analysis of Window-based Precomputation
|
|
322
|
+
*
|
|
323
|
+
* Base Case (256-bit scalar, 8-bit window):
|
|
324
|
+
* - Standard precomputation requires:
|
|
325
|
+
* - 31 additions per scalar × 256 scalars = 7,936 ops
|
|
326
|
+
* - Plus 255 summary additions = 8,191 total ops
|
|
327
|
+
* Note: Summary additions can be optimized via accumulator
|
|
328
|
+
*
|
|
329
|
+
* Chunked Precomputation Analysis:
|
|
330
|
+
* - Using 32 chunks requires:
|
|
331
|
+
* - 255 additions per chunk
|
|
332
|
+
* - 256 doublings
|
|
333
|
+
* - Total: (255 × 32) + 256 = 8,416 ops
|
|
334
|
+
*
|
|
335
|
+
* Memory Usage Comparison:
|
|
336
|
+
* Window Size | Standard Points | Chunked Points
|
|
337
|
+
* ------------|-----------------|---------------
|
|
338
|
+
* 4-bit | 520 | 15
|
|
339
|
+
* 8-bit | 4,224 | 255
|
|
340
|
+
* 10-bit | 13,824 | 1,023
|
|
341
|
+
* 16-bit | 557,056 | 65,535
|
|
342
|
+
*
|
|
343
|
+
* Key Advantages:
|
|
344
|
+
* 1. Enables larger window sizes due to reduced memory overhead
|
|
345
|
+
* 2. More efficient for smaller scalar counts:
|
|
346
|
+
* - 16 chunks: (16 × 255) + 256 = 4,336 ops
|
|
347
|
+
* - ~2x faster than standard 8,191 ops
|
|
348
|
+
*
|
|
349
|
+
* Limitations:
|
|
350
|
+
* - Not suitable for plain precomputes (requires 256 constant doublings)
|
|
351
|
+
* - Performance degrades with larger scalar counts:
|
|
352
|
+
* - Optimal for ~256 scalars
|
|
353
|
+
* - Less efficient for 4096+ scalars (Pippenger preferred)
|
|
354
|
+
*/
|
|
355
|
+
validateW(windowSize, fieldN.BITS);
|
|
356
|
+
validateMSMPoints(points, c);
|
|
357
|
+
const zero = c.ZERO;
|
|
358
|
+
const tableSize = 2 ** windowSize - 1; // table size (without zero)
|
|
359
|
+
const chunks = Math.ceil(fieldN.BITS / windowSize); // chunks of item
|
|
360
|
+
const MASK = BigInt((1 << windowSize) - 1);
|
|
361
|
+
const tables = points.map((p: T) => {
|
|
362
|
+
const res = [];
|
|
363
|
+
for (let i = 0, acc = p; i < tableSize; i++) {
|
|
364
|
+
res.push(acc);
|
|
365
|
+
acc = acc.add(p);
|
|
366
|
+
}
|
|
367
|
+
return res;
|
|
368
|
+
});
|
|
369
|
+
return (scalars: bigint[]): T => {
|
|
370
|
+
validateMSMScalars(scalars, fieldN);
|
|
371
|
+
if (scalars.length > points.length)
|
|
372
|
+
throw new Error('array of scalars must be smaller than array of points');
|
|
373
|
+
let res = zero;
|
|
374
|
+
for (let i = 0; i < chunks; i++) {
|
|
375
|
+
// No need to double if accumulator is still zero.
|
|
376
|
+
if (res !== zero) for (let j = 0; j < windowSize; j++) res = res.double();
|
|
377
|
+
const shiftBy = BigInt(chunks * windowSize - (i + 1) * windowSize);
|
|
378
|
+
for (let j = 0; j < scalars.length; j++) {
|
|
379
|
+
const n = scalars[j];
|
|
380
|
+
const curr = Number((n >> shiftBy) & MASK);
|
|
381
|
+
if (!curr) continue; // skip zero scalars chunks
|
|
382
|
+
res = res.add(tables[j][curr - 1]);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return res;
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
184
389
|
// Generic BasicCurve interface: works even for polynomial fields (BLS): P, n, h would be ok.
|
|
185
390
|
// Though generator can be different (Fp2 / Fp6 for BLS).
|
|
186
391
|
export type BasicCurve<T> = {
|
package/src/abstract/edwards.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
2
|
// Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
AffinePoint,
|
|
5
|
+
BasicCurve,
|
|
6
|
+
Group,
|
|
7
|
+
GroupConstructor,
|
|
8
|
+
validateBasic,
|
|
9
|
+
wNAF,
|
|
10
|
+
pippenger,
|
|
11
|
+
} from './curve.js';
|
|
12
|
+
import { mod, Field } from './modular.js';
|
|
5
13
|
import * as ut from './utils.js';
|
|
6
14
|
import { ensureBytes, FHash, Hex, memoized, abool } from './utils.js';
|
|
7
15
|
|
|
@@ -63,6 +71,7 @@ export interface ExtPointType extends Group<ExtPointType> {
|
|
|
63
71
|
toAffine(iz?: bigint): AffinePoint<bigint>;
|
|
64
72
|
toRawBytes(isCompressed?: boolean): Uint8Array;
|
|
65
73
|
toHex(isCompressed?: boolean): string;
|
|
74
|
+
_setWindowSize(windowSize: number): void;
|
|
66
75
|
}
|
|
67
76
|
// Static methods of Extended Point with coordinates in X, Y, Z, T
|
|
68
77
|
export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
|
|
@@ -70,6 +79,7 @@ export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
|
|
|
70
79
|
fromAffine(p: AffinePoint<bigint>): ExtPointType;
|
|
71
80
|
fromHex(hex: Hex): ExtPointType;
|
|
72
81
|
fromPrivateKey(privateKey: Hex): ExtPointType;
|
|
82
|
+
msm(points: ExtPointType[], scalars: bigint[]): ExtPointType;
|
|
73
83
|
}
|
|
74
84
|
|
|
75
85
|
/**
|
|
@@ -96,6 +106,7 @@ export type CurveFn = {
|
|
|
96
106
|
point: ExtPointType;
|
|
97
107
|
pointBytes: Uint8Array;
|
|
98
108
|
};
|
|
109
|
+
precompute: (windowSize?: number, point?: ExtPointType) => ExtPointType;
|
|
99
110
|
};
|
|
100
111
|
};
|
|
101
112
|
|
|
@@ -117,8 +128,13 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|
|
117
128
|
nByteLength,
|
|
118
129
|
h: cofactor,
|
|
119
130
|
} = CURVE;
|
|
131
|
+
// Important:
|
|
132
|
+
// There are some places where Fp.BYTES is used instead of nByteLength.
|
|
133
|
+
// So far, everything has been tested with curves of Fp.BYTES == nByteLength.
|
|
134
|
+
// TODO: test and find curves which behave otherwise.
|
|
120
135
|
const MASK = _2n << (BigInt(nByteLength * 8) - _1n);
|
|
121
136
|
const modP = Fp.create; // Function overrides
|
|
137
|
+
const Fn = Field(CURVE.n, CURVE.nBitLength);
|
|
122
138
|
|
|
123
139
|
// sqrt(u/v)
|
|
124
140
|
const uvRatio =
|
|
@@ -218,6 +234,10 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|
|
218
234
|
const toInv = Fp.invertBatch(points.map((p) => p.ez));
|
|
219
235
|
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
220
236
|
}
|
|
237
|
+
// Multiscalar Multiplication
|
|
238
|
+
static msm(points: Point[], scalars: bigint[]): Point {
|
|
239
|
+
return pippenger(Point, Fn, points, scalars);
|
|
240
|
+
}
|
|
221
241
|
|
|
222
242
|
// "Private method", don't use it directly
|
|
223
243
|
_setWindowSize(windowSize: number) {
|
|
@@ -336,13 +356,13 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|
|
336
356
|
// It's faster, but should only be used when you don't care about
|
|
337
357
|
// an exposed private key e.g. sig verification.
|
|
338
358
|
// Does NOT allow scalars higher than CURVE.n.
|
|
339
|
-
|
|
359
|
+
// Accepts optional accumulator to merge with multiply (important for sparse scalars)
|
|
360
|
+
multiplyUnsafe(scalar: bigint, acc = Point.ZERO): Point {
|
|
340
361
|
const n = scalar;
|
|
341
362
|
ut.aInRange('scalar', n, _0n, CURVE_ORDER); // 0 <= scalar < L
|
|
342
363
|
if (n === _0n) return I;
|
|
343
|
-
if (this.
|
|
344
|
-
|
|
345
|
-
return wnaf.unsafeLadder(this, n);
|
|
364
|
+
if (this.is0() || n === _1n) return this;
|
|
365
|
+
return wnaf.wNAFCachedUnsafe(this, n, Point.normalizeZ, acc);
|
|
346
366
|
}
|
|
347
367
|
|
|
348
368
|
// Checks if point is of small order.
|
|
@@ -383,6 +403,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|
|
383
403
|
normed[len - 1] = lastByte & ~0x80; // clear last bit
|
|
384
404
|
const y = ut.bytesToNumberLE(normed);
|
|
385
405
|
|
|
406
|
+
// zip215=true is good for consensus-critical apps. =false follows RFC8032 / NIST186-5.
|
|
386
407
|
// RFC8032 prohibits >= p, but ZIP215 doesn't
|
|
387
408
|
// zip215=true: 0 <= y < MASK (2^256 for ed25519)
|
|
388
409
|
// zip215=false: 0 <= y < P (2^255-19 for ed25519)
|
|
@@ -430,7 +451,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|
|
430
451
|
|
|
431
452
|
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
|
|
432
453
|
function getExtendedPublicKey(key: Hex) {
|
|
433
|
-
const len =
|
|
454
|
+
const len = Fp.BYTES;
|
|
434
455
|
key = ensureBytes('private key', key, len);
|
|
435
456
|
// Hash private key with curve's hash function to produce uniformingly random input
|
|
436
457
|
// Check byte lengths: ensure(64, h(ensure(32, key)))
|
|
@@ -465,23 +486,30 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|
|
465
486
|
const s = modN(r + k * scalar); // S = (r + k * s) mod L
|
|
466
487
|
ut.aInRange('signature.s', s, _0n, CURVE_ORDER); // 0 <= s < l
|
|
467
488
|
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
|
|
468
|
-
return ensureBytes('result', res,
|
|
489
|
+
return ensureBytes('result', res, Fp.BYTES * 2); // 64-byte signature
|
|
469
490
|
}
|
|
470
491
|
|
|
471
492
|
const verifyOpts: { context?: Hex; zip215?: boolean } = VERIFY_DEFAULT;
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
|
|
496
|
+
* An extended group equation is checked.
|
|
497
|
+
*/
|
|
472
498
|
function verify(sig: Hex, msg: Hex, publicKey: Hex, options = verifyOpts): boolean {
|
|
473
499
|
const { context, zip215 } = options;
|
|
474
500
|
const len = Fp.BYTES; // Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
|
|
475
501
|
sig = ensureBytes('signature', sig, 2 * len); // An extended group equation is checked.
|
|
476
502
|
msg = ensureBytes('message', msg);
|
|
503
|
+
publicKey = ensureBytes('publicKey', publicKey, len);
|
|
477
504
|
if (zip215 !== undefined) abool('zip215', zip215);
|
|
478
505
|
if (prehash) msg = prehash(msg); // for ed25519ph, etc
|
|
479
506
|
|
|
480
507
|
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len));
|
|
481
|
-
// zip215: true is good for consensus-critical apps and allows points < 2^256
|
|
482
|
-
// zip215: false follows RFC8032 / NIST186-5 and restricts points to CURVE.p
|
|
483
508
|
let A, R, SB;
|
|
484
509
|
try {
|
|
510
|
+
// zip215=true is good for consensus-critical apps. =false follows RFC8032 / NIST186-5.
|
|
511
|
+
// zip215=true: 0 <= y < MASK (2^256 for ed25519)
|
|
512
|
+
// zip215=false: 0 <= y < P (2^255-19 for ed25519)
|
|
485
513
|
A = Point.fromHex(publicKey, zip215);
|
|
486
514
|
R = Point.fromHex(sig.slice(0, len), zip215);
|
|
487
515
|
SB = G.multiplyUnsafe(s); // 0 <= s < l is done inside
|
|
@@ -492,6 +520,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|
|
492
520
|
|
|
493
521
|
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
|
|
494
522
|
const RkA = R.add(A.multiplyUnsafe(k));
|
|
523
|
+
// Extended group equation
|
|
495
524
|
// [8][S]B = [8]R + [8][k]A'
|
|
496
525
|
return RkA.subtract(SB).clearCofactor().equals(Point.ZERO);
|
|
497
526
|
}
|
|
@@ -509,7 +538,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|
|
509
538
|
* but allows to speed-up subsequent getPublicKey() calls up to 20x.
|
|
510
539
|
* @param windowSize 2, 4, 8, 16
|
|
511
540
|
*/
|
|
512
|
-
precompute(windowSize = 8, point = Point.BASE):
|
|
541
|
+
precompute(windowSize = 8, point: ExtPointType = Point.BASE): ExtPointType {
|
|
513
542
|
point._setWindowSize(windowSize);
|
|
514
543
|
point.multiply(BigInt(3));
|
|
515
544
|
return point;
|
|
@@ -27,9 +27,9 @@ const os2ip = bytesToNumberBE;
|
|
|
27
27
|
|
|
28
28
|
// Integer to Octet Stream (numberToBytesBE)
|
|
29
29
|
function i2osp(value: number, length: number): Uint8Array {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
anum(value);
|
|
31
|
+
anum(length);
|
|
32
|
+
if (value < 0 || value >= 1 << (8 * length)) throw new Error('invalid I2OSP input: ' + value);
|
|
33
33
|
const res = Array.from({ length }).fill(0) as number[];
|
|
34
34
|
for (let i = length - 1; i >= 0; i--) {
|
|
35
35
|
res[i] = value & 0xff;
|
|
@@ -65,7 +65,7 @@ export function expand_message_xmd(
|
|
|
65
65
|
if (DST.length > 255) DST = H(concatBytes(utf8ToBytes('H2C-OVERSIZE-DST-'), DST));
|
|
66
66
|
const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H;
|
|
67
67
|
const ell = Math.ceil(lenInBytes / b_in_bytes);
|
|
68
|
-
if (ell > 255) throw new Error('
|
|
68
|
+
if (lenInBytes > 65535 || ell > 255) throw new Error('expand_message_xmd: invalid lenInBytes');
|
|
69
69
|
const DST_prime = concatBytes(DST, i2osp(DST.length, 1));
|
|
70
70
|
const Z_pad = i2osp(0, r_in_bytes);
|
|
71
71
|
const l_i_b_str = i2osp(lenInBytes, 2); // len_in_bytes_str
|
|
@@ -221,8 +221,7 @@ export function createHasher<T>(
|
|
|
221
221
|
mapToCurve(scalars: bigint[]) {
|
|
222
222
|
if (!Array.isArray(scalars)) throw new Error('mapToCurve: expected array of bigints');
|
|
223
223
|
for (const i of scalars)
|
|
224
|
-
if (typeof i !== 'bigint')
|
|
225
|
-
throw new Error(`mapToCurve: expected array of bigints, got ${i} in array`);
|
|
224
|
+
if (typeof i !== 'bigint') throw new Error('mapToCurve: expected array of bigints');
|
|
226
225
|
const P = Point.fromAffine(mapToCurve(scalars)).clearCofactor();
|
|
227
226
|
P.assertValidity();
|
|
228
227
|
return P;
|
package/src/abstract/modular.ts
CHANGED
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
validateObject,
|
|
11
11
|
} from './utils.js';
|
|
12
12
|
// prettier-ignore
|
|
13
|
-
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
|
13
|
+
const _0n = BigInt(0), _1n = BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _3n = /* @__PURE__ */ BigInt(3);
|
|
14
14
|
// prettier-ignore
|
|
15
|
-
const _4n = BigInt(4), _5n = BigInt(5), _8n = BigInt(8);
|
|
15
|
+
const _4n = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5), _8n = /* @__PURE__ */ BigInt(8);
|
|
16
16
|
// prettier-ignore
|
|
17
|
-
const _9n
|
|
17
|
+
const _9n =/* @__PURE__ */ BigInt(9), _16n = /* @__PURE__ */ BigInt(16);
|
|
18
18
|
|
|
19
19
|
// Calculates a modulo b
|
|
20
20
|
export function mod(a: bigint, b: bigint): bigint {
|
|
@@ -29,7 +29,8 @@ export function mod(a: bigint, b: bigint): bigint {
|
|
|
29
29
|
*/
|
|
30
30
|
// TODO: use field version && remove
|
|
31
31
|
export function pow(num: bigint, power: bigint, modulo: bigint): bigint {
|
|
32
|
-
if (
|
|
32
|
+
if (power < _0n) throw new Error('invalid exponent, negatives unsupported');
|
|
33
|
+
if (modulo <= _0n) throw new Error('invalid modulus');
|
|
33
34
|
if (modulo === _1n) return _0n;
|
|
34
35
|
let res = _1n;
|
|
35
36
|
while (power > _0n) {
|
|
@@ -52,9 +53,8 @@ export function pow2(x: bigint, power: bigint, modulo: bigint): bigint {
|
|
|
52
53
|
|
|
53
54
|
// Inverses number over modulo
|
|
54
55
|
export function invert(number: bigint, modulo: bigint): bigint {
|
|
55
|
-
if (number === _0n
|
|
56
|
-
|
|
57
|
-
}
|
|
56
|
+
if (number === _0n) throw new Error('invert: expected non-zero number');
|
|
57
|
+
if (modulo <= _0n) throw new Error('invert: expected positive modulus, got ' + modulo);
|
|
58
58
|
// Euclidean GCD https://brilliant.org/wiki/extended-euclidean-algorithm/
|
|
59
59
|
// Fermat's little theorem "CT-like" version inv(n) = n^(m-2) mod m is 30x slower.
|
|
60
60
|
let a = mod(number, modulo);
|
|
@@ -97,7 +97,10 @@ export function tonelliShanks(P: bigint) {
|
|
|
97
97
|
for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++);
|
|
98
98
|
|
|
99
99
|
// Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
|
|
100
|
-
for (Z = _2n; Z < P && pow(Z, legendreC, P) !== P - _1n; Z++)
|
|
100
|
+
for (Z = _2n; Z < P && pow(Z, legendreC, P) !== P - _1n; Z++) {
|
|
101
|
+
// Crash instead of infinity loop, we cannot reasonable count until P.
|
|
102
|
+
if (Z > 1000) throw new Error('Cannot find square root: likely non-prime P');
|
|
103
|
+
}
|
|
101
104
|
|
|
102
105
|
// Fast-path
|
|
103
106
|
if (S === 1) {
|
|
@@ -273,7 +276,7 @@ export function validateField<T>(field: IField<T>) {
|
|
|
273
276
|
export function FpPow<T>(f: IField<T>, num: T, power: bigint): T {
|
|
274
277
|
// Should have same speed as pow for bigints
|
|
275
278
|
// TODO: benchmark!
|
|
276
|
-
if (power < _0n) throw new Error('
|
|
279
|
+
if (power < _0n) throw new Error('invalid exponent, negatives unsupported');
|
|
277
280
|
if (power === _0n) return f.ONE;
|
|
278
281
|
if (power === _1n) return num;
|
|
279
282
|
let p = f.ONE;
|
|
@@ -360,10 +363,10 @@ export function Field(
|
|
|
360
363
|
isLE = false,
|
|
361
364
|
redef: Partial<IField<bigint>> = {}
|
|
362
365
|
): Readonly<FpField> {
|
|
363
|
-
if (ORDER <= _0n) throw new Error(
|
|
366
|
+
if (ORDER <= _0n) throw new Error('invalid field: expected ORDER > 0, got ' + ORDER);
|
|
364
367
|
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
|
|
365
|
-
if (BYTES > 2048) throw new Error('
|
|
366
|
-
|
|
368
|
+
if (BYTES > 2048) throw new Error('invalid field: expected ORDER of <= 2048 bytes');
|
|
369
|
+
let sqrtP: ReturnType<typeof FpSqrt>; // cached sqrtP
|
|
367
370
|
const f: Readonly<FpField> = Object.freeze({
|
|
368
371
|
ORDER,
|
|
369
372
|
BITS,
|
|
@@ -374,7 +377,7 @@ export function Field(
|
|
|
374
377
|
create: (num) => mod(num, ORDER),
|
|
375
378
|
isValid: (num) => {
|
|
376
379
|
if (typeof num !== 'bigint')
|
|
377
|
-
throw new Error(
|
|
380
|
+
throw new Error('invalid field element: expected bigint, got ' + typeof num);
|
|
378
381
|
return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
|
|
379
382
|
},
|
|
380
383
|
is0: (num) => num === _0n,
|
|
@@ -396,7 +399,12 @@ export function Field(
|
|
|
396
399
|
mulN: (lhs, rhs) => lhs * rhs,
|
|
397
400
|
|
|
398
401
|
inv: (num) => invert(num, ORDER),
|
|
399
|
-
sqrt:
|
|
402
|
+
sqrt:
|
|
403
|
+
redef.sqrt ||
|
|
404
|
+
((n) => {
|
|
405
|
+
if (!sqrtP) sqrtP = FpSqrt(ORDER);
|
|
406
|
+
return sqrtP(f, n);
|
|
407
|
+
}),
|
|
400
408
|
invertBatch: (lst) => FpInvertBatch(f, lst),
|
|
401
409
|
// TODO: do we really need constant cmov?
|
|
402
410
|
// We don't have const-time bigints anyway, so probably will be not very useful
|
|
@@ -404,7 +412,7 @@ export function Field(
|
|
|
404
412
|
toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
|
|
405
413
|
fromBytes: (bytes) => {
|
|
406
414
|
if (bytes.length !== BYTES)
|
|
407
|
-
throw new Error(
|
|
415
|
+
throw new Error('Field.fromBytes: expected ' + BYTES + ' bytes, got ' + bytes.length);
|
|
408
416
|
return isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
|
409
417
|
},
|
|
410
418
|
} as FpField);
|
|
@@ -412,13 +420,13 @@ export function Field(
|
|
|
412
420
|
}
|
|
413
421
|
|
|
414
422
|
export function FpSqrtOdd<T>(Fp: IField<T>, elm: T) {
|
|
415
|
-
if (!Fp.isOdd) throw new Error(
|
|
423
|
+
if (!Fp.isOdd) throw new Error("Field doesn't have isOdd");
|
|
416
424
|
const root = Fp.sqrt(elm);
|
|
417
425
|
return Fp.isOdd(root) ? root : Fp.neg(root);
|
|
418
426
|
}
|
|
419
427
|
|
|
420
428
|
export function FpSqrtEven<T>(Fp: IField<T>, elm: T) {
|
|
421
|
-
if (!Fp.isOdd) throw new Error(
|
|
429
|
+
if (!Fp.isOdd) throw new Error("Field doesn't have isOdd");
|
|
422
430
|
const root = Fp.sqrt(elm);
|
|
423
431
|
return Fp.isOdd(root) ? Fp.neg(root) : root;
|
|
424
432
|
}
|
|
@@ -438,7 +446,9 @@ export function hashToPrivateScalar(
|
|
|
438
446
|
const hashLen = hash.length;
|
|
439
447
|
const minLen = nLength(groupOrder).nByteLength + 8;
|
|
440
448
|
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
|
|
441
|
-
throw new Error(
|
|
449
|
+
throw new Error(
|
|
450
|
+
'hashToPrivateScalar: expected ' + minLen + '-1024 bytes of input, got ' + hashLen
|
|
451
|
+
);
|
|
442
452
|
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
|
|
443
453
|
return mod(num, groupOrder - _1n) + _1n;
|
|
444
454
|
}
|
|
@@ -486,7 +496,7 @@ export function mapHashToField(key: Uint8Array, fieldOrder: bigint, isLE = false
|
|
|
486
496
|
const minLen = getMinHashLength(fieldOrder);
|
|
487
497
|
// No small numbers: need to understand bias story. No huge numbers: easier to detect JS timings.
|
|
488
498
|
if (len < 16 || len < minLen || len > 1024)
|
|
489
|
-
throw new Error(
|
|
499
|
+
throw new Error('expected ' + minLen + '-1024 bytes of input, got ' + len);
|
|
490
500
|
const num = isLE ? bytesToNumberBE(key) : bytesToNumberLE(key);
|
|
491
501
|
// `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0
|
|
492
502
|
const reduced = mod(num, fieldOrder - _1n) + _1n;
|
|
@@ -158,8 +158,10 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|
|
158
158
|
function decodeScalar(n: Hex): bigint {
|
|
159
159
|
const bytes = ensureBytes('scalar', n);
|
|
160
160
|
const len = bytes.length;
|
|
161
|
-
if (len !== montgomeryBytes && len !== fieldLen)
|
|
162
|
-
|
|
161
|
+
if (len !== montgomeryBytes && len !== fieldLen) {
|
|
162
|
+
let valid = '' + montgomeryBytes + ' or ' + fieldLen;
|
|
163
|
+
throw new Error('invalid scalar, expected ' + valid + ' bytes, got ' + len);
|
|
164
|
+
}
|
|
163
165
|
return bytesToNumberLE(adjustScalarBytes(bytes));
|
|
164
166
|
}
|
|
165
167
|
function scalarMult(scalar: Hex, u: Hex): Uint8Array {
|
|
@@ -168,7 +170,7 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|
|
168
170
|
const pu = montgomeryLadder(pointU, _scalar);
|
|
169
171
|
// The result was not contributory
|
|
170
172
|
// https://cr.yp.to/ecdh.html#validate
|
|
171
|
-
if (pu === _0n) throw new Error('
|
|
173
|
+
if (pu === _0n) throw new Error('invalid private or public key received');
|
|
172
174
|
return encodeUCoordinate(pu);
|
|
173
175
|
}
|
|
174
176
|
// Computes public key from private. By doing scalar multiplication of base point.
|