@noble/curves 0.5.2 → 0.6.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 +49 -5
- package/lib/_shortw_utils.d.ts +10 -21
- package/lib/abstract/bls.d.ts +39 -32
- package/lib/abstract/bls.js +74 -73
- package/lib/abstract/{group.d.ts → curve.d.ts} +31 -1
- package/lib/abstract/{group.js → curve.js} +39 -2
- package/lib/abstract/edwards.d.ts +30 -72
- package/lib/abstract/edwards.js +197 -375
- package/lib/abstract/hash-to-curve.d.ts +25 -6
- package/lib/abstract/hash-to-curve.js +40 -12
- package/lib/abstract/modular.d.ts +20 -7
- package/lib/abstract/modular.js +61 -35
- package/lib/abstract/montgomery.js +4 -5
- package/lib/abstract/poseidon.d.ts +29 -0
- package/lib/abstract/poseidon.js +115 -0
- package/lib/abstract/utils.d.ts +5 -36
- package/lib/abstract/utils.js +23 -71
- package/lib/abstract/weierstrass.d.ts +51 -74
- package/lib/abstract/weierstrass.js +455 -628
- package/lib/bls12-381.js +63 -58
- package/lib/bn.js +1 -1
- package/lib/ed25519.d.ts +7 -5
- package/lib/ed25519.js +82 -79
- package/lib/ed448.d.ts +3 -0
- package/lib/ed448.js +86 -83
- package/lib/esm/abstract/bls.js +75 -74
- package/lib/esm/abstract/{group.js → curve.js} +37 -1
- package/lib/esm/abstract/edwards.js +196 -374
- package/lib/esm/abstract/hash-to-curve.js +38 -11
- package/lib/esm/abstract/modular.js +58 -34
- package/lib/esm/abstract/montgomery.js +5 -6
- package/lib/esm/abstract/poseidon.js +109 -0
- package/lib/esm/abstract/utils.js +21 -66
- package/lib/esm/abstract/weierstrass.js +454 -627
- package/lib/esm/bls12-381.js +75 -70
- package/lib/esm/bn.js +1 -1
- package/lib/esm/ed25519.js +80 -78
- package/lib/esm/ed448.js +84 -82
- package/lib/esm/jubjub.js +1 -1
- package/lib/esm/p256.js +11 -9
- package/lib/esm/p384.js +11 -9
- package/lib/esm/p521.js +13 -12
- package/lib/esm/secp256k1.js +115 -151
- package/lib/esm/stark.js +104 -40
- package/lib/jubjub.d.ts +2 -2
- package/lib/jubjub.js +1 -1
- package/lib/p192.d.ts +20 -42
- package/lib/p224.d.ts +20 -42
- package/lib/p256.d.ts +23 -42
- package/lib/p256.js +13 -10
- package/lib/p384.d.ts +23 -42
- package/lib/p384.js +13 -10
- package/lib/p521.d.ts +23 -42
- package/lib/p521.js +15 -13
- package/lib/secp256k1.d.ts +25 -37
- package/lib/secp256k1.js +115 -151
- package/lib/stark.d.ts +36 -19
- package/lib/stark.js +107 -40
- package/package.json +13 -8
package/README.md
CHANGED
|
@@ -6,7 +6,9 @@ Minimal, auditable JS implementation of elliptic curve cryptography.
|
|
|
6
6
|
- ECDSA, EdDSA, Schnorr, BLS signature schemes, ECDH key agreement
|
|
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
|
+
- 🏎 [Ultra-fast](#speed), hand-optimized for caveats of JS engines
|
|
10
12
|
- 🔍 Unique tests ensure correctness. Wycheproof vectors included
|
|
11
13
|
- 🔻 Tree-shaking-friendly: there is no entry point, which ensures small size of your app
|
|
12
14
|
|
|
@@ -24,7 +26,6 @@ Curves incorporate work from previous noble packages
|
|
|
24
26
|
[ed25519](https://github.com/paulmillr/noble-ed25519),
|
|
25
27
|
[bls12-381](https://github.com/paulmillr/noble-bls12-381)),
|
|
26
28
|
which had security audits and were developed from 2019 to 2022.
|
|
27
|
-
The goal is to replace them with lean UMD builds based on single-codebase noble-curves.
|
|
28
29
|
|
|
29
30
|
### This library belongs to _noble_ crypto
|
|
30
31
|
|
|
@@ -88,6 +89,7 @@ To define a custom curve, check out API below.
|
|
|
88
89
|
- [abstract/montgomery: Montgomery curve](#abstractmontgomery-montgomery-curve)
|
|
89
90
|
- [abstract/weierstrass: Short Weierstrass curve](#abstractweierstrass-short-weierstrass-curve)
|
|
90
91
|
- [abstract/hash-to-curve: Hashing strings to curve points](#abstracthash-to-curve-hashing-strings-to-curve-points)
|
|
92
|
+
- [abstract/poseidon: Poseidon hash](#abstractposeidon-poseidon-hash)
|
|
91
93
|
- [abstract/modular](#abstractmodular)
|
|
92
94
|
- [abstract/utils](#abstractutils)
|
|
93
95
|
|
|
@@ -302,13 +304,12 @@ const shared = secp256k1.getSharedSecret(key, someonesPubkey);
|
|
|
302
304
|
export type CurveFn = {
|
|
303
305
|
CURVE: ReturnType<typeof validateOpts>;
|
|
304
306
|
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
|
305
|
-
getSharedSecret: (privateA: PrivKey, publicB:
|
|
307
|
+
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
|
|
306
308
|
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType;
|
|
307
|
-
signUnhashed: (msg: Uint8Array, privKey: PrivKey, opts?: SignOpts) => SignatureType;
|
|
308
309
|
verify: (
|
|
309
310
|
signature: Hex | SignatureType,
|
|
310
311
|
msgHash: Hex,
|
|
311
|
-
publicKey:
|
|
312
|
+
publicKey: Hex,
|
|
312
313
|
opts?: { lowS?: boolean }
|
|
313
314
|
) => boolean;
|
|
314
315
|
Point: PointConstructor;
|
|
@@ -370,6 +371,30 @@ hashes arbitrary-length byte strings to a list of one or more elements of a fini
|
|
|
370
371
|
};
|
|
371
372
|
```
|
|
372
373
|
|
|
374
|
+
### abstract/poseidon: Poseidon hash
|
|
375
|
+
|
|
376
|
+
Implements [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash.
|
|
377
|
+
|
|
378
|
+
There are many poseidon instances with different constants. We don't provide them,
|
|
379
|
+
but we provide ability to specify them manually. For actual usage, check out
|
|
380
|
+
stark curve source code.
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
import { poseidon } from '@noble/curves/abstract/poseidon';
|
|
384
|
+
|
|
385
|
+
type PoseidonOpts = {
|
|
386
|
+
Fp: Field<bigint>;
|
|
387
|
+
t: number;
|
|
388
|
+
roundsFull: number;
|
|
389
|
+
roundsPartial: number;
|
|
390
|
+
sboxPower?: number;
|
|
391
|
+
reversePartialPowIdx?: boolean; // Hack for stark
|
|
392
|
+
mds: bigint[][];
|
|
393
|
+
roundConstants: bigint[][];
|
|
394
|
+
};
|
|
395
|
+
const instance = poseidon(opts: PoseidonOpts);
|
|
396
|
+
```
|
|
397
|
+
|
|
373
398
|
### abstract/modular
|
|
374
399
|
|
|
375
400
|
Modular arithmetics utilities.
|
|
@@ -459,6 +484,25 @@ verify
|
|
|
459
484
|
noble x 698 ops/sec @ 1ms/op
|
|
460
485
|
```
|
|
461
486
|
|
|
487
|
+
## Upgrading
|
|
488
|
+
|
|
489
|
+
Differences from @noble/secp256k1 1.7:
|
|
490
|
+
|
|
491
|
+
1. Different double() formula (but same addition)
|
|
492
|
+
2. Different sqrt() function
|
|
493
|
+
3. DRBG supports outputLen bigger than outputLen of hmac
|
|
494
|
+
4. Support for different hash functions
|
|
495
|
+
|
|
496
|
+
Differences from @noble/ed25519 1.7:
|
|
497
|
+
|
|
498
|
+
1. Variable field element lengths between EDDSA/ECDH:
|
|
499
|
+
EDDSA (RFC8032) is 456 bits / 57 bytes, ECDH (RFC7748) is 448 bits / 56 bytes
|
|
500
|
+
2. Different addition formula (doubling is same)
|
|
501
|
+
3. uvRatio differs between curves (half-expected, not only pow fn changes)
|
|
502
|
+
4. Point decompression code is different (unexpected), now using generalized formula
|
|
503
|
+
5. Domain function was no-op for ed25519, but adds some data even with empty context for ed448
|
|
504
|
+
|
|
505
|
+
|
|
462
506
|
## Contributing & testing
|
|
463
507
|
|
|
464
508
|
1. Clone the repository
|
package/lib/_shortw_utils.d.ts
CHANGED
|
@@ -32,37 +32,26 @@ export declare function createCurve(curveDef: CurveDef, defHash: CHash): Readonl
|
|
|
32
32
|
k2: bigint;
|
|
33
33
|
};
|
|
34
34
|
} | undefined;
|
|
35
|
-
readonly isTorsionFree?: ((c: import("./abstract/weierstrass.js").
|
|
36
|
-
readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").
|
|
37
|
-
readonly htfDefaults?: import("./abstract/hash-to-curve.js").htfOpts | undefined;
|
|
38
|
-
readonly mapToCurve?: ((scalar: bigint[]) => {
|
|
39
|
-
x: bigint;
|
|
40
|
-
y: bigint;
|
|
41
|
-
}) | undefined;
|
|
35
|
+
readonly isTorsionFree?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => boolean) | undefined;
|
|
36
|
+
readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => import("./abstract/weierstrass.js").ProjPointType<bigint>) | undefined;
|
|
42
37
|
lowS: boolean;
|
|
43
38
|
readonly hash: CHash;
|
|
44
39
|
readonly hmac: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
|
|
45
40
|
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array;
|
|
46
|
-
readonly
|
|
41
|
+
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined;
|
|
42
|
+
readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined;
|
|
47
43
|
}>;
|
|
48
44
|
getPublicKey: (privateKey: import("./abstract/utils.js").PrivKey, isCompressed?: boolean | undefined) => Uint8Array;
|
|
49
|
-
getSharedSecret: (privateA: import("./abstract/utils.js").PrivKey, publicB: import("./abstract/
|
|
45
|
+
getSharedSecret: (privateA: import("./abstract/utils.js").PrivKey, publicB: import("./abstract/utils.js").Hex, isCompressed?: boolean | undefined) => Uint8Array;
|
|
50
46
|
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: import("./abstract/utils.js").PrivKey, opts?: import("./abstract/weierstrass.js").SignOpts | undefined) => import("./abstract/weierstrass.js").SignatureType;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
} | undefined) => boolean;
|
|
55
|
-
|
|
56
|
-
ProjectivePoint: import("./abstract/weierstrass.js").ProjectiveConstructor<bigint>;
|
|
47
|
+
verify: (signature: import("./abstract/utils.js").Hex | {
|
|
48
|
+
r: bigint;
|
|
49
|
+
s: bigint;
|
|
50
|
+
}, msgHash: import("./abstract/utils.js").Hex, publicKey: import("./abstract/utils.js").Hex, opts?: import("./abstract/weierstrass.js").VerOpts | undefined) => boolean;
|
|
51
|
+
ProjectivePoint: import("./abstract/weierstrass.js").ProjConstructor<bigint>;
|
|
57
52
|
Signature: import("./abstract/weierstrass.js").SignatureConstructor;
|
|
58
53
|
utils: {
|
|
59
|
-
_bigintToBytes: (num: bigint) => Uint8Array;
|
|
60
|
-
_bigintToString: (num: bigint) => string;
|
|
61
54
|
_normalizePrivateKey: (key: import("./abstract/utils.js").PrivKey) => bigint;
|
|
62
|
-
_normalizePublicKey: (publicKey: import("./abstract/weierstrass.js").PubKey) => import("./abstract/weierstrass.js").PointType<bigint>;
|
|
63
|
-
_isWithinCurveOrder: (num: bigint) => boolean;
|
|
64
|
-
_isValidFieldElement: (num: bigint) => boolean;
|
|
65
|
-
_weierstrassEquation: (x: bigint) => bigint;
|
|
66
55
|
isValidPrivateKey(privateKey: import("./abstract/utils.js").PrivKey): boolean;
|
|
67
56
|
hashToPrivateKey: (hash: import("./abstract/utils.js").Hex) => Uint8Array;
|
|
68
57
|
randomPrivateKey: () => Uint8Array;
|
package/lib/abstract/bls.d.ts
CHANGED
|
@@ -11,26 +11,31 @@
|
|
|
11
11
|
* We are using Fp for private keys (shorter) and Fp₂ for signatures (longer).
|
|
12
12
|
* Some projects may prefer to swap this relation, it is not supported for now.
|
|
13
13
|
*/
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import { Hex, PrivKey } from './utils.js';
|
|
17
|
-
import
|
|
18
|
-
import { CurvePointsType,
|
|
14
|
+
import { AffinePoint } from './curve.js';
|
|
15
|
+
import { Field } from './modular.js';
|
|
16
|
+
import { Hex, PrivKey, CHash } from './utils.js';
|
|
17
|
+
import * as htf from './hash-to-curve.js';
|
|
18
|
+
import { CurvePointsType, ProjPointType as ProjPointType, CurvePointsRes } from './weierstrass.js';
|
|
19
19
|
declare type Fp = bigint;
|
|
20
20
|
export declare type SignatureCoder<Fp2> = {
|
|
21
|
-
decode(hex: Hex):
|
|
22
|
-
encode(point:
|
|
21
|
+
decode(hex: Hex): ProjPointType<Fp2>;
|
|
22
|
+
encode(point: ProjPointType<Fp2>): Uint8Array;
|
|
23
23
|
};
|
|
24
24
|
export declare type CurveType<Fp, Fp2, Fp6, Fp12> = {
|
|
25
25
|
r: bigint;
|
|
26
|
-
G1: Omit<CurvePointsType<Fp>, 'n'
|
|
26
|
+
G1: Omit<CurvePointsType<Fp>, 'n'> & {
|
|
27
|
+
mapToCurve: htf.MapToCurve<Fp>;
|
|
28
|
+
htfDefaults: htf.Opts;
|
|
29
|
+
};
|
|
27
30
|
G2: Omit<CurvePointsType<Fp2>, 'n'> & {
|
|
28
31
|
Signature: SignatureCoder<Fp2>;
|
|
32
|
+
mapToCurve: htf.MapToCurve<Fp2>;
|
|
33
|
+
htfDefaults: htf.Opts;
|
|
29
34
|
};
|
|
30
35
|
x: bigint;
|
|
31
|
-
Fp:
|
|
32
|
-
Fr:
|
|
33
|
-
Fp2:
|
|
36
|
+
Fp: Field<Fp>;
|
|
37
|
+
Fr: Field<bigint>;
|
|
38
|
+
Fp2: Field<Fp2> & {
|
|
34
39
|
reim: (num: Fp2) => {
|
|
35
40
|
re: bigint;
|
|
36
41
|
im: bigint;
|
|
@@ -38,51 +43,53 @@ export declare type CurveType<Fp, Fp2, Fp6, Fp12> = {
|
|
|
38
43
|
multiplyByB: (num: Fp2) => Fp2;
|
|
39
44
|
frobeniusMap(num: Fp2, power: number): Fp2;
|
|
40
45
|
};
|
|
41
|
-
Fp6:
|
|
42
|
-
Fp12:
|
|
46
|
+
Fp6: Field<Fp6>;
|
|
47
|
+
Fp12: Field<Fp12> & {
|
|
43
48
|
frobeniusMap(num: Fp12, power: number): Fp12;
|
|
44
49
|
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
|
|
45
50
|
conjugate(num: Fp12): Fp12;
|
|
46
51
|
finalExponentiate(num: Fp12): Fp12;
|
|
47
52
|
};
|
|
48
|
-
htfDefaults:
|
|
49
|
-
hash:
|
|
53
|
+
htfDefaults: htf.Opts;
|
|
54
|
+
hash: CHash;
|
|
50
55
|
randomBytes: (bytesLength?: number) => Uint8Array;
|
|
51
56
|
};
|
|
52
57
|
export declare type CurveFn<Fp, Fp2, Fp6, Fp12> = {
|
|
53
58
|
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>;
|
|
54
|
-
Fr:
|
|
55
|
-
Fp:
|
|
56
|
-
Fp2:
|
|
57
|
-
Fp6:
|
|
58
|
-
Fp12:
|
|
59
|
+
Fr: Field<bigint>;
|
|
60
|
+
Fp: Field<Fp>;
|
|
61
|
+
Fp2: Field<Fp2>;
|
|
62
|
+
Fp6: Field<Fp6>;
|
|
63
|
+
Fp12: Field<Fp12>;
|
|
59
64
|
G1: CurvePointsRes<Fp>;
|
|
60
65
|
G2: CurvePointsRes<Fp2>;
|
|
61
66
|
Signature: SignatureCoder<Fp2>;
|
|
62
67
|
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
|
|
63
|
-
calcPairingPrecomputes: (
|
|
64
|
-
|
|
68
|
+
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
|
|
69
|
+
hashToCurve: {
|
|
70
|
+
G1: ReturnType<(typeof htf.hashToCurve<Fp>)>;
|
|
71
|
+
G2: ReturnType<(typeof htf.hashToCurve<Fp2>)>;
|
|
72
|
+
};
|
|
73
|
+
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
|
|
65
74
|
getPublicKey: (privateKey: PrivKey) => Uint8Array;
|
|
66
75
|
sign: {
|
|
67
76
|
(message: Hex, privateKey: PrivKey): Uint8Array;
|
|
68
|
-
(message:
|
|
77
|
+
(message: ProjPointType<Fp2>, privateKey: PrivKey): ProjPointType<Fp2>;
|
|
69
78
|
};
|
|
70
|
-
verify: (signature: Hex |
|
|
79
|
+
verify: (signature: Hex | ProjPointType<Fp2>, message: Hex | ProjPointType<Fp2>, publicKey: Hex | ProjPointType<Fp>) => boolean;
|
|
71
80
|
aggregatePublicKeys: {
|
|
72
81
|
(publicKeys: Hex[]): Uint8Array;
|
|
73
|
-
(publicKeys:
|
|
82
|
+
(publicKeys: ProjPointType<Fp>[]): ProjPointType<Fp>;
|
|
74
83
|
};
|
|
75
84
|
aggregateSignatures: {
|
|
76
85
|
(signatures: Hex[]): Uint8Array;
|
|
77
|
-
(signatures:
|
|
86
|
+
(signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>;
|
|
78
87
|
};
|
|
79
|
-
verifyBatch: (signature: Hex |
|
|
88
|
+
verifyBatch: (signature: Hex | ProjPointType<Fp2>, messages: (Hex | ProjPointType<Fp2>)[], publicKeys: (Hex | ProjPointType<Fp>)[]) => boolean;
|
|
80
89
|
utils: {
|
|
81
|
-
stringToBytes: typeof stringToBytes;
|
|
82
|
-
hashToField: typeof
|
|
83
|
-
expandMessageXMD: typeof
|
|
84
|
-
getDSTLabel: () => string;
|
|
85
|
-
setDSTLabel(newLabel: string): void;
|
|
90
|
+
stringToBytes: typeof htf.stringToBytes;
|
|
91
|
+
hashToField: typeof htf.hash_to_field;
|
|
92
|
+
expandMessageXMD: typeof htf.expand_message_xmd;
|
|
86
93
|
};
|
|
87
94
|
};
|
|
88
95
|
export declare function bls<Fp2, Fp6, Fp12>(CURVE: CurveType<Fp, Fp2, Fp6, Fp12>): CurveFn<Fp, Fp2, Fp6, Fp12>;
|
package/lib/abstract/bls.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.bls = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const modular_js_1 = require("./modular.js");
|
|
5
|
+
const utils_js_1 = require("./utils.js");
|
|
6
|
+
const htf = require("./hash-to-curve.js");
|
|
6
7
|
const weierstrass_js_1 = require("./weierstrass.js");
|
|
7
8
|
function bls(CURVE) {
|
|
8
9
|
// Fields looks pretty specific for curve, so for now we need to pass them with options
|
|
9
10
|
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE;
|
|
10
|
-
const BLS_X_LEN =
|
|
11
|
+
const BLS_X_LEN = (0, utils_js_1.bitLen)(CURVE.x);
|
|
11
12
|
const groupLen = 32; // TODO: calculate; hardcoded for now
|
|
12
13
|
// Pre-compute coefficients for sparse multiplication
|
|
13
14
|
// Point addition and point double calculations is reused for coefficients
|
|
14
|
-
function calcPairingPrecomputes(
|
|
15
|
+
function calcPairingPrecomputes(p) {
|
|
16
|
+
const { x, y } = p;
|
|
15
17
|
// prettier-ignore
|
|
16
18
|
const Qx = x, Qy = y, Qz = Fp2.ONE;
|
|
17
19
|
// prettier-ignore
|
|
@@ -19,32 +21,32 @@ function bls(CURVE) {
|
|
|
19
21
|
let ell_coeff = [];
|
|
20
22
|
for (let i = BLS_X_LEN - 2; i >= 0; i--) {
|
|
21
23
|
// Double
|
|
22
|
-
let t0 = Fp2.
|
|
23
|
-
let t1 = Fp2.
|
|
24
|
+
let t0 = Fp2.sqr(Ry); // Ry²
|
|
25
|
+
let t1 = Fp2.sqr(Rz); // Rz²
|
|
24
26
|
let t2 = Fp2.multiplyByB(Fp2.mul(t1, 3n)); // 3 * T1 * B
|
|
25
27
|
let t3 = Fp2.mul(t2, 3n); // 3 * T2
|
|
26
|
-
let t4 = Fp2.sub(Fp2.sub(Fp2.
|
|
28
|
+
let t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
|
|
27
29
|
ell_coeff.push([
|
|
28
30
|
Fp2.sub(t2, t0),
|
|
29
|
-
Fp2.mul(Fp2.
|
|
30
|
-
Fp2.
|
|
31
|
+
Fp2.mul(Fp2.sqr(Rx), 3n),
|
|
32
|
+
Fp2.neg(t4), // -T4
|
|
31
33
|
]);
|
|
32
34
|
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), 2n); // ((T0 - T3) * Rx * Ry) / 2
|
|
33
|
-
Ry = Fp2.sub(Fp2.
|
|
35
|
+
Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3), 2n)), Fp2.mul(Fp2.sqr(t2), 3n)); // ((T0 + T3) / 2)² - 3 * T2²
|
|
34
36
|
Rz = Fp2.mul(t0, t4); // T0 * T4
|
|
35
|
-
if (
|
|
37
|
+
if ((0, utils_js_1.bitGet)(CURVE.x, i)) {
|
|
36
38
|
// Addition
|
|
37
39
|
let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
|
|
38
40
|
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
|
|
39
41
|
ell_coeff.push([
|
|
40
42
|
Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)),
|
|
41
|
-
Fp2.
|
|
43
|
+
Fp2.neg(t0),
|
|
42
44
|
t1, // T1
|
|
43
45
|
]);
|
|
44
|
-
let t2 = Fp2.
|
|
46
|
+
let t2 = Fp2.sqr(t1); // T1²
|
|
45
47
|
let t3 = Fp2.mul(t2, t1); // T2 * T1
|
|
46
48
|
let t4 = Fp2.mul(t2, Rx); // T2 * Rx
|
|
47
|
-
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, 2n)), Fp2.mul(Fp2.
|
|
49
|
+
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, 2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
|
|
48
50
|
Rx = Fp2.mul(t1, t5); // T1 * T5
|
|
49
51
|
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
|
|
50
52
|
Rz = Fp2.mul(Rz, t3); // Rz * T3
|
|
@@ -60,115 +62,114 @@ function bls(CURVE) {
|
|
|
60
62
|
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
|
|
61
63
|
const E = ell[j];
|
|
62
64
|
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
|
|
63
|
-
if (
|
|
65
|
+
if ((0, utils_js_1.bitGet)(x, i)) {
|
|
64
66
|
j += 1;
|
|
65
67
|
const F = ell[j];
|
|
66
68
|
f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
|
|
67
69
|
}
|
|
68
70
|
if (i !== 0)
|
|
69
|
-
f12 = Fp12.
|
|
71
|
+
f12 = Fp12.sqr(f12);
|
|
70
72
|
}
|
|
71
73
|
return Fp12.conjugate(f12);
|
|
72
74
|
}
|
|
73
75
|
const utils = {
|
|
74
|
-
hexToBytes:
|
|
75
|
-
bytesToHex:
|
|
76
|
-
stringToBytes:
|
|
76
|
+
hexToBytes: utils_js_1.hexToBytes,
|
|
77
|
+
bytesToHex: utils_js_1.bytesToHex,
|
|
78
|
+
stringToBytes: htf.stringToBytes,
|
|
77
79
|
// TODO: do we need to export it here?
|
|
78
|
-
hashToField: (msg, count, options = {}) =>
|
|
79
|
-
expandMessageXMD: (msg, DST, lenInBytes, H = CURVE.hash) =>
|
|
80
|
-
hashToPrivateKey: (hash) => Fr.toBytes(
|
|
80
|
+
hashToField: (msg, count, options = {}) => htf.hash_to_field(msg, count, { ...CURVE.htfDefaults, ...options }),
|
|
81
|
+
expandMessageXMD: (msg, DST, lenInBytes, H = CURVE.hash) => htf.expand_message_xmd(msg, DST, lenInBytes, H),
|
|
82
|
+
hashToPrivateKey: (hash) => Fr.toBytes((0, modular_js_1.hashToPrivateScalar)(hash, CURVE.r)),
|
|
81
83
|
randomBytes: (bytesLength = groupLen) => CURVE.randomBytes(bytesLength),
|
|
82
84
|
randomPrivateKey: () => utils.hashToPrivateKey(utils.randomBytes(groupLen + 8)),
|
|
83
|
-
getDSTLabel: () => CURVE.htfDefaults.DST,
|
|
84
|
-
setDSTLabel(newLabel) {
|
|
85
|
-
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3.1
|
|
86
|
-
if (typeof newLabel !== 'string' || newLabel.length > 2048 || newLabel.length === 0) {
|
|
87
|
-
throw new TypeError('Invalid DST');
|
|
88
|
-
}
|
|
89
|
-
CURVE.htfDefaults.DST = newLabel;
|
|
90
|
-
},
|
|
91
85
|
};
|
|
92
86
|
// Point on G1 curve: (x, y)
|
|
93
87
|
const G1 = (0, weierstrass_js_1.weierstrassPoints)({
|
|
94
88
|
n: Fr.ORDER,
|
|
95
89
|
...CURVE.G1,
|
|
96
90
|
});
|
|
91
|
+
const G1HashToCurve = htf.hashToCurve(G1.ProjectivePoint, CURVE.G1.mapToCurve, {
|
|
92
|
+
...CURVE.htfDefaults,
|
|
93
|
+
...CURVE.G1.htfDefaults,
|
|
94
|
+
});
|
|
97
95
|
function pairingPrecomputes(point) {
|
|
98
96
|
const p = point;
|
|
99
97
|
if (p._PPRECOMPUTES)
|
|
100
98
|
return p._PPRECOMPUTES;
|
|
101
|
-
p._PPRECOMPUTES = calcPairingPrecomputes(
|
|
99
|
+
p._PPRECOMPUTES = calcPairingPrecomputes(point.toAffine());
|
|
102
100
|
return p._PPRECOMPUTES;
|
|
103
101
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
function millerLoopG1(Q, P) {
|
|
110
|
-
return millerLoop(pairingPrecomputes(P), [Q.x, Q.y]);
|
|
111
|
-
}
|
|
102
|
+
// TODO: export
|
|
103
|
+
// function clearPairingPrecomputes(point: G2) {
|
|
104
|
+
// const p = point as G2 & withPairingPrecomputes;
|
|
105
|
+
// p._PPRECOMPUTES = undefined;
|
|
106
|
+
// }
|
|
112
107
|
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
|
|
113
108
|
const G2 = (0, weierstrass_js_1.weierstrassPoints)({
|
|
114
109
|
n: Fr.ORDER,
|
|
115
110
|
...CURVE.G2,
|
|
116
111
|
});
|
|
112
|
+
const C = G2.ProjectivePoint; // TODO: fix
|
|
113
|
+
const G2HashToCurve = htf.hashToCurve(C, CURVE.G2.mapToCurve, {
|
|
114
|
+
...CURVE.htfDefaults,
|
|
115
|
+
...CURVE.G2.htfDefaults,
|
|
116
|
+
});
|
|
117
117
|
const { Signature } = CURVE.G2;
|
|
118
118
|
// Calculates bilinear pairing
|
|
119
|
-
function pairing(
|
|
120
|
-
if (
|
|
121
|
-
throw new Error('
|
|
122
|
-
P.assertValidity();
|
|
119
|
+
function pairing(Q, P, withFinalExponent = true) {
|
|
120
|
+
if (Q.equals(G1.ProjectivePoint.ZERO) || P.equals(G2.ProjectivePoint.ZERO))
|
|
121
|
+
throw new Error('pairing is not available for ZERO point');
|
|
123
122
|
Q.assertValidity();
|
|
123
|
+
P.assertValidity();
|
|
124
124
|
// Performance: 9ms for millerLoop and ~14ms for exp.
|
|
125
|
-
const
|
|
125
|
+
const Qa = Q.toAffine();
|
|
126
|
+
const looped = millerLoop(pairingPrecomputes(P), [Qa.x, Qa.y]);
|
|
126
127
|
return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
|
|
127
128
|
}
|
|
128
129
|
function normP1(point) {
|
|
129
|
-
return point instanceof G1.
|
|
130
|
+
return point instanceof G1.ProjectivePoint ? point : G1.ProjectivePoint.fromHex(point);
|
|
130
131
|
}
|
|
131
132
|
function normP2(point) {
|
|
132
|
-
return point instanceof G2.
|
|
133
|
+
return point instanceof G2.ProjectivePoint ? point : Signature.decode(point);
|
|
133
134
|
}
|
|
134
|
-
function normP2Hash(point) {
|
|
135
|
-
return point instanceof G2.
|
|
135
|
+
function normP2Hash(point, htfOpts) {
|
|
136
|
+
return point instanceof G2.ProjectivePoint
|
|
137
|
+
? point
|
|
138
|
+
: G2HashToCurve.hashToCurve(point, htfOpts);
|
|
136
139
|
}
|
|
137
140
|
// Multiplies generator by private key.
|
|
138
141
|
// P = pk x G
|
|
139
142
|
function getPublicKey(privateKey) {
|
|
140
|
-
return G1.
|
|
143
|
+
return G1.ProjectivePoint.fromPrivateKey(privateKey).toRawBytes(true);
|
|
141
144
|
}
|
|
142
|
-
function sign(message, privateKey) {
|
|
143
|
-
const msgPoint = normP2Hash(message);
|
|
145
|
+
function sign(message, privateKey, htfOpts) {
|
|
146
|
+
const msgPoint = normP2Hash(message, htfOpts);
|
|
144
147
|
msgPoint.assertValidity();
|
|
145
148
|
const sigPoint = msgPoint.multiply(G1.normalizePrivateKey(privateKey));
|
|
146
|
-
if (message instanceof G2.
|
|
149
|
+
if (message instanceof G2.ProjectivePoint)
|
|
147
150
|
return sigPoint;
|
|
148
151
|
return Signature.encode(sigPoint);
|
|
149
152
|
}
|
|
150
153
|
// Checks if pairing of public key & hash is equal to pairing of generator & signature.
|
|
151
154
|
// e(P, H(m)) == e(G, S)
|
|
152
|
-
function verify(signature, message, publicKey) {
|
|
155
|
+
function verify(signature, message, publicKey, htfOpts) {
|
|
153
156
|
const P = normP1(publicKey);
|
|
154
|
-
const Hm = normP2Hash(message);
|
|
155
|
-
const G = G1.
|
|
157
|
+
const Hm = normP2Hash(message, htfOpts);
|
|
158
|
+
const G = G1.ProjectivePoint.BASE;
|
|
156
159
|
const S = normP2(signature);
|
|
157
160
|
// Instead of doing 2 exponentiations, we use property of billinear maps
|
|
158
161
|
// and do one exp after multiplying 2 points.
|
|
159
162
|
const ePHm = pairing(P.negate(), Hm, false);
|
|
160
163
|
const eGS = pairing(G, S, false);
|
|
161
164
|
const exp = Fp12.finalExponentiate(Fp12.mul(eGS, ePHm));
|
|
162
|
-
return Fp12.
|
|
165
|
+
return Fp12.eql(exp, Fp12.ONE);
|
|
163
166
|
}
|
|
164
167
|
function aggregatePublicKeys(publicKeys) {
|
|
165
168
|
if (!publicKeys.length)
|
|
166
169
|
throw new Error('Expected non-empty array');
|
|
167
|
-
const agg = publicKeys
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const aggAffine = agg.toAffine();
|
|
171
|
-
if (publicKeys[0] instanceof G1.Point) {
|
|
170
|
+
const agg = publicKeys.map(normP1).reduce((sum, p) => sum.add(p), G1.ProjectivePoint.ZERO);
|
|
171
|
+
const aggAffine = agg; //.toAffine();
|
|
172
|
+
if (publicKeys[0] instanceof G1.ProjectivePoint) {
|
|
172
173
|
aggAffine.assertValidity();
|
|
173
174
|
return aggAffine;
|
|
174
175
|
}
|
|
@@ -178,11 +179,9 @@ function bls(CURVE) {
|
|
|
178
179
|
function aggregateSignatures(signatures) {
|
|
179
180
|
if (!signatures.length)
|
|
180
181
|
throw new Error('Expected non-empty array');
|
|
181
|
-
const agg = signatures
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const aggAffine = agg.toAffine();
|
|
185
|
-
if (signatures[0] instanceof G2.Point) {
|
|
182
|
+
const agg = signatures.map(normP2).reduce((sum, s) => sum.add(s), G2.ProjectivePoint.ZERO);
|
|
183
|
+
const aggAffine = agg; //.toAffine();
|
|
184
|
+
if (signatures[0] instanceof G2.ProjectivePoint) {
|
|
186
185
|
aggAffine.assertValidity();
|
|
187
186
|
return aggAffine;
|
|
188
187
|
}
|
|
@@ -190,33 +189,34 @@ function bls(CURVE) {
|
|
|
190
189
|
}
|
|
191
190
|
// https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
|
|
192
191
|
// e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
|
|
193
|
-
function verifyBatch(signature, messages, publicKeys) {
|
|
192
|
+
function verifyBatch(signature, messages, publicKeys, htfOpts) {
|
|
193
|
+
// @ts-ignore
|
|
194
|
+
// console.log('verifyBatch', bytesToHex(signature as any), messages, publicKeys.map(bytesToHex));
|
|
194
195
|
if (!messages.length)
|
|
195
196
|
throw new Error('Expected non-empty messages array');
|
|
196
197
|
if (publicKeys.length !== messages.length)
|
|
197
198
|
throw new Error('Pubkey count should equal msg count');
|
|
198
199
|
const sig = normP2(signature);
|
|
199
|
-
const nMessages = messages.map(normP2Hash);
|
|
200
|
+
const nMessages = messages.map((i) => normP2Hash(i, htfOpts));
|
|
200
201
|
const nPublicKeys = publicKeys.map(normP1);
|
|
201
202
|
try {
|
|
202
203
|
const paired = [];
|
|
203
204
|
for (const message of new Set(nMessages)) {
|
|
204
|
-
const groupPublicKey = nMessages.reduce((groupPublicKey, subMessage, i) => subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey, G1.
|
|
205
|
+
const groupPublicKey = nMessages.reduce((groupPublicKey, subMessage, i) => subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey, G1.ProjectivePoint.ZERO);
|
|
205
206
|
// const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
|
|
206
207
|
// Possible to batch pairing for same msg with different groupPublicKey here
|
|
207
208
|
paired.push(pairing(groupPublicKey, message, false));
|
|
208
209
|
}
|
|
209
|
-
paired.push(pairing(G1.
|
|
210
|
+
paired.push(pairing(G1.ProjectivePoint.BASE.negate(), sig, false));
|
|
210
211
|
const product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
|
|
211
212
|
const exp = Fp12.finalExponentiate(product);
|
|
212
|
-
return Fp12.
|
|
213
|
+
return Fp12.eql(exp, Fp12.ONE);
|
|
213
214
|
}
|
|
214
215
|
catch {
|
|
215
216
|
return false;
|
|
216
217
|
}
|
|
217
218
|
}
|
|
218
|
-
|
|
219
|
-
G1.Point.BASE._setWindowSize(4);
|
|
219
|
+
G1.ProjectivePoint.BASE._setWindowSize(4);
|
|
220
220
|
return {
|
|
221
221
|
CURVE,
|
|
222
222
|
Fr,
|
|
@@ -229,6 +229,7 @@ function bls(CURVE) {
|
|
|
229
229
|
Signature,
|
|
230
230
|
millerLoop,
|
|
231
231
|
calcPairingPrecomputes,
|
|
232
|
+
hashToCurve: { G1: G1HashToCurve, G2: G2HashToCurve },
|
|
232
233
|
pairing,
|
|
233
234
|
getPublicKey,
|
|
234
235
|
sign,
|
|
@@ -1,15 +1,25 @@
|
|
|
1
|
+
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
|
+
import { Field } from './modular.js';
|
|
3
|
+
export declare type AffinePoint<T> = {
|
|
4
|
+
x: T;
|
|
5
|
+
y: T;
|
|
6
|
+
} & {
|
|
7
|
+
z?: never;
|
|
8
|
+
t?: never;
|
|
9
|
+
};
|
|
1
10
|
export interface Group<T extends Group<T>> {
|
|
2
11
|
double(): T;
|
|
3
12
|
negate(): T;
|
|
4
13
|
add(other: T): T;
|
|
5
14
|
subtract(other: T): T;
|
|
6
15
|
equals(other: T): boolean;
|
|
7
|
-
multiply(scalar:
|
|
16
|
+
multiply(scalar: bigint): T;
|
|
8
17
|
}
|
|
9
18
|
export declare type GroupConstructor<T> = {
|
|
10
19
|
BASE: T;
|
|
11
20
|
ZERO: T;
|
|
12
21
|
};
|
|
22
|
+
export declare type Mapper<T> = (i: T[]) => T[];
|
|
13
23
|
export declare function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number): {
|
|
14
24
|
constTimeNegate: (condition: boolean, item: T) => T;
|
|
15
25
|
unsafeLadder(elm: T, n: bigint): T;
|
|
@@ -31,4 +41,24 @@ export declare function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: n
|
|
|
31
41
|
p: T;
|
|
32
42
|
f: T;
|
|
33
43
|
};
|
|
44
|
+
wNAFCached(P: T, precomputesMap: Map<T, T[]>, n: bigint, transform: Mapper<T>): {
|
|
45
|
+
p: T;
|
|
46
|
+
f: T;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
export declare type AbstractCurve<T> = {
|
|
50
|
+
Fp: Field<T>;
|
|
51
|
+
n: bigint;
|
|
52
|
+
nBitLength?: number;
|
|
53
|
+
nByteLength?: number;
|
|
54
|
+
h: bigint;
|
|
55
|
+
hEff?: bigint;
|
|
56
|
+
Gx: T;
|
|
57
|
+
Gy: T;
|
|
58
|
+
wrapPrivateKey?: boolean;
|
|
59
|
+
allowInfinityPoint?: boolean;
|
|
34
60
|
};
|
|
61
|
+
export declare function validateAbsOpts<FP, T>(curve: AbstractCurve<FP> & T): Readonly<{
|
|
62
|
+
readonly nBitLength: number;
|
|
63
|
+
readonly nByteLength: number;
|
|
64
|
+
} & AbstractCurve<FP> & T>;
|