@noble/curves 1.9.1 → 1.9.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 +56 -25
- package/_shortw_utils.d.ts +7 -5
- package/_shortw_utils.d.ts.map +1 -1
- package/_shortw_utils.js +2 -8
- package/_shortw_utils.js.map +1 -1
- package/abstract/bls.d.ts +60 -24
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +158 -109
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +44 -9
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +86 -7
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +112 -25
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +138 -102
- package/abstract/edwards.js.map +1 -1
- package/abstract/fft.d.ts +12 -10
- package/abstract/fft.d.ts.map +1 -1
- package/abstract/fft.js +12 -13
- package/abstract/fft.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +25 -11
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +17 -14
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +24 -11
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +49 -20
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +1 -1
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +5 -4
- package/abstract/montgomery.js.map +1 -1
- package/abstract/poseidon.d.ts +5 -13
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +12 -7
- package/abstract/poseidon.js.map +1 -1
- package/abstract/tower.d.ts +20 -46
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js +9 -3
- package/abstract/tower.js.map +1 -1
- package/abstract/utils.d.ts +1 -115
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +17 -371
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +132 -76
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +462 -398
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +2 -0
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +504 -466
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +2 -0
- package/bn254.d.ts.map +1 -1
- package/bn254.js +44 -32
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +8 -5
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +67 -54
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +10 -6
- package/ed448.d.ts.map +1 -1
- package/ed448.js +80 -57
- package/ed448.js.map +1 -1
- package/esm/_shortw_utils.d.ts +7 -5
- package/esm/_shortw_utils.d.ts.map +1 -1
- package/esm/_shortw_utils.js +2 -8
- package/esm/_shortw_utils.js.map +1 -1
- package/esm/abstract/bls.d.ts +60 -24
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +158 -109
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +44 -9
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +83 -8
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +112 -25
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +138 -104
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/fft.d.ts +12 -10
- package/esm/abstract/fft.d.ts.map +1 -1
- package/esm/abstract/fft.js +10 -11
- package/esm/abstract/fft.js.map +1 -1
- package/esm/abstract/hash-to-curve.d.ts +25 -11
- package/esm/abstract/hash-to-curve.d.ts.map +1 -1
- package/esm/abstract/hash-to-curve.js +17 -14
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.d.ts +24 -11
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +48 -19
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts +1 -1
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +5 -4
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/poseidon.d.ts +5 -13
- package/esm/abstract/poseidon.d.ts.map +1 -1
- package/esm/abstract/poseidon.js +12 -7
- package/esm/abstract/poseidon.js.map +1 -1
- package/esm/abstract/tower.d.ts +20 -46
- package/esm/abstract/tower.d.ts.map +1 -1
- package/esm/abstract/tower.js +9 -3
- package/esm/abstract/tower.js.map +1 -1
- package/esm/abstract/utils.d.ts +1 -115
- package/esm/abstract/utils.d.ts.map +1 -1
- package/esm/abstract/utils.js +3 -344
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +132 -76
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +460 -400
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts +2 -0
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +503 -465
- package/esm/bls12-381.js.map +1 -1
- package/esm/bn254.d.ts +2 -0
- package/esm/bn254.d.ts.map +1 -1
- package/esm/bn254.js +41 -29
- package/esm/bn254.js.map +1 -1
- package/esm/ed25519.d.ts +8 -5
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +62 -49
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +10 -6
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +74 -51
- package/esm/ed448.js.map +1 -1
- package/esm/misc.d.ts.map +1 -1
- package/esm/misc.js +31 -26
- package/esm/misc.js.map +1 -1
- package/esm/nist.d.ts +7 -16
- package/esm/nist.d.ts.map +1 -1
- package/esm/nist.js +86 -97
- package/esm/nist.js.map +1 -1
- package/esm/p256.d.ts +3 -3
- package/esm/p384.d.ts +3 -3
- package/esm/p521.d.ts +3 -3
- package/esm/secp256k1.d.ts +6 -6
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +43 -40
- package/esm/secp256k1.js.map +1 -1
- package/esm/utils.d.ts +96 -0
- package/esm/utils.d.ts.map +1 -0
- package/esm/utils.js +279 -0
- package/esm/utils.js.map +1 -0
- package/misc.d.ts.map +1 -1
- package/misc.js +35 -30
- package/misc.js.map +1 -1
- package/nist.d.ts +7 -16
- package/nist.d.ts.map +1 -1
- package/nist.js +86 -97
- package/nist.js.map +1 -1
- package/p256.d.ts +3 -3
- package/p384.d.ts +3 -3
- package/p521.d.ts +3 -3
- package/package.json +14 -5
- package/secp256k1.d.ts +6 -6
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +46 -43
- package/secp256k1.js.map +1 -1
- package/src/_shortw_utils.ts +5 -15
- package/src/abstract/bls.ts +260 -145
- package/src/abstract/curve.ts +115 -13
- package/src/abstract/edwards.ts +279 -138
- package/src/abstract/fft.ts +30 -19
- package/src/abstract/hash-to-curve.ts +51 -27
- package/src/abstract/modular.ts +49 -28
- package/src/abstract/montgomery.ts +9 -7
- package/src/abstract/poseidon.ts +22 -18
- package/src/abstract/tower.ts +36 -67
- package/src/abstract/utils.ts +3 -378
- package/src/abstract/weierstrass.ts +700 -453
- package/src/bls12-381.ts +540 -489
- package/src/bn254.ts +47 -35
- package/src/ed25519.ts +80 -64
- package/src/ed448.ts +129 -92
- package/src/misc.ts +39 -34
- package/src/nist.ts +138 -127
- package/src/p256.ts +3 -3
- package/src/p384.ts +3 -3
- package/src/p521.ts +3 -3
- package/src/secp256k1.ts +58 -46
- package/src/utils.ts +328 -0
- package/utils.d.ts +96 -0
- package/utils.d.ts.map +1 -0
- package/utils.js +313 -0
- package/utils.js.map +1 -0
package/esm/bls12-381.js
CHANGED
|
@@ -1,69 +1,86 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* bls12-381 is pairing-friendly Barreto-Lynn-Scott elliptic curve construction allowing to:
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
|
|
4
|
+
* Construct zk-SNARKs at the ~120-bit security, as per [Barbulescu-Duquesne 2017](https://hal.science/hal-01534101/file/main.pdf)
|
|
5
|
+
* Efficiently verify N aggregate signatures with 1 pairing and N ec additions:
|
|
6
|
+
the Boneh-Lynn-Shacham signature scheme is orders of magnitude more efficient than Schnorr
|
|
7
|
+
|
|
8
|
+
BLS can mean 2 different things:
|
|
9
|
+
|
|
10
|
+
* Barreto-Lynn-Scott: BLS12, a Pairing Friendly Elliptic Curve
|
|
11
|
+
* Boneh-Lynn-Shacham: A Signature Scheme.
|
|
12
|
+
|
|
13
|
+
### Summary
|
|
14
|
+
|
|
15
|
+
1. BLS Relies on expensive bilinear pairing
|
|
16
|
+
2. Private Keys: 32 bytes
|
|
17
|
+
3. Public Keys: 48 OR 96 bytes - big-endian x coordinate of point on G1 OR G2 curve
|
|
18
|
+
4. Signatures: 96 OR 48 bytes - big-endian x coordinate of point on G2 OR G1 curve
|
|
19
|
+
5. The 12 stands for the Embedding degree.
|
|
20
|
+
|
|
21
|
+
Modes of operation:
|
|
22
|
+
|
|
23
|
+
* Long signatures: 48-byte keys + 96-byte sigs (G1 keys + G2 sigs).
|
|
24
|
+
* Short signatures: 96-byte keys + 48-byte sigs (G2 keys + G1 sigs).
|
|
25
|
+
|
|
26
|
+
### Formulas
|
|
27
|
+
|
|
28
|
+
- `P = pk x G` - public keys
|
|
29
|
+
- `S = pk x H(m)` - signing, uses hash-to-curve on m
|
|
30
|
+
- `e(P, H(m)) == e(G, S)` - verification using pairings
|
|
31
|
+
- `e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))` - signature aggregation
|
|
32
|
+
|
|
33
|
+
### Curves
|
|
34
|
+
|
|
35
|
+
G1 is ordinary elliptic curve. G2 is extension field curve, think "over complex numbers".
|
|
36
|
+
|
|
37
|
+
- G1: y² = x³ + 4
|
|
38
|
+
- G2: y² = x³ + 4(u + 1) where u = √−1; r-order subgroup of E'(Fp²), M-type twist
|
|
39
|
+
|
|
40
|
+
### Towers
|
|
41
|
+
|
|
42
|
+
Pairing G1 + G2 produces element in Fp₁₂, 12-degree polynomial.
|
|
43
|
+
Fp₁₂ is usually implemented using tower of lower-degree polynomials for speed.
|
|
44
|
+
|
|
45
|
+
- Fp₁₂ = Fp₆² => Fp₂³
|
|
46
|
+
- Fp(u) / (u² - β) where β = -1
|
|
47
|
+
- Fp₂(v) / (v³ - ξ) where ξ = u + 1
|
|
48
|
+
- Fp₆(w) / (w² - γ) where γ = v
|
|
49
|
+
- Fp²[u] = Fp/u²+1
|
|
50
|
+
- Fp⁶[v] = Fp²/v³-1-u
|
|
51
|
+
- Fp¹²[w] = Fp⁶/w²-v
|
|
52
|
+
|
|
53
|
+
### Params
|
|
54
|
+
|
|
55
|
+
* Embedding degree (k): 12
|
|
56
|
+
* Seed is sometimes named x or t
|
|
57
|
+
* t = -15132376222941642752
|
|
58
|
+
* p = (t-1)² * (t⁴-t²+1)/3 + t
|
|
59
|
+
* r = t⁴-t²+1
|
|
60
|
+
* Ate loop size: X
|
|
61
|
+
|
|
62
|
+
To verify curve parameters, see
|
|
63
|
+
[pairing-friendly-curves spec](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11).
|
|
64
|
+
Basic math is done over finite fields over p.
|
|
65
|
+
More complicated math is done over polynominal extension fields.
|
|
66
|
+
|
|
67
|
+
### Compatibility and notes
|
|
68
|
+
1. It is compatible with Algorand, Chia, Dfinity, Ethereum, Filecoin, ZEC.
|
|
69
|
+
Filecoin uses little endian byte arrays for private keys - make sure to reverse byte order.
|
|
70
|
+
2. Make sure to correctly select mode: "long signature" or "short signature".
|
|
71
|
+
3. Compatible with specs:
|
|
72
|
+
RFC 9380,
|
|
73
|
+
[cfrg-pairing-friendly-curves-11](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11),
|
|
74
|
+
[cfrg-bls-signature-05](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/).
|
|
75
|
+
|
|
6
76
|
*
|
|
7
|
-
* ### Summary
|
|
8
|
-
* 1. BLS Relies on Bilinear Pairing (expensive)
|
|
9
|
-
* 2. Private Keys: 32 bytes
|
|
10
|
-
* 3. Public Keys: 48 bytes: 381 bit affine x coordinate, encoded into 48 big-endian bytes.
|
|
11
|
-
* 4. Signatures: 96 bytes: two 381 bit integers (affine x coordinate), encoded into two 48 big-endian byte arrays.
|
|
12
|
-
* - The signature is a point on the G2 subgroup, which is defined over a finite field
|
|
13
|
-
* with elements twice as big as the G1 curve (G2 is over Fp2 rather than Fp. Fp2 is analogous to the
|
|
14
|
-
* complex numbers).
|
|
15
|
-
* - We also support reversed 96-byte pubkeys & 48-byte short signatures.
|
|
16
|
-
* 5. The 12 stands for the Embedding degree.
|
|
17
|
-
*
|
|
18
|
-
* ### Formulas
|
|
19
|
-
* - `P = pk x G` - public keys
|
|
20
|
-
* - `S = pk x H(m)` - signing
|
|
21
|
-
* - `e(P, H(m)) == e(G, S)` - verification using pairings
|
|
22
|
-
* - `e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))` - signature aggregation
|
|
23
|
-
*
|
|
24
|
-
* ### Compatibility and notes
|
|
25
|
-
* 1. It is compatible with Algorand, Chia, Dfinity, Ethereum, Filecoin, ZEC.
|
|
26
|
-
* Filecoin uses little endian byte arrays for private keys - make sure to reverse byte order.
|
|
27
|
-
* 2. Some projects use G2 for public keys and G1 for signatures. It's called "short signature".
|
|
28
|
-
* 3. Curve security level is about 120 bits as per [Barbulescu-Duquesne 2017](https://hal.science/hal-01534101/file/main.pdf)
|
|
29
|
-
* 4. Compatible with specs:
|
|
30
|
-
* [cfrg-pairing-friendly-curves-11](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11),
|
|
31
|
-
* [cfrg-bls-signature-05](https://www.rfc-editor.org/rfc/draft-irtf-cfrg-bls-signature-05),
|
|
32
|
-
* RFC 9380.
|
|
33
|
-
*
|
|
34
|
-
* ### Params
|
|
35
|
-
* To verify curve parameters, see
|
|
36
|
-
* [pairing-friendly-curves spec](https://www.rfc-editor.org/rfc/draft-irtf-cfrg-pairing-friendly-curves-11).
|
|
37
|
-
* Basic math is done over finite fields over p.
|
|
38
|
-
* More complicated math is done over polynominal extension fields.
|
|
39
|
-
* To simplify calculations in Fp12, we construct extension tower:
|
|
40
|
-
*
|
|
41
|
-
* Embedding degree (k): 12
|
|
42
|
-
* Seed (X): -15132376222941642752
|
|
43
|
-
* Fr: (x⁴-x²+1)
|
|
44
|
-
* Fp: ((x-1)² ⋅ r(x)/3+x)
|
|
45
|
-
* (E/Fp): Y²=X³+4
|
|
46
|
-
* (Eₜ/Fp²): Y² = X³+4(u+1) (M-type twist)
|
|
47
|
-
* Ate loop size: X
|
|
48
|
-
*
|
|
49
|
-
* ### Towers
|
|
50
|
-
* - Fp₁₂ = Fp₆² => Fp₂³
|
|
51
|
-
* - Fp(u) / (u² - β) where β = -1
|
|
52
|
-
* - Fp₂(v) / (v³ - ξ) where ξ = u + 1
|
|
53
|
-
* - Fp₆(w) / (w² - γ) where γ = v
|
|
54
|
-
* - Fp²[u] = Fp/u²+1
|
|
55
|
-
* - Fp⁶[v] = Fp²/v³-1-u
|
|
56
|
-
* - Fp¹²[w] = Fp⁶/w²-v
|
|
57
|
-
*
|
|
58
|
-
* @todo construct bls & bn fp/fr from seed.
|
|
59
77
|
* @module
|
|
60
78
|
*/
|
|
61
79
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
62
|
-
import { sha256 } from '@noble/hashes/sha2';
|
|
63
|
-
import { randomBytes } from '@noble/hashes/utils';
|
|
80
|
+
import { sha256 } from '@noble/hashes/sha2.js';
|
|
64
81
|
import { bls } from "./abstract/bls.js";
|
|
65
82
|
import { Field } from "./abstract/modular.js";
|
|
66
|
-
import { bitGet, bitLen, bytesToHex, bytesToNumberBE, concatBytes
|
|
83
|
+
import { abytes, bitGet, bitLen, bytesToHex, bytesToNumberBE, concatBytes, ensureBytes, numberToBytesBE, } from "./utils.js";
|
|
67
84
|
// Types
|
|
68
85
|
import { isogenyMap } from "./abstract/hash-to-curve.js";
|
|
69
86
|
import { psiFrobenius, tower12 } from "./abstract/tower.js";
|
|
@@ -71,13 +88,40 @@ import { mapToCurveSimpleSWU, } from "./abstract/weierstrass.js";
|
|
|
71
88
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
72
89
|
// prettier-ignore
|
|
73
90
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
|
91
|
+
// To verify math:
|
|
92
|
+
// https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11
|
|
74
93
|
// The BLS parameter x (seed) for BLS12-381. NOTE: it is negative!
|
|
94
|
+
// x = -2^63 - 2^62 - 2^60 - 2^57 - 2^48 - 2^16
|
|
75
95
|
const BLS_X = BigInt('0xd201000000010000');
|
|
96
|
+
// t = x (called differently in different places)
|
|
97
|
+
// const t = -BLS_X;
|
|
76
98
|
const BLS_X_LEN = bitLen(BLS_X);
|
|
99
|
+
// a=0, b=4
|
|
100
|
+
// P is characteristic of field Fp, in which curve calculations are done.
|
|
101
|
+
// p = (t-1)² * (t⁴-t²+1)/3 + t
|
|
102
|
+
// bls12_381_Fp = (t-1n)**2n * (t**4n - t**2n + 1n) / 3n + t
|
|
103
|
+
// r*h is curve order, amount of points on curve,
|
|
104
|
+
// where r is order of prime subgroup and h is cofactor.
|
|
105
|
+
// r = t⁴-t²+1
|
|
106
|
+
// r = (t**4n - t**2n + 1n)
|
|
107
|
+
// cofactor h of G1: (t - 1)²/3
|
|
108
|
+
// cofactorG1 = (t-1n)**2n/3n
|
|
109
|
+
// x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507
|
|
110
|
+
// y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
|
|
111
|
+
const bls12_381_CURVE_G1 = {
|
|
112
|
+
p: BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'),
|
|
113
|
+
n: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'),
|
|
114
|
+
h: BigInt('0x396c8c005555e1568c00aaab0000aaab'),
|
|
115
|
+
a: _0n,
|
|
116
|
+
b: _4n,
|
|
117
|
+
Gx: BigInt('0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'),
|
|
118
|
+
Gy: BigInt('0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'),
|
|
119
|
+
};
|
|
77
120
|
// CURVE FIELDS
|
|
121
|
+
export const bls12_381_Fr = Field(bls12_381_CURVE_G1.n);
|
|
78
122
|
const { Fp, Fp2, Fp6, Fp4Square, Fp12 } = tower12({
|
|
79
123
|
// Order of Fp
|
|
80
|
-
ORDER:
|
|
124
|
+
ORDER: bls12_381_CURVE_G1.p,
|
|
81
125
|
// Finite extension field over irreducible polynominal.
|
|
82
126
|
// Fp(u) / (u² - β) where β = -1
|
|
83
127
|
FP2_NONRESIDUE: [_1n, _1n],
|
|
@@ -143,11 +187,395 @@ const { Fp, Fp2, Fp6, Fp4Square, Fp12 } = tower12({
|
|
|
143
187
|
return Fp12.mul(Fp12.mul(Fp12.mul(t2_t5_pow_q2, t4_t1_pow_q3), t6_t1c_pow_q1), t7_t3c_t1);
|
|
144
188
|
},
|
|
145
189
|
});
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
190
|
+
// GLV endomorphism Ψ(P), for fast cofactor clearing
|
|
191
|
+
const { G2psi, G2psi2 } = psiFrobenius(Fp, Fp2, Fp2.div(Fp2.ONE, Fp2.NONRESIDUE)); // 1/(u+1)
|
|
192
|
+
/**
|
|
193
|
+
* Default hash_to_field / hash-to-curve for BLS.
|
|
194
|
+
* m: 1 for G1, 2 for G2
|
|
195
|
+
* k: target security level in bits
|
|
196
|
+
* hash: any function, e.g. BBS+ uses BLAKE2: see [github](https://github.com/hyperledger/aries-framework-go/issues/2247).
|
|
197
|
+
* Parameter values come from [section 8.8.2 of RFC 9380](https://www.rfc-editor.org/rfc/rfc9380#section-8.8.2).
|
|
198
|
+
*/
|
|
199
|
+
const htfDefaults = Object.freeze({
|
|
200
|
+
DST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
|
|
201
|
+
encodeDST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
|
|
202
|
+
p: Fp.ORDER,
|
|
203
|
+
m: 2,
|
|
204
|
+
k: 128,
|
|
205
|
+
expand: 'xmd',
|
|
206
|
+
hash: sha256,
|
|
207
|
+
});
|
|
208
|
+
// a=0, b=4
|
|
209
|
+
// cofactor h of G2
|
|
210
|
+
// (t^8 - 4t^7 + 5t^6 - 4t^4 + 6t^3 - 4t^2 - 4t + 13)/9
|
|
211
|
+
// cofactorG2 = (t**8n - 4n*t**7n + 5n*t**6n - 4n*t**4n + 6n*t**3n - 4n*t**2n - 4n*t+13n)/9n
|
|
212
|
+
// x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160
|
|
213
|
+
// y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905
|
|
214
|
+
const bls12_381_CURVE_G2 = {
|
|
215
|
+
p: Fp2.ORDER,
|
|
216
|
+
n: bls12_381_CURVE_G1.n,
|
|
217
|
+
h: BigInt('0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5'),
|
|
218
|
+
a: Fp2.ZERO,
|
|
219
|
+
b: Fp2.fromBigTuple([_4n, _4n]),
|
|
220
|
+
Gx: Fp2.fromBigTuple([
|
|
221
|
+
BigInt('0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8'),
|
|
222
|
+
BigInt('0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e'),
|
|
223
|
+
]),
|
|
224
|
+
Gy: Fp2.fromBigTuple([
|
|
225
|
+
BigInt('0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801'),
|
|
226
|
+
BigInt('0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be'),
|
|
227
|
+
]),
|
|
228
|
+
};
|
|
229
|
+
// Encoding utils
|
|
230
|
+
// Compressed point of infinity
|
|
231
|
+
// Set compressed & point-at-infinity bits
|
|
232
|
+
const COMPZERO = setMask(Fp.toBytes(_0n), { infinity: true, compressed: true });
|
|
233
|
+
function parseMask(bytes) {
|
|
234
|
+
// Copy, so we can remove mask data. It will be removed also later, when Fp.create will call modulo.
|
|
235
|
+
bytes = bytes.slice();
|
|
236
|
+
const mask = bytes[0] & 224;
|
|
237
|
+
const compressed = !!((mask >> 7) & 1); // compression bit (0b1000_0000)
|
|
238
|
+
const infinity = !!((mask >> 6) & 1); // point at infinity bit (0b0100_0000)
|
|
239
|
+
const sort = !!((mask >> 5) & 1); // sort bit (0b0010_0000)
|
|
240
|
+
bytes[0] &= 31; // clear mask (zero first 3 bits)
|
|
241
|
+
return { compressed, infinity, sort, value: bytes };
|
|
242
|
+
}
|
|
243
|
+
function setMask(bytes, mask) {
|
|
244
|
+
if (bytes[0] & 224)
|
|
245
|
+
throw new Error('setMask: non-empty mask');
|
|
246
|
+
if (mask.compressed)
|
|
247
|
+
bytes[0] |= 128;
|
|
248
|
+
if (mask.infinity)
|
|
249
|
+
bytes[0] |= 64;
|
|
250
|
+
if (mask.sort)
|
|
251
|
+
bytes[0] |= 32;
|
|
252
|
+
return bytes;
|
|
253
|
+
}
|
|
254
|
+
function pointG1ToBytes(_c, point, isComp) {
|
|
255
|
+
const { BYTES: L, ORDER: P } = Fp;
|
|
256
|
+
const is0 = point.is0();
|
|
257
|
+
const { x, y } = point.toAffine();
|
|
258
|
+
if (isComp) {
|
|
259
|
+
if (is0)
|
|
260
|
+
return COMPZERO.slice();
|
|
261
|
+
const sort = Boolean((y * _2n) / P);
|
|
262
|
+
return setMask(numberToBytesBE(x, L), { compressed: true, sort });
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
if (is0) {
|
|
266
|
+
return concatBytes(Uint8Array.of(0x40), new Uint8Array(2 * L - 1));
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
return concatBytes(numberToBytesBE(x, L), numberToBytesBE(y, L));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function signatureG1ToBytes(point) {
|
|
274
|
+
point.assertValidity();
|
|
275
|
+
const { BYTES: L, ORDER: P } = Fp;
|
|
276
|
+
const { x, y } = point.toAffine();
|
|
277
|
+
if (point.is0())
|
|
278
|
+
return COMPZERO.slice();
|
|
279
|
+
const sort = Boolean((y * _2n) / P);
|
|
280
|
+
return setMask(numberToBytesBE(x, L), { compressed: true, sort });
|
|
281
|
+
}
|
|
282
|
+
function pointG1FromBytes(bytes) {
|
|
283
|
+
const { compressed, infinity, sort, value } = parseMask(bytes);
|
|
284
|
+
const { BYTES: L, ORDER: P } = Fp;
|
|
285
|
+
if (value.length === 48 && compressed) {
|
|
286
|
+
const compressedValue = bytesToNumberBE(value);
|
|
287
|
+
// Zero
|
|
288
|
+
const x = Fp.create(compressedValue & Fp.MASK);
|
|
289
|
+
if (infinity) {
|
|
290
|
+
if (x !== _0n)
|
|
291
|
+
throw new Error('invalid G1 point: non-empty, at infinity, with compression');
|
|
292
|
+
return { x: _0n, y: _0n };
|
|
293
|
+
}
|
|
294
|
+
const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381_CURVE_G1.b)); // y² = x³ + b
|
|
295
|
+
let y = Fp.sqrt(right);
|
|
296
|
+
if (!y)
|
|
297
|
+
throw new Error('invalid G1 point: compressed point');
|
|
298
|
+
if ((y * _2n) / P !== BigInt(sort))
|
|
299
|
+
y = Fp.neg(y);
|
|
300
|
+
return { x: Fp.create(x), y: Fp.create(y) };
|
|
301
|
+
}
|
|
302
|
+
else if (value.length === 96 && !compressed) {
|
|
303
|
+
// Check if the infinity flag is set
|
|
304
|
+
const x = bytesToNumberBE(value.subarray(0, L));
|
|
305
|
+
const y = bytesToNumberBE(value.subarray(L));
|
|
306
|
+
if (infinity) {
|
|
307
|
+
if (x !== _0n || y !== _0n)
|
|
308
|
+
throw new Error('G1: non-empty point at infinity');
|
|
309
|
+
return bls12_381.G1.Point.ZERO.toAffine();
|
|
310
|
+
}
|
|
311
|
+
return { x: Fp.create(x), y: Fp.create(y) };
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
throw new Error('invalid G1 point: expected 48/96 bytes');
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function signatureG1FromBytes(hex) {
|
|
318
|
+
const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex, 48));
|
|
319
|
+
const P = Fp.ORDER;
|
|
320
|
+
const Point = bls12_381.G1.Point;
|
|
321
|
+
const compressedValue = bytesToNumberBE(value);
|
|
322
|
+
// Zero
|
|
323
|
+
if (infinity)
|
|
324
|
+
return Point.ZERO;
|
|
325
|
+
const x = Fp.create(compressedValue & Fp.MASK);
|
|
326
|
+
const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381_CURVE_G1.b)); // y² = x³ + b
|
|
327
|
+
let y = Fp.sqrt(right);
|
|
328
|
+
if (!y)
|
|
329
|
+
throw new Error('invalid G1 point: compressed');
|
|
330
|
+
const aflag = BigInt(sort);
|
|
331
|
+
if ((y * _2n) / P !== aflag)
|
|
332
|
+
y = Fp.neg(y);
|
|
333
|
+
const point = Point.fromAffine({ x, y });
|
|
334
|
+
point.assertValidity();
|
|
335
|
+
return point;
|
|
336
|
+
}
|
|
337
|
+
function pointG2ToBytes(_c, point, isComp) {
|
|
338
|
+
const { BYTES: L, ORDER: P } = Fp;
|
|
339
|
+
const is0 = point.is0();
|
|
340
|
+
const { x, y } = point.toAffine();
|
|
341
|
+
if (isComp) {
|
|
342
|
+
if (is0)
|
|
343
|
+
return concatBytes(COMPZERO, numberToBytesBE(_0n, L));
|
|
344
|
+
const flag = Boolean(y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P);
|
|
345
|
+
return concatBytes(setMask(numberToBytesBE(x.c1, L), { compressed: true, sort: flag }), numberToBytesBE(x.c0, L));
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
if (is0)
|
|
349
|
+
return concatBytes(Uint8Array.of(0x40), new Uint8Array(4 * L - 1));
|
|
350
|
+
const { re: x0, im: x1 } = Fp2.reim(x);
|
|
351
|
+
const { re: y0, im: y1 } = Fp2.reim(y);
|
|
352
|
+
return concatBytes(numberToBytesBE(x1, L), numberToBytesBE(x0, L), numberToBytesBE(y1, L), numberToBytesBE(y0, L));
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function signatureG2ToBytes(point) {
|
|
356
|
+
point.assertValidity();
|
|
357
|
+
const { BYTES: L } = Fp;
|
|
358
|
+
if (point.is0())
|
|
359
|
+
return concatBytes(COMPZERO, numberToBytesBE(_0n, L));
|
|
360
|
+
const { x, y } = point.toAffine();
|
|
361
|
+
const { re: x0, im: x1 } = Fp2.reim(x);
|
|
362
|
+
const { re: y0, im: y1 } = Fp2.reim(y);
|
|
363
|
+
const tmp = y1 > _0n ? y1 * _2n : y0 * _2n;
|
|
364
|
+
const sort = Boolean((tmp / Fp.ORDER) & _1n);
|
|
365
|
+
const z2 = x0;
|
|
366
|
+
return concatBytes(setMask(numberToBytesBE(x1, L), { sort, compressed: true }), numberToBytesBE(z2, L));
|
|
367
|
+
}
|
|
368
|
+
function pointG2FromBytes(bytes) {
|
|
369
|
+
const { BYTES: L, ORDER: P } = Fp;
|
|
370
|
+
const { compressed, infinity, sort, value } = parseMask(bytes);
|
|
371
|
+
if ((!compressed && !infinity && sort) || // 00100000
|
|
372
|
+
(!compressed && infinity && sort) || // 01100000
|
|
373
|
+
(sort && infinity && compressed) // 11100000
|
|
374
|
+
) {
|
|
375
|
+
throw new Error('invalid encoding flag: ' + (bytes[0] & 224));
|
|
376
|
+
}
|
|
377
|
+
const slc = (b, from, to) => bytesToNumberBE(b.slice(from, to));
|
|
378
|
+
if (value.length === 96 && compressed) {
|
|
379
|
+
if (infinity) {
|
|
380
|
+
// check that all bytes are 0
|
|
381
|
+
if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
|
|
382
|
+
throw new Error('invalid G2 point: compressed');
|
|
383
|
+
}
|
|
384
|
+
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
|
385
|
+
}
|
|
386
|
+
const x_1 = slc(value, 0, L);
|
|
387
|
+
const x_0 = slc(value, L, 2 * L);
|
|
388
|
+
const x = Fp2.create({ c0: Fp.create(x_0), c1: Fp.create(x_1) });
|
|
389
|
+
const right = Fp2.add(Fp2.pow(x, _3n), bls12_381_CURVE_G2.b); // y² = x³ + 4 * (u+1) = x³ + b
|
|
390
|
+
let y = Fp2.sqrt(right);
|
|
391
|
+
const Y_bit = y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P ? _1n : _0n;
|
|
392
|
+
y = sort && Y_bit > 0 ? y : Fp2.neg(y);
|
|
393
|
+
return { x, y };
|
|
394
|
+
}
|
|
395
|
+
else if (value.length === 192 && !compressed) {
|
|
396
|
+
if (infinity) {
|
|
397
|
+
if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
|
|
398
|
+
throw new Error('invalid G2 point: uncompressed');
|
|
399
|
+
}
|
|
400
|
+
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
|
401
|
+
}
|
|
402
|
+
const x1 = slc(value, 0 * L, 1 * L);
|
|
403
|
+
const x0 = slc(value, 1 * L, 2 * L);
|
|
404
|
+
const y1 = slc(value, 2 * L, 3 * L);
|
|
405
|
+
const y0 = slc(value, 3 * L, 4 * L);
|
|
406
|
+
return { x: Fp2.fromBigTuple([x0, x1]), y: Fp2.fromBigTuple([y0, y1]) };
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
throw new Error('invalid G2 point: expected 96/192 bytes');
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
function signatureG2FromBytes(hex) {
|
|
413
|
+
const { ORDER: P } = Fp;
|
|
414
|
+
// TODO: Optimize, it's very slow because of sqrt.
|
|
415
|
+
const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex));
|
|
416
|
+
const Point = bls12_381.G2.Point;
|
|
417
|
+
const half = value.length / 2;
|
|
418
|
+
if (half !== 48 && half !== 96)
|
|
419
|
+
throw new Error('invalid compressed signature length, expected 96/192 bytes');
|
|
420
|
+
const z1 = bytesToNumberBE(value.slice(0, half));
|
|
421
|
+
const z2 = bytesToNumberBE(value.slice(half));
|
|
422
|
+
// Indicates the infinity point
|
|
423
|
+
if (infinity)
|
|
424
|
+
return Point.ZERO;
|
|
425
|
+
const x1 = Fp.create(z1 & Fp.MASK);
|
|
426
|
+
const x2 = Fp.create(z2);
|
|
427
|
+
const x = Fp2.create({ c0: x2, c1: x1 });
|
|
428
|
+
const y2 = Fp2.add(Fp2.pow(x, _3n), bls12_381_CURVE_G2.b); // y² = x³ + 4
|
|
429
|
+
// The slow part
|
|
430
|
+
let y = Fp2.sqrt(y2);
|
|
431
|
+
if (!y)
|
|
432
|
+
throw new Error('Failed to find a square root');
|
|
433
|
+
// Choose the y whose leftmost bit of the imaginary part is equal to the a_flag1
|
|
434
|
+
// If y1 happens to be zero, then use the bit of y0
|
|
435
|
+
const { re: y0, im: y1 } = Fp2.reim(y);
|
|
436
|
+
const aflag1 = BigInt(sort);
|
|
437
|
+
const isGreater = y1 > _0n && (y1 * _2n) / P !== aflag1;
|
|
438
|
+
const is0 = y1 === _0n && (y0 * _2n) / P !== aflag1;
|
|
439
|
+
if (isGreater || is0)
|
|
440
|
+
y = Fp2.neg(y);
|
|
441
|
+
const point = Point.fromAffine({ x, y });
|
|
442
|
+
point.assertValidity();
|
|
443
|
+
return point;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* bls12-381 pairing-friendly curve.
|
|
447
|
+
* @example
|
|
448
|
+
* import { bls12_381 as bls } from '@noble/curves/bls12-381';
|
|
449
|
+
* // G1 keys, G2 signatures
|
|
450
|
+
* const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c';
|
|
451
|
+
* const message = '64726e3da8';
|
|
452
|
+
* const publicKey = bls.getPublicKey(privateKey);
|
|
453
|
+
* const signature = bls.sign(message, privateKey);
|
|
454
|
+
* const isValid = bls.verify(signature, message, publicKey);
|
|
455
|
+
*/
|
|
456
|
+
export const bls12_381 = bls({
|
|
457
|
+
// Fields
|
|
458
|
+
fields: {
|
|
459
|
+
Fp,
|
|
460
|
+
Fp2,
|
|
461
|
+
Fp6,
|
|
462
|
+
Fp12,
|
|
463
|
+
Fr: bls12_381_Fr,
|
|
464
|
+
},
|
|
465
|
+
// G1: y² = x³ + 4
|
|
466
|
+
G1: {
|
|
467
|
+
...bls12_381_CURVE_G1,
|
|
468
|
+
Fp,
|
|
469
|
+
htfDefaults: { ...htfDefaults, m: 1, DST: 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_' },
|
|
470
|
+
wrapPrivateKey: true,
|
|
471
|
+
allowInfinityPoint: true,
|
|
472
|
+
// Checks is the point resides in prime-order subgroup.
|
|
473
|
+
// point.isTorsionFree() should return true for valid points
|
|
474
|
+
// It returns false for shitty points.
|
|
475
|
+
// https://eprint.iacr.org/2021/1130.pdf
|
|
476
|
+
isTorsionFree: (c, point) => {
|
|
477
|
+
// GLV endomorphism ψ(P)
|
|
478
|
+
const beta = BigInt('0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe');
|
|
479
|
+
const phi = new c(Fp.mul(point.px, beta), point.py, point.pz);
|
|
480
|
+
// TODO: unroll
|
|
481
|
+
const xP = point.multiplyUnsafe(BLS_X).negate(); // [x]P
|
|
482
|
+
const u2P = xP.multiplyUnsafe(BLS_X); // [u2]P
|
|
483
|
+
return u2P.equals(phi);
|
|
484
|
+
},
|
|
485
|
+
// Clear cofactor of G1
|
|
486
|
+
// https://eprint.iacr.org/2019/403
|
|
487
|
+
clearCofactor: (_c, point) => {
|
|
488
|
+
// return this.multiplyUnsafe(CURVE.h);
|
|
489
|
+
return point.multiplyUnsafe(BLS_X).add(point); // x*P + P
|
|
490
|
+
},
|
|
491
|
+
mapToCurve: mapToG1,
|
|
492
|
+
fromBytes: pointG1FromBytes,
|
|
493
|
+
toBytes: pointG1ToBytes,
|
|
494
|
+
ShortSignature: {
|
|
495
|
+
fromBytes(bytes) {
|
|
496
|
+
abytes(bytes);
|
|
497
|
+
return signatureG1FromBytes(bytes);
|
|
498
|
+
},
|
|
499
|
+
fromHex(hex) {
|
|
500
|
+
return signatureG1FromBytes(hex);
|
|
501
|
+
},
|
|
502
|
+
toBytes(point) {
|
|
503
|
+
return signatureG1ToBytes(point);
|
|
504
|
+
},
|
|
505
|
+
toRawBytes(point) {
|
|
506
|
+
return signatureG1ToBytes(point);
|
|
507
|
+
},
|
|
508
|
+
toHex(point) {
|
|
509
|
+
return bytesToHex(signatureG1ToBytes(point));
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
},
|
|
513
|
+
G2: {
|
|
514
|
+
...bls12_381_CURVE_G2,
|
|
515
|
+
Fp: Fp2,
|
|
516
|
+
// https://datatracker.ietf.org/doc/html/rfc9380#name-clearing-the-cofactor
|
|
517
|
+
// https://datatracker.ietf.org/doc/html/rfc9380#name-cofactor-clearing-for-bls12
|
|
518
|
+
hEff: BigInt('0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551'),
|
|
519
|
+
htfDefaults: { ...htfDefaults },
|
|
520
|
+
wrapPrivateKey: true,
|
|
521
|
+
allowInfinityPoint: true,
|
|
522
|
+
mapToCurve: mapToG2,
|
|
523
|
+
// Checks is the point resides in prime-order subgroup.
|
|
524
|
+
// point.isTorsionFree() should return true for valid points
|
|
525
|
+
// It returns false for shitty points.
|
|
526
|
+
// https://eprint.iacr.org/2021/1130.pdf
|
|
527
|
+
// Older version: https://eprint.iacr.org/2019/814.pdf
|
|
528
|
+
isTorsionFree: (c, P) => {
|
|
529
|
+
return P.multiplyUnsafe(BLS_X).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
|
|
530
|
+
},
|
|
531
|
+
// Maps the point into the prime-order subgroup G2.
|
|
532
|
+
// clear_cofactor_bls12381_g2 from RFC 9380.
|
|
533
|
+
// https://eprint.iacr.org/2017/419.pdf
|
|
534
|
+
// prettier-ignore
|
|
535
|
+
clearCofactor: (c, P) => {
|
|
536
|
+
const x = BLS_X;
|
|
537
|
+
let t1 = P.multiplyUnsafe(x).negate(); // [-x]P
|
|
538
|
+
let t2 = G2psi(c, P); // Ψ(P)
|
|
539
|
+
let t3 = P.double(); // 2P
|
|
540
|
+
t3 = G2psi2(c, t3); // Ψ²(2P)
|
|
541
|
+
t3 = t3.subtract(t2); // Ψ²(2P) - Ψ(P)
|
|
542
|
+
t2 = t1.add(t2); // [-x]P + Ψ(P)
|
|
543
|
+
t2 = t2.multiplyUnsafe(x).negate(); // [x²]P - [x]Ψ(P)
|
|
544
|
+
t3 = t3.add(t2); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P)
|
|
545
|
+
t3 = t3.subtract(t1); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P) + [x]P
|
|
546
|
+
const Q = t3.subtract(P); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P) + [x]P - 1P
|
|
547
|
+
return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P)
|
|
548
|
+
},
|
|
549
|
+
fromBytes: pointG2FromBytes,
|
|
550
|
+
toBytes: pointG2ToBytes,
|
|
551
|
+
Signature: {
|
|
552
|
+
fromBytes(bytes) {
|
|
553
|
+
abytes(bytes);
|
|
554
|
+
return signatureG2FromBytes(bytes);
|
|
555
|
+
},
|
|
556
|
+
fromHex(hex) {
|
|
557
|
+
return signatureG2FromBytes(hex);
|
|
558
|
+
},
|
|
559
|
+
toBytes(point) {
|
|
560
|
+
return signatureG2ToBytes(point);
|
|
561
|
+
},
|
|
562
|
+
toRawBytes(point) {
|
|
563
|
+
return signatureG2ToBytes(point);
|
|
564
|
+
},
|
|
565
|
+
toHex(point) {
|
|
566
|
+
return bytesToHex(signatureG2ToBytes(point));
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
},
|
|
570
|
+
params: {
|
|
571
|
+
ateLoopSize: BLS_X, // The BLS parameter x for BLS12-381
|
|
572
|
+
r: bls12_381_CURVE_G1.n, // order; z⁴ − z² + 1; CURVE.n from other curves
|
|
573
|
+
xNegative: true,
|
|
574
|
+
twistType: 'multiplicative',
|
|
575
|
+
},
|
|
576
|
+
htfDefaults,
|
|
577
|
+
hash: sha256,
|
|
578
|
+
});
|
|
151
579
|
// 3-isogeny map from E' to E https://www.rfc-editor.org/rfc/rfc9380#appendix-E.3
|
|
152
580
|
const isogenyMapG2 = isogenyMap(Fp2, [
|
|
153
581
|
// xNum
|
|
@@ -287,414 +715,24 @@ const isogenyMapG1 = isogenyMap(Fp, [
|
|
|
287
715
|
'0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
|
288
716
|
],
|
|
289
717
|
].map((i) => i.map((j) => BigInt(j))));
|
|
290
|
-
// SWU Map - Fp2 to G2': y² = x³ + 240i * x + 1012 + 1012i
|
|
291
|
-
const G2_SWU = mapToCurveSimpleSWU(Fp2, {
|
|
292
|
-
A: Fp2.create({ c0: Fp.create(_0n), c1: Fp.create(BigInt(240)) }), // A' = 240 * I
|
|
293
|
-
B: Fp2.create({ c0: Fp.create(BigInt(1012)), c1: Fp.create(BigInt(1012)) }), // B' = 1012 * (1 + I)
|
|
294
|
-
Z: Fp2.create({ c0: Fp.create(BigInt(-2)), c1: Fp.create(BigInt(-1)) }), // Z: -(2 + I)
|
|
295
|
-
});
|
|
296
718
|
// Optimized SWU Map - Fp to G1
|
|
297
719
|
const G1_SWU = mapToCurveSimpleSWU(Fp, {
|
|
298
720
|
A: Fp.create(BigInt('0x144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d')),
|
|
299
721
|
B: Fp.create(BigInt('0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0')),
|
|
300
722
|
Z: Fp.create(BigInt(11)),
|
|
301
723
|
});
|
|
302
|
-
//
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
//
|
|
306
|
-
|
|
307
|
-
// Parameter values come from section 8.8.2 of the spec.
|
|
308
|
-
// https://www.rfc-editor.org/rfc/rfc9380#section-8.8.2
|
|
309
|
-
//
|
|
310
|
-
// Base field F is GF(p^m)
|
|
311
|
-
// p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
|
|
312
|
-
// m = 2 (or 1 for G1 see section 8.8.1)
|
|
313
|
-
// k = 128
|
|
314
|
-
const htfDefaults = Object.freeze({
|
|
315
|
-
// DST: a domain separation tag
|
|
316
|
-
// defined in section 2.2.5
|
|
317
|
-
// Use utils.getDSTLabel(), utils.setDSTLabel(value)
|
|
318
|
-
DST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
|
|
319
|
-
encodeDST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
|
|
320
|
-
// p: the characteristic of F
|
|
321
|
-
// where F is a finite field of characteristic p and order q = p^m
|
|
322
|
-
p: Fp.ORDER,
|
|
323
|
-
// m: the extension degree of F, m >= 1
|
|
324
|
-
// where F is a finite field of characteristic p and order q = p^m
|
|
325
|
-
m: 2,
|
|
326
|
-
// k: the target security level for the suite in bits
|
|
327
|
-
// defined in section 5.1
|
|
328
|
-
k: 128,
|
|
329
|
-
// option to use a message that has already been processed by
|
|
330
|
-
// expand_message_xmd
|
|
331
|
-
expand: 'xmd',
|
|
332
|
-
// Hash functions for: expand_message_xmd is appropriate for use with a
|
|
333
|
-
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
|
|
334
|
-
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
|
|
335
|
-
hash: sha256,
|
|
724
|
+
// SWU Map - Fp2 to G2': y² = x³ + 240i * x + 1012 + 1012i
|
|
725
|
+
const G2_SWU = mapToCurveSimpleSWU(Fp2, {
|
|
726
|
+
A: Fp2.create({ c0: Fp.create(_0n), c1: Fp.create(BigInt(240)) }), // A' = 240 * I
|
|
727
|
+
B: Fp2.create({ c0: Fp.create(BigInt(1012)), c1: Fp.create(BigInt(1012)) }), // B' = 1012 * (1 + I)
|
|
728
|
+
Z: Fp2.create({ c0: Fp.create(BigInt(-2)), c1: Fp.create(BigInt(-1)) }), // Z: -(2 + I)
|
|
336
729
|
});
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const COMPRESSED_ZERO = setMask(Fp.toBytes(_0n), { infinity: true, compressed: true }); // set compressed & point-at-infinity bits
|
|
341
|
-
function parseMask(bytes) {
|
|
342
|
-
// Copy, so we can remove mask data. It will be removed also later, when Fp.create will call modulo.
|
|
343
|
-
bytes = bytes.slice();
|
|
344
|
-
const mask = bytes[0] & 224;
|
|
345
|
-
const compressed = !!((mask >> 7) & 1); // compression bit (0b1000_0000)
|
|
346
|
-
const infinity = !!((mask >> 6) & 1); // point at infinity bit (0b0100_0000)
|
|
347
|
-
const sort = !!((mask >> 5) & 1); // sort bit (0b0010_0000)
|
|
348
|
-
bytes[0] &= 31; // clear mask (zero first 3 bits)
|
|
349
|
-
return { compressed, infinity, sort, value: bytes };
|
|
350
|
-
}
|
|
351
|
-
function setMask(bytes, mask) {
|
|
352
|
-
if (bytes[0] & 224)
|
|
353
|
-
throw new Error('setMask: non-empty mask');
|
|
354
|
-
if (mask.compressed)
|
|
355
|
-
bytes[0] |= 128;
|
|
356
|
-
if (mask.infinity)
|
|
357
|
-
bytes[0] |= 64;
|
|
358
|
-
if (mask.sort)
|
|
359
|
-
bytes[0] |= 32;
|
|
360
|
-
return bytes;
|
|
730
|
+
function mapToG1(scalars) {
|
|
731
|
+
const { x, y } = G1_SWU(Fp.create(scalars[0]));
|
|
732
|
+
return isogenyMapG1(x, y);
|
|
361
733
|
}
|
|
362
|
-
function
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
const { x, y } = point.toAffine();
|
|
366
|
-
if (isZero)
|
|
367
|
-
return COMPRESSED_ZERO.slice();
|
|
368
|
-
const P = Fp.ORDER;
|
|
369
|
-
const sort = Boolean((y * _2n) / P);
|
|
370
|
-
return setMask(numberToBytesBE(x, Fp.BYTES), { compressed: true, sort });
|
|
371
|
-
}
|
|
372
|
-
function signatureG2ToRawBytes(point) {
|
|
373
|
-
// NOTE: by some reasons it was missed in bls12-381, looks like bug
|
|
374
|
-
point.assertValidity();
|
|
375
|
-
const len = Fp.BYTES;
|
|
376
|
-
if (point.equals(bls12_381.G2.ProjectivePoint.ZERO))
|
|
377
|
-
return concatB(COMPRESSED_ZERO, numberToBytesBE(_0n, len));
|
|
378
|
-
const { x, y } = point.toAffine();
|
|
379
|
-
const { re: x0, im: x1 } = Fp2.reim(x);
|
|
380
|
-
const { re: y0, im: y1 } = Fp2.reim(y);
|
|
381
|
-
const tmp = y1 > _0n ? y1 * _2n : y0 * _2n;
|
|
382
|
-
const sort = Boolean((tmp / Fp.ORDER) & _1n);
|
|
383
|
-
const z2 = x0;
|
|
384
|
-
return concatB(setMask(numberToBytesBE(x1, len), { sort, compressed: true }), numberToBytesBE(z2, len));
|
|
734
|
+
function mapToG2(scalars) {
|
|
735
|
+
const { x, y } = G2_SWU(Fp2.fromBigTuple(scalars));
|
|
736
|
+
return isogenyMapG2(x, y);
|
|
385
737
|
}
|
|
386
|
-
/**
|
|
387
|
-
* bls12-381 pairing-friendly curve.
|
|
388
|
-
* @example
|
|
389
|
-
* import { bls12_381 as bls } from '@noble/curves/bls12-381';
|
|
390
|
-
* // G1 keys, G2 signatures
|
|
391
|
-
* const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c';
|
|
392
|
-
* const message = '64726e3da8';
|
|
393
|
-
* const publicKey = bls.getPublicKey(privateKey);
|
|
394
|
-
* const signature = bls.sign(message, privateKey);
|
|
395
|
-
* const isValid = bls.verify(signature, message, publicKey);
|
|
396
|
-
*/
|
|
397
|
-
export const bls12_381 = bls({
|
|
398
|
-
// Fields
|
|
399
|
-
fields: {
|
|
400
|
-
Fp,
|
|
401
|
-
Fp2,
|
|
402
|
-
Fp6,
|
|
403
|
-
Fp12,
|
|
404
|
-
Fr,
|
|
405
|
-
},
|
|
406
|
-
// G1 is the order-q subgroup of E1(Fp) : y² = x³ + 4, #E1(Fp) = h1q, where
|
|
407
|
-
// characteristic; z + (z⁴ - z² + 1)(z - 1)²/3
|
|
408
|
-
G1: {
|
|
409
|
-
Fp,
|
|
410
|
-
// cofactor; (z - 1)²/3
|
|
411
|
-
h: BigInt('0x396c8c005555e1568c00aaab0000aaab'),
|
|
412
|
-
// generator's coordinates
|
|
413
|
-
// x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507
|
|
414
|
-
// y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
|
|
415
|
-
Gx: BigInt('0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'),
|
|
416
|
-
Gy: BigInt('0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'),
|
|
417
|
-
a: Fp.ZERO,
|
|
418
|
-
b: _4n,
|
|
419
|
-
htfDefaults: { ...htfDefaults, m: 1, DST: 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_' },
|
|
420
|
-
wrapPrivateKey: true,
|
|
421
|
-
allowInfinityPoint: true,
|
|
422
|
-
// Checks is the point resides in prime-order subgroup.
|
|
423
|
-
// point.isTorsionFree() should return true for valid points
|
|
424
|
-
// It returns false for shitty points.
|
|
425
|
-
// https://eprint.iacr.org/2021/1130.pdf
|
|
426
|
-
isTorsionFree: (c, point) => {
|
|
427
|
-
// GLV endomorphism ψ(P)
|
|
428
|
-
const beta = BigInt('0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe');
|
|
429
|
-
const phi = new c(Fp.mul(point.px, beta), point.py, point.pz);
|
|
430
|
-
// TODO: unroll
|
|
431
|
-
const xP = point.multiplyUnsafe(BLS_X).negate(); // [x]P
|
|
432
|
-
const u2P = xP.multiplyUnsafe(BLS_X); // [u2]P
|
|
433
|
-
return u2P.equals(phi);
|
|
434
|
-
},
|
|
435
|
-
// Clear cofactor of G1
|
|
436
|
-
// https://eprint.iacr.org/2019/403
|
|
437
|
-
clearCofactor: (_c, point) => {
|
|
438
|
-
// return this.multiplyUnsafe(CURVE.h);
|
|
439
|
-
return point.multiplyUnsafe(BLS_X).add(point); // x*P + P
|
|
440
|
-
},
|
|
441
|
-
mapToCurve: (scalars) => {
|
|
442
|
-
const { x, y } = G1_SWU(Fp.create(scalars[0]));
|
|
443
|
-
return isogenyMapG1(x, y);
|
|
444
|
-
},
|
|
445
|
-
fromBytes: (bytes) => {
|
|
446
|
-
const { compressed, infinity, sort, value } = parseMask(bytes);
|
|
447
|
-
if (value.length === 48 && compressed) {
|
|
448
|
-
// TODO: Fp.bytes
|
|
449
|
-
const P = Fp.ORDER;
|
|
450
|
-
const compressedValue = bytesToNumberBE(value);
|
|
451
|
-
// Zero
|
|
452
|
-
const x = Fp.create(compressedValue & Fp.MASK);
|
|
453
|
-
if (infinity) {
|
|
454
|
-
if (x !== _0n)
|
|
455
|
-
throw new Error('G1: non-empty compressed point at infinity');
|
|
456
|
-
return { x: _0n, y: _0n };
|
|
457
|
-
}
|
|
458
|
-
const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.params.G1b)); // y² = x³ + b
|
|
459
|
-
let y = Fp.sqrt(right);
|
|
460
|
-
if (!y)
|
|
461
|
-
throw new Error('invalid compressed G1 point');
|
|
462
|
-
if ((y * _2n) / P !== BigInt(sort))
|
|
463
|
-
y = Fp.neg(y);
|
|
464
|
-
return { x: Fp.create(x), y: Fp.create(y) };
|
|
465
|
-
}
|
|
466
|
-
else if (value.length === 96 && !compressed) {
|
|
467
|
-
// Check if the infinity flag is set
|
|
468
|
-
const x = bytesToNumberBE(value.subarray(0, Fp.BYTES));
|
|
469
|
-
const y = bytesToNumberBE(value.subarray(Fp.BYTES));
|
|
470
|
-
if (infinity) {
|
|
471
|
-
if (x !== _0n || y !== _0n)
|
|
472
|
-
throw new Error('G1: non-empty point at infinity');
|
|
473
|
-
return bls12_381.G1.ProjectivePoint.ZERO.toAffine();
|
|
474
|
-
}
|
|
475
|
-
return { x: Fp.create(x), y: Fp.create(y) };
|
|
476
|
-
}
|
|
477
|
-
else {
|
|
478
|
-
throw new Error('invalid point G1, expected 48/96 bytes');
|
|
479
|
-
}
|
|
480
|
-
},
|
|
481
|
-
toBytes: (c, point, isCompressed) => {
|
|
482
|
-
const isZero = point.equals(c.ZERO);
|
|
483
|
-
const { x, y } = point.toAffine();
|
|
484
|
-
if (isCompressed) {
|
|
485
|
-
if (isZero)
|
|
486
|
-
return COMPRESSED_ZERO.slice();
|
|
487
|
-
const P = Fp.ORDER;
|
|
488
|
-
const sort = Boolean((y * _2n) / P);
|
|
489
|
-
return setMask(numberToBytesBE(x, Fp.BYTES), { compressed: true, sort });
|
|
490
|
-
}
|
|
491
|
-
else {
|
|
492
|
-
if (isZero) {
|
|
493
|
-
// 2x PUBLIC_KEY_LENGTH
|
|
494
|
-
const x = concatB(new Uint8Array([0x40]), new Uint8Array(2 * Fp.BYTES - 1));
|
|
495
|
-
return x;
|
|
496
|
-
}
|
|
497
|
-
else {
|
|
498
|
-
return concatB(numberToBytesBE(x, Fp.BYTES), numberToBytesBE(y, Fp.BYTES));
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
},
|
|
502
|
-
ShortSignature: {
|
|
503
|
-
fromHex(hex) {
|
|
504
|
-
const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex, 48));
|
|
505
|
-
const P = Fp.ORDER;
|
|
506
|
-
const compressedValue = bytesToNumberBE(value);
|
|
507
|
-
// Zero
|
|
508
|
-
if (infinity)
|
|
509
|
-
return bls12_381.G1.ProjectivePoint.ZERO;
|
|
510
|
-
const x = Fp.create(compressedValue & Fp.MASK);
|
|
511
|
-
const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.params.G1b)); // y² = x³ + b
|
|
512
|
-
let y = Fp.sqrt(right);
|
|
513
|
-
if (!y)
|
|
514
|
-
throw new Error('invalid compressed G1 point');
|
|
515
|
-
const aflag = BigInt(sort);
|
|
516
|
-
if ((y * _2n) / P !== aflag)
|
|
517
|
-
y = Fp.neg(y);
|
|
518
|
-
const point = bls12_381.G1.ProjectivePoint.fromAffine({ x, y });
|
|
519
|
-
point.assertValidity();
|
|
520
|
-
return point;
|
|
521
|
-
},
|
|
522
|
-
toRawBytes(point) {
|
|
523
|
-
return signatureG1ToRawBytes(point);
|
|
524
|
-
},
|
|
525
|
-
toHex(point) {
|
|
526
|
-
return bytesToHex(signatureG1ToRawBytes(point));
|
|
527
|
-
},
|
|
528
|
-
},
|
|
529
|
-
},
|
|
530
|
-
// G2 is the order-q subgroup of E2(Fp²) : y² = x³+4(1+√−1),
|
|
531
|
-
// where Fp2 is Fp[√−1]/(x2+1). #E2(Fp2 ) = h2q, where
|
|
532
|
-
// G² - 1
|
|
533
|
-
// h2q
|
|
534
|
-
G2: {
|
|
535
|
-
Fp: Fp2,
|
|
536
|
-
// cofactor
|
|
537
|
-
h: BigInt('0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5'),
|
|
538
|
-
Gx: Fp2.fromBigTuple([
|
|
539
|
-
BigInt('0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8'),
|
|
540
|
-
BigInt('0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e'),
|
|
541
|
-
]),
|
|
542
|
-
// y =
|
|
543
|
-
// 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582,
|
|
544
|
-
// 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905
|
|
545
|
-
Gy: Fp2.fromBigTuple([
|
|
546
|
-
BigInt('0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801'),
|
|
547
|
-
BigInt('0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be'),
|
|
548
|
-
]),
|
|
549
|
-
a: Fp2.ZERO,
|
|
550
|
-
b: Fp2.fromBigTuple([_4n, _4n]),
|
|
551
|
-
hEff: BigInt('0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551'),
|
|
552
|
-
htfDefaults: { ...htfDefaults },
|
|
553
|
-
wrapPrivateKey: true,
|
|
554
|
-
allowInfinityPoint: true,
|
|
555
|
-
mapToCurve: (scalars) => {
|
|
556
|
-
const { x, y } = G2_SWU(Fp2.fromBigTuple(scalars));
|
|
557
|
-
return isogenyMapG2(x, y);
|
|
558
|
-
},
|
|
559
|
-
// Checks is the point resides in prime-order subgroup.
|
|
560
|
-
// point.isTorsionFree() should return true for valid points
|
|
561
|
-
// It returns false for shitty points.
|
|
562
|
-
// https://eprint.iacr.org/2021/1130.pdf
|
|
563
|
-
// Older version: https://eprint.iacr.org/2019/814.pdf
|
|
564
|
-
isTorsionFree: (c, P) => {
|
|
565
|
-
return P.multiplyUnsafe(BLS_X).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
|
|
566
|
-
},
|
|
567
|
-
// Maps the point into the prime-order subgroup G2.
|
|
568
|
-
// clear_cofactor_bls12381_g2 from RFC 9380.
|
|
569
|
-
// https://eprint.iacr.org/2017/419.pdf
|
|
570
|
-
// prettier-ignore
|
|
571
|
-
clearCofactor: (c, P) => {
|
|
572
|
-
const x = BLS_X;
|
|
573
|
-
let t1 = P.multiplyUnsafe(x).negate(); // [-x]P
|
|
574
|
-
let t2 = G2psi(c, P); // Ψ(P)
|
|
575
|
-
let t3 = P.double(); // 2P
|
|
576
|
-
t3 = G2psi2(c, t3); // Ψ²(2P)
|
|
577
|
-
t3 = t3.subtract(t2); // Ψ²(2P) - Ψ(P)
|
|
578
|
-
t2 = t1.add(t2); // [-x]P + Ψ(P)
|
|
579
|
-
t2 = t2.multiplyUnsafe(x).negate(); // [x²]P - [x]Ψ(P)
|
|
580
|
-
t3 = t3.add(t2); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P)
|
|
581
|
-
t3 = t3.subtract(t1); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P) + [x]P
|
|
582
|
-
const Q = t3.subtract(P); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P) + [x]P - 1P
|
|
583
|
-
return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P)
|
|
584
|
-
},
|
|
585
|
-
fromBytes: (bytes) => {
|
|
586
|
-
const { compressed, infinity, sort, value } = parseMask(bytes);
|
|
587
|
-
if ((!compressed && !infinity && sort) || // 00100000
|
|
588
|
-
(!compressed && infinity && sort) || // 01100000
|
|
589
|
-
(sort && infinity && compressed) // 11100000
|
|
590
|
-
) {
|
|
591
|
-
throw new Error('invalid encoding flag: ' + (bytes[0] & 224));
|
|
592
|
-
}
|
|
593
|
-
const L = Fp.BYTES;
|
|
594
|
-
const slc = (b, from, to) => bytesToNumberBE(b.slice(from, to));
|
|
595
|
-
if (value.length === 96 && compressed) {
|
|
596
|
-
const b = bls12_381.params.G2b;
|
|
597
|
-
const P = Fp.ORDER;
|
|
598
|
-
if (infinity) {
|
|
599
|
-
// check that all bytes are 0
|
|
600
|
-
if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
|
|
601
|
-
throw new Error('invalid compressed G2 point');
|
|
602
|
-
}
|
|
603
|
-
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
|
604
|
-
}
|
|
605
|
-
const x_1 = slc(value, 0, L);
|
|
606
|
-
const x_0 = slc(value, L, 2 * L);
|
|
607
|
-
const x = Fp2.create({ c0: Fp.create(x_0), c1: Fp.create(x_1) });
|
|
608
|
-
const right = Fp2.add(Fp2.pow(x, _3n), b); // y² = x³ + 4 * (u+1) = x³ + b
|
|
609
|
-
let y = Fp2.sqrt(right);
|
|
610
|
-
const Y_bit = y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P ? _1n : _0n;
|
|
611
|
-
y = sort && Y_bit > 0 ? y : Fp2.neg(y);
|
|
612
|
-
return { x, y };
|
|
613
|
-
}
|
|
614
|
-
else if (value.length === 192 && !compressed) {
|
|
615
|
-
if (infinity) {
|
|
616
|
-
if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
|
|
617
|
-
throw new Error('invalid uncompressed G2 point');
|
|
618
|
-
}
|
|
619
|
-
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
|
620
|
-
}
|
|
621
|
-
const x1 = slc(value, 0, L);
|
|
622
|
-
const x0 = slc(value, L, 2 * L);
|
|
623
|
-
const y1 = slc(value, 2 * L, 3 * L);
|
|
624
|
-
const y0 = slc(value, 3 * L, 4 * L);
|
|
625
|
-
return { x: Fp2.fromBigTuple([x0, x1]), y: Fp2.fromBigTuple([y0, y1]) };
|
|
626
|
-
}
|
|
627
|
-
else {
|
|
628
|
-
throw new Error('invalid point G2, expected 96/192 bytes');
|
|
629
|
-
}
|
|
630
|
-
},
|
|
631
|
-
toBytes: (c, point, isCompressed) => {
|
|
632
|
-
const { BYTES: len, ORDER: P } = Fp;
|
|
633
|
-
const isZero = point.equals(c.ZERO);
|
|
634
|
-
const { x, y } = point.toAffine();
|
|
635
|
-
if (isCompressed) {
|
|
636
|
-
if (isZero)
|
|
637
|
-
return concatB(COMPRESSED_ZERO, numberToBytesBE(_0n, len));
|
|
638
|
-
const flag = Boolean(y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P);
|
|
639
|
-
return concatB(setMask(numberToBytesBE(x.c1, len), { compressed: true, sort: flag }), numberToBytesBE(x.c0, len));
|
|
640
|
-
}
|
|
641
|
-
else {
|
|
642
|
-
if (isZero)
|
|
643
|
-
return concatB(new Uint8Array([0x40]), new Uint8Array(4 * len - 1)); // bytes[0] |= 1 << 6;
|
|
644
|
-
const { re: x0, im: x1 } = Fp2.reim(x);
|
|
645
|
-
const { re: y0, im: y1 } = Fp2.reim(y);
|
|
646
|
-
return concatB(numberToBytesBE(x1, len), numberToBytesBE(x0, len), numberToBytesBE(y1, len), numberToBytesBE(y0, len));
|
|
647
|
-
}
|
|
648
|
-
},
|
|
649
|
-
Signature: {
|
|
650
|
-
// TODO: Optimize, it's very slow because of sqrt.
|
|
651
|
-
fromHex(hex) {
|
|
652
|
-
const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex));
|
|
653
|
-
const P = Fp.ORDER;
|
|
654
|
-
const half = value.length / 2;
|
|
655
|
-
if (half !== 48 && half !== 96)
|
|
656
|
-
throw new Error('invalid compressed signature length, must be 96 or 192');
|
|
657
|
-
const z1 = bytesToNumberBE(value.slice(0, half));
|
|
658
|
-
const z2 = bytesToNumberBE(value.slice(half));
|
|
659
|
-
// Indicates the infinity point
|
|
660
|
-
if (infinity)
|
|
661
|
-
return bls12_381.G2.ProjectivePoint.ZERO;
|
|
662
|
-
const x1 = Fp.create(z1 & Fp.MASK);
|
|
663
|
-
const x2 = Fp.create(z2);
|
|
664
|
-
const x = Fp2.create({ c0: x2, c1: x1 });
|
|
665
|
-
const y2 = Fp2.add(Fp2.pow(x, _3n), bls12_381.params.G2b); // y² = x³ + 4
|
|
666
|
-
// The slow part
|
|
667
|
-
let y = Fp2.sqrt(y2);
|
|
668
|
-
if (!y)
|
|
669
|
-
throw new Error('Failed to find a square root');
|
|
670
|
-
// Choose the y whose leftmost bit of the imaginary part is equal to the a_flag1
|
|
671
|
-
// If y1 happens to be zero, then use the bit of y0
|
|
672
|
-
const { re: y0, im: y1 } = Fp2.reim(y);
|
|
673
|
-
const aflag1 = BigInt(sort);
|
|
674
|
-
const isGreater = y1 > _0n && (y1 * _2n) / P !== aflag1;
|
|
675
|
-
const isZero = y1 === _0n && (y0 * _2n) / P !== aflag1;
|
|
676
|
-
if (isGreater || isZero)
|
|
677
|
-
y = Fp2.neg(y);
|
|
678
|
-
const point = bls12_381.G2.ProjectivePoint.fromAffine({ x, y });
|
|
679
|
-
point.assertValidity();
|
|
680
|
-
return point;
|
|
681
|
-
},
|
|
682
|
-
toRawBytes(point) {
|
|
683
|
-
return signatureG2ToRawBytes(point);
|
|
684
|
-
},
|
|
685
|
-
toHex(point) {
|
|
686
|
-
return bytesToHex(signatureG2ToRawBytes(point));
|
|
687
|
-
},
|
|
688
|
-
},
|
|
689
|
-
},
|
|
690
|
-
params: {
|
|
691
|
-
ateLoopSize: BLS_X, // The BLS parameter x for BLS12-381
|
|
692
|
-
r: Fr.ORDER, // order; z⁴ − z² + 1; CURVE.n from other curves
|
|
693
|
-
xNegative: true,
|
|
694
|
-
twistType: 'multiplicative',
|
|
695
|
-
},
|
|
696
|
-
htfDefaults,
|
|
697
|
-
hash: sha256,
|
|
698
|
-
randomBytes,
|
|
699
|
-
});
|
|
700
738
|
//# sourceMappingURL=bls12-381.js.map
|