@noble/curves 0.6.3 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/README.md +429 -281
  2. package/{lib/_shortw_utils.d.ts → _shortw_utils.d.ts} +3 -1
  3. package/_shortw_utils.d.ts.map +1 -0
  4. package/{lib/_shortw_utils.js → _shortw_utils.js} +2 -0
  5. package/_shortw_utils.js.map +1 -0
  6. package/{lib/abstract → abstract}/bls.d.ts +4 -9
  7. package/abstract/bls.d.ts.map +1 -0
  8. package/{lib/abstract → abstract}/bls.js +12 -25
  9. package/abstract/bls.js.map +1 -0
  10. package/{lib/abstract → abstract}/curve.d.ts +1 -0
  11. package/abstract/curve.d.ts.map +1 -0
  12. package/{lib/abstract → abstract}/curve.js +1 -0
  13. package/abstract/curve.js.map +1 -0
  14. package/{lib/abstract → abstract}/edwards.d.ts +1 -0
  15. package/abstract/edwards.d.ts.map +1 -0
  16. package/{lib/abstract → abstract}/edwards.js +9 -15
  17. package/abstract/edwards.js.map +1 -0
  18. package/{lib/abstract → abstract}/hash-to-curve.d.ts +5 -5
  19. package/abstract/hash-to-curve.d.ts.map +1 -0
  20. package/{lib/abstract → abstract}/hash-to-curve.js +41 -38
  21. package/abstract/hash-to-curve.js.map +1 -0
  22. package/{lib/abstract → abstract}/modular.d.ts +1 -0
  23. package/abstract/modular.d.ts.map +1 -0
  24. package/{lib/abstract → abstract}/modular.js +2 -1
  25. package/abstract/modular.js.map +1 -0
  26. package/{lib/abstract → abstract}/montgomery.d.ts +1 -0
  27. package/abstract/montgomery.d.ts.map +1 -0
  28. package/{lib/abstract → abstract}/montgomery.js +3 -2
  29. package/abstract/montgomery.js.map +1 -0
  30. package/{lib/abstract → abstract}/poseidon.d.ts +1 -0
  31. package/abstract/poseidon.d.ts.map +1 -0
  32. package/{lib/abstract → abstract}/poseidon.js +1 -0
  33. package/abstract/poseidon.js.map +1 -0
  34. package/{lib/abstract → abstract}/utils.d.ts +12 -1
  35. package/abstract/utils.d.ts.map +1 -0
  36. package/{lib/abstract → abstract}/utils.js +96 -10
  37. package/abstract/utils.js.map +1 -0
  38. package/{lib/abstract → abstract}/weierstrass.d.ts +6 -4
  39. package/abstract/weierstrass.d.ts.map +1 -0
  40. package/{lib/abstract → abstract}/weierstrass.js +55 -93
  41. package/abstract/weierstrass.js.map +1 -0
  42. package/{lib/bls12-381.d.ts → bls12-381.d.ts} +1 -0
  43. package/bls12-381.d.ts.map +1 -0
  44. package/{lib/bls12-381.js → bls12-381.js} +41 -7
  45. package/bls12-381.js.map +1 -0
  46. package/{lib/bn.d.ts → bn.d.ts} +1 -0
  47. package/bn.d.ts.map +1 -0
  48. package/{lib/bn.js → bn.js} +1 -0
  49. package/bn.js.map +1 -0
  50. package/{lib/ed25519.d.ts → ed25519.d.ts} +2 -1
  51. package/ed25519.d.ts.map +1 -0
  52. package/{lib/ed25519.js → ed25519.js} +4 -3
  53. package/ed25519.js.map +1 -0
  54. package/{lib/ed448.d.ts → ed448.d.ts} +2 -1
  55. package/ed448.d.ts.map +1 -0
  56. package/{lib/ed448.js → ed448.js} +2 -1
  57. package/ed448.js.map +1 -0
  58. package/{lib/esm → esm}/_shortw_utils.js +2 -0
  59. package/esm/_shortw_utils.js.map +1 -0
  60. package/{lib/esm → esm}/abstract/bls.js +13 -26
  61. package/esm/abstract/bls.js.map +1 -0
  62. package/{lib/esm → esm}/abstract/curve.js +1 -0
  63. package/esm/abstract/curve.js.map +1 -0
  64. package/{lib/esm → esm}/abstract/edwards.js +9 -15
  65. package/esm/abstract/edwards.js.map +1 -0
  66. package/{lib/esm → esm}/abstract/hash-to-curve.js +40 -36
  67. package/esm/abstract/hash-to-curve.js.map +1 -0
  68. package/{lib/esm → esm}/abstract/modular.js +2 -1
  69. package/esm/abstract/modular.js.map +1 -0
  70. package/{lib/esm → esm}/abstract/montgomery.js +3 -2
  71. package/esm/abstract/montgomery.js.map +1 -0
  72. package/{lib/esm → esm}/abstract/poseidon.js +1 -0
  73. package/esm/abstract/poseidon.js.map +1 -0
  74. package/{lib/esm → esm}/abstract/utils.js +93 -9
  75. package/esm/abstract/utils.js.map +1 -0
  76. package/{lib/esm → esm}/abstract/weierstrass.js +55 -93
  77. package/esm/abstract/weierstrass.js.map +1 -0
  78. package/{lib/esm → esm}/bls12-381.js +41 -7
  79. package/esm/bls12-381.js.map +1 -0
  80. package/{lib/esm → esm}/bn.js +1 -0
  81. package/esm/bn.js.map +1 -0
  82. package/{lib/esm → esm}/ed25519.js +5 -4
  83. package/esm/ed25519.js.map +1 -0
  84. package/{lib/esm → esm}/ed448.js +2 -1
  85. package/esm/ed448.js.map +1 -0
  86. package/{lib → esm}/index.js +1 -0
  87. package/esm/index.js.map +1 -0
  88. package/{lib/esm → esm}/jubjub.js +1 -0
  89. package/esm/jubjub.js.map +1 -0
  90. package/{lib/esm → esm}/p192.js +1 -0
  91. package/esm/p192.js.map +1 -0
  92. package/{lib/esm → esm}/p224.js +1 -0
  93. package/esm/p224.js.map +1 -0
  94. package/{lib/esm → esm}/p256.js +2 -1
  95. package/esm/p256.js.map +1 -0
  96. package/{lib/esm → esm}/p384.js +2 -1
  97. package/esm/p384.js.map +1 -0
  98. package/{lib/esm → esm}/p521.js +2 -1
  99. package/esm/p521.js.map +1 -0
  100. package/{lib/esm → esm}/package.json +0 -0
  101. package/{lib/esm → esm}/pasta.js +1 -0
  102. package/esm/pasta.js.map +1 -0
  103. package/{lib/esm → esm}/secp256k1.js +41 -50
  104. package/esm/secp256k1.js.map +1 -0
  105. package/{lib/esm → esm}/stark.js +1 -0
  106. package/esm/stark.js.map +1 -0
  107. package/index.d.ts +1 -0
  108. package/index.d.ts.map +1 -0
  109. package/index.js +3 -0
  110. package/index.js.map +1 -0
  111. package/{lib/jubjub.d.ts → jubjub.d.ts} +1 -0
  112. package/jubjub.d.ts.map +1 -0
  113. package/{lib/jubjub.js → jubjub.js} +1 -0
  114. package/jubjub.js.map +1 -0
  115. package/{lib/p192.d.ts → p192.d.ts} +5 -2
  116. package/p192.d.ts.map +1 -0
  117. package/{lib/p192.js → p192.js} +1 -0
  118. package/p192.js.map +1 -0
  119. package/{lib/p224.d.ts → p224.d.ts} +5 -2
  120. package/p224.d.ts.map +1 -0
  121. package/{lib/p224.js → p224.js} +1 -0
  122. package/p224.js.map +1 -0
  123. package/{lib/p256.d.ts → p256.d.ts} +6 -3
  124. package/p256.d.ts.map +1 -0
  125. package/{lib/p256.js → p256.js} +2 -1
  126. package/p256.js.map +1 -0
  127. package/{lib/p384.d.ts → p384.d.ts} +6 -3
  128. package/p384.d.ts.map +1 -0
  129. package/{lib/p384.js → p384.js} +2 -1
  130. package/p384.js.map +1 -0
  131. package/{lib/p521.d.ts → p521.d.ts} +6 -3
  132. package/p521.d.ts.map +1 -0
  133. package/{lib/p521.js → p521.js} +2 -1
  134. package/p521.js.map +1 -0
  135. package/package.json +84 -79
  136. package/{lib/pasta.d.ts → pasta.d.ts} +1 -0
  137. package/pasta.d.ts.map +1 -0
  138. package/{lib/pasta.js → pasta.js} +1 -0
  139. package/pasta.js.map +1 -0
  140. package/{lib/secp256k1.d.ts → secp256k1.d.ts} +17 -6
  141. package/secp256k1.d.ts.map +1 -0
  142. package/{lib/secp256k1.js → secp256k1.js} +38 -47
  143. package/secp256k1.js.map +1 -0
  144. package/src/_shortw_utils.ts +20 -0
  145. package/src/abstract/bls.ts +376 -0
  146. package/src/abstract/curve.ts +199 -0
  147. package/src/abstract/edwards.ts +479 -0
  148. package/src/abstract/hash-to-curve.ts +220 -0
  149. package/src/abstract/modular.ts +417 -0
  150. package/src/abstract/montgomery.ts +184 -0
  151. package/src/abstract/poseidon.ts +119 -0
  152. package/src/abstract/utils.ts +246 -0
  153. package/src/abstract/weierstrass.ts +1175 -0
  154. package/src/bls12-381.ts +1274 -0
  155. package/src/bn.ts +21 -0
  156. package/src/ed25519.ts +428 -0
  157. package/src/ed448.ts +241 -0
  158. package/{lib/esm/index.js → src/index.ts} +0 -1
  159. package/src/jubjub.ts +58 -0
  160. package/src/p192.ts +25 -0
  161. package/src/p224.ts +25 -0
  162. package/src/p256.ts +53 -0
  163. package/src/p384.ts +57 -0
  164. package/src/p521.ts +57 -0
  165. package/src/pasta.ts +31 -0
  166. package/src/secp256k1.ts +260 -0
  167. package/src/stark.ts +356 -0
  168. package/{lib/stark.d.ts → stark.d.ts} +3 -1
  169. package/stark.d.ts.map +1 -0
  170. package/{lib/stark.js → stark.js} +1 -0
  171. package/stark.js.map +1 -0
  172. package/lib/index.d.ts +0 -0
@@ -0,0 +1,220 @@
1
+ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
+ import type { Group, GroupConstructor, AffinePoint } from './curve.js';
3
+ import { mod, Field } from './modular.js';
4
+ import { CHash, concatBytes, utf8ToBytes, validateObject } from './utils.js';
5
+
6
+ export type Opts = {
7
+ DST: string; // DST: a domain separation tag, defined in section 2.2.5
8
+ encodeDST: string;
9
+ p: bigint; // characteristic of F, where F is a finite field of characteristic p and order q = p^m
10
+ m: number; // extension degree of F, m >= 1
11
+ k: number; // k: the target security level for the suite in bits, defined in section 5.1
12
+ expand?: 'xmd' | 'xof'; // use a message that has already been processed by expand_message_xmd
13
+ // Hash functions for: expand_message_xmd is appropriate for use with a
14
+ // wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
15
+ // BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
16
+ // TODO: verify that hash is shake if expand==='xof' via types
17
+ hash: CHash;
18
+ };
19
+
20
+ // Octet Stream to Integer (bytesToNumberBE)
21
+ function os2ip(bytes: Uint8Array): bigint {
22
+ let result = 0n;
23
+ for (let i = 0; i < bytes.length; i++) {
24
+ result <<= 8n;
25
+ result += BigInt(bytes[i]);
26
+ }
27
+ return result;
28
+ }
29
+
30
+ // Integer to Octet Stream
31
+ function i2osp(value: number, length: number): Uint8Array {
32
+ if (value < 0 || value >= 1 << (8 * length)) {
33
+ throw new Error(`bad I2OSP call: value=${value} length=${length}`);
34
+ }
35
+ const res = Array.from({ length }).fill(0) as number[];
36
+ for (let i = length - 1; i >= 0; i--) {
37
+ res[i] = value & 0xff;
38
+ value >>>= 8;
39
+ }
40
+ return new Uint8Array(res);
41
+ }
42
+
43
+ function strxor(a: Uint8Array, b: Uint8Array): Uint8Array {
44
+ const arr = new Uint8Array(a.length);
45
+ for (let i = 0; i < a.length; i++) {
46
+ arr[i] = a[i] ^ b[i];
47
+ }
48
+ return arr;
49
+ }
50
+
51
+ function isBytes(item: unknown): void {
52
+ if (!(item instanceof Uint8Array)) throw new Error('Uint8Array expected');
53
+ }
54
+ function isNum(item: unknown): void {
55
+ if (!Number.isSafeInteger(item)) throw new Error('number expected');
56
+ }
57
+
58
+ // Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits
59
+ // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1
60
+ export function expand_message_xmd(
61
+ msg: Uint8Array,
62
+ DST: Uint8Array,
63
+ lenInBytes: number,
64
+ H: CHash
65
+ ): Uint8Array {
66
+ isBytes(msg);
67
+ isBytes(DST);
68
+ isNum(lenInBytes);
69
+ // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
70
+ if (DST.length > 255) DST = H(concatBytes(utf8ToBytes('H2C-OVERSIZE-DST-'), DST));
71
+ const b_in_bytes = H.outputLen;
72
+ const r_in_bytes = H.blockLen;
73
+ const ell = Math.ceil(lenInBytes / b_in_bytes);
74
+ if (ell > 255) throw new Error('Invalid xmd length');
75
+ const DST_prime = concatBytes(DST, i2osp(DST.length, 1));
76
+ const Z_pad = i2osp(0, r_in_bytes);
77
+ const l_i_b_str = i2osp(lenInBytes, 2);
78
+ const b = new Array<Uint8Array>(ell);
79
+ const b_0 = H(concatBytes(Z_pad, msg, l_i_b_str, i2osp(0, 1), DST_prime));
80
+ b[0] = H(concatBytes(b_0, i2osp(1, 1), DST_prime));
81
+ for (let i = 1; i <= ell; i++) {
82
+ const args = [strxor(b_0, b[i - 1]), i2osp(i + 1, 1), DST_prime];
83
+ b[i] = H(concatBytes(...args));
84
+ }
85
+ const pseudo_random_bytes = concatBytes(...b);
86
+ return pseudo_random_bytes.slice(0, lenInBytes);
87
+ }
88
+
89
+ export function expand_message_xof(
90
+ msg: Uint8Array,
91
+ DST: Uint8Array,
92
+ lenInBytes: number,
93
+ k: number,
94
+ H: CHash
95
+ ): Uint8Array {
96
+ isBytes(msg);
97
+ isBytes(DST);
98
+ isNum(lenInBytes);
99
+ // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
100
+ // DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
101
+ if (DST.length > 255) {
102
+ const dkLen = Math.ceil((2 * k) / 8);
103
+ DST = H.create({ dkLen }).update(utf8ToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
104
+ }
105
+ if (lenInBytes > 65535 || DST.length > 255)
106
+ throw new Error('expand_message_xof: invalid lenInBytes');
107
+ return (
108
+ H.create({ dkLen: lenInBytes })
109
+ .update(msg)
110
+ .update(i2osp(lenInBytes, 2))
111
+ // 2. DST_prime = DST || I2OSP(len(DST), 1)
112
+ .update(DST)
113
+ .update(i2osp(DST.length, 1))
114
+ .digest()
115
+ );
116
+ }
117
+
118
+ /**
119
+ * Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
120
+ * https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
121
+ * @param msg a byte string containing the message to hash
122
+ * @param count the number of elements of F to output
123
+ * @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
124
+ * @returns [u_0, ..., u_(count - 1)], a list of field elements.
125
+ */
126
+ export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][] {
127
+ const { p, k, m, hash, expand, DST: _DST } = options;
128
+ isBytes(msg);
129
+ isNum(count);
130
+ if (typeof _DST !== 'string') throw new Error('DST must be valid');
131
+ const log2p = p.toString(2).length;
132
+ const L = Math.ceil((log2p + k) / 8); // section 5.1 of ietf draft link above
133
+ const len_in_bytes = count * m * L;
134
+ const DST = utf8ToBytes(_DST);
135
+ const pseudo_random_bytes =
136
+ expand === 'xmd'
137
+ ? expand_message_xmd(msg, DST, len_in_bytes, hash)
138
+ : expand === 'xof'
139
+ ? expand_message_xof(msg, DST, len_in_bytes, k, hash)
140
+ : msg;
141
+ const u = new Array(count);
142
+ for (let i = 0; i < count; i++) {
143
+ const e = new Array(m);
144
+ for (let j = 0; j < m; j++) {
145
+ const elm_offset = L * (j + i * m);
146
+ const tv = pseudo_random_bytes.subarray(elm_offset, elm_offset + L);
147
+ e[j] = mod(os2ip(tv), p);
148
+ }
149
+ u[i] = e;
150
+ }
151
+ return u;
152
+ }
153
+
154
+ export function isogenyMap<T, F extends Field<T>>(field: F, map: [T[], T[], T[], T[]]) {
155
+ // Make same order as in spec
156
+ const COEFF = map.map((i) => Array.from(i).reverse());
157
+ return (x: T, y: T) => {
158
+ const [xNum, xDen, yNum, yDen] = COEFF.map((val) =>
159
+ val.reduce((acc, i) => field.add(field.mul(acc, x), i))
160
+ );
161
+ x = field.div(xNum, xDen); // xNum / xDen
162
+ y = field.mul(y, field.div(yNum, yDen)); // y * (yNum / yDev)
163
+ return { x, y };
164
+ };
165
+ }
166
+
167
+ export interface H2CPoint<T> extends Group<H2CPoint<T>> {
168
+ add(rhs: H2CPoint<T>): H2CPoint<T>;
169
+ toAffine(iz?: bigint): AffinePoint<T>;
170
+ clearCofactor(): H2CPoint<T>;
171
+ assertValidity(): void;
172
+ }
173
+
174
+ export interface H2CPointConstructor<T> extends GroupConstructor<H2CPoint<T>> {
175
+ fromAffine(ap: AffinePoint<T>): H2CPoint<T>;
176
+ }
177
+
178
+ export type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
179
+
180
+ // Separated from initialization opts, so users won't accidentally change per-curve parameters
181
+ // (changing DST is ok!)
182
+ export type htfBasicOpts = { DST: string };
183
+
184
+ export function createHasher<T>(
185
+ Point: H2CPointConstructor<T>,
186
+ mapToCurve: MapToCurve<T>,
187
+ def: Opts
188
+ ) {
189
+ validateObject(def, {
190
+ DST: 'string',
191
+ p: 'bigint',
192
+ m: 'isSafeInteger',
193
+ k: 'isSafeInteger',
194
+ hash: 'hash',
195
+ });
196
+ if (def.expand !== 'xmd' && def.expand !== 'xof' && def.expand !== undefined)
197
+ throw new Error('Invalid htf/expand');
198
+ if (typeof mapToCurve !== 'function')
199
+ throw new Error('hashToCurve: mapToCurve() has not been defined');
200
+ return {
201
+ // Encodes byte string to elliptic curve
202
+ // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
203
+ hashToCurve(msg: Uint8Array, options?: htfBasicOpts) {
204
+ const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options } as Opts);
205
+ const u0 = Point.fromAffine(mapToCurve(u[0]));
206
+ const u1 = Point.fromAffine(mapToCurve(u[1]));
207
+ const P = u0.add(u1).clearCofactor();
208
+ P.assertValidity();
209
+ return P;
210
+ },
211
+
212
+ // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
213
+ encodeToCurve(msg: Uint8Array, options?: htfBasicOpts) {
214
+ const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options } as Opts);
215
+ const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();
216
+ P.assertValidity();
217
+ return P;
218
+ },
219
+ };
220
+ }
@@ -0,0 +1,417 @@
1
+ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
+ // Utilities for modular arithmetics and finite fields
3
+ import {
4
+ bitMask,
5
+ numberToBytesBE,
6
+ numberToBytesLE,
7
+ bytesToNumberBE,
8
+ bytesToNumberLE,
9
+ ensureBytes,
10
+ validateObject,
11
+ } from './utils.js';
12
+ // prettier-ignore
13
+ const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
14
+ // prettier-ignore
15
+ const _4n = BigInt(4), _5n = BigInt(5), _8n = BigInt(8);
16
+ // prettier-ignore
17
+ const _9n = BigInt(9), _16n = BigInt(16);
18
+
19
+ // Calculates a modulo b
20
+ export function mod(a: bigint, b: bigint): bigint {
21
+ const result = a % b;
22
+ return result >= _0n ? result : b + result;
23
+ }
24
+ /**
25
+ * Efficiently exponentiate num to power and do modular division.
26
+ * Unsafe in some contexts: uses ladder, so can expose bigint bits.
27
+ * @example
28
+ * powMod(2n, 6n, 11n) // 64n % 11n == 9n
29
+ */
30
+ // TODO: use field version && remove
31
+ export function pow(num: bigint, power: bigint, modulo: bigint): bigint {
32
+ if (modulo <= _0n || power < _0n) throw new Error('Expected power/modulo > 0');
33
+ if (modulo === _1n) return _0n;
34
+ let res = _1n;
35
+ while (power > _0n) {
36
+ if (power & _1n) res = (res * num) % modulo;
37
+ num = (num * num) % modulo;
38
+ power >>= _1n;
39
+ }
40
+ return res;
41
+ }
42
+
43
+ // Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4)
44
+ export function pow2(x: bigint, power: bigint, modulo: bigint): bigint {
45
+ let res = x;
46
+ while (power-- > _0n) {
47
+ res *= res;
48
+ res %= modulo;
49
+ }
50
+ return res;
51
+ }
52
+
53
+ // Inverses number over modulo
54
+ export function invert(number: bigint, modulo: bigint): bigint {
55
+ if (number === _0n || modulo <= _0n) {
56
+ throw new Error(`invert: expected positive integers, got n=${number} mod=${modulo}`);
57
+ }
58
+ // Eucledian GCD https://brilliant.org/wiki/extended-euclidean-algorithm/
59
+ let a = mod(number, modulo);
60
+ let b = modulo;
61
+ // prettier-ignore
62
+ let x = _0n, y = _1n, u = _1n, v = _0n;
63
+ while (a !== _0n) {
64
+ // JIT applies optimization if those two lines follow each other
65
+ const q = b / a;
66
+ const r = b % a;
67
+ const m = x - u * q;
68
+ const n = y - v * q;
69
+ // prettier-ignore
70
+ b = a, a = r, x = u, y = v, u = m, v = n;
71
+ }
72
+ const gcd = b;
73
+ if (gcd !== _1n) throw new Error('invert: does not exist');
74
+ return mod(x, modulo);
75
+ }
76
+
77
+ // Tonelli-Shanks algorithm
78
+ // Paper 1: https://eprint.iacr.org/2012/685.pdf (page 12)
79
+ // Paper 2: Square Roots from 1; 24, 51, 10 to Dan Shanks
80
+ export function tonelliShanks(P: bigint) {
81
+ // Legendre constant: used to calculate Legendre symbol (a | p),
82
+ // which denotes the value of a^((p-1)/2) (mod p).
83
+ // (a | p) ≡ 1 if a is a square (mod p)
84
+ // (a | p) ≡ -1 if a is not a square (mod p)
85
+ // (a | p) ≡ 0 if a ≡ 0 (mod p)
86
+ const legendreC = (P - _1n) / _2n;
87
+
88
+ let Q: bigint, S: number, Z: bigint;
89
+ // Step 1: By factoring out powers of 2 from p - 1,
90
+ // find q and s such that p - 1 = q*(2^s) with q odd
91
+ for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++);
92
+
93
+ // Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
94
+ for (Z = _2n; Z < P && pow(Z, legendreC, P) !== P - _1n; Z++);
95
+
96
+ // Fast-path
97
+ if (S === 1) {
98
+ const p1div4 = (P + _1n) / _4n;
99
+ return function tonelliFast<T>(Fp: Field<T>, n: T) {
100
+ const root = Fp.pow(n, p1div4);
101
+ if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
102
+ return root;
103
+ };
104
+ }
105
+
106
+ // Slow-path
107
+ const Q1div2 = (Q + _1n) / _2n;
108
+ return function tonelliSlow<T>(Fp: Field<T>, n: T): T {
109
+ // Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
110
+ if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE)) throw new Error('Cannot find square root');
111
+ let r = S;
112
+ // TODO: will fail at Fp2/etc
113
+ let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b
114
+ let x = Fp.pow(n, Q1div2); // first guess at the square root
115
+ let b = Fp.pow(n, Q); // first guess at the fudge factor
116
+
117
+ while (!Fp.eql(b, Fp.ONE)) {
118
+ if (Fp.eql(b, Fp.ZERO)) return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0)
119
+ // Find m such b^(2^m)==1
120
+ let m = 1;
121
+ for (let t2 = Fp.sqr(b); m < r; m++) {
122
+ if (Fp.eql(t2, Fp.ONE)) break;
123
+ t2 = Fp.sqr(t2); // t2 *= t2
124
+ }
125
+ // NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow
126
+ const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1)
127
+ g = Fp.sqr(ge); // g = ge * ge
128
+ x = Fp.mul(x, ge); // x *= ge
129
+ b = Fp.mul(b, g); // b *= g
130
+ r = m;
131
+ }
132
+ return x;
133
+ };
134
+ }
135
+
136
+ export function FpSqrt(P: bigint) {
137
+ // NOTE: different algorithms can give different roots, it is up to user to decide which one they want.
138
+ // For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
139
+
140
+ // P ≡ 3 (mod 4)
141
+ // √n = n^((P+1)/4)
142
+ if (P % _4n === _3n) {
143
+ // Not all roots possible!
144
+ // const ORDER =
145
+ // 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;
146
+ // const NUM = 72057594037927816n;
147
+ const p1div4 = (P + _1n) / _4n;
148
+ return function sqrt3mod4<T>(Fp: Field<T>, n: T) {
149
+ const root = Fp.pow(n, p1div4);
150
+ // Throw if root**2 != n
151
+ if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
152
+ return root;
153
+ };
154
+ }
155
+
156
+ // Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10)
157
+ if (P % _8n === _5n) {
158
+ const c1 = (P - _5n) / _8n;
159
+ return function sqrt5mod8<T>(Fp: Field<T>, n: T) {
160
+ const n2 = Fp.mul(n, _2n);
161
+ const v = Fp.pow(n2, c1);
162
+ const nv = Fp.mul(n, v);
163
+ const i = Fp.mul(Fp.mul(nv, _2n), v);
164
+ const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
165
+ if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
166
+ return root;
167
+ };
168
+ }
169
+
170
+ // P ≡ 9 (mod 16)
171
+ if (P % _16n === _9n) {
172
+ // NOTE: tonelli is too slow for bls-Fp2 calculations even on start
173
+ // Means we cannot use sqrt for constants at all!
174
+ //
175
+ // const c1 = Fp.sqrt(Fp.negate(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
176
+ // const c2 = Fp.sqrt(c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
177
+ // const c3 = Fp.sqrt(Fp.negate(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
178
+ // const c4 = (P + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
179
+ // sqrt = (x) => {
180
+ // let tv1 = Fp.pow(x, c4); // 1. tv1 = x^c4
181
+ // let tv2 = Fp.mul(c1, tv1); // 2. tv2 = c1 * tv1
182
+ // const tv3 = Fp.mul(c2, tv1); // 3. tv3 = c2 * tv1
183
+ // let tv4 = Fp.mul(c3, tv1); // 4. tv4 = c3 * tv1
184
+ // const e1 = Fp.equals(Fp.square(tv2), x); // 5. e1 = (tv2^2) == x
185
+ // const e2 = Fp.equals(Fp.square(tv3), x); // 6. e2 = (tv3^2) == x
186
+ // tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
187
+ // tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
188
+ // const e3 = Fp.equals(Fp.square(tv2), x); // 9. e3 = (tv2^2) == x
189
+ // return Fp.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select the sqrt from tv1 and tv2
190
+ // }
191
+ }
192
+
193
+ // Other cases: Tonelli-Shanks algorithm
194
+ return tonelliShanks(P);
195
+ }
196
+
197
+ // Little-endian check for first LE bit (last BE bit);
198
+ export const isNegativeLE = (num: bigint, modulo: bigint) => (mod(num, modulo) & _1n) === _1n;
199
+
200
+ // Currently completly inconsistent naming:
201
+ // - readable: add, mul, sqr, sqrt, inv, div, pow, eq, sub
202
+ // - unreadable mess: addition, multiply, square, squareRoot, inversion, divide, power, equals, subtract
203
+
204
+ // Field is not always over prime, Fp2 for example has ORDER(q)=p^m
205
+ export interface Field<T> {
206
+ ORDER: bigint;
207
+ BYTES: number;
208
+ BITS: number;
209
+ MASK: bigint;
210
+ ZERO: T;
211
+ ONE: T;
212
+ // 1-arg
213
+ create: (num: T) => T;
214
+ isValid: (num: T) => boolean;
215
+ is0: (num: T) => boolean;
216
+ neg(num: T): T;
217
+ inv(num: T): T;
218
+ sqrt(num: T): T;
219
+ sqr(num: T): T;
220
+ // 2-args
221
+ eql(lhs: T, rhs: T): boolean;
222
+ add(lhs: T, rhs: T): T;
223
+ sub(lhs: T, rhs: T): T;
224
+ mul(lhs: T, rhs: T | bigint): T;
225
+ pow(lhs: T, power: bigint): T;
226
+ div(lhs: T, rhs: T | bigint): T;
227
+ // N for NonNormalized (for now)
228
+ addN(lhs: T, rhs: T): T;
229
+ subN(lhs: T, rhs: T): T;
230
+ mulN(lhs: T, rhs: T | bigint): T;
231
+ sqrN(num: T): T;
232
+
233
+ // Optional
234
+ // Should be same as sgn0 function in https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/
235
+ // NOTE: sgn0 is 'negative in LE', which is same as odd. And negative in LE is kinda strange definition anyway.
236
+ isOdd?(num: T): boolean; // Odd instead of even since we have it for Fp2
237
+ // legendre?(num: T): T;
238
+ pow(lhs: T, power: bigint): T;
239
+ invertBatch: (lst: T[]) => T[];
240
+ toBytes(num: T): Uint8Array;
241
+ fromBytes(bytes: Uint8Array): T;
242
+ // If c is False, CMOV returns a, otherwise it returns b.
243
+ cmov(a: T, b: T, c: boolean): T;
244
+ }
245
+ // prettier-ignore
246
+ const FIELD_FIELDS = [
247
+ 'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',
248
+ 'eql', 'add', 'sub', 'mul', 'pow', 'div',
249
+ 'addN', 'subN', 'mulN', 'sqrN'
250
+ ] as const;
251
+ export function validateField<T>(field: Field<T>) {
252
+ const initial = {
253
+ ORDER: 'bigint',
254
+ MASK: 'bigint',
255
+ BYTES: 'isSafeInteger',
256
+ BITS: 'isSafeInteger',
257
+ } as Record<string, string>;
258
+ const opts = FIELD_FIELDS.reduce((map, val: string) => {
259
+ map[val] = 'function';
260
+ return map;
261
+ }, initial);
262
+ return validateObject(field, opts);
263
+ }
264
+
265
+ // Generic field functions
266
+ export function FpPow<T>(f: Field<T>, num: T, power: bigint): T {
267
+ // Should have same speed as pow for bigints
268
+ // TODO: benchmark!
269
+ if (power < _0n) throw new Error('Expected power > 0');
270
+ if (power === _0n) return f.ONE;
271
+ if (power === _1n) return num;
272
+ let p = f.ONE;
273
+ let d = num;
274
+ while (power > _0n) {
275
+ if (power & _1n) p = f.mul(p, d);
276
+ d = f.sqr(d);
277
+ power >>= 1n;
278
+ }
279
+ return p;
280
+ }
281
+
282
+ export function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[] {
283
+ const tmp = new Array(nums.length);
284
+ // Walk from first to last, multiply them by each other MOD p
285
+ const lastMultiplied = nums.reduce((acc, num, i) => {
286
+ if (f.is0(num)) return acc;
287
+ tmp[i] = acc;
288
+ return f.mul(acc, num);
289
+ }, f.ONE);
290
+ // Invert last element
291
+ const inverted = f.inv(lastMultiplied);
292
+ // Walk from last to first, multiply them by inverted each other MOD p
293
+ nums.reduceRight((acc, num, i) => {
294
+ if (f.is0(num)) return acc;
295
+ tmp[i] = f.mul(acc, tmp[i]);
296
+ return f.mul(acc, num);
297
+ }, inverted);
298
+ return tmp;
299
+ }
300
+
301
+ export function FpDiv<T>(f: Field<T>, lhs: T, rhs: T | bigint): T {
302
+ return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs));
303
+ }
304
+
305
+ // This function returns True whenever the value x is a square in the field F.
306
+ export function FpIsSquare<T>(f: Field<T>) {
307
+ const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
308
+ return (x: T): boolean => {
309
+ const p = f.pow(x, legendreConst);
310
+ return f.eql(p, f.ZERO) || f.eql(p, f.ONE);
311
+ };
312
+ }
313
+
314
+ // CURVE.n lengths
315
+ export function nLength(n: bigint, nBitLength?: number) {
316
+ // Bit size, byte size of CURVE.n
317
+ const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
318
+ const nByteLength = Math.ceil(_nBitLength / 8);
319
+ return { nBitLength: _nBitLength, nByteLength };
320
+ }
321
+
322
+ // NOTE: very fragile, always bench. Major performance points:
323
+ // - NonNormalized ops
324
+ // - Object.freeze
325
+ // - same shape of object (don't add/remove keys)
326
+ type FpField = Field<bigint> & Required<Pick<Field<bigint>, 'isOdd'>>;
327
+ export function Fp(
328
+ ORDER: bigint,
329
+ bitLen?: number,
330
+ isLE = false,
331
+ redef: Partial<Field<bigint>> = {}
332
+ ): Readonly<FpField> {
333
+ if (ORDER <= _0n) throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
334
+ const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
335
+ if (BYTES > 2048) throw new Error('Field lengths over 2048 bytes are not supported');
336
+ const sqrtP = FpSqrt(ORDER);
337
+ const f: Readonly<FpField> = Object.freeze({
338
+ ORDER,
339
+ BITS,
340
+ BYTES,
341
+ MASK: bitMask(BITS),
342
+ ZERO: _0n,
343
+ ONE: _1n,
344
+ create: (num) => mod(num, ORDER),
345
+ isValid: (num) => {
346
+ if (typeof num !== 'bigint')
347
+ throw new Error(`Invalid field element: expected bigint, got ${typeof num}`);
348
+ return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
349
+ },
350
+ is0: (num) => num === _0n,
351
+ isOdd: (num) => (num & _1n) === _1n,
352
+ neg: (num) => mod(-num, ORDER),
353
+ eql: (lhs, rhs) => lhs === rhs,
354
+
355
+ sqr: (num) => mod(num * num, ORDER),
356
+ add: (lhs, rhs) => mod(lhs + rhs, ORDER),
357
+ sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
358
+ mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
359
+ pow: (num, power) => FpPow(f, num, power),
360
+ div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
361
+
362
+ // Same as above, but doesn't normalize
363
+ sqrN: (num) => num * num,
364
+ addN: (lhs, rhs) => lhs + rhs,
365
+ subN: (lhs, rhs) => lhs - rhs,
366
+ mulN: (lhs, rhs) => lhs * rhs,
367
+
368
+ inv: (num) => invert(num, ORDER),
369
+ sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
370
+ invertBatch: (lst) => FpInvertBatch(f, lst),
371
+ // TODO: do we really need constant cmov?
372
+ // We don't have const-time bigints anyway, so probably will be not very useful
373
+ cmov: (a, b, c) => (c ? b : a),
374
+ toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
375
+ fromBytes: (bytes) => {
376
+ if (bytes.length !== BYTES)
377
+ throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
378
+ return isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
379
+ },
380
+ } as FpField);
381
+ return Object.freeze(f);
382
+ }
383
+
384
+ export function FpSqrtOdd<T>(Fp: Field<T>, elm: T) {
385
+ if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
386
+ const root = Fp.sqrt(elm);
387
+ return Fp.isOdd(root) ? root : Fp.neg(root);
388
+ }
389
+
390
+ export function FpSqrtEven<T>(Fp: Field<T>, elm: T) {
391
+ if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
392
+ const root = Fp.sqrt(elm);
393
+ return Fp.isOdd(root) ? Fp.neg(root) : root;
394
+ }
395
+
396
+ /**
397
+ * FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
398
+ * Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
399
+ * and convert them into private scalar, with the modulo bias being neglible.
400
+ * Needs at least 40 bytes of input for 32-byte private key.
401
+ * https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
402
+ * @param hash hash output from SHA3 or a similar function
403
+ * @returns valid private scalar
404
+ */
405
+ export function hashToPrivateScalar(
406
+ hash: string | Uint8Array,
407
+ groupOrder: bigint,
408
+ isLE = false
409
+ ): bigint {
410
+ hash = ensureBytes('privateHash', hash);
411
+ const hashLen = hash.length;
412
+ const minLen = nLength(groupOrder).nByteLength + 8;
413
+ if (minLen < 24 || hashLen < minLen || hashLen > 1024)
414
+ throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
415
+ const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
416
+ return mod(num, groupOrder - _1n) + _1n;
417
+ }