@noble/curves 1.9.2 → 1.9.3

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 (179) hide show
  1. package/README.md +186 -206
  2. package/_shortw_utils.d.ts +1 -0
  3. package/_shortw_utils.d.ts.map +1 -1
  4. package/_shortw_utils.js +1 -0
  5. package/_shortw_utils.js.map +1 -1
  6. package/abstract/bls.d.ts +87 -62
  7. package/abstract/bls.d.ts.map +1 -1
  8. package/abstract/bls.js +170 -163
  9. package/abstract/bls.js.map +1 -1
  10. package/abstract/curve.d.ts +109 -23
  11. package/abstract/curve.d.ts.map +1 -1
  12. package/abstract/curve.js +158 -156
  13. package/abstract/curve.js.map +1 -1
  14. package/abstract/edwards.d.ts +124 -70
  15. package/abstract/edwards.d.ts.map +1 -1
  16. package/abstract/edwards.js +212 -62
  17. package/abstract/edwards.js.map +1 -1
  18. package/abstract/hash-to-curve.d.ts +8 -4
  19. package/abstract/hash-to-curve.d.ts.map +1 -1
  20. package/abstract/hash-to-curve.js +23 -11
  21. package/abstract/hash-to-curve.js.map +1 -1
  22. package/abstract/modular.d.ts +8 -3
  23. package/abstract/modular.d.ts.map +1 -1
  24. package/abstract/modular.js +79 -35
  25. package/abstract/modular.js.map +1 -1
  26. package/abstract/montgomery.d.ts +17 -4
  27. package/abstract/montgomery.d.ts.map +1 -1
  28. package/abstract/montgomery.js +19 -3
  29. package/abstract/montgomery.js.map +1 -1
  30. package/abstract/tower.d.ts +3 -3
  31. package/abstract/tower.d.ts.map +1 -1
  32. package/abstract/tower.js.map +1 -1
  33. package/abstract/weierstrass.d.ts +142 -116
  34. package/abstract/weierstrass.d.ts.map +1 -1
  35. package/abstract/weierstrass.js +414 -335
  36. package/abstract/weierstrass.js.map +1 -1
  37. package/bls12-381.d.ts.map +1 -1
  38. package/bls12-381.js +4 -4
  39. package/bls12-381.js.map +1 -1
  40. package/ed25519.d.ts +52 -66
  41. package/ed25519.d.ts.map +1 -1
  42. package/ed25519.js +128 -155
  43. package/ed25519.js.map +1 -1
  44. package/ed448.d.ts +57 -58
  45. package/ed448.d.ts.map +1 -1
  46. package/ed448.js +114 -131
  47. package/ed448.js.map +1 -1
  48. package/esm/_shortw_utils.d.ts +1 -0
  49. package/esm/_shortw_utils.d.ts.map +1 -1
  50. package/esm/_shortw_utils.js +1 -0
  51. package/esm/_shortw_utils.js.map +1 -1
  52. package/esm/abstract/bls.d.ts +87 -62
  53. package/esm/abstract/bls.d.ts.map +1 -1
  54. package/esm/abstract/bls.js +171 -164
  55. package/esm/abstract/bls.js.map +1 -1
  56. package/esm/abstract/curve.d.ts +109 -23
  57. package/esm/abstract/curve.d.ts.map +1 -1
  58. package/esm/abstract/curve.js +156 -155
  59. package/esm/abstract/curve.js.map +1 -1
  60. package/esm/abstract/edwards.d.ts +124 -70
  61. package/esm/abstract/edwards.d.ts.map +1 -1
  62. package/esm/abstract/edwards.js +210 -62
  63. package/esm/abstract/edwards.js.map +1 -1
  64. package/esm/abstract/hash-to-curve.d.ts +8 -4
  65. package/esm/abstract/hash-to-curve.d.ts.map +1 -1
  66. package/esm/abstract/hash-to-curve.js +22 -11
  67. package/esm/abstract/hash-to-curve.js.map +1 -1
  68. package/esm/abstract/modular.d.ts +8 -3
  69. package/esm/abstract/modular.d.ts.map +1 -1
  70. package/esm/abstract/modular.js +79 -35
  71. package/esm/abstract/modular.js.map +1 -1
  72. package/esm/abstract/montgomery.d.ts +17 -4
  73. package/esm/abstract/montgomery.d.ts.map +1 -1
  74. package/esm/abstract/montgomery.js +19 -3
  75. package/esm/abstract/montgomery.js.map +1 -1
  76. package/esm/abstract/tower.d.ts +3 -3
  77. package/esm/abstract/tower.d.ts.map +1 -1
  78. package/esm/abstract/tower.js.map +1 -1
  79. package/esm/abstract/weierstrass.d.ts +142 -116
  80. package/esm/abstract/weierstrass.d.ts.map +1 -1
  81. package/esm/abstract/weierstrass.js +411 -333
  82. package/esm/abstract/weierstrass.js.map +1 -1
  83. package/esm/bls12-381.d.ts.map +1 -1
  84. package/esm/bls12-381.js +4 -4
  85. package/esm/bls12-381.js.map +1 -1
  86. package/esm/ed25519.d.ts +52 -66
  87. package/esm/ed25519.d.ts.map +1 -1
  88. package/esm/ed25519.js +131 -157
  89. package/esm/ed25519.js.map +1 -1
  90. package/esm/ed448.d.ts +57 -58
  91. package/esm/ed448.d.ts.map +1 -1
  92. package/esm/ed448.js +116 -132
  93. package/esm/ed448.js.map +1 -1
  94. package/esm/index.js +7 -9
  95. package/esm/index.js.map +1 -1
  96. package/esm/jubjub.d.ts +3 -3
  97. package/esm/jubjub.d.ts.map +1 -1
  98. package/esm/jubjub.js +3 -3
  99. package/esm/jubjub.js.map +1 -1
  100. package/esm/misc.d.ts +3 -5
  101. package/esm/misc.d.ts.map +1 -1
  102. package/esm/misc.js +0 -3
  103. package/esm/misc.js.map +1 -1
  104. package/esm/nist.d.ts +0 -6
  105. package/esm/nist.d.ts.map +1 -1
  106. package/esm/nist.js +31 -15
  107. package/esm/nist.js.map +1 -1
  108. package/esm/p256.d.ts +4 -0
  109. package/esm/p256.d.ts.map +1 -1
  110. package/esm/p256.js +4 -0
  111. package/esm/p256.js.map +1 -1
  112. package/esm/p384.d.ts +4 -1
  113. package/esm/p384.d.ts.map +1 -1
  114. package/esm/p384.js +4 -1
  115. package/esm/p384.js.map +1 -1
  116. package/esm/p521.d.ts +4 -0
  117. package/esm/p521.d.ts.map +1 -1
  118. package/esm/p521.js +4 -0
  119. package/esm/p521.js.map +1 -1
  120. package/esm/secp256k1.d.ts +32 -15
  121. package/esm/secp256k1.d.ts.map +1 -1
  122. package/esm/secp256k1.js +72 -67
  123. package/esm/secp256k1.js.map +1 -1
  124. package/esm/utils.d.ts +1 -1
  125. package/esm/utils.js +1 -1
  126. package/index.js +7 -9
  127. package/index.js.map +1 -1
  128. package/jubjub.d.ts +3 -3
  129. package/jubjub.d.ts.map +1 -1
  130. package/jubjub.js +3 -3
  131. package/jubjub.js.map +1 -1
  132. package/misc.d.ts +3 -5
  133. package/misc.d.ts.map +1 -1
  134. package/misc.js +0 -3
  135. package/misc.js.map +1 -1
  136. package/nist.d.ts +0 -6
  137. package/nist.d.ts.map +1 -1
  138. package/nist.js +31 -15
  139. package/nist.js.map +1 -1
  140. package/p256.d.ts +4 -0
  141. package/p256.d.ts.map +1 -1
  142. package/p256.js +4 -0
  143. package/p256.js.map +1 -1
  144. package/p384.d.ts +4 -1
  145. package/p384.d.ts.map +1 -1
  146. package/p384.js +4 -1
  147. package/p384.js.map +1 -1
  148. package/p521.d.ts +4 -0
  149. package/p521.d.ts.map +1 -1
  150. package/p521.js +4 -0
  151. package/p521.js.map +1 -1
  152. package/package.json +4 -2
  153. package/secp256k1.d.ts +32 -15
  154. package/secp256k1.d.ts.map +1 -1
  155. package/secp256k1.js +70 -65
  156. package/secp256k1.js.map +1 -1
  157. package/src/_shortw_utils.ts +1 -0
  158. package/src/abstract/bls.ts +319 -257
  159. package/src/abstract/curve.ts +226 -170
  160. package/src/abstract/edwards.ts +350 -139
  161. package/src/abstract/hash-to-curve.ts +33 -16
  162. package/src/abstract/modular.ts +86 -35
  163. package/src/abstract/montgomery.ts +36 -9
  164. package/src/abstract/tower.ts +4 -4
  165. package/src/abstract/weierstrass.ts +567 -474
  166. package/src/bls12-381.ts +28 -20
  167. package/src/ed25519.ts +161 -179
  168. package/src/ed448.ts +150 -156
  169. package/src/index.ts +7 -9
  170. package/src/jubjub.ts +3 -3
  171. package/src/misc.ts +3 -7
  172. package/src/nist.ts +40 -16
  173. package/src/p256.ts +4 -0
  174. package/src/p384.ts +4 -2
  175. package/src/p521.ts +4 -0
  176. package/src/secp256k1.ts +91 -73
  177. package/src/utils.ts +1 -1
  178. package/utils.d.ts +1 -1
  179. package/utils.js +1 -1
@@ -16,7 +16,7 @@ import {
16
16
  utf8ToBytes,
17
17
  } from '../utils.ts';
18
18
  import type { AffinePoint, Group, GroupConstructor } from './curve.ts';
19
- import { FpInvertBatch, type IField, mod } from './modular.ts';
19
+ import { FpInvertBatch, mod, type IField } from './modular.ts';
20
20
 
21
21
  export type UnicodeOrBytes = string | Uint8Array;
22
22
 
@@ -71,19 +71,24 @@ function anum(item: unknown): void {
71
71
  if (!Number.isSafeInteger(item)) throw new Error('number expected');
72
72
  }
73
73
 
74
+ function normDST(DST: UnicodeOrBytes): Uint8Array {
75
+ if (!isBytes(DST) && typeof DST !== 'string') throw new Error('DST must be Uint8Array or string');
76
+ return typeof DST === 'string' ? utf8ToBytes(DST) : DST;
77
+ }
78
+
74
79
  /**
75
80
  * Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits.
76
81
  * [RFC 9380 5.3.1](https://www.rfc-editor.org/rfc/rfc9380#section-5.3.1).
77
82
  */
78
83
  export function expand_message_xmd(
79
84
  msg: Uint8Array,
80
- DST: Uint8Array,
85
+ DST: UnicodeOrBytes,
81
86
  lenInBytes: number,
82
87
  H: CHash
83
88
  ): Uint8Array {
84
89
  abytes(msg);
85
- abytes(DST);
86
90
  anum(lenInBytes);
91
+ DST = normDST(DST);
87
92
  // https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
88
93
  if (DST.length > 255) DST = H(concatBytes(utf8ToBytes('H2C-OVERSIZE-DST-'), DST));
89
94
  const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H;
@@ -112,14 +117,14 @@ export function expand_message_xmd(
112
117
  */
113
118
  export function expand_message_xof(
114
119
  msg: Uint8Array,
115
- DST: Uint8Array,
120
+ DST: UnicodeOrBytes,
116
121
  lenInBytes: number,
117
122
  k: number,
118
123
  H: CHash
119
124
  ): Uint8Array {
120
125
  abytes(msg);
121
- abytes(DST);
122
126
  anum(lenInBytes);
127
+ DST = normDST(DST);
123
128
  // https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
124
129
  // DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
125
130
  if (DST.length > 255) {
@@ -154,13 +159,10 @@ export function hash_to_field(msg: Uint8Array, count: number, options: H2COpts):
154
159
  k: 'number',
155
160
  hash: 'function',
156
161
  });
157
- const { p, k, m, hash, expand, DST: _DST } = options;
158
- if (!isBytes(_DST) && typeof _DST !== 'string')
159
- throw new Error('DST must be string or uint8array');
162
+ const { p, k, m, hash, expand, DST } = options;
160
163
  if (!isHash(options.hash)) throw new Error('expected valid hash');
161
164
  abytes(msg);
162
165
  anum(count);
163
- const DST = typeof _DST === 'string' ? utf8ToBytes(_DST) : _DST;
164
166
  const log2p = p.toString(2).length;
165
167
  const L = Math.ceil((log2p + k) / 8); // section 5.1 of ietf draft link above
166
168
  const len_in_bytes = count * m * L;
@@ -229,6 +231,10 @@ export type H2CMethod<T> = (msg: Uint8Array, options?: htfBasicOpts) => H2CPoint
229
231
  // TODO: remove
230
232
  export type HTFMethod<T> = H2CMethod<T>;
231
233
  export type MapMethod<T> = (scalars: bigint[]) => H2CPoint<T>;
234
+ export type H2CHasherBase<T> = {
235
+ hashToCurve: H2CMethod<T>;
236
+ hashToScalar: (msg: Uint8Array, options: htfBasicOpts) => bigint;
237
+ };
232
238
  /**
233
239
  * RFC 9380 methods, with cofactor clearing. See https://www.rfc-editor.org/rfc/rfc9380#section-3.
234
240
  *
@@ -236,8 +242,7 @@ export type MapMethod<T> = (scalars: bigint[]) => H2CPoint<T>;
236
242
  * * encodeToCurve: `map(hash(input))`, encodes NON-UNIFORM bytes to curve (WITH hashing)
237
243
  * * mapToCurve: `map(scalars)`, encodes NON-UNIFORM scalars to curve (NO hashing)
238
244
  */
239
- export type H2CHasher<T> = {
240
- hashToCurve: H2CMethod<T>;
245
+ export type H2CHasher<T> = H2CHasherBase<T> & {
241
246
  encodeToCurve: H2CMethod<T>;
242
247
  mapToCurve: MapMethod<T>;
243
248
  defaults: H2COpts & { encodeDST?: UnicodeOrBytes };
@@ -245,6 +250,8 @@ export type H2CHasher<T> = {
245
250
  // TODO: remove
246
251
  export type Hasher<T> = H2CHasher<T>;
247
252
 
253
+ export const _DST_scalar: Uint8Array = utf8ToBytes('HashToScalar-');
254
+
248
255
  /** Creates hash-to-curve methods from EC Point and mapToCurve function. See {@link H2CHasher}. */
249
256
  export function createHasher<T>(
250
257
  Point: H2CPointConstructor<T>,
@@ -264,19 +271,20 @@ export function createHasher<T>(
264
271
 
265
272
  return {
266
273
  defaults,
274
+
267
275
  hashToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
268
- const dst = defaults.DST ? defaults.DST : {};
269
- const opts = Object.assign({}, defaults, dst, options);
276
+ const opts = Object.assign({}, defaults, options);
270
277
  const u = hash_to_field(msg, 2, opts);
271
278
  const u0 = map(u[0]);
272
279
  const u1 = map(u[1]);
273
280
  return clear(u0.add(u1));
274
281
  },
275
282
  encodeToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
276
- const dst = defaults.encodeDST ? defaults.encodeDST : {};
277
- const opts = Object.assign({}, defaults, dst, options);
283
+ const optsDst = defaults.encodeDST ? { DST: defaults.encodeDST } : {};
284
+ const opts = Object.assign({}, defaults, optsDst, options);
278
285
  const u = hash_to_field(msg, 1, opts);
279
- return clear(map(u[0]));
286
+ const u0 = map(u[0]);
287
+ return clear(u0);
280
288
  },
281
289
  /** See {@link H2CHasher} */
282
290
  mapToCurve(scalars: bigint[]): H2CPoint<T> {
@@ -285,5 +293,14 @@ export function createHasher<T>(
285
293
  if (typeof i !== 'bigint') throw new Error('expected array of bigints');
286
294
  return clear(map(scalars));
287
295
  },
296
+
297
+ // hash_to_scalar can produce 0: https://www.rfc-editor.org/errata/eid8393
298
+ // RFC 9380, draft-irtf-cfrg-bbs-signatures-08
299
+ hashToScalar(msg: Uint8Array, options?: htfBasicOpts): bigint {
300
+ // @ts-ignore
301
+ const N = Point.Fn.ORDER;
302
+ const opts = Object.assign({}, defaults, { p: N, m: 1, DST: _DST_scalar }, options);
303
+ return hash_to_field(msg, 1, opts)[0][0];
304
+ },
288
305
  };
289
306
  }
@@ -19,8 +19,9 @@ import {
19
19
  // prettier-ignore
20
20
  const _0n = BigInt(0), _1n = BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _3n = /* @__PURE__ */ BigInt(3);
21
21
  // prettier-ignore
22
- const _4n = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5);
23
- const _8n = /* @__PURE__ */ BigInt(8);
22
+ const _4n = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5), _7n = /* @__PURE__ */ BigInt(7);
23
+ // prettier-ignore
24
+ const _8n = /* @__PURE__ */ BigInt(8), _9n = /* @__PURE__ */ BigInt(9), _16n = /* @__PURE__ */ BigInt(16);
24
25
 
25
26
  // Calculates a modulo b
26
27
  export function mod(a: bigint, b: bigint): bigint {
@@ -73,6 +74,10 @@ export function invert(number: bigint, modulo: bigint): bigint {
73
74
  return mod(x, modulo);
74
75
  }
75
76
 
77
+ function assertIsSquare<T>(Fp: IField<T>, root: T, n: T): void {
78
+ if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
79
+ }
80
+
76
81
  // Not all roots are possible! Example which will throw:
77
82
  // const NUM =
78
83
  // n = 72057594037927816n;
@@ -80,8 +85,7 @@ export function invert(number: bigint, modulo: bigint): bigint {
80
85
  function sqrt3mod4<T>(Fp: IField<T>, n: T) {
81
86
  const p1div4 = (Fp.ORDER + _1n) / _4n;
82
87
  const root = Fp.pow(n, p1div4);
83
- // Throw if root^2 != n
84
- if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
88
+ assertIsSquare(Fp, root, n);
85
89
  return root;
86
90
  }
87
91
 
@@ -92,32 +96,34 @@ function sqrt5mod8<T>(Fp: IField<T>, n: T) {
92
96
  const nv = Fp.mul(n, v);
93
97
  const i = Fp.mul(Fp.mul(nv, _2n), v);
94
98
  const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
95
- if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
99
+ assertIsSquare(Fp, root, n);
96
100
  return root;
97
101
  }
98
102
 
99
- // TODO: Commented-out for now. Provide test vectors.
100
- // Tonelli is too slow for extension fields Fp2.
101
- // That means we can't use sqrt (c1, c2...) even for initialization constants.
102
- // if (P % _16n === _9n) return sqrt9mod16;
103
- // // prettier-ignore
104
- // function sqrt9mod16<T>(Fp: IField<T>, n: T, p7div16?: bigint) {
105
- // if (p7div16 === undefined) p7div16 = (Fp.ORDER + BigInt(7)) / _16n;
106
- // const c1 = Fp.sqrt(Fp.neg(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
107
- // const c2 = Fp.sqrt(c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
108
- // const c3 = Fp.sqrt(Fp.neg(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
109
- // const c4 = p7div16; // 4. c4 = (q + 7) / 16 # Integer arithmetic
110
- // let tv1 = Fp.pow(n, c4); // 1. tv1 = x^c4
111
- // let tv2 = Fp.mul(c1, tv1); // 2. tv2 = c1 * tv1
112
- // const tv3 = Fp.mul(c2, tv1); // 3. tv3 = c2 * tv1
113
- // let tv4 = Fp.mul(c3, tv1); // 4. tv4 = c3 * tv1
114
- // const e1 = Fp.eql(Fp.sqr(tv2), n); // 5. e1 = (tv2^2) == x
115
- // const e2 = Fp.eql(Fp.sqr(tv3), n); // 6. e2 = (tv3^2) == x
116
- // tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
117
- // tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
118
- // const e3 = Fp.eql(Fp.sqr(tv2), n); // 9. e3 = (tv2^2) == x
119
- // return Fp.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select the sqrt from tv1 and tv2
120
- // }
103
+ // Based on RFC9380, Kong algorithm
104
+ // prettier-ignore
105
+ function sqrt9mod16(P: bigint): <T>(Fp: IField<T>, n: T) => T {
106
+ const Fp_ = Field(P);
107
+ const tn = tonelliShanks(P);
108
+ const c1 = tn(Fp_, Fp_.neg(Fp_.ONE));// 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
109
+ const c2 = tn(Fp_, c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
110
+ const c3 = tn(Fp_, Fp_.neg(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
111
+ const c4 = (P + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
112
+ return <T>(Fp: IField<T>, n: T) => {
113
+ let tv1 = Fp.pow(n, c4); // 1. tv1 = x^c4
114
+ let tv2 = Fp.mul(tv1, c1); // 2. tv2 = c1 * tv1
115
+ const tv3 = Fp.mul(tv1, c2); // 3. tv3 = c2 * tv1
116
+ const tv4 = Fp.mul(tv1, c3); // 4. tv4 = c3 * tv1
117
+ const e1 = Fp.eql(Fp.sqr(tv2), n); // 5. e1 = (tv2^2) == x
118
+ const e2 = Fp.eql(Fp.sqr(tv3), n); // 6. e2 = (tv3^2) == x
119
+ tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
120
+ tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
121
+ const e3 = Fp.eql(Fp.sqr(tv2), n); // 9. e3 = (tv2^2) == x
122
+ const root = Fp.cmov(tv1, tv2, e3);// 10. z = CMOV(tv1, tv2, e3) # Select sqrt from tv1 & tv2
123
+ assertIsSquare(Fp, root, n);
124
+ return root;
125
+ };
126
+ }
121
127
 
122
128
  /**
123
129
  * Tonelli-Shanks square root search algorithm.
@@ -129,7 +135,7 @@ function sqrt5mod8<T>(Fp: IField<T>, n: T) {
129
135
  export function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T {
130
136
  // Initialization (precomputation).
131
137
  // Caching initialization could boost perf by 7%.
132
- if (P < BigInt(3)) throw new Error('sqrt is not defined for small field');
138
+ if (P < _3n) throw new Error('sqrt is not defined for small field');
133
139
  // Factor P - 1 = Q * 2^S, where Q is odd
134
140
  let Q = P - _1n;
135
141
  let S = 0;
@@ -197,7 +203,8 @@ export function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T {
197
203
  *
198
204
  * 1. P ≡ 3 (mod 4)
199
205
  * 2. P ≡ 5 (mod 8)
200
- * 3. Tonelli-Shanks algorithm
206
+ * 3. P ≡ 9 (mod 16)
207
+ * 4. Tonelli-Shanks algorithm
201
208
  *
202
209
  * Different algorithms can give different roots, it is up to user to decide which one they want.
203
210
  * For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
@@ -207,7 +214,8 @@ export function FpSqrt(P: bigint): <T>(Fp: IField<T>, n: T) => T {
207
214
  if (P % _4n === _3n) return sqrt3mod4;
208
215
  // P ≡ 5 (mod 8) => Atkin algorithm, page 10 of https://eprint.iacr.org/2012/685.pdf
209
216
  if (P % _8n === _5n) return sqrt5mod8;
210
- // P ≡ 9 (mod 16) not implemented, see above
217
+ // P ≡ 9 (mod 16) => Kong algorithm, page 11 of https://eprint.iacr.org/2012/685.pdf (algorithm 4)
218
+ if (P % _16n === _9n) return sqrt9mod16(P);
211
219
  // Tonelli-Shanks algorithm
212
220
  return tonelliShanks(P);
213
221
  }
@@ -252,10 +260,11 @@ export interface IField<T> {
252
260
  // [RFC9380](https://www.rfc-editor.org/rfc/rfc9380#section-4.1).
253
261
  // NOTE: sgn0 is 'negative in LE', which is same as odd. And negative in LE is kinda strange definition anyway.
254
262
  isOdd?(num: T): boolean; // Odd instead of even since we have it for Fp2
263
+ allowedLengths?: number[];
255
264
  // legendre?(num: T): T;
256
265
  invertBatch: (lst: T[]) => T[];
257
266
  toBytes(num: T): Uint8Array;
258
- fromBytes(bytes: Uint8Array): T;
267
+ fromBytes(bytes: Uint8Array, skipValidation?: boolean): T;
259
268
  // If c is False, CMOV returns a, otherwise it returns b.
260
269
  cmov(a: T, b: T, c: boolean): T;
261
270
  }
@@ -371,7 +380,13 @@ export function nLength(n: bigint, nBitLength?: number): NLength {
371
380
 
372
381
  type FpField = IField<bigint> & Required<Pick<IField<bigint>, 'isOdd'>>;
373
382
  type SqrtFn = (n: bigint) => bigint;
374
- type FieldOpts = Partial<{ sqrt: SqrtFn; isLE: boolean; BITS: number }>;
383
+ type FieldOpts = Partial<{
384
+ sqrt: SqrtFn;
385
+ isLE: boolean;
386
+ BITS: number;
387
+ modOnDecode: boolean; // bls12-381 requires mod(n) instead of rejecting keys >= n
388
+ allowedLengths?: readonly number[]; // for P521 (adds padding for smaller sizes)
389
+ }>;
375
390
  /**
376
391
  * Creates a finite field. Major performance optimizations:
377
392
  * * 1. Denormalized operations like mulN instead of mul.
@@ -393,19 +408,23 @@ type FieldOpts = Partial<{ sqrt: SqrtFn; isLE: boolean; BITS: number }>;
393
408
  */
394
409
  export function Field(
395
410
  ORDER: bigint,
396
- bitLenOrOpts?: number | FieldOpts,
411
+ bitLenOrOpts?: number | FieldOpts, // TODO: use opts only in v2?
397
412
  isLE = false,
398
413
  opts: { sqrt?: SqrtFn } = {}
399
414
  ): Readonly<FpField> {
400
415
  if (ORDER <= _0n) throw new Error('invalid field: expected ORDER > 0, got ' + ORDER);
401
416
  let _nbitLength: number | undefined = undefined;
402
417
  let _sqrt: SqrtFn | undefined = undefined;
418
+ let modOnDecode: boolean = false;
419
+ let allowedLengths: undefined | readonly number[] = undefined;
403
420
  if (typeof bitLenOrOpts === 'object' && bitLenOrOpts != null) {
404
421
  if (opts.sqrt || isLE) throw new Error('cannot specify opts in two arguments');
405
422
  const _opts = bitLenOrOpts;
406
423
  if (_opts.BITS) _nbitLength = _opts.BITS;
407
424
  if (_opts.sqrt) _sqrt = _opts.sqrt;
408
425
  if (typeof _opts.isLE === 'boolean') isLE = _opts.isLE;
426
+ if (typeof _opts.modOnDecode === 'boolean') modOnDecode = _opts.modOnDecode;
427
+ allowedLengths = _opts.allowedLengths;
409
428
  } else {
410
429
  if (typeof bitLenOrOpts === 'number') _nbitLength = bitLenOrOpts;
411
430
  if (opts.sqrt) _sqrt = opts.sqrt;
@@ -421,6 +440,7 @@ export function Field(
421
440
  MASK: bitMask(BITS),
422
441
  ZERO: _0n,
423
442
  ONE: _1n,
443
+ allowedLengths: allowedLengths,
424
444
  create: (num) => mod(num, ORDER),
425
445
  isValid: (num) => {
426
446
  if (typeof num !== 'bigint')
@@ -455,10 +475,27 @@ export function Field(
455
475
  return sqrtP(f, n);
456
476
  }),
457
477
  toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
458
- fromBytes: (bytes) => {
478
+ fromBytes: (bytes, skipValidation = true) => {
479
+ if (allowedLengths) {
480
+ if (!allowedLengths.includes(bytes.length) || bytes.length > BYTES) {
481
+ throw new Error(
482
+ 'Field.fromBytes: expected ' + allowedLengths + ' bytes, got ' + bytes.length
483
+ );
484
+ }
485
+ const padded = new Uint8Array(BYTES);
486
+ // isLE add 0 to right, !isLE to the left.
487
+ padded.set(bytes, isLE ? 0 : padded.length - bytes.length);
488
+ bytes = padded;
489
+ }
459
490
  if (bytes.length !== BYTES)
460
491
  throw new Error('Field.fromBytes: expected ' + BYTES + ' bytes, got ' + bytes.length);
461
- return isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
492
+ let scalar = isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
493
+ if (modOnDecode) scalar = mod(scalar, ORDER);
494
+ if (!skipValidation)
495
+ if (!f.isValid(scalar)) throw new Error('invalid field element: outside of range 0..ORDER');
496
+ // NOTE: we don't validate scalar here, please use isValid. This done such way because some
497
+ // protocol may allow non-reduced scalar that reduced later or changed some other way.
498
+ return scalar;
462
499
  },
463
500
  // TODO: we don't need it here, move out to separate fn
464
501
  invertBatch: (lst) => FpInvertBatch(f, lst),
@@ -469,6 +506,20 @@ export function Field(
469
506
  return Object.freeze(f);
470
507
  }
471
508
 
509
+ // Generic random scalar, we can do same for other fields if via Fp2.mul(Fp2.ONE, Fp2.random)?
510
+ // This allows unsafe methods like ignore bias or zero. These unsafe, but often used in different protocols (if deterministic RNG).
511
+ // which mean we cannot force this via opts.
512
+ // Not sure what to do with randomBytes, we can accept it inside opts if wanted.
513
+ // Probably need to export getMinHashLength somewhere?
514
+ // random(bytes?: Uint8Array, unsafeAllowZero = false, unsafeAllowBias = false) {
515
+ // const LEN = !unsafeAllowBias ? getMinHashLength(ORDER) : BYTES;
516
+ // if (bytes === undefined) bytes = randomBytes(LEN); // _opts.randomBytes?
517
+ // const num = isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
518
+ // // `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0
519
+ // const reduced = unsafeAllowZero ? mod(num, ORDER) : mod(num, ORDER - _1n) + _1n;
520
+ // return reduced;
521
+ // },
522
+
472
523
  export function FpSqrtOdd<T>(Fp: IField<T>, elm: T): T {
473
524
  if (!Fp.isOdd) throw new Error("Field doesn't have isOdd");
474
525
  const root = Fp.sqrt(elm);
@@ -13,6 +13,7 @@ import {
13
13
  numberToBytesLE,
14
14
  randomBytes,
15
15
  } from '../utils.ts';
16
+ import type { CurveInfo } from './curve.ts';
16
17
  import { mod } from './modular.ts';
17
18
 
18
19
  const _0n = BigInt(0);
@@ -28,14 +29,25 @@ export type CurveType = {
28
29
  randomBytes?: (bytesLength?: number) => Uint8Array;
29
30
  };
30
31
 
31
- export type CurveFn = {
32
+ export type MontgomeryECDH = {
32
33
  scalarMult: (scalar: Hex, u: Hex) => Uint8Array;
33
34
  scalarMultBase: (scalar: Hex) => Uint8Array;
34
- getSharedSecret: (privateKeyA: Hex, publicKeyB: Hex) => Uint8Array;
35
- getPublicKey: (privateKey: Hex) => Uint8Array;
36
- utils: { randomPrivateKey: () => Uint8Array };
35
+ getSharedSecret: (secretKeyA: Hex, publicKeyB: Hex) => Uint8Array;
36
+ getPublicKey: (secretKey: Hex) => Uint8Array;
37
+ utils: {
38
+ randomSecretKey: () => Uint8Array;
39
+ /** @deprecated use `randomSecretKey` */
40
+ randomPrivateKey: () => Uint8Array;
41
+ };
37
42
  GuBytes: Uint8Array;
43
+ info: {
44
+ type: 'montgomery';
45
+ lengths: Omit<CurveInfo['lengths'], 'signature'>;
46
+ publicKeyHasPrefix?: boolean;
47
+ };
48
+ keygen: (seed?: Uint8Array) => { secretKey: Uint8Array; publicKey: Uint8Array };
38
49
  };
50
+ export type CurveFn = MontgomeryECDH;
39
51
 
40
52
  function validateOpts(curve: CurveType) {
41
53
  _validateObject(curve, {
@@ -45,7 +57,7 @@ function validateOpts(curve: CurveType) {
45
57
  return Object.freeze({ ...curve } as const);
46
58
  }
47
59
 
48
- export function montgomery(curveDef: CurveType): CurveFn {
60
+ export function montgomery(curveDef: CurveType): MontgomeryECDH {
49
61
  const CURVE = validateOpts(curveDef);
50
62
  const { P, type, adjustScalarBytes, powPminus2, randomBytes: rand } = CURVE;
51
63
  const is25519 = type === 'x25519';
@@ -155,13 +167,28 @@ export function montgomery(curveDef: CurveType): CurveFn {
155
167
  const z2 = powPminus2(z_2); // `Fp.pow(x, P - _2n)` is much slower equivalent
156
168
  return modP(x_2 * z2); // Return x_2 * (z_2^(p - 2))
157
169
  }
158
-
170
+ const randomSecretKey = (seed = randomBytes_(fieldLen)) => seed;
171
+ const utils = {
172
+ randomSecretKey,
173
+ randomPrivateKey: randomSecretKey,
174
+ };
175
+ function keygen(seed?: Uint8Array) {
176
+ const secretKey = utils.randomSecretKey(seed);
177
+ return { secretKey, publicKey: scalarMultBase(secretKey) };
178
+ }
179
+ const lengths = {
180
+ secret: fieldLen,
181
+ public: fieldLen,
182
+ seed: fieldLen,
183
+ };
159
184
  return {
185
+ keygen,
186
+ getSharedSecret: (secretKey: Hex, publicKey: Hex) => scalarMult(secretKey, publicKey),
187
+ getPublicKey: (secretKey: Hex): Uint8Array => scalarMultBase(secretKey),
160
188
  scalarMult,
161
189
  scalarMultBase,
162
- getSharedSecret: (privateKey: Hex, publicKey: Hex) => scalarMult(privateKey, publicKey),
163
- getPublicKey: (privateKey: Hex): Uint8Array => scalarMultBase(privateKey),
164
- utils: { randomPrivateKey: () => randomBytes_(fieldLen) },
190
+ utils,
165
191
  GuBytes: GuBytes.slice(),
192
+ info: { type: 'montgomery' as const, lengths },
166
193
  };
167
194
  }
@@ -12,7 +12,7 @@
12
12
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
13
13
  import { bitLen, bitMask, concatBytes, notImplemented } from '../utils.ts';
14
14
  import * as mod from './modular.ts';
15
- import type { ProjConstructor, ProjPointType } from './weierstrass.ts';
15
+ import type { WeierstrassPoint, WeierstrassPointCons } from './weierstrass.ts';
16
16
 
17
17
  // Be friendly to bad ECMAScript parsers by not using bigint literals
18
18
  // prettier-ignore
@@ -95,8 +95,8 @@ export function psiFrobenius(
95
95
  ): {
96
96
  psi: (x: Fp2, y: Fp2) => [Fp2, Fp2];
97
97
  psi2: (x: Fp2, y: Fp2) => [Fp2, Fp2];
98
- G2psi: (c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) => ProjPointType<Fp2>;
99
- G2psi2: (c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) => ProjPointType<Fp2>;
98
+ G2psi: (c: WeierstrassPointCons<Fp2>, P: WeierstrassPoint<Fp2>) => WeierstrassPoint<Fp2>;
99
+ G2psi2: (c: WeierstrassPointCons<Fp2>, P: WeierstrassPoint<Fp2>) => WeierstrassPoint<Fp2>;
100
100
  PSI_X: Fp2;
101
101
  PSI_Y: Fp2;
102
102
  PSI2_X: Fp2;
@@ -123,7 +123,7 @@ export function psiFrobenius(
123
123
  // Map points
124
124
  const mapAffine =
125
125
  <T>(fn: (x: T, y: T) => [T, T]) =>
126
- (c: ProjConstructor<T>, P: ProjPointType<T>) => {
126
+ (c: WeierstrassPointCons<T>, P: WeierstrassPoint<T>) => {
127
127
  const affine = P.toAffine();
128
128
  const p = fn(affine.x, affine.y);
129
129
  return c.fromAffine({ x: p[0], y: p[1] });