@noble/curves 0.6.0 → 0.6.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 +83 -53
- package/lib/_shortw_utils.d.ts +3 -3
- package/lib/abstract/curve.d.ts +3 -4
- package/lib/abstract/curve.js +13 -19
- package/lib/abstract/edwards.d.ts +5 -5
- package/lib/abstract/edwards.js +20 -25
- package/lib/abstract/hash-to-curve.js +1 -1
- package/lib/abstract/modular.d.ts +1 -1
- package/lib/abstract/modular.js +11 -13
- package/lib/abstract/montgomery.js +20 -64
- package/lib/abstract/utils.d.ts +4 -1
- package/lib/abstract/utils.js +48 -26
- package/lib/abstract/weierstrass.d.ts +10 -10
- package/lib/abstract/weierstrass.js +58 -79
- package/lib/esm/abstract/curve.js +11 -17
- package/lib/esm/abstract/edwards.js +22 -27
- package/lib/esm/abstract/hash-to-curve.js +1 -1
- package/lib/esm/abstract/modular.js +12 -14
- package/lib/esm/abstract/montgomery.js +21 -65
- package/lib/esm/abstract/poseidon.js +1 -1
- package/lib/esm/abstract/utils.js +46 -25
- package/lib/esm/abstract/weierstrass.js +59 -80
- package/lib/esm/p224.js +1 -1
- package/lib/esm/p521.js +1 -13
- package/lib/esm/secp256k1.js +34 -36
- package/lib/esm/stark.js +1 -1
- package/lib/p192.d.ts +6 -6
- package/lib/p224.d.ts +6 -6
- package/lib/p224.js +1 -1
- package/lib/p256.d.ts +6 -6
- package/lib/p384.d.ts +6 -6
- package/lib/p521.d.ts +16 -17
- package/lib/p521.js +1 -13
- package/lib/secp256k1.d.ts +17 -14
- package/lib/secp256k1.js +28 -30
- package/lib/stark.d.ts +3 -3
- package/lib/stark.js +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -7,12 +7,11 @@ Minimal, auditable JS implementation of elliptic curve cryptography.
|
|
|
7
7
|
- [hash to curve](https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/)
|
|
8
8
|
for encoding or hashing an arbitrary string to a point on an elliptic curve
|
|
9
9
|
- [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash
|
|
10
|
-
- Auditable
|
|
11
10
|
- 🏎 [Ultra-fast](#speed), hand-optimized for caveats of JS engines
|
|
12
11
|
- 🔍 Unique tests ensure correctness. Wycheproof vectors included
|
|
13
12
|
- 🔻 Tree-shaking-friendly: there is no entry point, which ensures small size of your app
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
Package consists of two parts:
|
|
16
15
|
|
|
17
16
|
1. `abstract/` directory specifies zero-dependency EC algorithms
|
|
18
17
|
2. root directory utilizes one dependency `@noble/hashes` and provides ready-to-use:
|
|
@@ -26,6 +25,7 @@ Curves incorporate work from previous noble packages
|
|
|
26
25
|
[ed25519](https://github.com/paulmillr/noble-ed25519),
|
|
27
26
|
[bls12-381](https://github.com/paulmillr/noble-bls12-381)),
|
|
28
27
|
which had security audits and were developed from 2019 to 2022.
|
|
28
|
+
Check out [Upgrading](#upgrading) section if you've used them before.
|
|
29
29
|
|
|
30
30
|
### This library belongs to _noble_ crypto
|
|
31
31
|
|
|
@@ -445,63 +445,93 @@ We consider infrastructure attacks like rogue NPM modules very important; that's
|
|
|
445
445
|
Benchmark results on Apple M2 with node v18.10:
|
|
446
446
|
|
|
447
447
|
```
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
sign
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
verify
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
448
|
+
secp256k1
|
|
449
|
+
init x 57 ops/sec @ 17ms/op
|
|
450
|
+
getPublicKey x 4,946 ops/sec @ 202μs/op
|
|
451
|
+
sign x 3,914 ops/sec @ 255μs/op
|
|
452
|
+
verify x 682 ops/sec @ 1ms/op
|
|
453
|
+
getSharedSecret x 427 ops/sec @ 2ms/op
|
|
454
|
+
recoverPublicKey x 683 ops/sec @ 1ms/op
|
|
455
|
+
schnorr.sign x 539 ops/sec @ 1ms/op
|
|
456
|
+
schnorr.verify x 716 ops/sec @ 1ms/op
|
|
457
|
+
|
|
458
|
+
P256
|
|
459
|
+
init x 30 ops/sec @ 32ms/op
|
|
460
|
+
getPublicKey x 5,008 ops/sec @ 199μs/op
|
|
461
|
+
sign x 3,970 ops/sec @ 251μs/op
|
|
462
|
+
verify x 515 ops/sec @ 1ms/op
|
|
463
|
+
|
|
464
|
+
P384
|
|
465
|
+
init x 14 ops/sec @ 66ms/op
|
|
466
|
+
getPublicKey x 2,434 ops/sec @ 410μs/op
|
|
467
|
+
sign x 1,942 ops/sec @ 514μs/op
|
|
468
|
+
verify x 206 ops/sec @ 4ms/op
|
|
469
|
+
|
|
470
|
+
P521
|
|
471
|
+
init x 7 ops/sec @ 126ms/op
|
|
472
|
+
getPublicKey x 1,282 ops/sec @ 779μs/op
|
|
473
|
+
sign x 1,077 ops/sec @ 928μs/op
|
|
474
|
+
verify x 110 ops/sec @ 9ms/op
|
|
475
|
+
|
|
476
|
+
ed25519
|
|
477
|
+
init x 37 ops/sec @ 26ms/op
|
|
478
|
+
getPublicKey x 8,147 ops/sec @ 122μs/op
|
|
479
|
+
sign x 3,979 ops/sec @ 251μs/op
|
|
480
|
+
verify x 848 ops/sec @ 1ms/op
|
|
481
|
+
|
|
482
|
+
ed448
|
|
483
|
+
init x 17 ops/sec @ 58ms/op
|
|
484
|
+
getPublicKey x 3,083 ops/sec @ 324μs/op
|
|
485
|
+
sign x 1,473 ops/sec @ 678μs/op
|
|
486
|
+
verify x 323 ops/sec @ 3ms/op
|
|
487
|
+
|
|
488
|
+
bls12-381
|
|
489
|
+
init x 30 ops/sec @ 33ms/op
|
|
490
|
+
getPublicKey x 788 ops/sec @ 1ms/op
|
|
491
|
+
sign x 45 ops/sec @ 21ms/op
|
|
492
|
+
verify x 32 ops/sec @ 30ms/op
|
|
493
|
+
pairing x 88 ops/sec @ 11ms/op
|
|
494
|
+
|
|
495
|
+
stark
|
|
496
|
+
init x 31 ops/sec @ 31ms/op
|
|
479
497
|
pedersen
|
|
480
|
-
|
|
481
|
-
|
|
498
|
+
├─old x 84 ops/sec @ 11ms/op
|
|
499
|
+
└─noble x 802 ops/sec @ 1ms/op
|
|
500
|
+
poseidon x 7,466 ops/sec @ 133μs/op
|
|
482
501
|
verify
|
|
483
|
-
|
|
484
|
-
|
|
502
|
+
├─old x 300 ops/sec @ 3ms/op
|
|
503
|
+
└─noble x 474 ops/sec @ 2ms/op
|
|
485
504
|
```
|
|
486
505
|
|
|
487
506
|
## Upgrading
|
|
488
507
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
508
|
+
If you're coming from single-curve noble packages, the following changes need to be kept in mind:
|
|
509
|
+
|
|
510
|
+
- 2d affine (x, y) points have been removed to reduce complexity and improve speed
|
|
511
|
+
- Removed `number` support as a type for private keys. `bigint` is still supported
|
|
512
|
+
- `mod`, `invert` are no longer present in `utils`. Use `@noble/curves/abstract/modular.js` now.
|
|
513
|
+
|
|
514
|
+
Upgrading from @noble/secp256k1 1.7:
|
|
515
|
+
|
|
516
|
+
- Compressed (33-byte) public keys are now returned by default, instead of uncompressed
|
|
517
|
+
- Methods are now synchronous. Setting `secp.utils.hmacSha256` is no longer required
|
|
518
|
+
- `sign()`
|
|
519
|
+
- `der`, `recovered` options were removed
|
|
520
|
+
- `canonical` was renamed to `lowS`
|
|
521
|
+
- Return type is now `{ r: bigint, s: bigint, recovery: number }` instance of `Signature`
|
|
522
|
+
- `verify()`
|
|
523
|
+
- `strict` was renamed to `lowS`
|
|
524
|
+
- `recoverPublicKey()`: moved to sig instance `Signature#recoverPublicKey(msgHash)`
|
|
525
|
+
- `Point` was removed: use `ProjectivePoint` in xyz coordinates
|
|
526
|
+
- `utils`: Many methods were removed, others were moved to `schnorr` namespace
|
|
527
|
+
|
|
528
|
+
Upgrading from @noble/ed25519 1.7:
|
|
529
|
+
|
|
530
|
+
- Methods are now synchronous. Setting `secp.utils.hmacSha256` is no longer required
|
|
531
|
+
- ed25519ph, ed25519ctx
|
|
532
|
+
- `Point` was removed: use `ExtendedPoint` in xyzt coordinates
|
|
533
|
+
- `Signature` was removed
|
|
534
|
+
- `getSharedSecret` was removed: use separate x25519 sub-module
|
|
505
535
|
|
|
506
536
|
## Contributing & testing
|
|
507
537
|
|
package/lib/_shortw_utils.d.ts
CHANGED
|
@@ -18,11 +18,11 @@ export declare function createCurve(curveDef: CurveDef, defHash: CHash): Readonl
|
|
|
18
18
|
readonly hEff?: bigint | undefined;
|
|
19
19
|
readonly Gx: bigint;
|
|
20
20
|
readonly Gy: bigint;
|
|
21
|
-
readonly wrapPrivateKey?: boolean | undefined;
|
|
22
21
|
readonly allowInfinityPoint?: boolean | undefined;
|
|
23
22
|
readonly a: bigint;
|
|
24
23
|
readonly b: bigint;
|
|
25
|
-
readonly
|
|
24
|
+
readonly allowedPrivateKeyLengths?: readonly number[] | undefined;
|
|
25
|
+
readonly wrapPrivateKey?: boolean | undefined;
|
|
26
26
|
readonly endo?: {
|
|
27
27
|
beta: bigint;
|
|
28
28
|
splitScalar: (k: bigint) => {
|
|
@@ -34,10 +34,10 @@ export declare function createCurve(curveDef: CurveDef, defHash: CHash): Readonl
|
|
|
34
34
|
} | undefined;
|
|
35
35
|
readonly isTorsionFree?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => boolean) | undefined;
|
|
36
36
|
readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => import("./abstract/weierstrass.js").ProjPointType<bigint>) | undefined;
|
|
37
|
-
lowS: boolean;
|
|
38
37
|
readonly hash: CHash;
|
|
39
38
|
readonly hmac: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
|
|
40
39
|
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array;
|
|
40
|
+
lowS: boolean;
|
|
41
41
|
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined;
|
|
42
42
|
readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined;
|
|
43
43
|
}>;
|
package/lib/abstract/curve.d.ts
CHANGED
|
@@ -46,7 +46,7 @@ export declare function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: n
|
|
|
46
46
|
f: T;
|
|
47
47
|
};
|
|
48
48
|
};
|
|
49
|
-
export declare type
|
|
49
|
+
export declare type BasicCurve<T> = {
|
|
50
50
|
Fp: Field<T>;
|
|
51
51
|
n: bigint;
|
|
52
52
|
nBitLength?: number;
|
|
@@ -55,10 +55,9 @@ export declare type AbstractCurve<T> = {
|
|
|
55
55
|
hEff?: bigint;
|
|
56
56
|
Gx: T;
|
|
57
57
|
Gy: T;
|
|
58
|
-
wrapPrivateKey?: boolean;
|
|
59
58
|
allowInfinityPoint?: boolean;
|
|
60
59
|
};
|
|
61
|
-
export declare function
|
|
60
|
+
export declare function validateBasic<FP, T>(curve: BasicCurve<FP> & T): Readonly<{
|
|
62
61
|
readonly nBitLength: number;
|
|
63
62
|
readonly nByteLength: number;
|
|
64
|
-
} &
|
|
63
|
+
} & BasicCurve<FP> & T>;
|
package/lib/abstract/curve.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.validateBasic = exports.wNAF = void 0;
|
|
4
4
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
5
5
|
// Abelian group utilities
|
|
6
6
|
const modular_js_1 = require("./modular.js");
|
|
7
|
+
const utils_js_1 = require("./utils.js");
|
|
7
8
|
const _0n = BigInt(0);
|
|
8
9
|
const _1n = BigInt(1);
|
|
9
10
|
// Elliptic curve multiplication of Point by scalar. Complicated and fragile. Uses wNAF method.
|
|
@@ -125,25 +126,18 @@ function wNAF(c, bits) {
|
|
|
125
126
|
};
|
|
126
127
|
}
|
|
127
128
|
exports.wNAF = wNAF;
|
|
128
|
-
function
|
|
129
|
+
function validateBasic(curve) {
|
|
129
130
|
(0, modular_js_1.validateField)(curve.Fp);
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
for (const i of ['nBitLength', 'nByteLength']) {
|
|
140
|
-
const val = curve[i];
|
|
141
|
-
if (val === undefined)
|
|
142
|
-
continue; // Optional
|
|
143
|
-
if (!Number.isSafeInteger(val))
|
|
144
|
-
throw new Error(`Invalid param ${i}=${val} (${typeof val})`);
|
|
145
|
-
}
|
|
131
|
+
(0, utils_js_1.validateObject)(curve, {
|
|
132
|
+
n: 'bigint',
|
|
133
|
+
h: 'bigint',
|
|
134
|
+
Gx: 'field',
|
|
135
|
+
Gy: 'field',
|
|
136
|
+
}, {
|
|
137
|
+
nBitLength: 'isSafeInteger',
|
|
138
|
+
nByteLength: 'isSafeInteger',
|
|
139
|
+
});
|
|
146
140
|
// Set defaults
|
|
147
141
|
return Object.freeze({ ...(0, modular_js_1.nLength)(curve.n, curve.nBitLength), ...curve });
|
|
148
142
|
}
|
|
149
|
-
exports.
|
|
143
|
+
exports.validateBasic = validateBasic;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import * as ut from './utils.js';
|
|
1
2
|
import { FHash, Hex } from './utils.js';
|
|
2
|
-
import { Group, GroupConstructor,
|
|
3
|
-
export declare type CurveType =
|
|
3
|
+
import { Group, GroupConstructor, BasicCurve, AffinePoint } from './curve.js';
|
|
4
|
+
export declare type CurveType = BasicCurve<bigint> & {
|
|
4
5
|
a: bigint;
|
|
5
6
|
d: bigint;
|
|
6
7
|
hash: FHash;
|
|
@@ -23,11 +24,10 @@ declare function validateOpts(curve: CurveType): Readonly<{
|
|
|
23
24
|
readonly hEff?: bigint | undefined;
|
|
24
25
|
readonly Gx: bigint;
|
|
25
26
|
readonly Gy: bigint;
|
|
26
|
-
readonly wrapPrivateKey?: boolean | undefined;
|
|
27
27
|
readonly allowInfinityPoint?: boolean | undefined;
|
|
28
28
|
readonly a: bigint;
|
|
29
29
|
readonly d: bigint;
|
|
30
|
-
readonly hash: FHash;
|
|
30
|
+
readonly hash: ut.FHash;
|
|
31
31
|
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array;
|
|
32
32
|
readonly adjustScalarBytes?: ((bytes: Uint8Array) => Uint8Array) | undefined;
|
|
33
33
|
readonly domain?: ((data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array) | undefined;
|
|
@@ -35,7 +35,7 @@ declare function validateOpts(curve: CurveType): Readonly<{
|
|
|
35
35
|
isValid: boolean;
|
|
36
36
|
value: bigint;
|
|
37
37
|
}) | undefined;
|
|
38
|
-
readonly preHash?: FHash | undefined;
|
|
38
|
+
readonly preHash?: ut.FHash | undefined;
|
|
39
39
|
readonly mapToCurve?: ((scalar: bigint[]) => AffinePoint<bigint>) | undefined;
|
|
40
40
|
}>;
|
|
41
41
|
export interface ExtPointType extends Group<ExtPointType> {
|
package/lib/abstract/edwards.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.twistedEdwards = void 0;
|
|
|
4
4
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
5
5
|
// Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
|
|
6
6
|
const modular_js_1 = require("./modular.js");
|
|
7
|
+
const ut = require("./utils.js");
|
|
7
8
|
const utils_js_1 = require("./utils.js");
|
|
8
9
|
const curve_js_1 = require("./curve.js");
|
|
9
10
|
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
|
@@ -12,24 +13,18 @@ const _1n = BigInt(1);
|
|
|
12
13
|
const _2n = BigInt(2);
|
|
13
14
|
const _8n = BigInt(8);
|
|
14
15
|
function validateOpts(curve) {
|
|
15
|
-
const opts = (0, curve_js_1.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
for (const fn of ['adjustScalarBytes', 'domain', 'uvRatio', 'mapToCurve']) {
|
|
28
|
-
if (opts[fn] === undefined)
|
|
29
|
-
continue; // Optional
|
|
30
|
-
if (typeof opts[fn] !== 'function')
|
|
31
|
-
throw new Error(`Invalid ${fn} function`);
|
|
32
|
-
}
|
|
16
|
+
const opts = (0, curve_js_1.validateBasic)(curve);
|
|
17
|
+
ut.validateObject(curve, {
|
|
18
|
+
hash: 'function',
|
|
19
|
+
a: 'bigint',
|
|
20
|
+
d: 'bigint',
|
|
21
|
+
randomBytes: 'function',
|
|
22
|
+
}, {
|
|
23
|
+
adjustScalarBytes: 'function',
|
|
24
|
+
domain: 'function',
|
|
25
|
+
uvRatio: 'function',
|
|
26
|
+
mapToCurve: 'function',
|
|
27
|
+
});
|
|
33
28
|
// Set defaults
|
|
34
29
|
return Object.freeze({ ...opts });
|
|
35
30
|
}
|
|
@@ -264,7 +259,7 @@ function twistedEdwards(curveDef) {
|
|
|
264
259
|
const normed = hex.slice(); // copy again, we'll manipulate it
|
|
265
260
|
const lastByte = hex[len - 1]; // select last byte
|
|
266
261
|
normed[len - 1] = lastByte & ~0x80; // clear last bit
|
|
267
|
-
const y =
|
|
262
|
+
const y = ut.bytesToNumberLE(normed);
|
|
268
263
|
if (y === _0n) {
|
|
269
264
|
// y=0 is allowed
|
|
270
265
|
}
|
|
@@ -294,12 +289,12 @@ function twistedEdwards(curveDef) {
|
|
|
294
289
|
}
|
|
295
290
|
toRawBytes() {
|
|
296
291
|
const { x, y } = this.toAffine();
|
|
297
|
-
const bytes =
|
|
292
|
+
const bytes = ut.numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
|
|
298
293
|
bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
|
|
299
294
|
return bytes; // and use the last byte to encode sign of x
|
|
300
295
|
}
|
|
301
296
|
toHex() {
|
|
302
|
-
return
|
|
297
|
+
return ut.bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
|
|
303
298
|
}
|
|
304
299
|
}
|
|
305
300
|
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
|
|
@@ -311,7 +306,7 @@ function twistedEdwards(curveDef) {
|
|
|
311
306
|
}
|
|
312
307
|
// Little-endian SHA512 with modulo n
|
|
313
308
|
function modN_LE(hash) {
|
|
314
|
-
return modN(
|
|
309
|
+
return modN(ut.bytesToNumberLE(hash));
|
|
315
310
|
}
|
|
316
311
|
function isHex(item, err) {
|
|
317
312
|
if (typeof item !== 'string' && !(item instanceof Uint8Array))
|
|
@@ -337,7 +332,7 @@ function twistedEdwards(curveDef) {
|
|
|
337
332
|
}
|
|
338
333
|
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
|
|
339
334
|
function hashDomainToScalar(context = new Uint8Array(), ...msgs) {
|
|
340
|
-
const msg =
|
|
335
|
+
const msg = ut.concatBytes(...msgs);
|
|
341
336
|
return modN_LE(cHash(domain(msg, (0, utils_js_1.ensureBytes)(context), !!preHash)));
|
|
342
337
|
}
|
|
343
338
|
/** Signs message with privateKey. RFC8032 5.1.6 */
|
|
@@ -352,7 +347,7 @@ function twistedEdwards(curveDef) {
|
|
|
352
347
|
const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
|
|
353
348
|
const s = modN(r + k * scalar); // S = (r + k * s) mod L
|
|
354
349
|
assertGE0(s); // 0 <= s < l
|
|
355
|
-
const res =
|
|
350
|
+
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
|
|
356
351
|
return (0, utils_js_1.ensureBytes)(res, nByteLength * 2); // 64-byte signature
|
|
357
352
|
}
|
|
358
353
|
function verify(sig, msg, publicKey, context) {
|
|
@@ -365,7 +360,7 @@ function twistedEdwards(curveDef) {
|
|
|
365
360
|
msg = preHash(msg); // for ed25519ph, etc
|
|
366
361
|
const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
|
|
367
362
|
const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
|
|
368
|
-
const s =
|
|
363
|
+
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
|
|
369
364
|
const SB = G.multiplyUnsafe(s);
|
|
370
365
|
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
|
|
371
366
|
const RkA = R.add(A.multiplyUnsafe(k));
|
|
@@ -20,7 +20,7 @@ function validateOpts(opts) {
|
|
|
20
20
|
exports.validateOpts = validateOpts;
|
|
21
21
|
function stringToBytes(str) {
|
|
22
22
|
if (typeof str !== 'string') {
|
|
23
|
-
throw new
|
|
23
|
+
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
|
24
24
|
}
|
|
25
25
|
return new TextEncoder().encode(str);
|
|
26
26
|
}
|
|
@@ -42,7 +42,7 @@ export interface Field<T> {
|
|
|
42
42
|
fromBytes(bytes: Uint8Array): T;
|
|
43
43
|
cmov(a: T, b: T, c: boolean): T;
|
|
44
44
|
}
|
|
45
|
-
export declare function validateField<T>(field: Field<T>):
|
|
45
|
+
export declare function validateField<T>(field: Field<T>): object;
|
|
46
46
|
export declare function FpPow<T>(f: Field<T>, num: T, power: bigint): T;
|
|
47
47
|
export declare function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[];
|
|
48
48
|
export declare function FpDiv<T>(f: Field<T>, lhs: T, rhs: T | bigint): T;
|
package/lib/abstract/modular.js
CHANGED
|
@@ -39,7 +39,6 @@ function pow(num, power, modulo) {
|
|
|
39
39
|
}
|
|
40
40
|
exports.pow = pow;
|
|
41
41
|
// Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4)
|
|
42
|
-
// TODO: Fp version?
|
|
43
42
|
function pow2(x, power, modulo) {
|
|
44
43
|
let res = x;
|
|
45
44
|
while (power-- > _0n) {
|
|
@@ -203,18 +202,17 @@ const FIELD_FIELDS = [
|
|
|
203
202
|
'addN', 'subN', 'mulN', 'sqrN'
|
|
204
203
|
];
|
|
205
204
|
function validateField(field) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
205
|
+
const initial = {
|
|
206
|
+
ORDER: 'bigint',
|
|
207
|
+
MASK: 'bigint',
|
|
208
|
+
BYTES: 'isSafeInteger',
|
|
209
|
+
BITS: 'isSafeInteger',
|
|
210
|
+
};
|
|
211
|
+
const opts = FIELD_FIELDS.reduce((map, val) => {
|
|
212
|
+
map[val] = 'function';
|
|
213
|
+
return map;
|
|
214
|
+
}, initial);
|
|
215
|
+
return (0, utils_js_1.validateObject)(field, opts);
|
|
218
216
|
}
|
|
219
217
|
exports.validateField = validateField;
|
|
220
218
|
// Generic field functions
|
|
@@ -7,28 +7,16 @@ const utils_js_1 = require("./utils.js");
|
|
|
7
7
|
const _0n = BigInt(0);
|
|
8
8
|
const _1n = BigInt(1);
|
|
9
9
|
function validateOpts(curve) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2']) {
|
|
21
|
-
if (curve[fn] === undefined)
|
|
22
|
-
continue; // Optional
|
|
23
|
-
if (typeof curve[fn] !== 'function')
|
|
24
|
-
throw new Error(`Invalid ${fn} function`);
|
|
25
|
-
}
|
|
26
|
-
for (const i of ['Gu']) {
|
|
27
|
-
if (curve[i] === undefined)
|
|
28
|
-
continue; // Optional
|
|
29
|
-
if (typeof curve[i] !== 'string')
|
|
30
|
-
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
|
31
|
-
}
|
|
10
|
+
(0, utils_js_1.validateObject)(curve, {
|
|
11
|
+
a24: 'bigint',
|
|
12
|
+
}, {
|
|
13
|
+
montgomeryBits: 'isSafeInteger',
|
|
14
|
+
nByteLength: 'isSafeInteger',
|
|
15
|
+
adjustScalarBytes: 'function',
|
|
16
|
+
domain: 'function',
|
|
17
|
+
powPminus2: 'function',
|
|
18
|
+
Gu: 'string',
|
|
19
|
+
});
|
|
32
20
|
// Set defaults
|
|
33
21
|
return Object.freeze({ ...curve });
|
|
34
22
|
}
|
|
@@ -43,31 +31,7 @@ function montgomery(curveDef) {
|
|
|
43
31
|
const fieldLen = CURVE.nByteLength;
|
|
44
32
|
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes);
|
|
45
33
|
const powPminus2 = CURVE.powPminus2 || ((x) => (0, modular_js_1.pow)(x, P - BigInt(2), P));
|
|
46
|
-
|
|
47
|
-
* Checks for num to be in range:
|
|
48
|
-
* For strict == true: `0 < num < max`.
|
|
49
|
-
* For strict == false: `0 <= num < max`.
|
|
50
|
-
* Converts non-float safe numbers to bigints.
|
|
51
|
-
*/
|
|
52
|
-
function normalizeScalar(num, max, strict = true) {
|
|
53
|
-
if (!max)
|
|
54
|
-
throw new TypeError('Specify max value');
|
|
55
|
-
if (typeof num === 'number' && Number.isSafeInteger(num))
|
|
56
|
-
num = BigInt(num);
|
|
57
|
-
if (typeof num === 'bigint' && num < max) {
|
|
58
|
-
if (strict) {
|
|
59
|
-
if (_0n < num)
|
|
60
|
-
return num;
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
if (_0n <= num)
|
|
64
|
-
return num;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
throw new TypeError('Expected valid scalar: 0 < scalar < max');
|
|
68
|
-
}
|
|
69
|
-
// cswap from RFC7748
|
|
70
|
-
// NOTE: cswap is not from RFC7748!
|
|
34
|
+
// cswap from RFC7748. But it is not from RFC7748!
|
|
71
35
|
/*
|
|
72
36
|
cswap(swap, x_2, x_3):
|
|
73
37
|
dummy = mask(swap) AND (x_2 XOR x_3)
|
|
@@ -83,6 +47,11 @@ function montgomery(curveDef) {
|
|
|
83
47
|
x_3 = modP(x_3 + dummy);
|
|
84
48
|
return [x_2, x_3];
|
|
85
49
|
}
|
|
50
|
+
function assertFieldElement(n) {
|
|
51
|
+
if (typeof n === 'bigint' && _0n <= n && n < P)
|
|
52
|
+
return n;
|
|
53
|
+
throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
|
|
54
|
+
}
|
|
86
55
|
// x25519 from 4
|
|
87
56
|
/**
|
|
88
57
|
*
|
|
@@ -91,11 +60,10 @@ function montgomery(curveDef) {
|
|
|
91
60
|
* @returns new Point on Montgomery curve
|
|
92
61
|
*/
|
|
93
62
|
function montgomeryLadder(pointU, scalar) {
|
|
94
|
-
const
|
|
95
|
-
const u = normalizeScalar(pointU, P);
|
|
63
|
+
const u = assertFieldElement(pointU);
|
|
96
64
|
// Section 5: Implementations MUST accept non-canonical values and process them as
|
|
97
65
|
// if they had been reduced modulo the field prime.
|
|
98
|
-
const k =
|
|
66
|
+
const k = assertFieldElement(scalar);
|
|
99
67
|
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
|
|
100
68
|
const a24 = CURVE.a24;
|
|
101
69
|
const x_1 = u;
|
|
@@ -148,11 +116,11 @@ function montgomery(curveDef) {
|
|
|
148
116
|
return (0, utils_js_1.numberToBytesLE)(modP(u), montgomeryBytes);
|
|
149
117
|
}
|
|
150
118
|
function decodeUCoordinate(uEnc) {
|
|
151
|
-
const u = (0, utils_js_1.ensureBytes)(uEnc, montgomeryBytes);
|
|
152
119
|
// Section 5: When receiving such an array, implementations of X25519
|
|
153
120
|
// MUST mask the most significant bit in the final byte.
|
|
154
121
|
// This is very ugly way, but it works because fieldLen-1 is outside of bounds for X448, so this becomes NOOP
|
|
155
122
|
// fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519
|
|
123
|
+
const u = (0, utils_js_1.ensureBytes)(uEnc, montgomeryBytes);
|
|
156
124
|
u[fieldLen - 1] &= 127; // 0b0111_1111
|
|
157
125
|
return (0, utils_js_1.bytesToNumberLE)(u);
|
|
158
126
|
}
|
|
@@ -162,13 +130,6 @@ function montgomery(curveDef) {
|
|
|
162
130
|
throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
|
|
163
131
|
return (0, utils_js_1.bytesToNumberLE)(adjustScalarBytes(bytes));
|
|
164
132
|
}
|
|
165
|
-
/**
|
|
166
|
-
* Computes shared secret between private key "scalar" and public key's "u" (x) coordinate.
|
|
167
|
-
* We can get 'y' coordinate from 'u',
|
|
168
|
-
* but Point.fromHex also wants 'x' coordinate oddity flag,
|
|
169
|
-
* and we cannot get 'x' without knowing 'v'.
|
|
170
|
-
* Need to add generic conversion between twisted edwards and complimentary curve for JubJub.
|
|
171
|
-
*/
|
|
172
133
|
function scalarMult(scalar, u) {
|
|
173
134
|
const pointU = decodeUCoordinate(u);
|
|
174
135
|
const _scalar = decodeScalar(scalar);
|
|
@@ -179,12 +140,7 @@ function montgomery(curveDef) {
|
|
|
179
140
|
throw new Error('Invalid private or public key received');
|
|
180
141
|
return encodeUCoordinate(pu);
|
|
181
142
|
}
|
|
182
|
-
|
|
183
|
-
* Computes public key from private.
|
|
184
|
-
* Executes scalar multiplication of curve's base point by scalar.
|
|
185
|
-
* @param scalar private key
|
|
186
|
-
* @returns new public key
|
|
187
|
-
*/
|
|
143
|
+
// Computes public key from private. By doing scalar multiplication of base point.
|
|
188
144
|
function scalarMultBase(scalar) {
|
|
189
145
|
return scalarMult(scalar, CURVE.Gu);
|
|
190
146
|
}
|
package/lib/abstract/utils.d.ts
CHANGED
|
@@ -19,9 +19,12 @@ export declare const numberToBytesBE: (n: bigint, len: number) => Uint8Array;
|
|
|
19
19
|
export declare const numberToBytesLE: (n: bigint, len: number) => Uint8Array;
|
|
20
20
|
export declare const numberToVarBytesBE: (n: bigint) => Uint8Array;
|
|
21
21
|
export declare function ensureBytes(hex: Hex, expectedLength?: number): Uint8Array;
|
|
22
|
-
export declare function concatBytes(...
|
|
22
|
+
export declare function concatBytes(...arrs: Uint8Array[]): Uint8Array;
|
|
23
23
|
export declare function equalBytes(b1: Uint8Array, b2: Uint8Array): boolean;
|
|
24
24
|
export declare function bitLen(n: bigint): number;
|
|
25
25
|
export declare const bitGet: (n: bigint, pos: number) => bigint;
|
|
26
26
|
export declare const bitSet: (n: bigint, pos: number, value: boolean) => bigint;
|
|
27
27
|
export declare const bitMask: (n: number) => bigint;
|
|
28
|
+
declare type ValMap = Record<string, string>;
|
|
29
|
+
export declare function validateObject(object: object, validators: ValMap, optValidators?: ValMap): object;
|
|
30
|
+
export {};
|