@noble/curves 0.6.0 → 0.6.2
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 +130 -92
- 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 +44 -26
- 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 +23 -65
- package/lib/abstract/utils.d.ts +17 -1
- package/lib/abstract/utils.js +57 -27
- package/lib/abstract/weierstrass.d.ts +10 -10
- package/lib/abstract/weierstrass.js +61 -79
- package/lib/esm/abstract/curve.js +11 -17
- package/lib/esm/abstract/edwards.js +46 -28
- package/lib/esm/abstract/hash-to-curve.js +1 -1
- package/lib/esm/abstract/modular.js +12 -14
- package/lib/esm/abstract/montgomery.js +24 -66
- package/lib/esm/abstract/poseidon.js +1 -1
- package/lib/esm/abstract/utils.js +55 -26
- package/lib/esm/abstract/weierstrass.js +62 -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
|
|
|
@@ -329,47 +329,54 @@ The module allows to hash arbitrary strings to elliptic curve points.
|
|
|
329
329
|
|
|
330
330
|
- `expand_message_xmd` [(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1) produces a uniformly random byte string using a cryptographic hash function H that outputs b bits..
|
|
331
331
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
332
|
+
```ts
|
|
333
|
+
function expand_message_xmd(
|
|
334
|
+
msg: Uint8Array,
|
|
335
|
+
DST: Uint8Array,
|
|
336
|
+
lenInBytes: number,
|
|
337
|
+
H: CHash
|
|
338
|
+
): Uint8Array;
|
|
339
|
+
function expand_message_xof(
|
|
340
|
+
msg: Uint8Array,
|
|
341
|
+
DST: Uint8Array,
|
|
342
|
+
lenInBytes: number,
|
|
343
|
+
k: number,
|
|
344
|
+
H: CHash
|
|
345
|
+
): Uint8Array;
|
|
346
|
+
```
|
|
340
347
|
|
|
341
348
|
- `hash_to_field(msg, count, options)` [(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3)
|
|
342
|
-
hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
349
|
+
hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
|
|
350
|
+
_ `msg` a byte string containing the message to hash
|
|
351
|
+
_ `count` the number of elements of F to output
|
|
352
|
+
_ `options` `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
|
|
353
|
+
_ Returns `[u_0, ..., u_(count - 1)]`, a list of field elements.
|
|
354
|
+
|
|
355
|
+
```ts
|
|
356
|
+
function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][];
|
|
357
|
+
type htfOpts = {
|
|
358
|
+
// DST: a domain separation tag
|
|
359
|
+
// defined in section 2.2.5
|
|
360
|
+
DST: string;
|
|
361
|
+
// p: the characteristic of F
|
|
362
|
+
// where F is a finite field of characteristic p and order q = p^m
|
|
363
|
+
p: bigint;
|
|
364
|
+
// m: the extension degree of F, m >= 1
|
|
365
|
+
// where F is a finite field of characteristic p and order q = p^m
|
|
366
|
+
m: number;
|
|
367
|
+
// k: the target security level for the suite in bits
|
|
368
|
+
// defined in section 5.1
|
|
369
|
+
k: number;
|
|
370
|
+
// option to use a message that has already been processed by
|
|
371
|
+
// expand_message_xmd
|
|
372
|
+
expand?: 'xmd' | 'xof';
|
|
373
|
+
// Hash functions for: expand_message_xmd is appropriate for use with a
|
|
374
|
+
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
|
|
375
|
+
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
|
|
376
|
+
// TODO: verify that hash is shake if expand==='xof' via types
|
|
377
|
+
hash: CHash;
|
|
378
|
+
};
|
|
379
|
+
```
|
|
373
380
|
|
|
374
381
|
### abstract/poseidon: Poseidon hash
|
|
375
382
|
|
|
@@ -445,63 +452,94 @@ We consider infrastructure attacks like rogue NPM modules very important; that's
|
|
|
445
452
|
Benchmark results on Apple M2 with node v18.10:
|
|
446
453
|
|
|
447
454
|
```
|
|
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
|
-
|
|
455
|
+
secp256k1
|
|
456
|
+
init x 57 ops/sec @ 17ms/op
|
|
457
|
+
getPublicKey x 4,946 ops/sec @ 202μs/op
|
|
458
|
+
sign x 3,914 ops/sec @ 255μs/op
|
|
459
|
+
verify x 682 ops/sec @ 1ms/op
|
|
460
|
+
getSharedSecret x 427 ops/sec @ 2ms/op
|
|
461
|
+
recoverPublicKey x 683 ops/sec @ 1ms/op
|
|
462
|
+
schnorr.sign x 539 ops/sec @ 1ms/op
|
|
463
|
+
schnorr.verify x 716 ops/sec @ 1ms/op
|
|
464
|
+
|
|
465
|
+
P256
|
|
466
|
+
init x 30 ops/sec @ 32ms/op
|
|
467
|
+
getPublicKey x 5,008 ops/sec @ 199μs/op
|
|
468
|
+
sign x 3,970 ops/sec @ 251μs/op
|
|
469
|
+
verify x 515 ops/sec @ 1ms/op
|
|
470
|
+
|
|
471
|
+
P384
|
|
472
|
+
init x 14 ops/sec @ 66ms/op
|
|
473
|
+
getPublicKey x 2,434 ops/sec @ 410μs/op
|
|
474
|
+
sign x 1,942 ops/sec @ 514μs/op
|
|
475
|
+
verify x 206 ops/sec @ 4ms/op
|
|
476
|
+
|
|
477
|
+
P521
|
|
478
|
+
init x 7 ops/sec @ 126ms/op
|
|
479
|
+
getPublicKey x 1,282 ops/sec @ 779μs/op
|
|
480
|
+
sign x 1,077 ops/sec @ 928μs/op
|
|
481
|
+
verify x 110 ops/sec @ 9ms/op
|
|
482
|
+
|
|
483
|
+
ed25519
|
|
484
|
+
init x 37 ops/sec @ 26ms/op
|
|
485
|
+
getPublicKey x 8,147 ops/sec @ 122μs/op
|
|
486
|
+
sign x 3,979 ops/sec @ 251μs/op
|
|
487
|
+
verify x 848 ops/sec @ 1ms/op
|
|
488
|
+
|
|
489
|
+
ed448
|
|
490
|
+
init x 17 ops/sec @ 58ms/op
|
|
491
|
+
getPublicKey x 3,083 ops/sec @ 324μs/op
|
|
492
|
+
sign x 1,473 ops/sec @ 678μs/op
|
|
493
|
+
verify x 323 ops/sec @ 3ms/op
|
|
494
|
+
|
|
495
|
+
bls12-381
|
|
496
|
+
init x 30 ops/sec @ 33ms/op
|
|
497
|
+
getPublicKey x 788 ops/sec @ 1ms/op
|
|
498
|
+
sign x 45 ops/sec @ 21ms/op
|
|
499
|
+
verify x 32 ops/sec @ 30ms/op
|
|
500
|
+
pairing x 88 ops/sec @ 11ms/op
|
|
501
|
+
|
|
502
|
+
stark
|
|
503
|
+
init x 31 ops/sec @ 31ms/op
|
|
479
504
|
pedersen
|
|
480
|
-
|
|
481
|
-
|
|
505
|
+
├─old x 84 ops/sec @ 11ms/op
|
|
506
|
+
└─noble x 802 ops/sec @ 1ms/op
|
|
507
|
+
poseidon x 7,466 ops/sec @ 133μs/op
|
|
482
508
|
verify
|
|
483
|
-
|
|
484
|
-
|
|
509
|
+
├─old x 300 ops/sec @ 3ms/op
|
|
510
|
+
└─noble x 474 ops/sec @ 2ms/op
|
|
485
511
|
```
|
|
486
512
|
|
|
487
513
|
## Upgrading
|
|
488
514
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
515
|
+
If you're coming from single-curve noble packages, the following changes need to be kept in mind:
|
|
516
|
+
|
|
517
|
+
- 2d affine (x, y) points have been removed to reduce complexity and improve speed
|
|
518
|
+
- Removed `number` support as a type for private keys. `bigint` is still supported
|
|
519
|
+
- `mod`, `invert` are no longer present in `utils`. Use `@noble/curves/abstract/modular.js` now.
|
|
520
|
+
|
|
521
|
+
Upgrading from @noble/secp256k1 1.7:
|
|
522
|
+
|
|
523
|
+
- Compressed (33-byte) public keys are now returned by default, instead of uncompressed
|
|
524
|
+
- Methods are now synchronous. Setting `secp.utils.hmacSha256` is no longer required
|
|
525
|
+
- `sign()`
|
|
526
|
+
- `der`, `recovered` options were removed
|
|
527
|
+
- `canonical` was renamed to `lowS`
|
|
528
|
+
- Return type is now `{ r: bigint, s: bigint, recovery: number }` instance of `Signature`
|
|
529
|
+
- `verify()`
|
|
530
|
+
- `strict` was renamed to `lowS`
|
|
531
|
+
- `recoverPublicKey()`: moved to sig instance `Signature#recoverPublicKey(msgHash)`
|
|
532
|
+
- `Point` was removed: use `ProjectivePoint` in xyz coordinates
|
|
533
|
+
- `utils`: Many methods were removed, others were moved to `schnorr` namespace
|
|
534
|
+
|
|
535
|
+
Upgrading from @noble/ed25519 1.7:
|
|
536
|
+
|
|
537
|
+
- Methods are now synchronous. Setting `secp.utils.hmacSha256` is no longer required
|
|
538
|
+
- ed25519ph, ed25519ctx
|
|
539
|
+
- `Point` was removed: use `ExtendedPoint` in xyzt coordinates
|
|
540
|
+
- `Signature` was removed
|
|
541
|
+
- `getSharedSecret` was removed: use separate x25519 sub-module
|
|
542
|
+
- `bigint` is no longer allowed in `getPublicKey`, `sign`, `verify`. Reason: ed25519 is LE, can lead to bugs
|
|
505
543
|
|
|
506
544
|
## Contributing & testing
|
|
507
545
|
|
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
|
}
|
|
@@ -114,7 +109,30 @@ function twistedEdwards(curveDef) {
|
|
|
114
109
|
this._WINDOW_SIZE = windowSize;
|
|
115
110
|
pointPrecomputes.delete(this);
|
|
116
111
|
}
|
|
117
|
-
|
|
112
|
+
// Not required for fromHex(), which always creates valid points.
|
|
113
|
+
// Could be useful for fromAffine().
|
|
114
|
+
assertValidity() {
|
|
115
|
+
const { a, d } = CURVE;
|
|
116
|
+
if (this.is0())
|
|
117
|
+
throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
|
|
118
|
+
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
|
|
119
|
+
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
|
|
120
|
+
const { ex: X, ey: Y, ez: Z, et: T } = this;
|
|
121
|
+
const X2 = modP(X * X); // X²
|
|
122
|
+
const Y2 = modP(Y * Y); // Y²
|
|
123
|
+
const Z2 = modP(Z * Z); // Z²
|
|
124
|
+
const Z4 = modP(Z2 * Z2); // Z⁴
|
|
125
|
+
const aX2 = modP(X2 * a); // aX²
|
|
126
|
+
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
|
|
127
|
+
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
|
|
128
|
+
if (left !== right)
|
|
129
|
+
throw new Error('bad point: equation left != right (1)');
|
|
130
|
+
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
|
|
131
|
+
const XY = modP(X * Y);
|
|
132
|
+
const ZT = modP(Z * T);
|
|
133
|
+
if (XY !== ZT)
|
|
134
|
+
throw new Error('bad point: equation left != right (2)');
|
|
135
|
+
}
|
|
118
136
|
// Compare one point to another.
|
|
119
137
|
equals(other) {
|
|
120
138
|
isPoint(other);
|
|
@@ -264,7 +282,7 @@ function twistedEdwards(curveDef) {
|
|
|
264
282
|
const normed = hex.slice(); // copy again, we'll manipulate it
|
|
265
283
|
const lastByte = hex[len - 1]; // select last byte
|
|
266
284
|
normed[len - 1] = lastByte & ~0x80; // clear last bit
|
|
267
|
-
const y =
|
|
285
|
+
const y = ut.bytesToNumberLE(normed);
|
|
268
286
|
if (y === _0n) {
|
|
269
287
|
// y=0 is allowed
|
|
270
288
|
}
|
|
@@ -294,12 +312,12 @@ function twistedEdwards(curveDef) {
|
|
|
294
312
|
}
|
|
295
313
|
toRawBytes() {
|
|
296
314
|
const { x, y } = this.toAffine();
|
|
297
|
-
const bytes =
|
|
315
|
+
const bytes = ut.numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
|
|
298
316
|
bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
|
|
299
317
|
return bytes; // and use the last byte to encode sign of x
|
|
300
318
|
}
|
|
301
319
|
toHex() {
|
|
302
|
-
return
|
|
320
|
+
return ut.bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
|
|
303
321
|
}
|
|
304
322
|
}
|
|
305
323
|
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
|
|
@@ -311,7 +329,7 @@ function twistedEdwards(curveDef) {
|
|
|
311
329
|
}
|
|
312
330
|
// Little-endian SHA512 with modulo n
|
|
313
331
|
function modN_LE(hash) {
|
|
314
|
-
return modN(
|
|
332
|
+
return modN(ut.bytesToNumberLE(hash));
|
|
315
333
|
}
|
|
316
334
|
function isHex(item, err) {
|
|
317
335
|
if (typeof item !== 'string' && !(item instanceof Uint8Array))
|
|
@@ -337,7 +355,7 @@ function twistedEdwards(curveDef) {
|
|
|
337
355
|
}
|
|
338
356
|
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
|
|
339
357
|
function hashDomainToScalar(context = new Uint8Array(), ...msgs) {
|
|
340
|
-
const msg =
|
|
358
|
+
const msg = ut.concatBytes(...msgs);
|
|
341
359
|
return modN_LE(cHash(domain(msg, (0, utils_js_1.ensureBytes)(context), !!preHash)));
|
|
342
360
|
}
|
|
343
361
|
/** Signs message with privateKey. RFC8032 5.1.6 */
|
|
@@ -352,7 +370,7 @@ function twistedEdwards(curveDef) {
|
|
|
352
370
|
const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
|
|
353
371
|
const s = modN(r + k * scalar); // S = (r + k * s) mod L
|
|
354
372
|
assertGE0(s); // 0 <= s < l
|
|
355
|
-
const res =
|
|
373
|
+
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
|
|
356
374
|
return (0, utils_js_1.ensureBytes)(res, nByteLength * 2); // 64-byte signature
|
|
357
375
|
}
|
|
358
376
|
function verify(sig, msg, publicKey, context) {
|
|
@@ -365,7 +383,7 @@ function twistedEdwards(curveDef) {
|
|
|
365
383
|
msg = preHash(msg); // for ed25519ph, etc
|
|
366
384
|
const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
|
|
367
385
|
const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
|
|
368
|
-
const s =
|
|
386
|
+
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
|
|
369
387
|
const SB = G.multiplyUnsafe(s);
|
|
370
388
|
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
|
|
371
389
|
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>): Field<T>;
|
|
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
|