@noble/curves 1.9.0 → 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 (207) hide show
  1. package/README.md +78 -34
  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 +99 -11
  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 +141 -92
  17. package/abstract/edwards.js.map +1 -1
  18. package/abstract/fft.d.ts +122 -0
  19. package/abstract/fft.d.ts.map +1 -0
  20. package/abstract/fft.js +438 -0
  21. package/abstract/fft.js.map +1 -0
  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 +28 -17
  27. package/abstract/modular.d.ts.map +1 -1
  28. package/abstract/modular.js +156 -139
  29. package/abstract/modular.js.map +1 -1
  30. package/abstract/montgomery.d.ts +3 -8
  31. package/abstract/montgomery.d.ts.map +1 -1
  32. package/abstract/montgomery.js +73 -93
  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 +10 -4
  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 +152 -73
  47. package/abstract/weierstrass.d.ts.map +1 -1
  48. package/abstract/weierstrass.js +487 -404
  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 -480
  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 +25 -9
  59. package/ed25519.d.ts.map +1 -1
  60. package/ed25519.js +89 -65
  61. package/ed25519.js.map +1 -1
  62. package/ed448.d.ts +29 -10
  63. package/ed448.d.ts.map +1 -1
  64. package/ed448.js +116 -81
  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 +96 -12
  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 +141 -94
  81. package/esm/abstract/edwards.js.map +1 -1
  82. package/esm/abstract/fft.d.ts +122 -0
  83. package/esm/abstract/fft.d.ts.map +1 -0
  84. package/esm/abstract/fft.js +425 -0
  85. package/esm/abstract/fft.js.map +1 -0
  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 +28 -17
  91. package/esm/abstract/modular.d.ts.map +1 -1
  92. package/esm/abstract/modular.js +155 -138
  93. package/esm/abstract/modular.js.map +1 -1
  94. package/esm/abstract/montgomery.d.ts +3 -8
  95. package/esm/abstract/montgomery.d.ts.map +1 -1
  96. package/esm/abstract/montgomery.js +74 -94
  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 +10 -4
  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 +152 -73
  111. package/esm/abstract/weierstrass.d.ts.map +1 -1
  112. package/esm/abstract/weierstrass.js +485 -406
  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 -479
  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 +25 -9
  123. package/esm/ed25519.d.ts.map +1 -1
  124. package/esm/ed25519.js +84 -60
  125. package/esm/ed25519.js.map +1 -1
  126. package/esm/ed448.d.ts +29 -10
  127. package/esm/ed448.d.ts.map +1 -1
  128. package/esm/ed448.js +113 -78
  129. package/esm/ed448.js.map +1 -1
  130. package/esm/jubjub.d.ts +4 -0
  131. package/esm/jubjub.d.ts.map +1 -1
  132. package/esm/jubjub.js +4 -0
  133. package/esm/jubjub.js.map +1 -1
  134. package/esm/misc.d.ts.map +1 -1
  135. package/esm/misc.js +31 -26
  136. package/esm/misc.js.map +1 -1
  137. package/esm/nist.d.ts +8 -16
  138. package/esm/nist.d.ts.map +1 -1
  139. package/esm/nist.js +87 -97
  140. package/esm/nist.js.map +1 -1
  141. package/esm/p256.d.ts +3 -3
  142. package/esm/p384.d.ts +3 -3
  143. package/esm/p521.d.ts +3 -3
  144. package/esm/pasta.d.ts +4 -0
  145. package/esm/pasta.d.ts.map +1 -1
  146. package/esm/pasta.js +4 -0
  147. package/esm/pasta.js.map +1 -1
  148. package/esm/secp256k1.d.ts +6 -6
  149. package/esm/secp256k1.d.ts.map +1 -1
  150. package/esm/secp256k1.js +44 -41
  151. package/esm/secp256k1.js.map +1 -1
  152. package/esm/utils.d.ts +96 -0
  153. package/esm/utils.d.ts.map +1 -0
  154. package/esm/utils.js +279 -0
  155. package/esm/utils.js.map +1 -0
  156. package/jubjub.d.ts +4 -0
  157. package/jubjub.d.ts.map +1 -1
  158. package/jubjub.js +4 -0
  159. package/jubjub.js.map +1 -1
  160. package/misc.d.ts.map +1 -1
  161. package/misc.js +35 -30
  162. package/misc.js.map +1 -1
  163. package/nist.d.ts +8 -16
  164. package/nist.d.ts.map +1 -1
  165. package/nist.js +87 -97
  166. package/nist.js.map +1 -1
  167. package/p256.d.ts +3 -3
  168. package/p384.d.ts +3 -3
  169. package/p521.d.ts +3 -3
  170. package/package.json +26 -8
  171. package/pasta.d.ts +4 -0
  172. package/pasta.d.ts.map +1 -1
  173. package/pasta.js +4 -0
  174. package/pasta.js.map +1 -1
  175. package/secp256k1.d.ts +6 -6
  176. package/secp256k1.d.ts.map +1 -1
  177. package/secp256k1.js +47 -44
  178. package/secp256k1.js.map +1 -1
  179. package/src/_shortw_utils.ts +5 -15
  180. package/src/abstract/bls.ts +260 -145
  181. package/src/abstract/curve.ts +125 -18
  182. package/src/abstract/edwards.ts +282 -127
  183. package/src/abstract/fft.ts +519 -0
  184. package/src/abstract/hash-to-curve.ts +51 -27
  185. package/src/abstract/modular.ts +156 -143
  186. package/src/abstract/montgomery.ts +81 -111
  187. package/src/abstract/poseidon.ts +22 -18
  188. package/src/abstract/tower.ts +37 -68
  189. package/src/abstract/utils.ts +3 -378
  190. package/src/abstract/weierstrass.ts +752 -461
  191. package/src/bls12-381.ts +542 -507
  192. package/src/bn254.ts +47 -35
  193. package/src/ed25519.ts +104 -76
  194. package/src/ed448.ts +156 -105
  195. package/src/jubjub.ts +4 -0
  196. package/src/misc.ts +39 -34
  197. package/src/nist.ts +138 -126
  198. package/src/p256.ts +3 -3
  199. package/src/p384.ts +3 -3
  200. package/src/p521.ts +3 -3
  201. package/src/pasta.ts +5 -1
  202. package/src/secp256k1.ts +59 -47
  203. package/src/utils.ts +328 -0
  204. package/utils.d.ts +96 -0
  205. package/utils.d.ts.map +1 -0
  206. package/utils.js +313 -0
  207. package/utils.js.map +1 -0
@@ -5,29 +5,26 @@
5
5
  * @module
6
6
  */
7
7
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
8
- import { Field, mod } from './modular.ts';
9
8
  import {
9
+ _validateObject,
10
10
  aInRange,
11
11
  bytesToNumberLE,
12
12
  ensureBytes,
13
13
  numberToBytesLE,
14
- validateObject,
15
- } from './utils.ts';
14
+ randomBytes,
15
+ } from '../utils.ts';
16
+ import { mod } from './modular.ts';
16
17
 
17
18
  const _0n = BigInt(0);
18
19
  const _1n = BigInt(1);
20
+ const _2n = BigInt(2);
19
21
  type Hex = string | Uint8Array;
20
22
 
21
23
  export type CurveType = {
22
24
  P: bigint; // finite field prime
23
- nByteLength: number;
24
- adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
25
- domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
26
- a: bigint;
27
- montgomeryBits: number;
28
- powPminus2?: (x: bigint) => bigint;
29
- xyToU?: (x: bigint, y: bigint) => bigint;
30
- Gu: bigint;
25
+ type: 'x25519' | 'x448';
26
+ adjustScalarBytes: (bytes: Uint8Array) => Uint8Array;
27
+ powPminus2: (x: bigint) => bigint;
31
28
  randomBytes?: (bytesLength?: number) => Uint8Array;
32
29
  };
33
30
 
@@ -41,67 +38,88 @@ export type CurveFn = {
41
38
  };
42
39
 
43
40
  function validateOpts(curve: CurveType) {
44
- validateObject(
45
- curve,
46
- {
47
- a: 'bigint',
48
- },
49
- {
50
- montgomeryBits: 'isSafeInteger',
51
- nByteLength: 'isSafeInteger',
52
- adjustScalarBytes: 'function',
53
- domain: 'function',
54
- powPminus2: 'function',
55
- Gu: 'bigint',
56
- }
57
- );
58
- // Set defaults
41
+ _validateObject(curve, {
42
+ adjustScalarBytes: 'function',
43
+ powPminus2: 'function',
44
+ });
59
45
  return Object.freeze({ ...curve } as const);
60
46
  }
61
47
 
62
- // Uses only one coordinate instead of two
63
48
  export function montgomery(curveDef: CurveType): CurveFn {
64
49
  const CURVE = validateOpts(curveDef);
65
- const { P } = CURVE;
66
- const Fp = Field(P);
50
+ const { P, type, adjustScalarBytes, powPminus2, randomBytes: rand } = CURVE;
51
+ const is25519 = type === 'x25519';
52
+ if (!is25519 && type !== 'x448') throw new Error('invalid type');
53
+ const randomBytes_ = rand || randomBytes;
54
+
55
+ const montgomeryBits = is25519 ? 255 : 448;
56
+ const fieldLen = is25519 ? 32 : 56;
57
+ const Gu = is25519 ? BigInt(9) : BigInt(5);
58
+ // RFC 7748 #5:
59
+ // The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519 and
60
+ // (156326 - 2) / 4 = 39081 for curve448/X448
61
+ // const a = is25519 ? 156326n : 486662n;
62
+ const a24 = is25519 ? BigInt(121665) : BigInt(39081);
63
+ // RFC: x25519 "the resulting integer is of the form 2^254 plus
64
+ // eight times a value between 0 and 2^251 - 1 (inclusive)"
65
+ // x448: "2^447 plus four times a value between 0 and 2^445 - 1 (inclusive)"
66
+ const minScalar = is25519 ? _2n ** BigInt(254) : _2n ** BigInt(447);
67
+ const maxAdded = is25519
68
+ ? BigInt(8) * _2n ** BigInt(251) - _1n
69
+ : BigInt(4) * _2n ** BigInt(445) - _1n;
70
+ const maxScalar = minScalar + maxAdded + _1n; // (inclusive)
67
71
  const modP = (n: bigint) => mod(n, P);
68
- const montgomeryBits = CURVE.montgomeryBits;
69
- const montgomeryBytes = Math.ceil(montgomeryBits / 8);
70
- const fieldLen = CURVE.nByteLength;
71
- const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes: Uint8Array) => bytes);
72
- const powPminus2 = CURVE.powPminus2 || ((x: bigint) => Fp.pow(x, P - BigInt(2)));
72
+ const GuBytes = encodeU(Gu);
73
+ function encodeU(u: bigint): Uint8Array {
74
+ return numberToBytesLE(modP(u), fieldLen);
75
+ }
76
+ function decodeU(u: Hex): bigint {
77
+ const _u = ensureBytes('u coordinate', u, fieldLen);
78
+ // RFC: When receiving such an array, implementations of X25519
79
+ // (but not X448) MUST mask the most significant bit in the final byte.
80
+ if (is25519) _u[31] &= 127; // 0b0111_1111
81
+ // RFC: Implementations MUST accept non-canonical values and process them as
82
+ // if they had been reduced modulo the field prime. The non-canonical
83
+ // values are 2^255 - 19 through 2^255 - 1 for X25519 and 2^448 - 2^224
84
+ // - 1 through 2^448 - 1 for X448.
85
+ return modP(bytesToNumberLE(_u));
86
+ }
87
+ function decodeScalar(scalar: Hex): bigint {
88
+ return bytesToNumberLE(adjustScalarBytes(ensureBytes('scalar', scalar, fieldLen)));
89
+ }
90
+ function scalarMult(scalar: Hex, u: Hex): Uint8Array {
91
+ const pu = montgomeryLadder(decodeU(u), decodeScalar(scalar));
92
+ // Some public keys are useless, of low-order. Curve author doesn't think
93
+ // it needs to be validated, but we do it nonetheless.
94
+ // https://cr.yp.to/ecdh.html#validate
95
+ if (pu === _0n) throw new Error('invalid private or public key received');
96
+ return encodeU(pu);
97
+ }
98
+ // Computes public key from private. By doing scalar multiplication of base point.
99
+ function scalarMultBase(scalar: Hex): Uint8Array {
100
+ return scalarMult(scalar, GuBytes);
101
+ }
73
102
 
74
- // cswap from RFC7748. But it is not from RFC7748!
75
- /*
76
- cswap(swap, x_2, x_3):
77
- dummy = mask(swap) AND (x_2 XOR x_3)
78
- x_2 = x_2 XOR dummy
79
- x_3 = x_3 XOR dummy
80
- Return (x_2, x_3)
81
- Where mask(swap) is the all-1 or all-0 word of the same length as x_2
82
- and x_3, computed, e.g., as mask(swap) = 0 - swap.
83
- */
84
- function cswap(swap: bigint, x_2: bigint, x_3: bigint): [bigint, bigint] {
103
+ // cswap from RFC7748 "example code"
104
+ function cswap(swap: bigint, x_2: bigint, x_3: bigint): { x_2: bigint; x_3: bigint } {
105
+ // dummy = mask(swap) AND (x_2 XOR x_3)
106
+ // Where mask(swap) is the all-1 or all-0 word of the same length as x_2
107
+ // and x_3, computed, e.g., as mask(swap) = 0 - swap.
85
108
  const dummy = modP(swap * (x_2 - x_3));
86
- x_2 = modP(x_2 - dummy);
87
- x_3 = modP(x_3 + dummy);
88
- return [x_2, x_3];
109
+ x_2 = modP(x_2 - dummy); // x_2 = x_2 XOR dummy
110
+ x_3 = modP(x_3 + dummy); // x_3 = x_3 XOR dummy
111
+ return { x_2, x_3 };
89
112
  }
90
113
 
91
- // x25519 from 4
92
- // The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
93
- const a24 = (CURVE.a - BigInt(2)) / BigInt(4);
94
114
  /**
95
- *
115
+ * Montgomery x-only multiplication ladder.
96
116
  * @param pointU u coordinate (x) on Montgomery Curve 25519
97
117
  * @param scalar by which the point would be multiplied
98
118
  * @returns new Point on Montgomery curve
99
119
  */
100
120
  function montgomeryLadder(u: bigint, scalar: bigint): bigint {
101
121
  aInRange('u', u, _0n, P);
102
- aInRange('scalar', scalar, _0n, P);
103
- // Section 5: Implementations MUST accept non-canonical values and process them as
104
- // if they had been reduced modulo the field prime.
122
+ aInRange('scalar', scalar, minScalar, maxScalar);
105
123
  const k = scalar;
106
124
  const x_1 = u;
107
125
  let x_2 = _1n;
@@ -109,16 +127,11 @@ export function montgomery(curveDef: CurveType): CurveFn {
109
127
  let x_3 = u;
110
128
  let z_3 = _1n;
111
129
  let swap = _0n;
112
- let sw: [bigint, bigint];
113
130
  for (let t = BigInt(montgomeryBits - 1); t >= _0n; t--) {
114
131
  const k_t = (k >> t) & _1n;
115
132
  swap ^= k_t;
116
- sw = cswap(swap, x_2, x_3);
117
- x_2 = sw[0];
118
- x_3 = sw[1];
119
- sw = cswap(swap, z_2, z_3);
120
- z_2 = sw[0];
121
- z_3 = sw[1];
133
+ ({ x_2, x_3 } = cswap(swap, x_2, x_3));
134
+ ({ x_2: z_2, x_3: z_3 } = cswap(swap, z_2, z_3));
122
135
  swap = k_t;
123
136
 
124
137
  const A = x_2 + z_2;
@@ -137,53 +150,10 @@ export function montgomery(curveDef: CurveType): CurveFn {
137
150
  x_2 = modP(AA * BB);
138
151
  z_2 = modP(E * (AA + modP(a24 * E)));
139
152
  }
140
- // (x_2, x_3) = cswap(swap, x_2, x_3)
141
- sw = cswap(swap, x_2, x_3);
142
- x_2 = sw[0];
143
- x_3 = sw[1];
144
- // (z_2, z_3) = cswap(swap, z_2, z_3)
145
- sw = cswap(swap, z_2, z_3);
146
- z_2 = sw[0];
147
- z_3 = sw[1];
148
- // z_2^(p - 2)
149
- const z2 = powPminus2(z_2);
150
- // Return x_2 * (z_2^(p - 2))
151
- return modP(x_2 * z2);
152
- }
153
-
154
- function encodeUCoordinate(u: bigint): Uint8Array {
155
- return numberToBytesLE(modP(u), montgomeryBytes);
156
- }
157
-
158
- function decodeUCoordinate(uEnc: Hex): bigint {
159
- // Section 5: When receiving such an array, implementations of X25519
160
- // MUST mask the most significant bit in the final byte.
161
- const u = ensureBytes('u coordinate', uEnc, montgomeryBytes);
162
- if (fieldLen === 32) u[31] &= 127; // 0b0111_1111
163
- return bytesToNumberLE(u);
164
- }
165
- function decodeScalar(n: Hex): bigint {
166
- const bytes = ensureBytes('scalar', n);
167
- const len = bytes.length;
168
- if (len !== montgomeryBytes && len !== fieldLen) {
169
- let valid = '' + montgomeryBytes + ' or ' + fieldLen;
170
- throw new Error('invalid scalar, expected ' + valid + ' bytes, got ' + len);
171
- }
172
- return bytesToNumberLE(adjustScalarBytes(bytes));
173
- }
174
- function scalarMult(scalar: Hex, u: Hex): Uint8Array {
175
- const pointU = decodeUCoordinate(u);
176
- const _scalar = decodeScalar(scalar);
177
- const pu = montgomeryLadder(pointU, _scalar);
178
- // The result was not contributory
179
- // https://cr.yp.to/ecdh.html#validate
180
- if (pu === _0n) throw new Error('invalid private or public key received');
181
- return encodeUCoordinate(pu);
182
- }
183
- // Computes public key from private. By doing scalar multiplication of base point.
184
- const GuBytes = encodeUCoordinate(CURVE.Gu);
185
- function scalarMultBase(scalar: Hex): Uint8Array {
186
- return scalarMult(scalar, GuBytes);
153
+ ({ x_2, x_3 } = cswap(swap, x_2, x_3));
154
+ ({ x_2: z_2, x_3: z_3 } = cswap(swap, z_2, z_3));
155
+ const z2 = powPminus2(z_2); // `Fp.pow(x, P - _2n)` is much slower equivalent
156
+ return modP(x_2 * z2); // Return x_2 * (z_2^(p - 2))
187
157
  }
188
158
 
189
159
  return {
@@ -191,7 +161,7 @@ export function montgomery(curveDef: CurveType): CurveFn {
191
161
  scalarMultBase,
192
162
  getSharedSecret: (privateKey: Hex, publicKey: Hex) => scalarMult(privateKey, publicKey),
193
163
  getPublicKey: (privateKey: Hex): Uint8Array => scalarMultBase(privateKey),
194
- utils: { randomPrivateKey: () => CURVE.randomBytes!(CURVE.nByteLength) },
195
- GuBytes: GuBytes,
164
+ utils: { randomPrivateKey: () => randomBytes_(fieldLen) },
165
+ GuBytes: GuBytes.slice(),
196
166
  };
197
167
  }
@@ -7,8 +7,8 @@
7
7
  * @module
8
8
  */
9
9
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
10
+ import { _validateObject, bitGet } from '../utils.ts';
10
11
  import { FpInvertBatch, FpPow, type IField, validateField } from './modular.ts';
11
- import { bitGet } from './utils.ts';
12
12
 
13
13
  // Grain LFSR (Linear-Feedback Shift Register): https://eprint.iacr.org/2009/109.pdf
14
14
  function grainLFSR(state: number[]): () => boolean {
@@ -41,20 +41,28 @@ export type PoseidonBasicOpts = {
41
41
  isSboxInverse?: boolean;
42
42
  };
43
43
 
44
- function validateBasicOpts(opts: PoseidonBasicOpts) {
44
+ function assertValidPosOpts(opts: PoseidonBasicOpts) {
45
45
  const { Fp, roundsFull } = opts;
46
46
  validateField(Fp);
47
+ _validateObject(
48
+ opts,
49
+ {
50
+ t: 'number',
51
+ roundsFull: 'number',
52
+ roundsPartial: 'number',
53
+ },
54
+ {
55
+ isSboxInverse: 'boolean',
56
+ }
57
+ );
47
58
  for (const i of ['t', 'roundsFull', 'roundsPartial'] as const) {
48
- if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
49
- throw new Error('invalid number ' + i);
59
+ if (!Number.isSafeInteger(opts[i]) || opts[i] < 1) throw new Error('invalid number ' + i);
50
60
  }
51
- if (opts.isSboxInverse !== undefined && typeof opts.isSboxInverse !== 'boolean')
52
- throw new Error(`Poseidon: invalid param isSboxInverse=${opts.isSboxInverse}`);
53
61
  if (roundsFull & 1) throw new Error('roundsFull is not even' + roundsFull);
54
62
  }
55
63
 
56
64
  function poseidonGrain(opts: PoseidonBasicOpts) {
57
- validateBasicOpts(opts);
65
+ assertValidPosOpts(opts);
58
66
  const { Fp } = opts;
59
67
  const state = Array(80).fill(1);
60
68
  let pos = 0;
@@ -140,7 +148,7 @@ export function validateOpts(opts: PoseidonOpts): Readonly<{
140
148
  sboxPower?: number;
141
149
  reversePartialPowIdx?: boolean; // Hack for stark
142
150
  }> {
143
- validateBasicOpts(opts);
151
+ assertValidPosOpts(opts);
144
152
  const { Fp, mds, reversePartialPowIdx: rev, roundConstants: rc } = opts;
145
153
  const { roundsFull, roundsPartial, sboxPower, t } = opts;
146
154
 
@@ -196,12 +204,13 @@ export function splitConstants(rc: bigint[], t: number): bigint[][] {
196
204
  return res;
197
205
  }
198
206
 
199
- /** Poseidon NTT-friendly hash. */
200
- export function poseidon(opts: PoseidonOpts): {
207
+ export type PoseidonFn = {
201
208
  (values: bigint[]): bigint[];
202
209
  // For verification in tests
203
210
  roundConstants: bigint[][];
204
- } {
211
+ };
212
+ /** Poseidon NTT-friendly hash. */
213
+ export function poseidon(opts: PoseidonOpts): PoseidonFn {
205
214
  const _opts = validateOpts(opts);
206
215
  const { Fp, mds, roundConstants, rounds: totalRounds, roundsPartial, sboxFn, t } = _opts;
207
216
  const halfRoundsFull = _opts.roundsFull / 2;
@@ -242,17 +251,12 @@ export class PoseidonSponge {
242
251
  private Fp: IField<bigint>;
243
252
  readonly rate: number;
244
253
  readonly capacity: number;
245
- readonly hash: ReturnType<typeof poseidon>;
254
+ readonly hash: PoseidonFn;
246
255
  private state: bigint[]; // [...capacity, ...rate]
247
256
  private pos = 0;
248
257
  private isAbsorbing = true;
249
258
 
250
- constructor(
251
- Fp: IField<bigint>,
252
- rate: number,
253
- capacity: number,
254
- hash: ReturnType<typeof poseidon>
255
- ) {
259
+ constructor(Fp: IField<bigint>, rate: number, capacity: number, hash: PoseidonFn) {
256
260
  this.Fp = Fp;
257
261
  this.hash = hash;
258
262
  this.rate = rate;
@@ -10,8 +10,8 @@
10
10
  * @module
11
11
  */
12
12
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
13
+ import { bitLen, bitMask, concatBytes, notImplemented } from '../utils.ts';
13
14
  import * as mod from './modular.ts';
14
- import { bitLen, bitMask, concatBytes, notImplemented } from './utils.ts';
15
15
  import type { ProjConstructor, ProjPointType } from './weierstrass.ts';
16
16
 
17
17
  // Be friendly to bad ECMAScript parsers by not using bigint literals
@@ -34,19 +34,33 @@ export type BigintTwelve = [
34
34
  ];
35
35
 
36
36
  export type Fp2Bls = mod.IField<Fp2> & {
37
- reim: (num: Fp2) => { re: Fp; im: Fp };
38
- mulByB: (num: Fp2) => Fp2;
39
37
  frobeniusMap(num: Fp2, power: number): Fp2;
40
38
  fromBigTuple(num: [bigint, bigint]): Fp2;
39
+ mulByB: (num: Fp2) => Fp2;
40
+ mulByNonresidue: (num: Fp2) => Fp2;
41
+ reim: (num: Fp2) => { re: Fp; im: Fp };
42
+ NONRESIDUE: Fp2;
43
+ };
44
+
45
+ export type Fp6Bls = mod.IField<Fp6> & {
46
+ frobeniusMap(num: Fp6, power: number): Fp6;
47
+ fromBigSix: (tuple: BigintSix) => Fp6;
48
+ mul1(num: Fp6, b1: Fp2): Fp6;
49
+ mul01(num: Fp6, b0: Fp2, b1: Fp2): Fp6;
50
+ mulByFp2(lhs: Fp6, rhs: Fp2): Fp6;
51
+ mulByNonresidue: (num: Fp6) => Fp6;
41
52
  };
42
53
 
43
54
  export type Fp12Bls = mod.IField<Fp12> & {
44
55
  frobeniusMap(num: Fp12, power: number): Fp12;
56
+ fromBigTwelve: (t: BigintTwelve) => Fp12;
45
57
  mul014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
46
58
  mul034(num: Fp12, o0: Fp2, o3: Fp2, o4: Fp2): Fp12;
59
+ mulByFp2(lhs: Fp12, rhs: Fp2): Fp12;
47
60
  conjugate(num: Fp12): Fp12;
48
61
  finalExponentiate(num: Fp12): Fp12;
49
- fromBigTwelve(num: BigintTwelve): Fp12;
62
+ _cyclotomicSquare(num: Fp12): Fp12;
63
+ _cyclotomicExp(num: Fp12, n: bigint): Fp12;
50
64
  };
51
65
 
52
66
  function calcFrobeniusCoefficients<T>(
@@ -88,7 +102,7 @@ export function psiFrobenius(
88
102
  PSI2_X: Fp2;
89
103
  PSI2_Y: Fp2;
90
104
  } {
91
- // Ψ endomorphism
105
+ // GLV endomorphism Ψ(P)
92
106
  const PSI_X = Fp2.pow(base, (Fp.ORDER - _1n) / _3n); // u^((p-1)/3)
93
107
  const PSI_Y = Fp2.pow(base, (Fp.ORDER - _1n) / _2n); // u^((p-1)/2)
94
108
  function psi(x: Fp2, y: Fp2): [Fp2, Fp2] {
@@ -134,34 +148,10 @@ export type Tower12Opts = {
134
148
 
135
149
  export function tower12(opts: Tower12Opts): {
136
150
  Fp: Readonly<mod.IField<bigint> & Required<Pick<mod.IField<bigint>, 'isOdd'>>>;
137
- Fp2: mod.IField<Fp2> & {
138
- NONRESIDUE: Fp2;
139
- fromBigTuple: (tuple: BigintTuple | bigint[]) => Fp2;
140
- reim: (num: Fp2) => { re: bigint; im: bigint };
141
- mulByNonresidue: (num: Fp2) => Fp2;
142
- mulByB: (num: Fp2) => Fp2;
143
- frobeniusMap(num: Fp2, power: number): Fp2;
144
- };
145
- Fp6: mod.IField<Fp6> & {
146
- fromBigSix: (tuple: BigintSix) => Fp6;
147
- mulByNonresidue: (num: Fp6) => Fp6;
148
- frobeniusMap(num: Fp6, power: number): Fp6;
149
- mul1(num: Fp6, b1: Fp2): Fp6;
150
- mul01(num: Fp6, b0: Fp2, b1: Fp2): Fp6;
151
- mulByFp2(lhs: Fp6, rhs: Fp2): Fp6;
152
- };
151
+ Fp2: Fp2Bls;
152
+ Fp6: Fp6Bls;
153
+ Fp12: Fp12Bls;
153
154
  Fp4Square: (a: Fp2, b: Fp2) => { first: Fp2; second: Fp2 };
154
- Fp12: mod.IField<Fp12> & {
155
- fromBigTwelve: (t: BigintTwelve) => Fp12;
156
- frobeniusMap(num: Fp12, power: number): Fp12;
157
- mul014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
158
- mul034(num: Fp12, o0: Fp2, o3: Fp2, o4: Fp2): Fp12;
159
- mulByFp2(lhs: Fp12, rhs: Fp2): Fp12;
160
- conjugate(num: Fp12): Fp12;
161
- finalExponentiate(num: Fp12): Fp12;
162
- _cyclotomicSquare(num: Fp12): Fp12;
163
- _cyclotomicExp(num: Fp12, n: bigint): Fp12;
164
- };
165
155
  } {
166
156
  const { ORDER } = opts;
167
157
  // Fp
@@ -196,23 +186,19 @@ export function tower12(opts: Tower12Opts): {
196
186
  const c = Fp.add(c0, c0);
197
187
  return { c0: Fp.mul(a, b), c1: Fp.mul(c, c1) };
198
188
  };
199
- type Fp2Utils = {
200
- NONRESIDUE: Fp2;
201
- fromBigTuple: (tuple: BigintTuple | bigint[]) => Fp2;
202
- reim: (num: Fp2) => { re: bigint; im: bigint };
203
- mulByNonresidue: (num: Fp2) => Fp2;
204
- mulByB: (num: Fp2) => Fp2;
205
- frobeniusMap(num: Fp2, power: number): Fp2;
206
- };
207
189
  const Fp2fromBigTuple = (tuple: BigintTuple | bigint[]) => {
208
190
  if (tuple.length !== 2) throw new Error('invalid tuple');
209
191
  const fps = tuple.map((n) => Fp.create(n)) as [Fp, Fp];
210
192
  return { c0: fps[0], c1: fps[1] };
211
193
  };
212
194
 
195
+ function isValidC(num: bigint, ORDER: bigint) {
196
+ return typeof num === 'bigint' && _0n <= num && num < ORDER;
197
+ }
198
+
213
199
  const FP2_ORDER = ORDER * ORDER;
214
200
  const Fp2Nonresidue = Fp2fromBigTuple(opts.FP2_NONRESIDUE);
215
- const Fp2: mod.IField<Fp2> & Fp2Utils = {
201
+ const Fp2: Fp2Bls = {
216
202
  ORDER: FP2_ORDER,
217
203
  isLE: Fp.isLE,
218
204
  NONRESIDUE: Fp2Nonresidue,
@@ -222,8 +208,9 @@ export function tower12(opts: Tower12Opts): {
222
208
  ZERO: { c0: Fp.ZERO, c1: Fp.ZERO },
223
209
  ONE: { c0: Fp.ONE, c1: Fp.ZERO },
224
210
  create: (num) => num,
225
- isValid: ({ c0, c1 }) => typeof c0 === 'bigint' && typeof c1 === 'bigint',
211
+ isValid: ({ c0, c1 }) => isValidC(c0, FP2_ORDER) && isValidC(c1, FP2_ORDER),
226
212
  is0: ({ c0, c1 }) => Fp.is0(c0) && Fp.is0(c1),
213
+ isValidNot0: (num) => !Fp2.is0(num) && Fp2.isValid(num),
227
214
  eql: ({ c0, c1 }: Fp2, { c0: r0, c1: r1 }: Fp2) => Fp.eql(c0, r0) && Fp.eql(c1, r1),
228
215
  neg: ({ c0, c1 }) => ({ c0: Fp.neg(c0), c1: Fp.neg(c1) }),
229
216
  pow: (num, power) => mod.FpPow(Fp2, num, power),
@@ -361,15 +348,6 @@ export function tower12(opts: Tower12Opts): {
361
348
  c2: Fp2.sub(Fp2.sub(Fp2.add(Fp2.add(t1, Fp2.sqr(Fp2.add(Fp2.sub(c0, c1), c2))), t3), t0), t4),
362
349
  };
363
350
  };
364
- type Fp6Utils = {
365
- fromBigSix: (tuple: BigintSix) => Fp6;
366
- mulByNonresidue: (num: Fp6) => Fp6;
367
- frobeniusMap(num: Fp6, power: number): Fp6;
368
- mul1(num: Fp6, b1: Fp2): Fp6;
369
- mul01(num: Fp6, b0: Fp2, b1: Fp2): Fp6;
370
- mulByFp2(lhs: Fp6, rhs: Fp2): Fp6;
371
- };
372
-
373
351
  const [FP6_FROBENIUS_COEFFICIENTS_1, FP6_FROBENIUS_COEFFICIENTS_2] = calcFrobeniusCoefficients(
374
352
  Fp2,
375
353
  Fp2Nonresidue,
@@ -379,7 +357,7 @@ export function tower12(opts: Tower12Opts): {
379
357
  3
380
358
  );
381
359
 
382
- const Fp6: mod.IField<Fp6> & Fp6Utils = {
360
+ const Fp6: Fp6Bls = {
383
361
  ORDER: Fp2.ORDER, // TODO: unused, but need to verify
384
362
  isLE: Fp2.isLE,
385
363
  BITS: 3 * Fp2.BITS,
@@ -390,6 +368,7 @@ export function tower12(opts: Tower12Opts): {
390
368
  create: (num) => num,
391
369
  isValid: ({ c0, c1, c2 }) => Fp2.isValid(c0) && Fp2.isValid(c1) && Fp2.isValid(c2),
392
370
  is0: ({ c0, c1, c2 }) => Fp2.is0(c0) && Fp2.is0(c1) && Fp2.is0(c2),
371
+ isValidNot0: (num) => !Fp6.is0(num) && Fp6.isValid(num),
393
372
  neg: ({ c0, c1, c2 }) => ({ c0: Fp2.neg(c0), c1: Fp2.neg(c1), c2: Fp2.neg(c2) }),
394
373
  eql: ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) =>
395
374
  Fp2.eql(c0, r0) && Fp2.eql(c1, r1) && Fp2.eql(c2, r2),
@@ -439,9 +418,9 @@ export function tower12(opts: Tower12Opts): {
439
418
  fromBigSix: (t: BigintSix): Fp6 => {
440
419
  if (!Array.isArray(t) || t.length !== 6) throw new Error('invalid Fp6 usage');
441
420
  return {
442
- c0: Fp2.fromBigTuple(t.slice(0, 2)),
443
- c1: Fp2.fromBigTuple(t.slice(2, 4)),
444
- c2: Fp2.fromBigTuple(t.slice(4, 6)),
421
+ c0: Fp2.fromBigTuple(t.slice(0, 2) as BigintTuple),
422
+ c1: Fp2.fromBigTuple(t.slice(2, 4) as BigintTuple),
423
+ c2: Fp2.fromBigTuple(t.slice(4, 6) as BigintTuple),
445
424
  };
446
425
  },
447
426
  frobeniusMap: ({ c0, c1, c2 }, power: number) => ({
@@ -524,19 +503,8 @@ export function tower12(opts: Tower12Opts): {
524
503
  second: Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(a, b)), a2), b2), // (a + b)² - a² - b²
525
504
  };
526
505
  }
527
- type Fp12Utils = {
528
- fromBigTwelve: (t: BigintTwelve) => Fp12;
529
- frobeniusMap(num: Fp12, power: number): Fp12;
530
- mul014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
531
- mul034(num: Fp12, o0: Fp2, o3: Fp2, o4: Fp2): Fp12;
532
- mulByFp2(lhs: Fp12, rhs: Fp2): Fp12;
533
- conjugate(num: Fp12): Fp12;
534
- finalExponentiate(num: Fp12): Fp12;
535
- _cyclotomicSquare(num: Fp12): Fp12;
536
- _cyclotomicExp(num: Fp12, n: bigint): Fp12;
537
- };
538
506
 
539
- const Fp12: mod.IField<Fp12> & Fp12Utils = {
507
+ const Fp12: Fp12Bls = {
540
508
  ORDER: Fp2.ORDER, // TODO: unused, but need to verify
541
509
  isLE: Fp6.isLE,
542
510
  BITS: 2 * Fp6.BITS,
@@ -547,6 +515,7 @@ export function tower12(opts: Tower12Opts): {
547
515
  create: (num) => num,
548
516
  isValid: ({ c0, c1 }) => Fp6.isValid(c0) && Fp6.isValid(c1),
549
517
  is0: ({ c0, c1 }) => Fp6.is0(c0) && Fp6.is0(c1),
518
+ isValidNot0: (num) => !Fp12.is0(num) && Fp12.isValid(num),
550
519
  neg: ({ c0, c1 }) => ({ c0: Fp6.neg(c0), c1: Fp6.neg(c1) }),
551
520
  eql: ({ c0, c1 }, { c0: r0, c1: r1 }) => Fp6.eql(c0, r0) && Fp6.eql(c1, r1),
552
521
  sqrt: notImplemented,
@@ -646,5 +615,5 @@ export function tower12(opts: Tower12Opts): {
646
615
  finalExponentiate: opts.Fp12finalExponentiate,
647
616
  };
648
617
 
649
- return { Fp, Fp2, Fp6, Fp4Square, Fp12 };
618
+ return { Fp, Fp2, Fp6, Fp12, Fp4Square };
650
619
  }