@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.
Files changed (189) hide show
  1. package/README.md +56 -25
  2. package/_shortw_utils.d.ts +7 -5
  3. package/_shortw_utils.d.ts.map +1 -1
  4. package/_shortw_utils.js +2 -8
  5. package/_shortw_utils.js.map +1 -1
  6. package/abstract/bls.d.ts +60 -24
  7. package/abstract/bls.d.ts.map +1 -1
  8. package/abstract/bls.js +158 -109
  9. package/abstract/bls.js.map +1 -1
  10. package/abstract/curve.d.ts +44 -9
  11. package/abstract/curve.d.ts.map +1 -1
  12. package/abstract/curve.js +86 -7
  13. package/abstract/curve.js.map +1 -1
  14. package/abstract/edwards.d.ts +112 -25
  15. package/abstract/edwards.d.ts.map +1 -1
  16. package/abstract/edwards.js +138 -102
  17. package/abstract/edwards.js.map +1 -1
  18. package/abstract/fft.d.ts +12 -10
  19. package/abstract/fft.d.ts.map +1 -1
  20. package/abstract/fft.js +12 -13
  21. package/abstract/fft.js.map +1 -1
  22. package/abstract/hash-to-curve.d.ts +25 -11
  23. package/abstract/hash-to-curve.d.ts.map +1 -1
  24. package/abstract/hash-to-curve.js +17 -14
  25. package/abstract/hash-to-curve.js.map +1 -1
  26. package/abstract/modular.d.ts +24 -11
  27. package/abstract/modular.d.ts.map +1 -1
  28. package/abstract/modular.js +49 -20
  29. package/abstract/modular.js.map +1 -1
  30. package/abstract/montgomery.d.ts +1 -1
  31. package/abstract/montgomery.d.ts.map +1 -1
  32. package/abstract/montgomery.js +5 -4
  33. package/abstract/montgomery.js.map +1 -1
  34. package/abstract/poseidon.d.ts +5 -13
  35. package/abstract/poseidon.d.ts.map +1 -1
  36. package/abstract/poseidon.js +12 -7
  37. package/abstract/poseidon.js.map +1 -1
  38. package/abstract/tower.d.ts +20 -46
  39. package/abstract/tower.d.ts.map +1 -1
  40. package/abstract/tower.js +9 -3
  41. package/abstract/tower.js.map +1 -1
  42. package/abstract/utils.d.ts +1 -115
  43. package/abstract/utils.d.ts.map +1 -1
  44. package/abstract/utils.js +17 -371
  45. package/abstract/utils.js.map +1 -1
  46. package/abstract/weierstrass.d.ts +132 -76
  47. package/abstract/weierstrass.d.ts.map +1 -1
  48. package/abstract/weierstrass.js +462 -398
  49. package/abstract/weierstrass.js.map +1 -1
  50. package/bls12-381.d.ts +2 -0
  51. package/bls12-381.d.ts.map +1 -1
  52. package/bls12-381.js +504 -466
  53. package/bls12-381.js.map +1 -1
  54. package/bn254.d.ts +2 -0
  55. package/bn254.d.ts.map +1 -1
  56. package/bn254.js +44 -32
  57. package/bn254.js.map +1 -1
  58. package/ed25519.d.ts +8 -5
  59. package/ed25519.d.ts.map +1 -1
  60. package/ed25519.js +67 -54
  61. package/ed25519.js.map +1 -1
  62. package/ed448.d.ts +10 -6
  63. package/ed448.d.ts.map +1 -1
  64. package/ed448.js +80 -57
  65. package/ed448.js.map +1 -1
  66. package/esm/_shortw_utils.d.ts +7 -5
  67. package/esm/_shortw_utils.d.ts.map +1 -1
  68. package/esm/_shortw_utils.js +2 -8
  69. package/esm/_shortw_utils.js.map +1 -1
  70. package/esm/abstract/bls.d.ts +60 -24
  71. package/esm/abstract/bls.d.ts.map +1 -1
  72. package/esm/abstract/bls.js +158 -109
  73. package/esm/abstract/bls.js.map +1 -1
  74. package/esm/abstract/curve.d.ts +44 -9
  75. package/esm/abstract/curve.d.ts.map +1 -1
  76. package/esm/abstract/curve.js +83 -8
  77. package/esm/abstract/curve.js.map +1 -1
  78. package/esm/abstract/edwards.d.ts +112 -25
  79. package/esm/abstract/edwards.d.ts.map +1 -1
  80. package/esm/abstract/edwards.js +138 -104
  81. package/esm/abstract/edwards.js.map +1 -1
  82. package/esm/abstract/fft.d.ts +12 -10
  83. package/esm/abstract/fft.d.ts.map +1 -1
  84. package/esm/abstract/fft.js +10 -11
  85. package/esm/abstract/fft.js.map +1 -1
  86. package/esm/abstract/hash-to-curve.d.ts +25 -11
  87. package/esm/abstract/hash-to-curve.d.ts.map +1 -1
  88. package/esm/abstract/hash-to-curve.js +17 -14
  89. package/esm/abstract/hash-to-curve.js.map +1 -1
  90. package/esm/abstract/modular.d.ts +24 -11
  91. package/esm/abstract/modular.d.ts.map +1 -1
  92. package/esm/abstract/modular.js +48 -19
  93. package/esm/abstract/modular.js.map +1 -1
  94. package/esm/abstract/montgomery.d.ts +1 -1
  95. package/esm/abstract/montgomery.d.ts.map +1 -1
  96. package/esm/abstract/montgomery.js +5 -4
  97. package/esm/abstract/montgomery.js.map +1 -1
  98. package/esm/abstract/poseidon.d.ts +5 -13
  99. package/esm/abstract/poseidon.d.ts.map +1 -1
  100. package/esm/abstract/poseidon.js +12 -7
  101. package/esm/abstract/poseidon.js.map +1 -1
  102. package/esm/abstract/tower.d.ts +20 -46
  103. package/esm/abstract/tower.d.ts.map +1 -1
  104. package/esm/abstract/tower.js +9 -3
  105. package/esm/abstract/tower.js.map +1 -1
  106. package/esm/abstract/utils.d.ts +1 -115
  107. package/esm/abstract/utils.d.ts.map +1 -1
  108. package/esm/abstract/utils.js +3 -344
  109. package/esm/abstract/utils.js.map +1 -1
  110. package/esm/abstract/weierstrass.d.ts +132 -76
  111. package/esm/abstract/weierstrass.d.ts.map +1 -1
  112. package/esm/abstract/weierstrass.js +460 -400
  113. package/esm/abstract/weierstrass.js.map +1 -1
  114. package/esm/bls12-381.d.ts +2 -0
  115. package/esm/bls12-381.d.ts.map +1 -1
  116. package/esm/bls12-381.js +503 -465
  117. package/esm/bls12-381.js.map +1 -1
  118. package/esm/bn254.d.ts +2 -0
  119. package/esm/bn254.d.ts.map +1 -1
  120. package/esm/bn254.js +41 -29
  121. package/esm/bn254.js.map +1 -1
  122. package/esm/ed25519.d.ts +8 -5
  123. package/esm/ed25519.d.ts.map +1 -1
  124. package/esm/ed25519.js +62 -49
  125. package/esm/ed25519.js.map +1 -1
  126. package/esm/ed448.d.ts +10 -6
  127. package/esm/ed448.d.ts.map +1 -1
  128. package/esm/ed448.js +74 -51
  129. package/esm/ed448.js.map +1 -1
  130. package/esm/misc.d.ts.map +1 -1
  131. package/esm/misc.js +31 -26
  132. package/esm/misc.js.map +1 -1
  133. package/esm/nist.d.ts +7 -16
  134. package/esm/nist.d.ts.map +1 -1
  135. package/esm/nist.js +86 -97
  136. package/esm/nist.js.map +1 -1
  137. package/esm/p256.d.ts +3 -3
  138. package/esm/p384.d.ts +3 -3
  139. package/esm/p521.d.ts +3 -3
  140. package/esm/secp256k1.d.ts +6 -6
  141. package/esm/secp256k1.d.ts.map +1 -1
  142. package/esm/secp256k1.js +43 -40
  143. package/esm/secp256k1.js.map +1 -1
  144. package/esm/utils.d.ts +96 -0
  145. package/esm/utils.d.ts.map +1 -0
  146. package/esm/utils.js +279 -0
  147. package/esm/utils.js.map +1 -0
  148. package/misc.d.ts.map +1 -1
  149. package/misc.js +35 -30
  150. package/misc.js.map +1 -1
  151. package/nist.d.ts +7 -16
  152. package/nist.d.ts.map +1 -1
  153. package/nist.js +86 -97
  154. package/nist.js.map +1 -1
  155. package/p256.d.ts +3 -3
  156. package/p384.d.ts +3 -3
  157. package/p521.d.ts +3 -3
  158. package/package.json +14 -5
  159. package/secp256k1.d.ts +6 -6
  160. package/secp256k1.d.ts.map +1 -1
  161. package/secp256k1.js +46 -43
  162. package/secp256k1.js.map +1 -1
  163. package/src/_shortw_utils.ts +5 -15
  164. package/src/abstract/bls.ts +260 -145
  165. package/src/abstract/curve.ts +115 -13
  166. package/src/abstract/edwards.ts +279 -138
  167. package/src/abstract/fft.ts +30 -19
  168. package/src/abstract/hash-to-curve.ts +51 -27
  169. package/src/abstract/modular.ts +49 -28
  170. package/src/abstract/montgomery.ts +9 -7
  171. package/src/abstract/poseidon.ts +22 -18
  172. package/src/abstract/tower.ts +36 -67
  173. package/src/abstract/utils.ts +3 -378
  174. package/src/abstract/weierstrass.ts +700 -453
  175. package/src/bls12-381.ts +540 -489
  176. package/src/bn254.ts +47 -35
  177. package/src/ed25519.ts +80 -64
  178. package/src/ed448.ts +129 -92
  179. package/src/misc.ts +39 -34
  180. package/src/nist.ts +138 -127
  181. package/src/p256.ts +3 -3
  182. package/src/p384.ts +3 -3
  183. package/src/p521.ts +3 -3
  184. package/src/secp256k1.ts +58 -46
  185. package/src/utils.ts +328 -0
  186. package/utils.d.ts +96 -0
  187. package/utils.d.ts.map +1 -0
  188. package/utils.js +313 -0
  189. package/utils.js.map +1 -0
package/src/bls12-381.ts CHANGED
@@ -1,102 +1,155 @@
1
1
  /**
2
2
  * bls12-381 is pairing-friendly Barreto-Lynn-Scott elliptic curve construction allowing to:
3
- * * Construct zk-SNARKs at the ~120-bit security
4
- * * Efficiently verify N aggregate signatures with 1 pairing and N ec additions:
5
- * the Boneh-Lynn-Shacham signature scheme is orders of magnitude more efficient than Schnorr
6
- *
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²): = 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
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: = + 4
38
+ - G2: = + 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
+
57
76
  *
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, type CurveFn } from './abstract/bls.ts';
65
- import { Field } from './abstract/modular.ts';
82
+ import { Field, type IField } from './abstract/modular.ts';
66
83
  import {
84
+ abytes,
67
85
  bitGet,
68
86
  bitLen,
69
87
  bytesToHex,
70
88
  bytesToNumberBE,
71
- concatBytes as concatB,
89
+ concatBytes,
72
90
  ensureBytes,
73
91
  numberToBytesBE,
74
92
  type Hex,
75
- } from './abstract/utils.ts';
93
+ } from './utils.ts';
76
94
  // Types
77
95
  import { isogenyMap } from './abstract/hash-to-curve.ts';
78
- import type { Fp, Fp12, Fp2, Fp6 } from './abstract/tower.ts';
96
+ import type { BigintTuple, Fp, Fp12, Fp2, Fp6 } from './abstract/tower.ts';
79
97
  import { psiFrobenius, tower12 } from './abstract/tower.ts';
80
98
  import {
81
99
  mapToCurveSimpleSWU,
82
100
  type AffinePoint,
101
+ type ProjConstructor,
83
102
  type ProjPointType,
103
+ type WeierstrassOpts,
84
104
  } from './abstract/weierstrass.ts';
85
105
 
86
106
  // Be friendly to bad ECMAScript parsers by not using bigint literals
87
107
  // prettier-ignore
88
108
  const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
89
109
 
110
+ // To verify math:
111
+ // https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11
112
+
90
113
  // The BLS parameter x (seed) for BLS12-381. NOTE: it is negative!
114
+ // x = -2^63 - 2^62 - 2^60 - 2^57 - 2^48 - 2^16
91
115
  const BLS_X = BigInt('0xd201000000010000');
116
+ // t = x (called differently in different places)
117
+ // const t = -BLS_X;
92
118
  const BLS_X_LEN = bitLen(BLS_X);
93
119
 
120
+ // a=0, b=4
121
+ // P is characteristic of field Fp, in which curve calculations are done.
122
+ // p = (t-1)² * (t⁴-t²+1)/3 + t
123
+ // bls12_381_Fp = (t-1n)**2n * (t**4n - t**2n + 1n) / 3n + t
124
+ // r*h is curve order, amount of points on curve,
125
+ // where r is order of prime subgroup and h is cofactor.
126
+ // r = t⁴-t²+1
127
+ // r = (t**4n - t**2n + 1n)
128
+ // cofactor h of G1: (t - 1)²/3
129
+ // cofactorG1 = (t-1n)**2n/3n
130
+ // x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507
131
+ // y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
132
+ const bls12_381_CURVE_G1: WeierstrassOpts<bigint> = {
133
+ p: BigInt(
134
+ '0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'
135
+ ),
136
+ n: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'),
137
+ h: BigInt('0x396c8c005555e1568c00aaab0000aaab'),
138
+ a: _0n,
139
+ b: _4n,
140
+ Gx: BigInt(
141
+ '0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
142
+ ),
143
+ Gy: BigInt(
144
+ '0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
145
+ ),
146
+ };
147
+
94
148
  // CURVE FIELDS
149
+ export const bls12_381_Fr: IField<bigint> = Field(bls12_381_CURVE_G1.n);
95
150
  const { Fp, Fp2, Fp6, Fp4Square, Fp12 } = tower12({
96
151
  // Order of Fp
97
- ORDER: BigInt(
98
- '0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'
99
- ),
152
+ ORDER: bls12_381_CURVE_G1.p,
100
153
  // Finite extension field over irreducible polynominal.
101
154
  // Fp(u) / (u² - β) where β = -1
102
155
  FP2_NONRESIDUE: [_1n, _1n],
@@ -162,13 +215,413 @@ const { Fp, Fp2, Fp6, Fp4Square, Fp12 } = tower12({
162
215
  },
163
216
  });
164
217
 
165
- // Finite field over r.
166
- // This particular field is not used anywhere in bls12-381, but it is still useful.
167
- const Fr = Field(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'));
218
+ // GLV endomorphism Ψ(P), for fast cofactor clearing
219
+ const { G2psi, G2psi2 } = psiFrobenius(Fp, Fp2, Fp2.div(Fp2.ONE, Fp2.NONRESIDUE)); // 1/(u+1)
220
+
221
+ /**
222
+ * Default hash_to_field / hash-to-curve for BLS.
223
+ * m: 1 for G1, 2 for G2
224
+ * k: target security level in bits
225
+ * hash: any function, e.g. BBS+ uses BLAKE2: see [github](https://github.com/hyperledger/aries-framework-go/issues/2247).
226
+ * Parameter values come from [section 8.8.2 of RFC 9380](https://www.rfc-editor.org/rfc/rfc9380#section-8.8.2).
227
+ */
228
+ const htfDefaults = Object.freeze({
229
+ DST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
230
+ encodeDST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
231
+ p: Fp.ORDER,
232
+ m: 2,
233
+ k: 128,
234
+ expand: 'xmd',
235
+ hash: sha256,
236
+ });
237
+
238
+ // a=0, b=4
239
+ // cofactor h of G2
240
+ // (t^8 - 4t^7 + 5t^6 - 4t^4 + 6t^3 - 4t^2 - 4t + 13)/9
241
+ // cofactorG2 = (t**8n - 4n*t**7n + 5n*t**6n - 4n*t**4n + 6n*t**3n - 4n*t**2n - 4n*t+13n)/9n
242
+ // x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160
243
+ // y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905
244
+ const bls12_381_CURVE_G2 = {
245
+ p: Fp2.ORDER,
246
+ n: bls12_381_CURVE_G1.n,
247
+ h: BigInt(
248
+ '0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5'
249
+ ),
250
+ a: Fp2.ZERO,
251
+ b: Fp2.fromBigTuple([_4n, _4n]),
252
+ Gx: Fp2.fromBigTuple([
253
+ BigInt(
254
+ '0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8'
255
+ ),
256
+ BigInt(
257
+ '0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e'
258
+ ),
259
+ ]),
260
+ Gy: Fp2.fromBigTuple([
261
+ BigInt(
262
+ '0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801'
263
+ ),
264
+ BigInt(
265
+ '0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be'
266
+ ),
267
+ ]),
268
+ };
269
+
270
+ // Encoding utils
271
+ // Compressed point of infinity
272
+ // Set compressed & point-at-infinity bits
273
+ const COMPZERO = setMask(Fp.toBytes(_0n), { infinity: true, compressed: true });
274
+
275
+ function parseMask(bytes: Uint8Array) {
276
+ // Copy, so we can remove mask data. It will be removed also later, when Fp.create will call modulo.
277
+ bytes = bytes.slice();
278
+ const mask = bytes[0] & 0b1110_0000;
279
+ const compressed = !!((mask >> 7) & 1); // compression bit (0b1000_0000)
280
+ const infinity = !!((mask >> 6) & 1); // point at infinity bit (0b0100_0000)
281
+ const sort = !!((mask >> 5) & 1); // sort bit (0b0010_0000)
282
+ bytes[0] &= 0b0001_1111; // clear mask (zero first 3 bits)
283
+ return { compressed, infinity, sort, value: bytes };
284
+ }
285
+
286
+ function setMask(
287
+ bytes: Uint8Array,
288
+ mask: { compressed?: boolean; infinity?: boolean; sort?: boolean }
289
+ ) {
290
+ if (bytes[0] & 0b1110_0000) throw new Error('setMask: non-empty mask');
291
+ if (mask.compressed) bytes[0] |= 0b1000_0000;
292
+ if (mask.infinity) bytes[0] |= 0b0100_0000;
293
+ if (mask.sort) bytes[0] |= 0b0010_0000;
294
+ return bytes;
295
+ }
296
+
297
+ function pointG1ToBytes(_c: ProjConstructor<Fp>, point: ProjPointType<Fp>, isComp: boolean) {
298
+ const { BYTES: L, ORDER: P } = Fp;
299
+ const is0 = point.is0();
300
+ const { x, y } = point.toAffine();
301
+ if (isComp) {
302
+ if (is0) return COMPZERO.slice();
303
+ const sort = Boolean((y * _2n) / P);
304
+ return setMask(numberToBytesBE(x, L), { compressed: true, sort });
305
+ } else {
306
+ if (is0) {
307
+ return concatBytes(Uint8Array.of(0x40), new Uint8Array(2 * L - 1));
308
+ } else {
309
+ return concatBytes(numberToBytesBE(x, L), numberToBytesBE(y, L));
310
+ }
311
+ }
312
+ }
313
+
314
+ function signatureG1ToBytes(point: ProjPointType<Fp>) {
315
+ point.assertValidity();
316
+ const { BYTES: L, ORDER: P } = Fp;
317
+ const { x, y } = point.toAffine();
318
+ if (point.is0()) return COMPZERO.slice();
319
+ const sort = Boolean((y * _2n) / P);
320
+ return setMask(numberToBytesBE(x, L), { compressed: true, sort });
321
+ }
322
+
323
+ function pointG1FromBytes(bytes: Uint8Array): AffinePoint<Fp> {
324
+ const { compressed, infinity, sort, value } = parseMask(bytes);
325
+ const { BYTES: L, ORDER: P } = Fp;
326
+ if (value.length === 48 && compressed) {
327
+ const compressedValue = bytesToNumberBE(value);
328
+ // Zero
329
+ const x = Fp.create(compressedValue & Fp.MASK);
330
+ if (infinity) {
331
+ if (x !== _0n) throw new Error('invalid G1 point: non-empty, at infinity, with compression');
332
+ return { x: _0n, y: _0n };
333
+ }
334
+ const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381_CURVE_G1.b)); // y² = x³ + b
335
+ let y = Fp.sqrt(right);
336
+ if (!y) throw new Error('invalid G1 point: compressed point');
337
+ if ((y * _2n) / P !== BigInt(sort)) y = Fp.neg(y);
338
+ return { x: Fp.create(x), y: Fp.create(y) };
339
+ } else if (value.length === 96 && !compressed) {
340
+ // Check if the infinity flag is set
341
+ const x = bytesToNumberBE(value.subarray(0, L));
342
+ const y = bytesToNumberBE(value.subarray(L));
343
+ if (infinity) {
344
+ if (x !== _0n || y !== _0n) throw new Error('G1: non-empty point at infinity');
345
+ return bls12_381.G1.Point.ZERO.toAffine();
346
+ }
347
+ return { x: Fp.create(x), y: Fp.create(y) };
348
+ } else {
349
+ throw new Error('invalid G1 point: expected 48/96 bytes');
350
+ }
351
+ }
352
+
353
+ function signatureG1FromBytes(hex: Hex): ProjPointType<Fp> {
354
+ const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex, 48));
355
+ const P = Fp.ORDER;
356
+ const Point = bls12_381.G1.Point;
357
+ const compressedValue = bytesToNumberBE(value);
358
+ // Zero
359
+ if (infinity) return Point.ZERO;
360
+ const x = Fp.create(compressedValue & Fp.MASK);
361
+ const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381_CURVE_G1.b)); // y² = x³ + b
362
+ let y = Fp.sqrt(right);
363
+ if (!y) throw new Error('invalid G1 point: compressed');
364
+ const aflag = BigInt(sort);
365
+ if ((y * _2n) / P !== aflag) y = Fp.neg(y);
366
+ const point = Point.fromAffine({ x, y });
367
+ point.assertValidity();
368
+ return point;
369
+ }
370
+
371
+ function pointG2ToBytes(_c: ProjConstructor<Fp2>, point: ProjPointType<Fp2>, isComp: boolean) {
372
+ const { BYTES: L, ORDER: P } = Fp;
373
+ const is0 = point.is0();
374
+ const { x, y } = point.toAffine();
375
+ if (isComp) {
376
+ if (is0) return concatBytes(COMPZERO, numberToBytesBE(_0n, L));
377
+ const flag = Boolean(y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P);
378
+ return concatBytes(
379
+ setMask(numberToBytesBE(x.c1, L), { compressed: true, sort: flag }),
380
+ numberToBytesBE(x.c0, L)
381
+ );
382
+ } else {
383
+ if (is0) return concatBytes(Uint8Array.of(0x40), new Uint8Array(4 * L - 1));
384
+ const { re: x0, im: x1 } = Fp2.reim(x);
385
+ const { re: y0, im: y1 } = Fp2.reim(y);
386
+ return concatBytes(
387
+ numberToBytesBE(x1, L),
388
+ numberToBytesBE(x0, L),
389
+ numberToBytesBE(y1, L),
390
+ numberToBytesBE(y0, L)
391
+ );
392
+ }
393
+ }
394
+
395
+ function signatureG2ToBytes(point: ProjPointType<Fp2>) {
396
+ point.assertValidity();
397
+ const { BYTES: L } = Fp;
398
+ if (point.is0()) return concatBytes(COMPZERO, numberToBytesBE(_0n, L));
399
+ const { x, y } = point.toAffine();
400
+ const { re: x0, im: x1 } = Fp2.reim(x);
401
+ const { re: y0, im: y1 } = Fp2.reim(y);
402
+ const tmp = y1 > _0n ? y1 * _2n : y0 * _2n;
403
+ const sort = Boolean((tmp / Fp.ORDER) & _1n);
404
+ const z2 = x0;
405
+ return concatBytes(
406
+ setMask(numberToBytesBE(x1, L), { sort, compressed: true }),
407
+ numberToBytesBE(z2, L)
408
+ );
409
+ }
410
+
411
+ function pointG2FromBytes(bytes: Uint8Array): AffinePoint<Fp2> {
412
+ const { BYTES: L, ORDER: P } = Fp;
413
+ const { compressed, infinity, sort, value } = parseMask(bytes);
414
+ if (
415
+ (!compressed && !infinity && sort) || // 00100000
416
+ (!compressed && infinity && sort) || // 01100000
417
+ (sort && infinity && compressed) // 11100000
418
+ ) {
419
+ throw new Error('invalid encoding flag: ' + (bytes[0] & 0b1110_0000));
420
+ }
421
+ const slc = (b: Uint8Array, from: number, to?: number) => bytesToNumberBE(b.slice(from, to));
422
+ if (value.length === 96 && compressed) {
423
+ if (infinity) {
424
+ // check that all bytes are 0
425
+ if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
426
+ throw new Error('invalid G2 point: compressed');
427
+ }
428
+ return { x: Fp2.ZERO, y: Fp2.ZERO };
429
+ }
430
+ const x_1 = slc(value, 0, L);
431
+ const x_0 = slc(value, L, 2 * L);
432
+ const x = Fp2.create({ c0: Fp.create(x_0), c1: Fp.create(x_1) });
433
+ const right = Fp2.add(Fp2.pow(x, _3n), bls12_381_CURVE_G2.b); // y² = x³ + 4 * (u+1) = x³ + b
434
+ let y = Fp2.sqrt(right);
435
+ const Y_bit = y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P ? _1n : _0n;
436
+ y = sort && Y_bit > 0 ? y : Fp2.neg(y);
437
+ return { x, y };
438
+ } else if (value.length === 192 && !compressed) {
439
+ if (infinity) {
440
+ if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
441
+ throw new Error('invalid G2 point: uncompressed');
442
+ }
443
+ return { x: Fp2.ZERO, y: Fp2.ZERO };
444
+ }
445
+ const x1 = slc(value, 0 * L, 1 * L);
446
+ const x0 = slc(value, 1 * L, 2 * L);
447
+ const y1 = slc(value, 2 * L, 3 * L);
448
+ const y0 = slc(value, 3 * L, 4 * L);
449
+ return { x: Fp2.fromBigTuple([x0, x1]), y: Fp2.fromBigTuple([y0, y1]) };
450
+ } else {
451
+ throw new Error('invalid G2 point: expected 96/192 bytes');
452
+ }
453
+ }
168
454
 
169
- // END OF CURVE FIELDS
455
+ function signatureG2FromBytes(hex: Hex) {
456
+ const { ORDER: P } = Fp;
457
+ // TODO: Optimize, it's very slow because of sqrt.
458
+ const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex));
459
+ const Point = bls12_381.G2.Point;
460
+ const half = value.length / 2;
461
+ if (half !== 48 && half !== 96)
462
+ throw new Error('invalid compressed signature length, expected 96/192 bytes');
463
+ const z1 = bytesToNumberBE(value.slice(0, half));
464
+ const z2 = bytesToNumberBE(value.slice(half));
465
+ // Indicates the infinity point
466
+ if (infinity) return Point.ZERO;
467
+ const x1 = Fp.create(z1 & Fp.MASK);
468
+ const x2 = Fp.create(z2);
469
+ const x = Fp2.create({ c0: x2, c1: x1 });
470
+ const y2 = Fp2.add(Fp2.pow(x, _3n), bls12_381_CURVE_G2.b); // y² = x³ + 4
471
+ // The slow part
472
+ let y = Fp2.sqrt(y2);
473
+ if (!y) throw new Error('Failed to find a square root');
170
474
 
171
- // HashToCurve
475
+ // Choose the y whose leftmost bit of the imaginary part is equal to the a_flag1
476
+ // If y1 happens to be zero, then use the bit of y0
477
+ const { re: y0, im: y1 } = Fp2.reim(y);
478
+ const aflag1 = BigInt(sort);
479
+ const isGreater = y1 > _0n && (y1 * _2n) / P !== aflag1;
480
+ const is0 = y1 === _0n && (y0 * _2n) / P !== aflag1;
481
+ if (isGreater || is0) y = Fp2.neg(y);
482
+ const point = Point.fromAffine({ x, y });
483
+ point.assertValidity();
484
+ return point;
485
+ }
486
+
487
+ /**
488
+ * bls12-381 pairing-friendly curve.
489
+ * @example
490
+ * import { bls12_381 as bls } from '@noble/curves/bls12-381';
491
+ * // G1 keys, G2 signatures
492
+ * const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c';
493
+ * const message = '64726e3da8';
494
+ * const publicKey = bls.getPublicKey(privateKey);
495
+ * const signature = bls.sign(message, privateKey);
496
+ * const isValid = bls.verify(signature, message, publicKey);
497
+ */
498
+ export const bls12_381: CurveFn = bls({
499
+ // Fields
500
+ fields: {
501
+ Fp,
502
+ Fp2,
503
+ Fp6,
504
+ Fp12,
505
+ Fr: bls12_381_Fr,
506
+ },
507
+ // G1: y² = x³ + 4
508
+ G1: {
509
+ ...bls12_381_CURVE_G1,
510
+ Fp,
511
+ htfDefaults: { ...htfDefaults, m: 1, DST: 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_' },
512
+ wrapPrivateKey: true,
513
+ allowInfinityPoint: true,
514
+ // Checks is the point resides in prime-order subgroup.
515
+ // point.isTorsionFree() should return true for valid points
516
+ // It returns false for shitty points.
517
+ // https://eprint.iacr.org/2021/1130.pdf
518
+ isTorsionFree: (c, point): boolean => {
519
+ // GLV endomorphism ψ(P)
520
+ const beta = BigInt(
521
+ '0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
522
+ );
523
+ const phi = new c(Fp.mul(point.px, beta), point.py, point.pz);
524
+ // TODO: unroll
525
+ const xP = point.multiplyUnsafe(BLS_X).negate(); // [x]P
526
+ const u2P = xP.multiplyUnsafe(BLS_X); // [u2]P
527
+ return u2P.equals(phi);
528
+ },
529
+ // Clear cofactor of G1
530
+ // https://eprint.iacr.org/2019/403
531
+ clearCofactor: (_c, point) => {
532
+ // return this.multiplyUnsafe(CURVE.h);
533
+ return point.multiplyUnsafe(BLS_X).add(point); // x*P + P
534
+ },
535
+ mapToCurve: mapToG1,
536
+ fromBytes: pointG1FromBytes,
537
+ toBytes: pointG1ToBytes,
538
+ ShortSignature: {
539
+ fromBytes(bytes: Uint8Array) {
540
+ abytes(bytes);
541
+ return signatureG1FromBytes(bytes);
542
+ },
543
+ fromHex(hex: Hex): ProjPointType<Fp> {
544
+ return signatureG1FromBytes(hex);
545
+ },
546
+ toBytes(point: ProjPointType<Fp>) {
547
+ return signatureG1ToBytes(point);
548
+ },
549
+ toRawBytes(point: ProjPointType<Fp>) {
550
+ return signatureG1ToBytes(point);
551
+ },
552
+ toHex(point: ProjPointType<Fp>) {
553
+ return bytesToHex(signatureG1ToBytes(point));
554
+ },
555
+ },
556
+ },
557
+ G2: {
558
+ ...bls12_381_CURVE_G2,
559
+ Fp: Fp2,
560
+ // https://datatracker.ietf.org/doc/html/rfc9380#name-clearing-the-cofactor
561
+ // https://datatracker.ietf.org/doc/html/rfc9380#name-cofactor-clearing-for-bls12
562
+ hEff: BigInt(
563
+ '0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551'
564
+ ),
565
+ htfDefaults: { ...htfDefaults },
566
+ wrapPrivateKey: true,
567
+ allowInfinityPoint: true,
568
+ mapToCurve: mapToG2,
569
+ // Checks is the point resides in prime-order subgroup.
570
+ // point.isTorsionFree() should return true for valid points
571
+ // It returns false for shitty points.
572
+ // https://eprint.iacr.org/2021/1130.pdf
573
+ // Older version: https://eprint.iacr.org/2019/814.pdf
574
+ isTorsionFree: (c, P): boolean => {
575
+ return P.multiplyUnsafe(BLS_X).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
576
+ },
577
+ // Maps the point into the prime-order subgroup G2.
578
+ // clear_cofactor_bls12381_g2 from RFC 9380.
579
+ // https://eprint.iacr.org/2017/419.pdf
580
+ // prettier-ignore
581
+ clearCofactor: (c, P) => {
582
+ const x = BLS_X;
583
+ let t1 = P.multiplyUnsafe(x).negate(); // [-x]P
584
+ let t2 = G2psi(c, P); // Ψ(P)
585
+ let t3 = P.double(); // 2P
586
+ t3 = G2psi2(c, t3); // Ψ²(2P)
587
+ t3 = t3.subtract(t2); // Ψ²(2P) - Ψ(P)
588
+ t2 = t1.add(t2); // [-x]P + Ψ(P)
589
+ t2 = t2.multiplyUnsafe(x).negate(); // [x²]P - [x]Ψ(P)
590
+ t3 = t3.add(t2); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P)
591
+ t3 = t3.subtract(t1); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P) + [x]P
592
+ const Q = t3.subtract(P); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P) + [x]P - 1P
593
+ return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P)
594
+ },
595
+ fromBytes: pointG2FromBytes,
596
+ toBytes: pointG2ToBytes,
597
+ Signature: {
598
+ fromBytes(bytes: Uint8Array): ProjPointType<Fp2> {
599
+ abytes(bytes);
600
+ return signatureG2FromBytes(bytes);
601
+ },
602
+ fromHex(hex: Hex): ProjPointType<Fp2> {
603
+ return signatureG2FromBytes(hex);
604
+ },
605
+ toBytes(point: ProjPointType<Fp2>) {
606
+ return signatureG2ToBytes(point);
607
+ },
608
+ toRawBytes(point: ProjPointType<Fp2>) {
609
+ return signatureG2ToBytes(point);
610
+ },
611
+ toHex(point: ProjPointType<Fp2>) {
612
+ return bytesToHex(signatureG2ToBytes(point));
613
+ },
614
+ },
615
+ },
616
+ params: {
617
+ ateLoopSize: BLS_X, // The BLS parameter x for BLS12-381
618
+ r: bls12_381_CURVE_G1.n, // order; z⁴ − z² + 1; CURVE.n from other curves
619
+ xNegative: true,
620
+ twistType: 'multiplicative',
621
+ },
622
+ htfDefaults,
623
+ hash: sha256,
624
+ });
172
625
 
173
626
  // 3-isogeny map from E' to E https://www.rfc-editor.org/rfc/rfc9380#appendix-E.3
174
627
  const isogenyMapG2 = isogenyMap(
@@ -240,7 +693,12 @@ const isogenyMapG2 = isogenyMap(
240
693
  ],
241
694
  ['0x1', '0x0'], // LAST 1
242
695
  ],
243
- ].map((i) => i.map((pair) => Fp2.fromBigTuple(pair.map(BigInt)))) as [Fp2[], Fp2[], Fp2[], Fp2[]]
696
+ ].map((i) => i.map((pair) => Fp2.fromBigTuple(pair.map(BigInt) as BigintTuple))) as [
697
+ Fp2[],
698
+ Fp2[],
699
+ Fp2[],
700
+ Fp2[],
701
+ ]
244
702
  );
245
703
  // 11-isogeny map from E' to E
246
704
  const isogenyMapG1 = isogenyMap(
@@ -316,12 +774,6 @@ const isogenyMapG1 = isogenyMap(
316
774
  ].map((i) => i.map((j) => BigInt(j))) as [Fp[], Fp[], Fp[], Fp[]]
317
775
  );
318
776
 
319
- // SWU Map - Fp2 to G2': y² = x³ + 240i * x + 1012 + 1012i
320
- const G2_SWU = mapToCurveSimpleSWU(Fp2, {
321
- A: Fp2.create({ c0: Fp.create(_0n), c1: Fp.create(BigInt(240)) }), // A' = 240 * I
322
- B: Fp2.create({ c0: Fp.create(BigInt(1012)), c1: Fp.create(BigInt(1012)) }), // B' = 1012 * (1 + I)
323
- Z: Fp2.create({ c0: Fp.create(BigInt(-2)), c1: Fp.create(BigInt(-1)) }), // Z: -(2 + I)
324
- });
325
777
  // Optimized SWU Map - Fp to G1
326
778
  const G1_SWU = mapToCurveSimpleSWU(Fp, {
327
779
  A: Fp.create(
@@ -336,419 +788,18 @@ const G1_SWU = mapToCurveSimpleSWU(Fp, {
336
788
  ),
337
789
  Z: Fp.create(BigInt(11)),
338
790
  });
791
+ // SWU Map - Fp2 to G2': y² = x³ + 240i * x + 1012 + 1012i
792
+ const G2_SWU = mapToCurveSimpleSWU(Fp2, {
793
+ A: Fp2.create({ c0: Fp.create(_0n), c1: Fp.create(BigInt(240)) }), // A' = 240 * I
794
+ B: Fp2.create({ c0: Fp.create(BigInt(1012)), c1: Fp.create(BigInt(1012)) }), // B' = 1012 * (1 + I)
795
+ Z: Fp2.create({ c0: Fp.create(BigInt(-2)), c1: Fp.create(BigInt(-1)) }), // Z: -(2 + I)
796
+ });
339
797
 
340
- // GLV endomorphism Ψ(P), for fast cofactor clearing
341
- const { G2psi, G2psi2 } = psiFrobenius(Fp, Fp2, Fp2.div(Fp2.ONE, Fp2.NONRESIDUE)); // 1/(u+1)
342
-
343
- // Default hash_to_field options are for hash to G2.
344
- //
345
- // Parameter definitions are in section 5.3 of the spec unless otherwise noted.
346
- // Parameter values come from section 8.8.2 of the spec.
347
- // https://www.rfc-editor.org/rfc/rfc9380#section-8.8.2
348
- //
349
- // Base field F is GF(p^m)
350
- // p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
351
- // m = 2 (or 1 for G1 see section 8.8.1)
352
- // k = 128
353
- const htfDefaults = Object.freeze({
354
- // DST: a domain separation tag
355
- // defined in section 2.2.5
356
- // Use utils.getDSTLabel(), utils.setDSTLabel(value)
357
- DST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
358
- encodeDST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
359
- // p: the characteristic of F
360
- // where F is a finite field of characteristic p and order q = p^m
361
- p: Fp.ORDER,
362
- // m: the extension degree of F, m >= 1
363
- // where F is a finite field of characteristic p and order q = p^m
364
- m: 2,
365
- // k: the target security level for the suite in bits
366
- // defined in section 5.1
367
- k: 128,
368
- // option to use a message that has already been processed by
369
- // expand_message_xmd
370
- expand: 'xmd',
371
- // Hash functions for: expand_message_xmd is appropriate for use with a
372
- // wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
373
- // BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
374
- hash: sha256,
375
- } as const);
376
-
377
- // Encoding utils
378
- // Point on G1 curve: (x, y)
379
-
380
- // Compressed point of infinity
381
- const COMPRESSED_ZERO = setMask(Fp.toBytes(_0n), { infinity: true, compressed: true }); // set compressed & point-at-infinity bits
382
-
383
- function parseMask(bytes: Uint8Array) {
384
- // Copy, so we can remove mask data. It will be removed also later, when Fp.create will call modulo.
385
- bytes = bytes.slice();
386
- const mask = bytes[0] & 0b1110_0000;
387
- const compressed = !!((mask >> 7) & 1); // compression bit (0b1000_0000)
388
- const infinity = !!((mask >> 6) & 1); // point at infinity bit (0b0100_0000)
389
- const sort = !!((mask >> 5) & 1); // sort bit (0b0010_0000)
390
- bytes[0] &= 0b0001_1111; // clear mask (zero first 3 bits)
391
- return { compressed, infinity, sort, value: bytes };
392
- }
393
-
394
- function setMask(
395
- bytes: Uint8Array,
396
- mask: { compressed?: boolean; infinity?: boolean; sort?: boolean }
397
- ) {
398
- if (bytes[0] & 0b1110_0000) throw new Error('setMask: non-empty mask');
399
- if (mask.compressed) bytes[0] |= 0b1000_0000;
400
- if (mask.infinity) bytes[0] |= 0b0100_0000;
401
- if (mask.sort) bytes[0] |= 0b0010_0000;
402
- return bytes;
403
- }
404
-
405
- function signatureG1ToRawBytes(point: ProjPointType<Fp>) {
406
- point.assertValidity();
407
- const isZero = point.equals(bls12_381.G1.ProjectivePoint.ZERO);
408
- const { x, y } = point.toAffine();
409
- if (isZero) return COMPRESSED_ZERO.slice();
410
- const P = Fp.ORDER;
411
- const sort = Boolean((y * _2n) / P);
412
- return setMask(numberToBytesBE(x, Fp.BYTES), { compressed: true, sort });
798
+ function mapToG1(scalars: bigint[]) {
799
+ const { x, y } = G1_SWU(Fp.create(scalars[0]));
800
+ return isogenyMapG1(x, y);
413
801
  }
414
-
415
- function signatureG2ToRawBytes(point: ProjPointType<Fp2>) {
416
- // NOTE: by some reasons it was missed in bls12-381, looks like bug
417
- point.assertValidity();
418
- const len = Fp.BYTES;
419
- if (point.equals(bls12_381.G2.ProjectivePoint.ZERO))
420
- return concatB(COMPRESSED_ZERO, numberToBytesBE(_0n, len));
421
- const { x, y } = point.toAffine();
422
- const { re: x0, im: x1 } = Fp2.reim(x);
423
- const { re: y0, im: y1 } = Fp2.reim(y);
424
- const tmp = y1 > _0n ? y1 * _2n : y0 * _2n;
425
- const sort = Boolean((tmp / Fp.ORDER) & _1n);
426
- const z2 = x0;
427
- return concatB(
428
- setMask(numberToBytesBE(x1, len), { sort, compressed: true }),
429
- numberToBytesBE(z2, len)
430
- );
802
+ function mapToG2(scalars: bigint[]) {
803
+ const { x, y } = G2_SWU(Fp2.fromBigTuple(scalars as BigintTuple));
804
+ return isogenyMapG2(x, y);
431
805
  }
432
-
433
- /**
434
- * bls12-381 pairing-friendly curve.
435
- * @example
436
- * import { bls12_381 as bls } from '@noble/curves/bls12-381';
437
- * // G1 keys, G2 signatures
438
- * const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c';
439
- * const message = '64726e3da8';
440
- * const publicKey = bls.getPublicKey(privateKey);
441
- * const signature = bls.sign(message, privateKey);
442
- * const isValid = bls.verify(signature, message, publicKey);
443
- */
444
- export const bls12_381: CurveFn = bls({
445
- // Fields
446
- fields: {
447
- Fp,
448
- Fp2,
449
- Fp6,
450
- Fp12,
451
- Fr,
452
- },
453
- // G1 is the order-q subgroup of E1(Fp) : y² = x³ + 4, #E1(Fp) = h1q, where
454
- // characteristic; z + (z⁴ - z² + 1)(z - 1)²/3
455
- G1: {
456
- Fp,
457
- // cofactor; (z - 1)²/3
458
- h: BigInt('0x396c8c005555e1568c00aaab0000aaab'),
459
- // generator's coordinates
460
- // x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507
461
- // y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
462
- Gx: BigInt(
463
- '0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
464
- ),
465
- Gy: BigInt(
466
- '0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
467
- ),
468
- a: Fp.ZERO,
469
- b: _4n,
470
- htfDefaults: { ...htfDefaults, m: 1, DST: 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_' },
471
- wrapPrivateKey: true,
472
- allowInfinityPoint: true,
473
- // Checks is the point resides in prime-order subgroup.
474
- // point.isTorsionFree() should return true for valid points
475
- // It returns false for shitty points.
476
- // https://eprint.iacr.org/2021/1130.pdf
477
- isTorsionFree: (c, point): boolean => {
478
- // GLV endomorphism ψ(P)
479
- const beta = BigInt(
480
- '0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
481
- );
482
- const phi = new c(Fp.mul(point.px, beta), point.py, point.pz);
483
- // TODO: unroll
484
- const xP = point.multiplyUnsafe(BLS_X).negate(); // [x]P
485
- const u2P = xP.multiplyUnsafe(BLS_X); // [u2]P
486
- return u2P.equals(phi);
487
- },
488
- // Clear cofactor of G1
489
- // https://eprint.iacr.org/2019/403
490
- clearCofactor: (_c, point) => {
491
- // return this.multiplyUnsafe(CURVE.h);
492
- return point.multiplyUnsafe(BLS_X).add(point); // x*P + P
493
- },
494
- mapToCurve: (scalars: bigint[]) => {
495
- const { x, y } = G1_SWU(Fp.create(scalars[0]));
496
- return isogenyMapG1(x, y);
497
- },
498
- fromBytes: (bytes: Uint8Array): AffinePoint<Fp> => {
499
- const { compressed, infinity, sort, value } = parseMask(bytes);
500
- if (value.length === 48 && compressed) {
501
- // TODO: Fp.bytes
502
- const P = Fp.ORDER;
503
- const compressedValue = bytesToNumberBE(value);
504
- // Zero
505
- const x = Fp.create(compressedValue & Fp.MASK);
506
- if (infinity) {
507
- if (x !== _0n) throw new Error('G1: non-empty compressed point at infinity');
508
- return { x: _0n, y: _0n };
509
- }
510
- const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.params.G1b)); // y² = x³ + b
511
- let y = Fp.sqrt(right);
512
- if (!y) throw new Error('invalid compressed G1 point');
513
- if ((y * _2n) / P !== BigInt(sort)) y = Fp.neg(y);
514
- return { x: Fp.create(x), y: Fp.create(y) };
515
- } else if (value.length === 96 && !compressed) {
516
- // Check if the infinity flag is set
517
- const x = bytesToNumberBE(value.subarray(0, Fp.BYTES));
518
- const y = bytesToNumberBE(value.subarray(Fp.BYTES));
519
- if (infinity) {
520
- if (x !== _0n || y !== _0n) throw new Error('G1: non-empty point at infinity');
521
- return bls12_381.G1.ProjectivePoint.ZERO.toAffine();
522
- }
523
- return { x: Fp.create(x), y: Fp.create(y) };
524
- } else {
525
- throw new Error('invalid point G1, expected 48/96 bytes');
526
- }
527
- },
528
- toBytes: (c, point, isCompressed) => {
529
- const isZero = point.equals(c.ZERO);
530
- const { x, y } = point.toAffine();
531
- if (isCompressed) {
532
- if (isZero) return COMPRESSED_ZERO.slice();
533
- const P = Fp.ORDER;
534
- const sort = Boolean((y * _2n) / P);
535
- return setMask(numberToBytesBE(x, Fp.BYTES), { compressed: true, sort });
536
- } else {
537
- if (isZero) {
538
- // 2x PUBLIC_KEY_LENGTH
539
- const x = concatB(new Uint8Array([0x40]), new Uint8Array(2 * Fp.BYTES - 1));
540
- return x;
541
- } else {
542
- return concatB(numberToBytesBE(x, Fp.BYTES), numberToBytesBE(y, Fp.BYTES));
543
- }
544
- }
545
- },
546
- ShortSignature: {
547
- fromHex(hex: Hex): ProjPointType<Fp> {
548
- const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex, 48));
549
- const P = Fp.ORDER;
550
- const compressedValue = bytesToNumberBE(value);
551
- // Zero
552
- if (infinity) return bls12_381.G1.ProjectivePoint.ZERO;
553
- const x = Fp.create(compressedValue & Fp.MASK);
554
- const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.params.G1b)); // y² = x³ + b
555
- let y = Fp.sqrt(right);
556
- if (!y) throw new Error('invalid compressed G1 point');
557
- const aflag = BigInt(sort);
558
- if ((y * _2n) / P !== aflag) y = Fp.neg(y);
559
- const point = bls12_381.G1.ProjectivePoint.fromAffine({ x, y });
560
- point.assertValidity();
561
- return point;
562
- },
563
- toRawBytes(point: ProjPointType<Fp>) {
564
- return signatureG1ToRawBytes(point);
565
- },
566
- toHex(point: ProjPointType<Fp>) {
567
- return bytesToHex(signatureG1ToRawBytes(point));
568
- },
569
- },
570
- },
571
- // G2 is the order-q subgroup of E2(Fp²) : y² = x³+4(1+√−1),
572
- // where Fp2 is Fp[√−1]/(x2+1). #E2(Fp2 ) = h2q, where
573
- // G² - 1
574
- // h2q
575
- G2: {
576
- Fp: Fp2,
577
- // cofactor
578
- h: BigInt(
579
- '0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5'
580
- ),
581
- Gx: Fp2.fromBigTuple([
582
- BigInt(
583
- '0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8'
584
- ),
585
- BigInt(
586
- '0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e'
587
- ),
588
- ]),
589
- // y =
590
- // 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582,
591
- // 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905
592
- Gy: Fp2.fromBigTuple([
593
- BigInt(
594
- '0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801'
595
- ),
596
- BigInt(
597
- '0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be'
598
- ),
599
- ]),
600
- a: Fp2.ZERO,
601
- b: Fp2.fromBigTuple([_4n, _4n]),
602
- hEff: BigInt(
603
- '0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551'
604
- ),
605
- htfDefaults: { ...htfDefaults },
606
- wrapPrivateKey: true,
607
- allowInfinityPoint: true,
608
- mapToCurve: (scalars: bigint[]) => {
609
- const { x, y } = G2_SWU(Fp2.fromBigTuple(scalars));
610
- return isogenyMapG2(x, y);
611
- },
612
- // Checks is the point resides in prime-order subgroup.
613
- // point.isTorsionFree() should return true for valid points
614
- // It returns false for shitty points.
615
- // https://eprint.iacr.org/2021/1130.pdf
616
- // Older version: https://eprint.iacr.org/2019/814.pdf
617
- isTorsionFree: (c, P): boolean => {
618
- return P.multiplyUnsafe(BLS_X).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
619
- },
620
- // Maps the point into the prime-order subgroup G2.
621
- // clear_cofactor_bls12381_g2 from RFC 9380.
622
- // https://eprint.iacr.org/2017/419.pdf
623
- // prettier-ignore
624
- clearCofactor: (c, P) => {
625
- const x = BLS_X;
626
- let t1 = P.multiplyUnsafe(x).negate(); // [-x]P
627
- let t2 = G2psi(c, P); // Ψ(P)
628
- let t3 = P.double(); // 2P
629
- t3 = G2psi2(c, t3); // Ψ²(2P)
630
- t3 = t3.subtract(t2); // Ψ²(2P) - Ψ(P)
631
- t2 = t1.add(t2); // [-x]P + Ψ(P)
632
- t2 = t2.multiplyUnsafe(x).negate(); // [x²]P - [x]Ψ(P)
633
- t3 = t3.add(t2); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P)
634
- t3 = t3.subtract(t1); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P) + [x]P
635
- const Q = t3.subtract(P); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P) + [x]P - 1P
636
- return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P)
637
- },
638
- fromBytes: (bytes: Uint8Array): AffinePoint<Fp2> => {
639
- const { compressed, infinity, sort, value } = parseMask(bytes);
640
- if (
641
- (!compressed && !infinity && sort) || // 00100000
642
- (!compressed && infinity && sort) || // 01100000
643
- (sort && infinity && compressed) // 11100000
644
- ) {
645
- throw new Error('invalid encoding flag: ' + (bytes[0] & 0b1110_0000));
646
- }
647
- const L = Fp.BYTES;
648
- const slc = (b: Uint8Array, from: number, to?: number) => bytesToNumberBE(b.slice(from, to));
649
- if (value.length === 96 && compressed) {
650
- const b = bls12_381.params.G2b;
651
- const P = Fp.ORDER;
652
- if (infinity) {
653
- // check that all bytes are 0
654
- if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
655
- throw new Error('invalid compressed G2 point');
656
- }
657
- return { x: Fp2.ZERO, y: Fp2.ZERO };
658
- }
659
- const x_1 = slc(value, 0, L);
660
- const x_0 = slc(value, L, 2 * L);
661
- const x = Fp2.create({ c0: Fp.create(x_0), c1: Fp.create(x_1) });
662
- const right = Fp2.add(Fp2.pow(x, _3n), b); // y² = x³ + 4 * (u+1) = x³ + b
663
- let y = Fp2.sqrt(right);
664
- const Y_bit = y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P ? _1n : _0n;
665
- y = sort && Y_bit > 0 ? y : Fp2.neg(y);
666
- return { x, y };
667
- } else if (value.length === 192 && !compressed) {
668
- if (infinity) {
669
- if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
670
- throw new Error('invalid uncompressed G2 point');
671
- }
672
- return { x: Fp2.ZERO, y: Fp2.ZERO };
673
- }
674
- const x1 = slc(value, 0, L);
675
- const x0 = slc(value, L, 2 * L);
676
- const y1 = slc(value, 2 * L, 3 * L);
677
- const y0 = slc(value, 3 * L, 4 * L);
678
- return { x: Fp2.fromBigTuple([x0, x1]), y: Fp2.fromBigTuple([y0, y1]) };
679
- } else {
680
- throw new Error('invalid point G2, expected 96/192 bytes');
681
- }
682
- },
683
- toBytes: (c, point, isCompressed) => {
684
- const { BYTES: len, ORDER: P } = Fp;
685
- const isZero = point.equals(c.ZERO);
686
- const { x, y } = point.toAffine();
687
- if (isCompressed) {
688
- if (isZero) return concatB(COMPRESSED_ZERO, numberToBytesBE(_0n, len));
689
- const flag = Boolean(y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P);
690
- return concatB(
691
- setMask(numberToBytesBE(x.c1, len), { compressed: true, sort: flag }),
692
- numberToBytesBE(x.c0, len)
693
- );
694
- } else {
695
- if (isZero) return concatB(new Uint8Array([0x40]), new Uint8Array(4 * len - 1)); // bytes[0] |= 1 << 6;
696
- const { re: x0, im: x1 } = Fp2.reim(x);
697
- const { re: y0, im: y1 } = Fp2.reim(y);
698
- return concatB(
699
- numberToBytesBE(x1, len),
700
- numberToBytesBE(x0, len),
701
- numberToBytesBE(y1, len),
702
- numberToBytesBE(y0, len)
703
- );
704
- }
705
- },
706
- Signature: {
707
- // TODO: Optimize, it's very slow because of sqrt.
708
- fromHex(hex: Hex): ProjPointType<Fp2> {
709
- const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex));
710
- const P = Fp.ORDER;
711
- const half = value.length / 2;
712
- if (half !== 48 && half !== 96)
713
- throw new Error('invalid compressed signature length, must be 96 or 192');
714
- const z1 = bytesToNumberBE(value.slice(0, half));
715
- const z2 = bytesToNumberBE(value.slice(half));
716
- // Indicates the infinity point
717
- if (infinity) return bls12_381.G2.ProjectivePoint.ZERO;
718
- const x1 = Fp.create(z1 & Fp.MASK);
719
- const x2 = Fp.create(z2);
720
- const x = Fp2.create({ c0: x2, c1: x1 });
721
- const y2 = Fp2.add(Fp2.pow(x, _3n), bls12_381.params.G2b); // y² = x³ + 4
722
- // The slow part
723
- let y = Fp2.sqrt(y2);
724
- if (!y) throw new Error('Failed to find a square root');
725
-
726
- // Choose the y whose leftmost bit of the imaginary part is equal to the a_flag1
727
- // If y1 happens to be zero, then use the bit of y0
728
- const { re: y0, im: y1 } = Fp2.reim(y);
729
- const aflag1 = BigInt(sort);
730
- const isGreater = y1 > _0n && (y1 * _2n) / P !== aflag1;
731
- const isZero = y1 === _0n && (y0 * _2n) / P !== aflag1;
732
- if (isGreater || isZero) y = Fp2.neg(y);
733
- const point = bls12_381.G2.ProjectivePoint.fromAffine({ x, y });
734
- point.assertValidity();
735
- return point;
736
- },
737
- toRawBytes(point: ProjPointType<Fp2>) {
738
- return signatureG2ToRawBytes(point);
739
- },
740
- toHex(point: ProjPointType<Fp2>) {
741
- return bytesToHex(signatureG2ToRawBytes(point));
742
- },
743
- },
744
- },
745
- params: {
746
- ateLoopSize: BLS_X, // The BLS parameter x for BLS12-381
747
- r: Fr.ORDER, // order; z⁴ − z² + 1; CURVE.n from other curves
748
- xNegative: true,
749
- twistType: 'multiplicative',
750
- },
751
- htfDefaults,
752
- hash: sha256,
753
- randomBytes,
754
- });