@noble/curves 0.1.0 → 0.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 +99 -15
- package/lib/edwards.d.ts +108 -0
- package/lib/edwards.js +554 -0
- package/lib/esm/edwards.js +550 -0
- package/lib/esm/group.js +107 -0
- package/lib/esm/modular.js +10 -1
- package/lib/esm/montgomery.js +189 -0
- package/lib/esm/utils.js +62 -5
- package/lib/esm/{shortw.js → weierstrass.js} +79 -158
- package/lib/group.d.ts +33 -0
- package/lib/group.js +111 -0
- package/lib/modular.d.ts +2 -1
- package/lib/modular.js +13 -3
- package/lib/montgomery.d.ts +20 -0
- package/lib/montgomery.js +191 -0
- package/lib/utils.d.ts +26 -2
- package/lib/utils.js +71 -7
- package/lib/{shortw.d.ts → weierstrass.d.ts} +22 -35
- package/lib/{shortw.js → weierstrass.js} +78 -157
- package/package.json +23 -11
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
|
-
// Implementation of Short
|
|
2
|
+
// Implementation of Short Weierstrass curve. The formula is: y² = x³ + ax + b
|
|
3
3
|
// TODO: sync vs async naming
|
|
4
4
|
// TODO: default randomBytes
|
|
5
|
+
// Differences from @noble/secp256k1 1.7:
|
|
6
|
+
// 1. Different double() formula (but same addition)
|
|
7
|
+
// 2. Different sqrt() function
|
|
8
|
+
// 3. truncateHash() truncateOnly mode
|
|
9
|
+
// 4. DRBG supports outputLen bigger than outputLen of hmac
|
|
5
10
|
import * as mod from './modular.js';
|
|
6
|
-
import { bytesToHex,
|
|
11
|
+
import { bytesToHex, bytesToNumberBE, concatBytes, ensureBytes, hexToBytes, hexToNumber, numberToHexUnpadded, hashToPrivateScalar, validateOpts as utilOpts, } from './utils.js';
|
|
12
|
+
import { wNAF } from './group.js';
|
|
7
13
|
// Should be separate from overrides, since overrides can use information about curve (for example nBits)
|
|
8
14
|
function validateOpts(curve) {
|
|
9
|
-
|
|
15
|
+
const opts = utilOpts(curve);
|
|
16
|
+
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
|
|
10
17
|
throw new Error('Invalid hash function');
|
|
11
|
-
if (typeof
|
|
18
|
+
if (typeof opts.hmac !== 'function')
|
|
12
19
|
throw new Error('Invalid hmac function');
|
|
13
|
-
if (typeof
|
|
20
|
+
if (typeof opts.randomBytes !== 'function')
|
|
14
21
|
throw new Error('Invalid randomBytes function');
|
|
15
|
-
for (const i of ['a', 'b'
|
|
16
|
-
if (typeof
|
|
17
|
-
throw new Error(`Invalid curve param ${i}=${
|
|
22
|
+
for (const i of ['a', 'b']) {
|
|
23
|
+
if (typeof opts[i] !== 'bigint')
|
|
24
|
+
throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`);
|
|
18
25
|
}
|
|
19
|
-
|
|
20
|
-
if (curve[i] === undefined)
|
|
21
|
-
continue; // Optional
|
|
22
|
-
if (!Number.isSafeInteger(curve[i]))
|
|
23
|
-
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
|
24
|
-
}
|
|
25
|
-
const endo = curve.endo;
|
|
26
|
+
const endo = opts.endo;
|
|
26
27
|
if (endo) {
|
|
27
|
-
if (
|
|
28
|
+
if (opts.a !== _0n) {
|
|
28
29
|
throw new Error('Endomorphism can only be defined for Koblitz curves that have a=0');
|
|
29
30
|
}
|
|
30
31
|
if (typeof endo !== 'object' ||
|
|
@@ -33,10 +34,8 @@ function validateOpts(curve) {
|
|
|
33
34
|
throw new Error('Expected endomorphism with beta: bigint and splitScalar: function');
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
|
-
const nBitLength = curve.n.toString(2).length; // Bit size of CURVE.n
|
|
37
|
-
const nByteLength = Math.ceil(nBitLength / 8); // Byte size of CURVE.n
|
|
38
37
|
// Set defaults
|
|
39
|
-
return Object.freeze({ lowS: true,
|
|
38
|
+
return Object.freeze({ lowS: true, ...opts });
|
|
40
39
|
}
|
|
41
40
|
// TODO: convert bits to bytes aligned to 32 bits? (224 for example)
|
|
42
41
|
// DER encoding utilities
|
|
@@ -63,7 +62,7 @@ function parseDERInt(data) {
|
|
|
63
62
|
if (res[0] === 0x00 && res[1] <= 0x7f) {
|
|
64
63
|
throw new DERError('Invalid signature integer: trailing length');
|
|
65
64
|
}
|
|
66
|
-
return { data:
|
|
65
|
+
return { data: bytesToNumberBE(res), left: data.subarray(len + 2) };
|
|
67
66
|
}
|
|
68
67
|
function parseDERSignature(data) {
|
|
69
68
|
if (data.length < 2 || data[0] != 0x30) {
|
|
@@ -101,8 +100,8 @@ class HmacDrbg {
|
|
|
101
100
|
if (typeof hmacFn !== 'function')
|
|
102
101
|
throw new Error('hmacFn must be a function');
|
|
103
102
|
// Step B, Step C: set hashLen to 8*ceil(hlen/8)
|
|
104
|
-
this.v = new Uint8Array(
|
|
105
|
-
this.k = new Uint8Array(
|
|
103
|
+
this.v = new Uint8Array(hashLen).fill(1);
|
|
104
|
+
this.k = new Uint8Array(hashLen).fill(0);
|
|
106
105
|
this.counter = 0;
|
|
107
106
|
}
|
|
108
107
|
hmacSync(...values) {
|
|
@@ -138,7 +137,10 @@ class HmacDrbg {
|
|
|
138
137
|
// Use only input from curveOpts!
|
|
139
138
|
export function weierstrass(curveDef) {
|
|
140
139
|
const CURVE = validateOpts(curveDef);
|
|
140
|
+
const CURVE_ORDER = CURVE.n;
|
|
141
141
|
// Lengths
|
|
142
|
+
// All curves has same field / group length as for now, but it can be different for other curves
|
|
143
|
+
const groupLen = CURVE.nByteLength;
|
|
142
144
|
const fieldLen = CURVE.nByteLength; // 32 (length of one field element)
|
|
143
145
|
if (fieldLen > 2048)
|
|
144
146
|
throw new Error('Field lengths over 2048 are not supported');
|
|
@@ -188,15 +190,15 @@ export function weierstrass(curveDef) {
|
|
|
188
190
|
num = BigInt(key);
|
|
189
191
|
}
|
|
190
192
|
else if (typeof key === 'string') {
|
|
191
|
-
key = key.padStart(2 *
|
|
192
|
-
if (key.length !== 2 *
|
|
193
|
-
throw new Error(`Expected ${
|
|
193
|
+
key = key.padStart(2 * groupLen, '0'); // Eth-like hexes
|
|
194
|
+
if (key.length !== 2 * groupLen)
|
|
195
|
+
throw new Error(`Expected ${groupLen} bytes of private key`);
|
|
194
196
|
num = hexToNumber(key);
|
|
195
197
|
}
|
|
196
198
|
else if (key instanceof Uint8Array) {
|
|
197
|
-
if (key.length !==
|
|
198
|
-
throw new Error(`Expected ${
|
|
199
|
-
num =
|
|
199
|
+
if (key.length !== groupLen)
|
|
200
|
+
throw new Error(`Expected ${groupLen} bytes of private key`);
|
|
201
|
+
num = bytesToNumberBE(key);
|
|
200
202
|
}
|
|
201
203
|
else {
|
|
202
204
|
throw new TypeError('Expected valid private key');
|
|
@@ -221,11 +223,11 @@ export function weierstrass(curveDef) {
|
|
|
221
223
|
throw new Error(`Unknown type of public key: ${publicKey}`);
|
|
222
224
|
}
|
|
223
225
|
function isBiggerThanHalfOrder(number) {
|
|
224
|
-
const HALF =
|
|
226
|
+
const HALF = CURVE_ORDER >> _1n;
|
|
225
227
|
return number > HALF;
|
|
226
228
|
}
|
|
227
229
|
function normalizeS(s) {
|
|
228
|
-
return isBiggerThanHalfOrder(s) ? mod.mod(-s,
|
|
230
|
+
return isBiggerThanHalfOrder(s) ? mod.mod(-s, CURVE_ORDER) : s;
|
|
229
231
|
}
|
|
230
232
|
function normalizeScalar(num) {
|
|
231
233
|
if (typeof num === 'number' && Number.isSafeInteger(num) && num > 0)
|
|
@@ -240,7 +242,7 @@ export function weierstrass(curveDef) {
|
|
|
240
242
|
const { n, nBitLength } = CURVE;
|
|
241
243
|
const byteLength = hash.length;
|
|
242
244
|
const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits)
|
|
243
|
-
let h =
|
|
245
|
+
let h = bytesToNumberBE(hash);
|
|
244
246
|
if (delta > 0)
|
|
245
247
|
h = h >> BigInt(delta);
|
|
246
248
|
if (!truncateOnly && h >= n)
|
|
@@ -308,6 +310,22 @@ export function weierstrass(curveDef) {
|
|
|
308
310
|
double() {
|
|
309
311
|
const { x: X1, y: Y1, z: Z1 } = this;
|
|
310
312
|
const { a } = CURVE;
|
|
313
|
+
// Faster algorithm: when a=0
|
|
314
|
+
// From: https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
|
|
315
|
+
// Cost: 2M + 5S + 6add + 3*2 + 1*3 + 1*8.
|
|
316
|
+
if (a === _0n) {
|
|
317
|
+
const A = modP(X1 * X1);
|
|
318
|
+
const B = modP(Y1 * Y1);
|
|
319
|
+
const C = modP(B * B);
|
|
320
|
+
const x1b = X1 + B;
|
|
321
|
+
const D = modP(_2n * (modP(x1b * x1b) - A - C));
|
|
322
|
+
const E = modP(_3n * A);
|
|
323
|
+
const F = modP(E * E);
|
|
324
|
+
const X3 = modP(F - _2n * D);
|
|
325
|
+
const Y3 = modP(E * (D - X3) - _8n * C);
|
|
326
|
+
const Z3 = modP(_2n * Y1 * Z1);
|
|
327
|
+
return new JacobianPoint(X3, Y3, Z3);
|
|
328
|
+
}
|
|
311
329
|
const XX = modP(X1 * X1);
|
|
312
330
|
const YY = modP(Y1 * Y1);
|
|
313
331
|
const YYYY = modP(YY * YY);
|
|
@@ -329,9 +347,6 @@ export function weierstrass(curveDef) {
|
|
|
329
347
|
add(other) {
|
|
330
348
|
if (!(other instanceof JacobianPoint))
|
|
331
349
|
throw new TypeError('JacobianPoint expected');
|
|
332
|
-
// TODO: remove
|
|
333
|
-
if (this.equals(JacobianPoint.ZERO))
|
|
334
|
-
return other;
|
|
335
350
|
const { x: X1, y: Y1, z: Z1 } = this;
|
|
336
351
|
const { x: X2, y: Y2, z: Z2 } = other;
|
|
337
352
|
if (X2 === _0n || Y2 === _0n)
|
|
@@ -374,17 +389,8 @@ export function weierstrass(curveDef) {
|
|
|
374
389
|
let n = normalizeScalar(scalar);
|
|
375
390
|
if (n === _1n)
|
|
376
391
|
return this;
|
|
377
|
-
if (!CURVE.endo)
|
|
378
|
-
|
|
379
|
-
let d = this;
|
|
380
|
-
while (n > _0n) {
|
|
381
|
-
if (n & _1n)
|
|
382
|
-
p = p.add(d);
|
|
383
|
-
d = d.double();
|
|
384
|
-
n >>= _1n;
|
|
385
|
-
}
|
|
386
|
-
return p;
|
|
387
|
-
}
|
|
392
|
+
if (!CURVE.endo)
|
|
393
|
+
return wnaf.unsafeLadder(this, n);
|
|
388
394
|
// Apply endomorphism
|
|
389
395
|
let { k1neg, k1, k2neg, k2 } = CURVE.endo.splitScalar(n);
|
|
390
396
|
let k1p = P0;
|
|
@@ -406,99 +412,23 @@ export function weierstrass(curveDef) {
|
|
|
406
412
|
k2p = new JacobianPoint(modP(k2p.x * CURVE.endo.beta), k2p.y, k2p.z);
|
|
407
413
|
return k1p.add(k2p);
|
|
408
414
|
}
|
|
409
|
-
/**
|
|
410
|
-
* Creates a wNAF precomputation window. Used for caching.
|
|
411
|
-
* Default window size is set by `utils.precompute()` and is equal to 8.
|
|
412
|
-
* Which means we are caching 65536 points: 256 points for every bit from 0 to 256.
|
|
413
|
-
* @returns 65K precomputed points, depending on W
|
|
414
|
-
*/
|
|
415
|
-
precomputeWindow(W) {
|
|
416
|
-
const windows = CURVE.endo
|
|
417
|
-
? Math.ceil(CURVE.nBitLength / 2) / W + 1
|
|
418
|
-
: CURVE.nBitLength / W + 1;
|
|
419
|
-
const points = [];
|
|
420
|
-
let p = this;
|
|
421
|
-
let base = p;
|
|
422
|
-
for (let window = 0; window < windows; window++) {
|
|
423
|
-
base = p;
|
|
424
|
-
points.push(base);
|
|
425
|
-
for (let i = 1; i < 2 ** (W - 1); i++) {
|
|
426
|
-
base = base.add(p);
|
|
427
|
-
points.push(base);
|
|
428
|
-
}
|
|
429
|
-
p = base.double();
|
|
430
|
-
}
|
|
431
|
-
return points;
|
|
432
|
-
}
|
|
433
415
|
/**
|
|
434
416
|
* Implements w-ary non-adjacent form for calculating ec multiplication.
|
|
435
|
-
* @param n
|
|
436
|
-
* @param affinePoint optional 2d point to save cached precompute windows on it.
|
|
437
|
-
* @returns real and fake (for const-time) points
|
|
438
417
|
*/
|
|
439
418
|
wNAF(n, affinePoint) {
|
|
440
419
|
if (!affinePoint && this.equals(JacobianPoint.BASE))
|
|
441
420
|
affinePoint = Point.BASE;
|
|
442
421
|
const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1;
|
|
443
|
-
if (256 % W) {
|
|
444
|
-
throw new Error('Point#wNAF: Invalid precomputation window, must be power of 2');
|
|
445
|
-
}
|
|
446
422
|
// Calculate precomputes on a first run, reuse them after
|
|
447
423
|
let precomputes = affinePoint && pointPrecomputes.get(affinePoint);
|
|
448
424
|
if (!precomputes) {
|
|
449
|
-
precomputes =
|
|
425
|
+
precomputes = wnaf.precomputeWindow(this, W);
|
|
450
426
|
if (affinePoint && W !== 1) {
|
|
451
427
|
precomputes = JacobianPoint.normalizeZ(precomputes);
|
|
452
428
|
pointPrecomputes.set(affinePoint, precomputes);
|
|
453
429
|
}
|
|
454
430
|
}
|
|
455
|
-
|
|
456
|
-
let p = JacobianPoint.ZERO;
|
|
457
|
-
// Should be G (base) point, since otherwise f can be infinity point in the end
|
|
458
|
-
let f = JacobianPoint.BASE;
|
|
459
|
-
const nBits = CURVE.endo ? CURVE.nBitLength / 2 : CURVE.nBitLength;
|
|
460
|
-
const windows = 1 + Math.ceil(nBits / W); // W=8 17
|
|
461
|
-
const windowSize = 2 ** (W - 1); // W=8 128
|
|
462
|
-
const mask = BigInt(2 ** W - 1); // Create mask with W ones: 0b11111111 for W=8
|
|
463
|
-
const maxNumber = 2 ** W; // W=8 256
|
|
464
|
-
const shiftBy = BigInt(W); // W=8 8
|
|
465
|
-
for (let window = 0; window < windows; window++) {
|
|
466
|
-
const offset = window * windowSize;
|
|
467
|
-
// Extract W bits.
|
|
468
|
-
let wbits = Number(n & mask);
|
|
469
|
-
// Shift number by W bits.
|
|
470
|
-
n >>= shiftBy;
|
|
471
|
-
// If the bits are bigger than max size, we'll split those.
|
|
472
|
-
// +224 => 256 - 32
|
|
473
|
-
if (wbits > windowSize) {
|
|
474
|
-
wbits -= maxNumber;
|
|
475
|
-
n += _1n;
|
|
476
|
-
}
|
|
477
|
-
// This code was first written with assumption that 'f' and 'p' will never be infinity point:
|
|
478
|
-
// since each addition is multiplied by 2 ** W, it cannot cancel each other. However,
|
|
479
|
-
// there is negate now: it is possible that negated element from low value
|
|
480
|
-
// would be the same as high element, which will create carry into next window.
|
|
481
|
-
// It's not obvious how this can fail, but still worth investigating later.
|
|
482
|
-
// Check if we're onto Zero point.
|
|
483
|
-
// Add random point inside current window to f.
|
|
484
|
-
const offset1 = offset;
|
|
485
|
-
const offset2 = offset + Math.abs(wbits) - 1;
|
|
486
|
-
const cond1 = window % 2 !== 0;
|
|
487
|
-
const cond2 = wbits < 0;
|
|
488
|
-
if (wbits === 0) {
|
|
489
|
-
// The most important part for const-time getPublicKey
|
|
490
|
-
f = f.add(constTimeNegate(cond1, precomputes[offset1]));
|
|
491
|
-
}
|
|
492
|
-
else {
|
|
493
|
-
p = p.add(constTimeNegate(cond2, precomputes[offset2]));
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
// JIT-compiler should not eliminate f here, since it will later be used in normalizeZ()
|
|
497
|
-
// Even if the variable is still unused, there are some checks which will
|
|
498
|
-
// throw an exception, so compiler needs to prove they won't happen, which is hard.
|
|
499
|
-
// At this point there is a way to F be infinity-point even if p is not,
|
|
500
|
-
// which makes it less const-time: around 1 bigint multiply.
|
|
501
|
-
return { p, f };
|
|
431
|
+
return wnaf.wNAF(W, precomputes, n);
|
|
502
432
|
}
|
|
503
433
|
/**
|
|
504
434
|
* Constant time multiplication.
|
|
@@ -518,8 +448,8 @@ export function weierstrass(curveDef) {
|
|
|
518
448
|
const { k1neg, k1, k2neg, k2 } = CURVE.endo.splitScalar(n);
|
|
519
449
|
let { p: k1p, f: f1p } = this.wNAF(k1, affinePoint);
|
|
520
450
|
let { p: k2p, f: f2p } = this.wNAF(k2, affinePoint);
|
|
521
|
-
k1p = constTimeNegate(k1neg, k1p);
|
|
522
|
-
k2p = constTimeNegate(k2neg, k2p);
|
|
451
|
+
k1p = wnaf.constTimeNegate(k1neg, k1p);
|
|
452
|
+
k2p = wnaf.constTimeNegate(k2neg, k2p);
|
|
523
453
|
k2p = new JacobianPoint(modP(k2p.x * CURVE.endo.beta), k2p.y, k2p.z);
|
|
524
454
|
point = k1p.add(k2p);
|
|
525
455
|
fake = f1p.add(f2p);
|
|
@@ -558,11 +488,7 @@ export function weierstrass(curveDef) {
|
|
|
558
488
|
}
|
|
559
489
|
JacobianPoint.BASE = new JacobianPoint(CURVE.Gx, CURVE.Gy, _1n);
|
|
560
490
|
JacobianPoint.ZERO = new JacobianPoint(_0n, _1n, _0n);
|
|
561
|
-
|
|
562
|
-
function constTimeNegate(condition, item) {
|
|
563
|
-
const neg = item.negate();
|
|
564
|
-
return condition ? neg : item;
|
|
565
|
-
}
|
|
491
|
+
const wnaf = wNAF(JacobianPoint, CURVE.endo ? CURVE.nBitLength / 2 : CURVE.nBitLength);
|
|
566
492
|
// Stores precomputed values for points.
|
|
567
493
|
const pointPrecomputes = new WeakMap();
|
|
568
494
|
/**
|
|
@@ -583,13 +509,12 @@ export function weierstrass(curveDef) {
|
|
|
583
509
|
return this.y % _2n === _0n;
|
|
584
510
|
}
|
|
585
511
|
/**
|
|
586
|
-
* Supports compressed ECDSA
|
|
587
|
-
* @param bytes 33 bytes
|
|
512
|
+
* Supports compressed ECDSA points
|
|
588
513
|
* @returns Point instance
|
|
589
514
|
*/
|
|
590
515
|
static fromCompressedHex(bytes) {
|
|
591
516
|
const P = CURVE.P;
|
|
592
|
-
const x =
|
|
517
|
+
const x = bytesToNumberBE(bytes.subarray(1));
|
|
593
518
|
if (!isValidFieldElement(x))
|
|
594
519
|
throw new Error('Point is not on curve');
|
|
595
520
|
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
@@ -604,15 +529,15 @@ export function weierstrass(curveDef) {
|
|
|
604
529
|
return point;
|
|
605
530
|
}
|
|
606
531
|
static fromUncompressedHex(bytes) {
|
|
607
|
-
const x =
|
|
608
|
-
const y =
|
|
532
|
+
const x = bytesToNumberBE(bytes.subarray(1, fieldLen + 1));
|
|
533
|
+
const y = bytesToNumberBE(bytes.subarray(fieldLen + 1, 2 * fieldLen + 1));
|
|
609
534
|
const point = new Point(x, y);
|
|
610
535
|
point.assertValidity();
|
|
611
536
|
return point;
|
|
612
537
|
}
|
|
613
538
|
/**
|
|
614
539
|
* Converts hash string or Uint8Array to Point.
|
|
615
|
-
* @param hex
|
|
540
|
+
* @param hex short/long ECDSA hex
|
|
616
541
|
*/
|
|
617
542
|
static fromHex(hex) {
|
|
618
543
|
const bytes = ensureBytes(hex);
|
|
@@ -654,6 +579,8 @@ export function weierstrass(curveDef) {
|
|
|
654
579
|
throw new Error(msg);
|
|
655
580
|
}
|
|
656
581
|
equals(other) {
|
|
582
|
+
if (!(other instanceof Point))
|
|
583
|
+
throw new TypeError('Point#equals: expected Point');
|
|
657
584
|
return this.x === other.x && this.y === other.y;
|
|
658
585
|
}
|
|
659
586
|
// Returns the same point with inverted `y`
|
|
@@ -781,7 +708,7 @@ export function weierstrass(curveDef) {
|
|
|
781
708
|
}
|
|
782
709
|
normalizeS() {
|
|
783
710
|
return this.hasHighS()
|
|
784
|
-
? new Signature(this.r, mod.mod(-this.s,
|
|
711
|
+
? new Signature(this.r, mod.mod(-this.s, CURVE_ORDER), this.recovery)
|
|
785
712
|
: this;
|
|
786
713
|
}
|
|
787
714
|
// DER-encoded
|
|
@@ -819,26 +746,20 @@ export function weierstrass(curveDef) {
|
|
|
819
746
|
}
|
|
820
747
|
},
|
|
821
748
|
_bigintToBytes: numToField,
|
|
749
|
+
_bigintToString: numToFieldStr,
|
|
822
750
|
_normalizePrivateKey: normalizePrivateKey,
|
|
751
|
+
_normalizePublicKey: normalizePublicKey,
|
|
752
|
+
_isWithinCurveOrder: isWithinCurveOrder,
|
|
753
|
+
_isValidFieldElement: isValidFieldElement,
|
|
754
|
+
_weierstrassEquation: weierstrassEquation,
|
|
823
755
|
/**
|
|
824
|
-
*
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
*
|
|
829
|
-
*
|
|
756
|
+
* Converts some bytes to a valid private key. Needs at least (nBitLength+64) bytes.
|
|
757
|
+
*/
|
|
758
|
+
hashToPrivateKey: (hash) => numToField(hashToPrivateScalar(hash, CURVE_ORDER)),
|
|
759
|
+
/**
|
|
760
|
+
* Produces cryptographically secure private key from random of size (nBitLength+64)
|
|
761
|
+
* as per FIPS 186 B.4.1 with modulo bias being neglible.
|
|
830
762
|
*/
|
|
831
|
-
hashToPrivateKey: (hash) => {
|
|
832
|
-
hash = ensureBytes(hash);
|
|
833
|
-
const minLen = fieldLen + 8;
|
|
834
|
-
if (hash.length < minLen || hash.length > 1024) {
|
|
835
|
-
throw new Error(`Expected ${minLen}-1024 bytes of private key as per FIPS 186`);
|
|
836
|
-
}
|
|
837
|
-
const num = mod.mod(bytesToNumber(hash), CURVE.n - _1n) + _1n;
|
|
838
|
-
return numToField(num);
|
|
839
|
-
},
|
|
840
|
-
// Takes curve order + 64 bits from CSPRNG
|
|
841
|
-
// so that modulo bias is neglible, matches FIPS 186 B.4.1.
|
|
842
763
|
randomPrivateKey: () => utils.hashToPrivateKey(CURVE.randomBytes(fieldLen + 8)),
|
|
843
764
|
/**
|
|
844
765
|
* 1. Returns cached point which you can use to pass to `getSharedSecret` or `#multiply` by it.
|
|
@@ -857,8 +778,8 @@ export function weierstrass(curveDef) {
|
|
|
857
778
|
};
|
|
858
779
|
/**
|
|
859
780
|
* Computes public key for a private key.
|
|
860
|
-
* @param privateKey
|
|
861
|
-
* @param isCompressed whether to return compact
|
|
781
|
+
* @param privateKey private key
|
|
782
|
+
* @param isCompressed whether to return compact, or full key
|
|
862
783
|
* @returns Public key, full by default; short when isCompressed=true
|
|
863
784
|
*/
|
|
864
785
|
function getPublicKey(privateKey, isCompressed = false) {
|
|
@@ -900,11 +821,11 @@ export function weierstrass(curveDef) {
|
|
|
900
821
|
// RFC6979 methods
|
|
901
822
|
function bits2int(bytes) {
|
|
902
823
|
const slice = bytes.length > fieldLen ? bytes.slice(0, fieldLen) : bytes;
|
|
903
|
-
return
|
|
824
|
+
return bytesToNumberBE(slice);
|
|
904
825
|
}
|
|
905
826
|
function bits2octets(bytes) {
|
|
906
827
|
const z1 = bits2int(bytes);
|
|
907
|
-
const z2 = mod.mod(z1,
|
|
828
|
+
const z2 = mod.mod(z1, CURVE_ORDER);
|
|
908
829
|
return int2octets(z2 < _0n ? z1 : z2);
|
|
909
830
|
}
|
|
910
831
|
function int2octets(num) {
|
package/lib/group.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface Group<T extends Group<T>> {
|
|
2
|
+
double(): T;
|
|
3
|
+
negate(): T;
|
|
4
|
+
add(other: T): T;
|
|
5
|
+
subtract(other: T): T;
|
|
6
|
+
equals(other: T): boolean;
|
|
7
|
+
multiply(scalar: number | bigint): T;
|
|
8
|
+
}
|
|
9
|
+
export declare type GroupConstructor<T> = {
|
|
10
|
+
BASE: T;
|
|
11
|
+
ZERO: T;
|
|
12
|
+
};
|
|
13
|
+
export declare function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number): {
|
|
14
|
+
constTimeNegate: (condition: boolean, item: T) => T;
|
|
15
|
+
unsafeLadder(elm: T, n: bigint): T;
|
|
16
|
+
/**
|
|
17
|
+
* Creates a wNAF precomputation window. Used for caching.
|
|
18
|
+
* Default window size is set by `utils.precompute()` and is equal to 8.
|
|
19
|
+
* Which means we are caching 65536 points: 256 points for every bit from 0 to 256.
|
|
20
|
+
* @returns 65K precomputed points, depending on W
|
|
21
|
+
*/
|
|
22
|
+
precomputeWindow(elm: T, W: number): Group<T>[];
|
|
23
|
+
/**
|
|
24
|
+
* Implements w-ary non-adjacent form for calculating ec multiplication.
|
|
25
|
+
* @param n
|
|
26
|
+
* @param affinePoint optional 2d point to save cached precompute windows on it.
|
|
27
|
+
* @returns real and fake (for const-time) points
|
|
28
|
+
*/
|
|
29
|
+
wNAF(W: number, precomputes: T[], n: bigint): {
|
|
30
|
+
p: T;
|
|
31
|
+
f: T;
|
|
32
|
+
};
|
|
33
|
+
};
|
package/lib/group.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wNAF = void 0;
|
|
4
|
+
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
5
|
+
// Default group related functions
|
|
6
|
+
const _0n = BigInt(0);
|
|
7
|
+
const _1n = BigInt(1);
|
|
8
|
+
// Not big, but pretty complex and it is easy to break stuff. To avoid too much copy paste
|
|
9
|
+
function wNAF(c, bits) {
|
|
10
|
+
const constTimeNegate = (condition, item) => {
|
|
11
|
+
const neg = item.negate();
|
|
12
|
+
return condition ? neg : item;
|
|
13
|
+
};
|
|
14
|
+
const opts = (W) => {
|
|
15
|
+
if (256 % W)
|
|
16
|
+
throw new Error('Invalid precomputation window, must be power of 2');
|
|
17
|
+
const windows = Math.ceil(bits / W) + 1; // +1, because
|
|
18
|
+
const windowSize = 2 ** (W - 1); // -1 because we skip zero
|
|
19
|
+
return { windows, windowSize };
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
constTimeNegate,
|
|
23
|
+
// non-const time multiplication ladder
|
|
24
|
+
unsafeLadder(elm, n) {
|
|
25
|
+
let p = c.ZERO;
|
|
26
|
+
let d = elm;
|
|
27
|
+
while (n > _0n) {
|
|
28
|
+
if (n & _1n)
|
|
29
|
+
p = p.add(d);
|
|
30
|
+
d = d.double();
|
|
31
|
+
n >>= _1n;
|
|
32
|
+
}
|
|
33
|
+
return p;
|
|
34
|
+
},
|
|
35
|
+
/**
|
|
36
|
+
* Creates a wNAF precomputation window. Used for caching.
|
|
37
|
+
* Default window size is set by `utils.precompute()` and is equal to 8.
|
|
38
|
+
* Which means we are caching 65536 points: 256 points for every bit from 0 to 256.
|
|
39
|
+
* @returns 65K precomputed points, depending on W
|
|
40
|
+
*/
|
|
41
|
+
precomputeWindow(elm, W) {
|
|
42
|
+
const { windows, windowSize } = opts(W);
|
|
43
|
+
const points = [];
|
|
44
|
+
let p = elm;
|
|
45
|
+
let base = p;
|
|
46
|
+
for (let window = 0; window < windows; window++) {
|
|
47
|
+
base = p;
|
|
48
|
+
points.push(base);
|
|
49
|
+
// =1, because we skip zero
|
|
50
|
+
for (let i = 1; i < windowSize; i++) {
|
|
51
|
+
base = base.add(p);
|
|
52
|
+
points.push(base);
|
|
53
|
+
}
|
|
54
|
+
p = base.double();
|
|
55
|
+
}
|
|
56
|
+
return points;
|
|
57
|
+
},
|
|
58
|
+
/**
|
|
59
|
+
* Implements w-ary non-adjacent form for calculating ec multiplication.
|
|
60
|
+
* @param n
|
|
61
|
+
* @param affinePoint optional 2d point to save cached precompute windows on it.
|
|
62
|
+
* @returns real and fake (for const-time) points
|
|
63
|
+
*/
|
|
64
|
+
wNAF(W, precomputes, n) {
|
|
65
|
+
const { windows, windowSize } = opts(W);
|
|
66
|
+
let p = c.ZERO;
|
|
67
|
+
let f = c.BASE;
|
|
68
|
+
const mask = BigInt(2 ** W - 1); // Create mask with W ones: 0b1111 for W=4 etc.
|
|
69
|
+
const maxNumber = 2 ** W;
|
|
70
|
+
const shiftBy = BigInt(W);
|
|
71
|
+
for (let window = 0; window < windows; window++) {
|
|
72
|
+
const offset = window * windowSize;
|
|
73
|
+
// Extract W bits.
|
|
74
|
+
let wbits = Number(n & mask);
|
|
75
|
+
// Shift number by W bits.
|
|
76
|
+
n >>= shiftBy;
|
|
77
|
+
// If the bits are bigger than max size, we'll split those.
|
|
78
|
+
// +224 => 256 - 32
|
|
79
|
+
if (wbits > windowSize) {
|
|
80
|
+
wbits -= maxNumber;
|
|
81
|
+
n += _1n;
|
|
82
|
+
}
|
|
83
|
+
// This code was first written with assumption that 'f' and 'p' will never be infinity point:
|
|
84
|
+
// since each addition is multiplied by 2 ** W, it cannot cancel each other. However,
|
|
85
|
+
// there is negate now: it is possible that negated element from low value
|
|
86
|
+
// would be the same as high element, which will create carry into next window.
|
|
87
|
+
// It's not obvious how this can fail, but still worth investigating later.
|
|
88
|
+
// Check if we're onto Zero point.
|
|
89
|
+
// Add random point inside current window to f.
|
|
90
|
+
const offset1 = offset;
|
|
91
|
+
const offset2 = offset + Math.abs(wbits) - 1; // -1 because we skip zero
|
|
92
|
+
const cond1 = window % 2 !== 0;
|
|
93
|
+
const cond2 = wbits < 0;
|
|
94
|
+
if (wbits === 0) {
|
|
95
|
+
// The most important part for const-time getPublicKey
|
|
96
|
+
f = f.add(constTimeNegate(cond1, precomputes[offset1]));
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
p = p.add(constTimeNegate(cond2, precomputes[offset2]));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// JIT-compiler should not eliminate f here, since it will later be used in normalizeZ()
|
|
103
|
+
// Even if the variable is still unused, there are some checks which will
|
|
104
|
+
// throw an exception, so compiler needs to prove they won't happen, which is hard.
|
|
105
|
+
// At this point there is a way to F be infinity-point even if p is not,
|
|
106
|
+
// which makes it less const-time: around 1 bigint multiply.
|
|
107
|
+
return { p, f };
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
exports.wNAF = wNAF;
|
package/lib/modular.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
1
2
|
export declare function mod(a: bigint, b: bigint): bigint;
|
|
2
3
|
/**
|
|
3
4
|
* Efficiently exponentiate num to power and do modular division.
|
|
@@ -20,6 +21,6 @@ export declare function invertBatch(nums: bigint[], modulo: bigint): bigint[];
|
|
|
20
21
|
export declare function legendre(num: bigint, fieldPrime: bigint): bigint;
|
|
21
22
|
/**
|
|
22
23
|
* Calculates square root of a number in a finite field.
|
|
23
|
-
* Used to calculate y - the square root of y².
|
|
24
24
|
*/
|
|
25
25
|
export declare function sqrt(number: bigint, modulo: bigint): bigint;
|
|
26
|
+
export declare const isNegativeLE: (num: bigint, modulo: bigint) => boolean;
|
package/lib/modular.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sqrt = exports.legendre = exports.invertBatch = exports.invert = exports.pow2 = exports.pow = exports.mod = void 0;
|
|
4
2
|
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.isNegativeLE = exports.sqrt = exports.legendre = exports.invertBatch = exports.invert = exports.pow2 = exports.pow = exports.mod = void 0;
|
|
5
|
+
// Utilities for modular arithmetics
|
|
5
6
|
const _0n = BigInt(0);
|
|
6
7
|
const _1n = BigInt(1);
|
|
7
8
|
const _2n = BigInt(2);
|
|
@@ -106,7 +107,6 @@ function legendre(num, fieldPrime) {
|
|
|
106
107
|
exports.legendre = legendre;
|
|
107
108
|
/**
|
|
108
109
|
* Calculates square root of a number in a finite field.
|
|
109
|
-
* Used to calculate y - the square root of y².
|
|
110
110
|
*/
|
|
111
111
|
function sqrt(number, modulo) {
|
|
112
112
|
const n = number;
|
|
@@ -156,3 +156,13 @@ function sqrt(number, modulo) {
|
|
|
156
156
|
return r;
|
|
157
157
|
}
|
|
158
158
|
exports.sqrt = sqrt;
|
|
159
|
+
// Little-endian check for first LE bit (last BE bit);
|
|
160
|
+
const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n;
|
|
161
|
+
exports.isNegativeLE = isNegativeLE;
|
|
162
|
+
// An idea on modular arithmetic for bls12-381:
|
|
163
|
+
// const FIELD = {add, pow, sqrt, mul};
|
|
164
|
+
// Functions will take field elements, no need for an additional class
|
|
165
|
+
// Could be faster. 1 bigint field will just do operations and mod later:
|
|
166
|
+
// instead of 'r = mod(r * b, P)' we will write r = mul(r, b);
|
|
167
|
+
// Could be insecure without shape check, so it needs to be done.
|
|
168
|
+
// Functions could be inlined by JIT.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare type Hex = string | Uint8Array;
|
|
2
|
+
export declare type CurveType = {
|
|
3
|
+
P: bigint;
|
|
4
|
+
nByteLength: number;
|
|
5
|
+
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
|
|
6
|
+
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
|
|
7
|
+
a24: bigint;
|
|
8
|
+
montgomeryBits: number;
|
|
9
|
+
powPminus2?: (x: bigint) => bigint;
|
|
10
|
+
xyToU?: (x: bigint, y: bigint) => bigint;
|
|
11
|
+
Gu: string;
|
|
12
|
+
};
|
|
13
|
+
export declare type CurveFn = {
|
|
14
|
+
scalarMult: (u: Hex, scalar: Hex) => Uint8Array;
|
|
15
|
+
scalarMultBase: (scalar: Hex) => Uint8Array;
|
|
16
|
+
getPublicKey: (privateKey: Hex) => Uint8Array;
|
|
17
|
+
Gu: string;
|
|
18
|
+
};
|
|
19
|
+
export declare function montgomery(curveDef: CurveType): CurveFn;
|
|
20
|
+
export {};
|