@noble/curves 0.5.2 → 0.6.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 (59) hide show
  1. package/README.md +49 -5
  2. package/lib/_shortw_utils.d.ts +10 -21
  3. package/lib/abstract/bls.d.ts +39 -32
  4. package/lib/abstract/bls.js +74 -73
  5. package/lib/abstract/{group.d.ts → curve.d.ts} +31 -1
  6. package/lib/abstract/{group.js → curve.js} +39 -2
  7. package/lib/abstract/edwards.d.ts +30 -72
  8. package/lib/abstract/edwards.js +197 -375
  9. package/lib/abstract/hash-to-curve.d.ts +25 -6
  10. package/lib/abstract/hash-to-curve.js +40 -12
  11. package/lib/abstract/modular.d.ts +20 -7
  12. package/lib/abstract/modular.js +61 -35
  13. package/lib/abstract/montgomery.js +4 -5
  14. package/lib/abstract/poseidon.d.ts +29 -0
  15. package/lib/abstract/poseidon.js +115 -0
  16. package/lib/abstract/utils.d.ts +5 -36
  17. package/lib/abstract/utils.js +23 -71
  18. package/lib/abstract/weierstrass.d.ts +51 -74
  19. package/lib/abstract/weierstrass.js +455 -628
  20. package/lib/bls12-381.js +63 -58
  21. package/lib/bn.js +1 -1
  22. package/lib/ed25519.d.ts +7 -5
  23. package/lib/ed25519.js +82 -79
  24. package/lib/ed448.d.ts +3 -0
  25. package/lib/ed448.js +86 -83
  26. package/lib/esm/abstract/bls.js +75 -74
  27. package/lib/esm/abstract/{group.js → curve.js} +37 -1
  28. package/lib/esm/abstract/edwards.js +196 -374
  29. package/lib/esm/abstract/hash-to-curve.js +38 -11
  30. package/lib/esm/abstract/modular.js +58 -34
  31. package/lib/esm/abstract/montgomery.js +5 -6
  32. package/lib/esm/abstract/poseidon.js +109 -0
  33. package/lib/esm/abstract/utils.js +21 -66
  34. package/lib/esm/abstract/weierstrass.js +454 -627
  35. package/lib/esm/bls12-381.js +75 -70
  36. package/lib/esm/bn.js +1 -1
  37. package/lib/esm/ed25519.js +80 -78
  38. package/lib/esm/ed448.js +84 -82
  39. package/lib/esm/jubjub.js +1 -1
  40. package/lib/esm/p256.js +11 -9
  41. package/lib/esm/p384.js +11 -9
  42. package/lib/esm/p521.js +13 -12
  43. package/lib/esm/secp256k1.js +115 -151
  44. package/lib/esm/stark.js +104 -40
  45. package/lib/jubjub.d.ts +2 -2
  46. package/lib/jubjub.js +1 -1
  47. package/lib/p192.d.ts +20 -42
  48. package/lib/p224.d.ts +20 -42
  49. package/lib/p256.d.ts +23 -42
  50. package/lib/p256.js +13 -10
  51. package/lib/p384.d.ts +23 -42
  52. package/lib/p384.js +13 -10
  53. package/lib/p521.d.ts +23 -42
  54. package/lib/p521.js +15 -13
  55. package/lib/secp256k1.d.ts +25 -37
  56. package/lib/secp256k1.js +115 -151
  57. package/lib/stark.d.ts +36 -19
  58. package/lib/stark.js +107 -40
  59. package/package.json +13 -8
package/lib/esm/ed448.js CHANGED
@@ -4,6 +4,7 @@ import { concatBytes, randomBytes, utf8ToBytes, wrapConstructor } from '@noble/h
4
4
  import { twistedEdwards } from './abstract/edwards.js';
5
5
  import { mod, pow2, Fp as Field } from './abstract/modular.js';
6
6
  import { montgomery } from './abstract/montgomery.js';
7
+ import * as htf from './abstract/hash-to-curve.js';
7
8
  /**
8
9
  * Edwards448 (not Ed448-Goldilocks) curve with following addons:
9
10
  * * X448 ECDH
@@ -46,79 +47,6 @@ function adjustScalarBytes(bytes) {
46
47
  return bytes;
47
48
  }
48
49
  const Fp = Field(ed448P, 456, true);
49
- // Hash To Curve Elligator2 Map
50
- const ELL2_C1 = (Fp.ORDER - BigInt(3)) / BigInt(4); // 1. c1 = (q - 3) / 4 # Integer arithmetic
51
- const ELL2_J = BigInt(156326);
52
- function map_to_curve_elligator2_curve448(u) {
53
- let tv1 = Fp.square(u); // 1. tv1 = u^2
54
- let e1 = Fp.equals(tv1, Fp.ONE); // 2. e1 = tv1 == 1
55
- tv1 = Fp.cmov(tv1, Fp.ZERO, e1); // 3. tv1 = CMOV(tv1, 0, e1) # If Z * u^2 == -1, set tv1 = 0
56
- let xd = Fp.sub(Fp.ONE, tv1); // 4. xd = 1 - tv1
57
- let x1n = Fp.negate(ELL2_J); // 5. x1n = -J
58
- let tv2 = Fp.square(xd); // 6. tv2 = xd^2
59
- let gxd = Fp.mul(tv2, xd); // 7. gxd = tv2 * xd # gxd = xd^3
60
- let gx1 = Fp.mul(tv1, Fp.negate(ELL2_J)); // 8. gx1 = -J * tv1 # x1n + J * xd
61
- gx1 = Fp.mul(gx1, x1n); // 9. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
62
- gx1 = Fp.add(gx1, tv2); // 10. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
63
- gx1 = Fp.mul(gx1, x1n); // 11. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
64
- let tv3 = Fp.square(gxd); // 12. tv3 = gxd^2
65
- tv2 = Fp.mul(gx1, gxd); // 13. tv2 = gx1 * gxd # gx1 * gxd
66
- tv3 = Fp.mul(tv3, tv2); // 14. tv3 = tv3 * tv2 # gx1 * gxd^3
67
- let y1 = Fp.pow(tv3, ELL2_C1); // 15. y1 = tv3^c1 # (gx1 * gxd^3)^((p - 3) / 4)
68
- y1 = Fp.mul(y1, tv2); // 16. y1 = y1 * tv2 # gx1 * gxd * (gx1 * gxd^3)^((p - 3) / 4)
69
- let x2n = Fp.mul(x1n, Fp.negate(tv1)); // 17. x2n = -tv1 * x1n # x2 = x2n / xd = -1 * u^2 * x1n / xd
70
- let y2 = Fp.mul(y1, u); // 18. y2 = y1 * u
71
- y2 = Fp.cmov(y2, Fp.ZERO, e1); // 19. y2 = CMOV(y2, 0, e1)
72
- tv2 = Fp.square(y1); // 20. tv2 = y1^2
73
- tv2 = Fp.mul(tv2, gxd); // 21. tv2 = tv2 * gxd
74
- let e2 = Fp.equals(tv2, gx1); // 22. e2 = tv2 == gx1
75
- let xn = Fp.cmov(x2n, x1n, e2); // 23. xn = CMOV(x2n, x1n, e2) # If e2, x = x1, else x = x2
76
- let y = Fp.cmov(y2, y1, e2); // 24. y = CMOV(y2, y1, e2) # If e2, y = y1, else y = y2
77
- let e3 = Fp.isOdd(y); // 25. e3 = sgn0(y) == 1 # Fix sign of y
78
- y = Fp.cmov(y, Fp.negate(y), e2 !== e3); // 26. y = CMOV(y, -y, e2 XOR e3)
79
- return { xn, xd, yn: y, yd: Fp.ONE }; // 27. return (xn, xd, y, 1)
80
- }
81
- function map_to_curve_elligator2_edwards448(u) {
82
- let { xn, xd, yn, yd } = map_to_curve_elligator2_curve448(u); // 1. (xn, xd, yn, yd) = map_to_curve_elligator2_curve448(u)
83
- let xn2 = Fp.square(xn); // 2. xn2 = xn^2
84
- let xd2 = Fp.square(xd); // 3. xd2 = xd^2
85
- let xd4 = Fp.square(xd2); // 4. xd4 = xd2^2
86
- let yn2 = Fp.square(yn); // 5. yn2 = yn^2
87
- let yd2 = Fp.square(yd); // 6. yd2 = yd^2
88
- let xEn = Fp.sub(xn2, xd2); // 7. xEn = xn2 - xd2
89
- let tv2 = Fp.sub(xEn, xd2); // 8. tv2 = xEn - xd2
90
- xEn = Fp.mul(xEn, xd2); // 9. xEn = xEn * xd2
91
- xEn = Fp.mul(xEn, yd); // 10. xEn = xEn * yd
92
- xEn = Fp.mul(xEn, yn); // 11. xEn = xEn * yn
93
- xEn = Fp.mul(xEn, 4n); // 12. xEn = xEn * 4
94
- tv2 = Fp.mul(tv2, xn2); // 13. tv2 = tv2 * xn2
95
- tv2 = Fp.mul(tv2, yd2); // 14. tv2 = tv2 * yd2
96
- let tv3 = Fp.mul(yn2, 4n); // 15. tv3 = 4 * yn2
97
- let tv1 = Fp.add(tv3, yd2); // 16. tv1 = tv3 + yd2
98
- tv1 = Fp.mul(tv1, xd4); // 17. tv1 = tv1 * xd4
99
- let xEd = Fp.add(tv1, tv2); // 18. xEd = tv1 + tv2
100
- tv2 = Fp.mul(tv2, xn); // 19. tv2 = tv2 * xn
101
- let tv4 = Fp.mul(xn, xd4); // 20. tv4 = xn * xd4
102
- let yEn = Fp.sub(tv3, yd2); // 21. yEn = tv3 - yd2
103
- yEn = Fp.mul(yEn, tv4); // 22. yEn = yEn * tv4
104
- yEn = Fp.sub(yEn, tv2); // 23. yEn = yEn - tv2
105
- tv1 = Fp.add(xn2, xd2); // 24. tv1 = xn2 + xd2
106
- tv1 = Fp.mul(tv1, xd2); // 25. tv1 = tv1 * xd2
107
- tv1 = Fp.mul(tv1, xd); // 26. tv1 = tv1 * xd
108
- tv1 = Fp.mul(tv1, yn2); // 27. tv1 = tv1 * yn2
109
- tv1 = Fp.mul(tv1, BigInt(-2)); // 28. tv1 = -2 * tv1
110
- let yEd = Fp.add(tv2, tv1); // 29. yEd = tv2 + tv1
111
- tv4 = Fp.mul(tv4, yd2); // 30. tv4 = tv4 * yd2
112
- yEd = Fp.add(yEd, tv4); // 31. yEd = yEd + tv4
113
- tv1 = Fp.mul(xEd, yEd); // 32. tv1 = xEd * yEd
114
- let e = Fp.equals(tv1, Fp.ZERO); // 33. e = tv1 == 0
115
- xEn = Fp.cmov(xEn, Fp.ZERO, e); // 34. xEn = CMOV(xEn, 0, e)
116
- xEd = Fp.cmov(xEd, Fp.ONE, e); // 35. xEd = CMOV(xEd, 1, e)
117
- yEn = Fp.cmov(yEn, Fp.ONE, e); // 36. yEn = CMOV(yEn, 1, e)
118
- yEd = Fp.cmov(yEd, Fp.ONE, e); // 37. yEd = CMOV(yEd, 1, e)
119
- const inv = Fp.invertBatch([xEd, yEd]); // batch division
120
- return { x: Fp.mul(xEn, inv[0]), y: Fp.mul(yEn, inv[1]) }; // 38. return (xEn, xEd, yEn, yEd)
121
- }
122
50
  const ED448_DEF = {
123
51
  // Param: a
124
52
  a: BigInt(1),
@@ -166,15 +94,6 @@ const ED448_DEF = {
166
94
  // square root exists, and the decoding fails.
167
95
  return { isValid: mod(x2 * v, P) === u, value: x };
168
96
  },
169
- htfDefaults: {
170
- DST: 'edwards448_XOF:SHAKE256_ELL2_RO_',
171
- p: Fp.ORDER,
172
- m: 1,
173
- k: 224,
174
- expand: 'xof',
175
- hash: shake256,
176
- },
177
- mapToCurve: (scalars) => map_to_curve_elligator2_edwards448(scalars[0]),
178
97
  };
179
98
  export const ed448 = twistedEdwards(ED448_DEF);
180
99
  // NOTE: there is no ed448ctx, since ed448 supports ctx by default
@@ -207,3 +126,86 @@ export const x448 = montgomery({
207
126
  // return numberToBytesLE(u, 56);
208
127
  // },
209
128
  });
129
+ // Hash To Curve Elligator2 Map
130
+ const ELL2_C1 = (Fp.ORDER - BigInt(3)) / BigInt(4); // 1. c1 = (q - 3) / 4 # Integer arithmetic
131
+ const ELL2_J = BigInt(156326);
132
+ function map_to_curve_elligator2_curve448(u) {
133
+ let tv1 = Fp.sqr(u); // 1. tv1 = u^2
134
+ let e1 = Fp.eql(tv1, Fp.ONE); // 2. e1 = tv1 == 1
135
+ tv1 = Fp.cmov(tv1, Fp.ZERO, e1); // 3. tv1 = CMOV(tv1, 0, e1) # If Z * u^2 == -1, set tv1 = 0
136
+ let xd = Fp.sub(Fp.ONE, tv1); // 4. xd = 1 - tv1
137
+ let x1n = Fp.neg(ELL2_J); // 5. x1n = -J
138
+ let tv2 = Fp.sqr(xd); // 6. tv2 = xd^2
139
+ let gxd = Fp.mul(tv2, xd); // 7. gxd = tv2 * xd # gxd = xd^3
140
+ let gx1 = Fp.mul(tv1, Fp.neg(ELL2_J)); // 8. gx1 = -J * tv1 # x1n + J * xd
141
+ gx1 = Fp.mul(gx1, x1n); // 9. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
142
+ gx1 = Fp.add(gx1, tv2); // 10. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
143
+ gx1 = Fp.mul(gx1, x1n); // 11. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
144
+ let tv3 = Fp.sqr(gxd); // 12. tv3 = gxd^2
145
+ tv2 = Fp.mul(gx1, gxd); // 13. tv2 = gx1 * gxd # gx1 * gxd
146
+ tv3 = Fp.mul(tv3, tv2); // 14. tv3 = tv3 * tv2 # gx1 * gxd^3
147
+ let y1 = Fp.pow(tv3, ELL2_C1); // 15. y1 = tv3^c1 # (gx1 * gxd^3)^((p - 3) / 4)
148
+ y1 = Fp.mul(y1, tv2); // 16. y1 = y1 * tv2 # gx1 * gxd * (gx1 * gxd^3)^((p - 3) / 4)
149
+ let x2n = Fp.mul(x1n, Fp.neg(tv1)); // 17. x2n = -tv1 * x1n # x2 = x2n / xd = -1 * u^2 * x1n / xd
150
+ let y2 = Fp.mul(y1, u); // 18. y2 = y1 * u
151
+ y2 = Fp.cmov(y2, Fp.ZERO, e1); // 19. y2 = CMOV(y2, 0, e1)
152
+ tv2 = Fp.sqr(y1); // 20. tv2 = y1^2
153
+ tv2 = Fp.mul(tv2, gxd); // 21. tv2 = tv2 * gxd
154
+ let e2 = Fp.eql(tv2, gx1); // 22. e2 = tv2 == gx1
155
+ let xn = Fp.cmov(x2n, x1n, e2); // 23. xn = CMOV(x2n, x1n, e2) # If e2, x = x1, else x = x2
156
+ let y = Fp.cmov(y2, y1, e2); // 24. y = CMOV(y2, y1, e2) # If e2, y = y1, else y = y2
157
+ let e3 = Fp.isOdd(y); // 25. e3 = sgn0(y) == 1 # Fix sign of y
158
+ y = Fp.cmov(y, Fp.neg(y), e2 !== e3); // 26. y = CMOV(y, -y, e2 XOR e3)
159
+ return { xn, xd, yn: y, yd: Fp.ONE }; // 27. return (xn, xd, y, 1)
160
+ }
161
+ function map_to_curve_elligator2_edwards448(u) {
162
+ let { xn, xd, yn, yd } = map_to_curve_elligator2_curve448(u); // 1. (xn, xd, yn, yd) = map_to_curve_elligator2_curve448(u)
163
+ let xn2 = Fp.sqr(xn); // 2. xn2 = xn^2
164
+ let xd2 = Fp.sqr(xd); // 3. xd2 = xd^2
165
+ let xd4 = Fp.sqr(xd2); // 4. xd4 = xd2^2
166
+ let yn2 = Fp.sqr(yn); // 5. yn2 = yn^2
167
+ let yd2 = Fp.sqr(yd); // 6. yd2 = yd^2
168
+ let xEn = Fp.sub(xn2, xd2); // 7. xEn = xn2 - xd2
169
+ let tv2 = Fp.sub(xEn, xd2); // 8. tv2 = xEn - xd2
170
+ xEn = Fp.mul(xEn, xd2); // 9. xEn = xEn * xd2
171
+ xEn = Fp.mul(xEn, yd); // 10. xEn = xEn * yd
172
+ xEn = Fp.mul(xEn, yn); // 11. xEn = xEn * yn
173
+ xEn = Fp.mul(xEn, 4n); // 12. xEn = xEn * 4
174
+ tv2 = Fp.mul(tv2, xn2); // 13. tv2 = tv2 * xn2
175
+ tv2 = Fp.mul(tv2, yd2); // 14. tv2 = tv2 * yd2
176
+ let tv3 = Fp.mul(yn2, 4n); // 15. tv3 = 4 * yn2
177
+ let tv1 = Fp.add(tv3, yd2); // 16. tv1 = tv3 + yd2
178
+ tv1 = Fp.mul(tv1, xd4); // 17. tv1 = tv1 * xd4
179
+ let xEd = Fp.add(tv1, tv2); // 18. xEd = tv1 + tv2
180
+ tv2 = Fp.mul(tv2, xn); // 19. tv2 = tv2 * xn
181
+ let tv4 = Fp.mul(xn, xd4); // 20. tv4 = xn * xd4
182
+ let yEn = Fp.sub(tv3, yd2); // 21. yEn = tv3 - yd2
183
+ yEn = Fp.mul(yEn, tv4); // 22. yEn = yEn * tv4
184
+ yEn = Fp.sub(yEn, tv2); // 23. yEn = yEn - tv2
185
+ tv1 = Fp.add(xn2, xd2); // 24. tv1 = xn2 + xd2
186
+ tv1 = Fp.mul(tv1, xd2); // 25. tv1 = tv1 * xd2
187
+ tv1 = Fp.mul(tv1, xd); // 26. tv1 = tv1 * xd
188
+ tv1 = Fp.mul(tv1, yn2); // 27. tv1 = tv1 * yn2
189
+ tv1 = Fp.mul(tv1, BigInt(-2)); // 28. tv1 = -2 * tv1
190
+ let yEd = Fp.add(tv2, tv1); // 29. yEd = tv2 + tv1
191
+ tv4 = Fp.mul(tv4, yd2); // 30. tv4 = tv4 * yd2
192
+ yEd = Fp.add(yEd, tv4); // 31. yEd = yEd + tv4
193
+ tv1 = Fp.mul(xEd, yEd); // 32. tv1 = xEd * yEd
194
+ let e = Fp.eql(tv1, Fp.ZERO); // 33. e = tv1 == 0
195
+ xEn = Fp.cmov(xEn, Fp.ZERO, e); // 34. xEn = CMOV(xEn, 0, e)
196
+ xEd = Fp.cmov(xEd, Fp.ONE, e); // 35. xEd = CMOV(xEd, 1, e)
197
+ yEn = Fp.cmov(yEn, Fp.ONE, e); // 36. yEn = CMOV(yEn, 1, e)
198
+ yEd = Fp.cmov(yEd, Fp.ONE, e); // 37. yEd = CMOV(yEd, 1, e)
199
+ const inv = Fp.invertBatch([xEd, yEd]); // batch division
200
+ return { x: Fp.mul(xEn, inv[0]), y: Fp.mul(yEn, inv[1]) }; // 38. return (xEn, xEd, yEn, yEd)
201
+ }
202
+ const { hashToCurve, encodeToCurve } = htf.hashToCurve(ed448.ExtendedPoint, (scalars) => map_to_curve_elligator2_edwards448(scalars[0]), {
203
+ DST: 'edwards448_XOF:SHAKE256_ELL2_RO_',
204
+ encodeDST: 'edwards448_XOF:SHAKE256_ELL2_NU_',
205
+ p: Fp.ORDER,
206
+ m: 1,
207
+ k: 224,
208
+ expand: 'xof',
209
+ hash: shake256,
210
+ });
211
+ export { hashToCurve, encodeToCurve };
package/lib/esm/jubjub.js CHANGED
@@ -33,7 +33,7 @@ export function groupHash(tag, personalization) {
33
33
  h.update(GH_FIRST_BLOCK);
34
34
  h.update(tag);
35
35
  // NOTE: returns ExtendedPoint, in case it will be multiplied later
36
- let p = jubjub.ExtendedPoint.fromAffine(jubjub.Point.fromHex(h.digest()));
36
+ let p = jubjub.ExtendedPoint.fromHex(h.digest());
37
37
  // NOTE: cannot replace with isSmallOrder, returns Point*8
38
38
  p = p.multiply(jubjub.CURVE.h);
39
39
  if (p.equals(jubjub.ExtendedPoint.ZERO))
package/lib/esm/p256.js CHANGED
@@ -3,6 +3,7 @@ import { createCurve } from './_shortw_utils.js';
3
3
  import { sha256 } from '@noble/hashes/sha256';
4
4
  import { Fp as Field } from './abstract/modular.js';
5
5
  import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
6
+ import * as htf from './abstract/hash-to-curve.js';
6
7
  // NIST secp256r1 aka P256
7
8
  // https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-256
8
9
  // Field over which we'll do calculations; 2n**224n * (2n**32n-1n) + 2n**192n + 2n**96n-1n
@@ -26,14 +27,15 @@ export const P256 = createCurve({
26
27
  Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
27
28
  h: BigInt(1),
28
29
  lowS: false,
29
- mapToCurve: (scalars) => mapSWU(scalars[0]),
30
- htfDefaults: {
31
- DST: 'P256_XMD:SHA-256_SSWU_RO_',
32
- p: Fp.ORDER,
33
- m: 1,
34
- k: 128,
35
- expand: 'xmd',
36
- hash: sha256,
37
- },
38
30
  }, sha256);
39
31
  export const secp256r1 = P256;
32
+ const { hashToCurve, encodeToCurve } = htf.hashToCurve(secp256r1.ProjectivePoint, (scalars) => mapSWU(scalars[0]), {
33
+ DST: 'P256_XMD:SHA-256_SSWU_RO_',
34
+ encodeDST: 'P256_XMD:SHA-256_SSWU_NU_',
35
+ p: Fp.ORDER,
36
+ m: 1,
37
+ k: 128,
38
+ expand: 'xmd',
39
+ hash: sha256,
40
+ });
41
+ export { hashToCurve, encodeToCurve };
package/lib/esm/p384.js CHANGED
@@ -3,6 +3,7 @@ import { createCurve } from './_shortw_utils.js';
3
3
  import { sha384 } from '@noble/hashes/sha512';
4
4
  import { Fp as Field } from './abstract/modular.js';
5
5
  import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
6
+ import * as htf from './abstract/hash-to-curve.js';
6
7
  // NIST secp384r1 aka P384
7
8
  // https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-384
8
9
  // Field over which we'll do calculations. 2n**384n - 2n**128n - 2n**96n + 2n**32n - 1n
@@ -31,14 +32,15 @@ export const P384 = createCurve({
31
32
  Gy: BigInt('0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f'),
32
33
  h: BigInt(1),
33
34
  lowS: false,
34
- mapToCurve: (scalars) => mapSWU(scalars[0]),
35
- htfDefaults: {
36
- DST: 'P384_XMD:SHA-384_SSWU_RO_',
37
- p: Fp.ORDER,
38
- m: 1,
39
- k: 192,
40
- expand: 'xmd',
41
- hash: sha384,
42
- },
43
35
  }, sha384);
44
36
  export const secp384r1 = P384;
37
+ const { hashToCurve, encodeToCurve } = htf.hashToCurve(secp384r1.ProjectivePoint, (scalars) => mapSWU(scalars[0]), {
38
+ DST: 'P384_XMD:SHA-384_SSWU_RO_',
39
+ encodeDST: 'P384_XMD:SHA-384_SSWU_NU_',
40
+ p: Fp.ORDER,
41
+ m: 1,
42
+ k: 192,
43
+ expand: 'xmd',
44
+ hash: sha384,
45
+ });
46
+ export { hashToCurve, encodeToCurve };
package/lib/esm/p521.js CHANGED
@@ -4,6 +4,7 @@ import { sha512 } from '@noble/hashes/sha512';
4
4
  import { bytesToHex } from './abstract/utils.js';
5
5
  import { Fp as Field } from './abstract/modular.js';
6
6
  import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
7
+ import * as htf from './abstract/hash-to-curve.js';
7
8
  // NIST secp521r1 aka P521
8
9
  // Note that it's 521, which differs from 512 of its hash function.
9
10
  // https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-521
@@ -32,8 +33,7 @@ export const P521 = createCurve({
32
33
  Gy: BigInt('0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650'),
33
34
  h: BigInt(1),
34
35
  lowS: false,
35
- // P521 keys could be 130, 131, 132 bytes - which doesn't play nicely.
36
- // We ensure all keys are 132 bytes.
36
+ // P521 keys could be 130, 131, 132 bytes. We normalize to 132 bytes.
37
37
  // Does not replace validation; invalid keys would still be rejected.
38
38
  normalizePrivateKey(key) {
39
39
  if (typeof key === 'bigint')
@@ -43,16 +43,17 @@ export const P521 = createCurve({
43
43
  if (typeof key !== 'string' || !([130, 131, 132].includes(key.length))) {
44
44
  throw new Error('Invalid key');
45
45
  }
46
- return key.padStart(66 * 2, '0');
47
- },
48
- mapToCurve: (scalars) => mapSWU(scalars[0]),
49
- htfDefaults: {
50
- DST: 'P521_XMD:SHA-512_SSWU_RO_',
51
- p: Fp.ORDER,
52
- m: 1,
53
- k: 256,
54
- expand: 'xmd',
55
- hash: sha512,
46
+ return key.padStart(66 * 2, '0'); // ensure it's always 132 bytes
56
47
  },
57
48
  }, sha512);
58
49
  export const secp521r1 = P521;
50
+ const { hashToCurve, encodeToCurve } = htf.hashToCurve(secp521r1.ProjectivePoint, (scalars) => mapSWU(scalars[0]), {
51
+ DST: 'P521_XMD:SHA-512_SSWU_RO_',
52
+ encodeDST: 'P521_XMD:SHA-512_SSWU_NU_',
53
+ p: Fp.ORDER,
54
+ m: 1,
55
+ k: 256,
56
+ expand: 'xmd',
57
+ hash: sha512,
58
+ });
59
+ export { hashToCurve, encodeToCurve };
@@ -3,9 +3,9 @@ import { sha256 } from '@noble/hashes/sha256';
3
3
  import { Fp as Field, mod, pow2 } from './abstract/modular.js';
4
4
  import { createCurve } from './_shortw_utils.js';
5
5
  import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
6
- import { ensureBytes, concatBytes, hexToBytes, bytesToNumberBE, } from './abstract/utils.js';
6
+ import { ensureBytes, concatBytes, bytesToNumberBE as bytesToNum, numberToBytesBE, } from './abstract/utils.js';
7
7
  import { randomBytes } from '@noble/hashes/utils';
8
- import { isogenyMap } from './abstract/hash-to-curve.js';
8
+ import * as htf from './abstract/hash-to-curve.js';
9
9
  /**
10
10
  * secp256k1 belongs to Koblitz curves: it has efficiently computable endomorphism.
11
11
  * Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%.
@@ -19,10 +19,7 @@ const _1n = BigInt(1);
19
19
  const _2n = BigInt(2);
20
20
  const divNearest = (a, b) => (a + b / _2n) / b;
21
21
  /**
22
- * Allows to compute square root √y 2x faster.
23
- * To calculate √y, we need to exponentiate it to a very big number:
24
- * `y² = x³ + ax + b; y = y² ^ (p+1)/4`
25
- * We are unwrapping the loop and multiplying it bit-by-bit.
22
+ * √n = n^((p+1)/4) for fields p = 3 mod 4. We unwrap the loop and multiply bit-by-bit.
26
23
  * (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
27
24
  */
28
25
  function sqrtMod(y) {
@@ -45,45 +42,11 @@ function sqrtMod(y) {
45
42
  const t1 = (pow2(b223, _23n, P) * b22) % P;
46
43
  const t2 = (pow2(t1, _6n, P) * b2) % P;
47
44
  const root = pow2(t2, _2n, P);
48
- if (!Fp.equals(Fp.square(root), y))
45
+ if (!Fp.eql(Fp.sqr(root), y))
49
46
  throw new Error('Cannot find square root');
50
47
  return root;
51
48
  }
52
49
  const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
53
- const isoMap = isogenyMap(Fp, [
54
- // xNum
55
- [
56
- '0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7',
57
- '0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581',
58
- '0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262',
59
- '0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c',
60
- ],
61
- // xDen
62
- [
63
- '0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b',
64
- '0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14',
65
- '0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
66
- ],
67
- // yNum
68
- [
69
- '0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c',
70
- '0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3',
71
- '0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931',
72
- '0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84',
73
- ],
74
- // yDen
75
- [
76
- '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b',
77
- '0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573',
78
- '0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f',
79
- '0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
80
- ],
81
- ].map((i) => i.map((j) => BigInt(j))));
82
- const mapSWU = mapToCurveSimpleSWU(Fp, {
83
- A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
84
- B: BigInt('1771'),
85
- Z: Fp.create(BigInt('-11')),
86
- });
87
50
  export const secp256k1 = createCurve({
88
51
  // Params: a, b
89
52
  // Seem to be rigid https://bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
@@ -126,51 +89,13 @@ export const secp256k1 = createCurve({
126
89
  return { k1neg, k1, k2neg, k2 };
127
90
  },
128
91
  },
129
- mapToCurve: (scalars) => {
130
- const { x, y } = mapSWU(Fp.create(scalars[0]));
131
- return isoMap(x, y);
132
- },
133
- htfDefaults: {
134
- DST: 'secp256k1_XMD:SHA-256_SSWU_RO_',
135
- p: Fp.ORDER,
136
- m: 1,
137
- k: 128,
138
- expand: 'xmd',
139
- hash: sha256,
140
- },
141
92
  }, sha256);
142
- // Schnorr
93
+ // Schnorr signatures are superior to ECDSA from above.
94
+ // Below is Schnorr-specific code as per BIP0340.
95
+ // https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
143
96
  const _0n = BigInt(0);
144
- const numTo32b = secp256k1.utils._bigintToBytes;
145
- const numTo32bStr = secp256k1.utils._bigintToString;
146
- const normalizePrivateKey = secp256k1.utils._normalizePrivateKey;
147
- // TODO: export?
148
- function normalizePublicKey(publicKey) {
149
- if (publicKey instanceof secp256k1.Point) {
150
- publicKey.assertValidity();
151
- return publicKey;
152
- }
153
- else {
154
- const bytes = ensureBytes(publicKey);
155
- // Schnorr is 32 bytes
156
- if (bytes.length !== 32)
157
- throw new Error('Schnorr pubkeys must be 32 bytes');
158
- const x = bytesToNumberBE(bytes);
159
- if (!isValidFieldElement(x))
160
- throw new Error('Point is not on curve');
161
- const y2 = secp256k1.utils._weierstrassEquation(x); // y² = x³ + ax + b
162
- let y = sqrtMod(y2); // y = y² ^ (p+1)/4
163
- const isYOdd = (y & _1n) === _1n;
164
- // Schnorr
165
- if (isYOdd)
166
- y = secp256k1.CURVE.Fp.negate(y);
167
- const point = new secp256k1.Point(x, y);
168
- point.assertValidity();
169
- return point;
170
- }
171
- }
172
- const isWithinCurveOrder = secp256k1.utils._isWithinCurveOrder;
173
- const isValidFieldElement = secp256k1.utils._isValidFieldElement;
97
+ const fe = (x) => typeof x === 'bigint' && _0n < x && x < secp256k1P;
98
+ const ge = (x) => typeof x === 'bigint' && _0n < x && x < secp256k1N;
174
99
  const TAGS = {
175
100
  challenge: 'BIP0340/challenge',
176
101
  aux: 'BIP0340/aux',
@@ -178,7 +103,7 @@ const TAGS = {
178
103
  };
179
104
  /** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
180
105
  const TAGGED_HASH_PREFIXES = {};
181
- export function taggedHash(tag, ...messages) {
106
+ function taggedHash(tag, ...messages) {
182
107
  let tagP = TAGGED_HASH_PREFIXES[tag];
183
108
  if (tagP === undefined) {
184
109
  const tagH = sha256(Uint8Array.from(tag, (c) => c.charCodeAt(0)));
@@ -188,44 +113,37 @@ export function taggedHash(tag, ...messages) {
188
113
  return sha256(concatBytes(tagP, ...messages));
189
114
  }
190
115
  const toRawX = (point) => point.toRawBytes(true).slice(1);
191
- // Schnorr signatures are superior to ECDSA from above.
192
- // Below is Schnorr-specific code as per BIP0340.
193
- function schnorrChallengeFinalize(ch) {
194
- return mod(bytesToNumberBE(ch), secp256k1.CURVE.n);
195
- }
196
- // Do we need this at all for Schnorr?
197
- class SchnorrSignature {
198
- constructor(r, s) {
199
- this.r = r;
200
- this.s = s;
201
- this.assertValidity();
202
- }
203
- static fromHex(hex) {
204
- const bytes = ensureBytes(hex);
205
- const len = 32; // group length
206
- if (bytes.length !== 2 * len)
207
- throw new TypeError(`SchnorrSignature.fromHex: expected ${2 * len} bytes, not ${bytes.length}`);
208
- const r = bytesToNumberBE(bytes.subarray(0, len));
209
- const s = bytesToNumberBE(bytes.subarray(len, 2 * len));
210
- return new SchnorrSignature(r, s);
211
- }
212
- assertValidity() {
213
- const { r, s } = this;
214
- if (!isValidFieldElement(r) || !isWithinCurveOrder(s))
215
- throw new Error('Invalid signature');
216
- }
217
- toHex() {
218
- return numTo32bStr(this.r) + numTo32bStr(this.s);
219
- }
220
- toRawBytes() {
221
- return hexToBytes(this.toHex());
222
- }
223
- }
116
+ const numTo32b = (n) => numberToBytesBE(n, 32);
117
+ const modN = (x) => mod(x, secp256k1N);
118
+ const _Point = secp256k1.ProjectivePoint;
119
+ const Gmul = (priv) => _Point.fromPrivateKey(priv);
120
+ const GmulAdd = (Q, a, b) => _Point.BASE.multiplyAndAddUnsafe(Q, a, b);
224
121
  function schnorrGetScalar(priv) {
225
- const point = secp256k1.Point.fromPrivateKey(priv);
226
- const scalar = point.hasEvenY() ? priv : secp256k1.CURVE.n - priv;
122
+ // Let d' = int(sk)
123
+ // Fail if d' = 0 or d' n
124
+ // Let P = d'⋅G
125
+ // Let d = d' if has_even_y(P), otherwise let d = n - d' .
126
+ const point = Gmul(priv);
127
+ const scalar = point.hasEvenY() ? priv : modN(-priv);
227
128
  return { point, scalar, x: toRawX(point) };
228
129
  }
130
+ function lift_x(x) {
131
+ if (!fe(x))
132
+ throw new Error('bad x: need 0 < x < p'); // Fail if x ≥ p.
133
+ const c = mod(x * x * x + BigInt(7), secp256k1P); // Let c = x³ + 7 mod p.
134
+ let y = sqrtMod(c); // Let y = c^(p+1)/4 mod p.
135
+ if (y % 2n !== 0n)
136
+ y = mod(-y, secp256k1P); // Return the unique point P such that x(P) = x and
137
+ const p = new _Point(x, y, _1n); // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise.
138
+ p.assertValidity();
139
+ return p;
140
+ }
141
+ function challenge(...args) {
142
+ return modN(bytesToNum(taggedHash(TAGS.challenge, ...args)));
143
+ }
144
+ function schnorrGetPublicKey(privateKey) {
145
+ return toRawX(Gmul(privateKey)); // Let d' = int(sk). Fail if d' = 0 or d' ≥ n. Return bytes(d'⋅G)
146
+ }
229
147
  /**
230
148
  * Synchronously creates Schnorr signature. Improved security: verifies itself before
231
149
  * producing an output.
@@ -235,23 +153,23 @@ function schnorrGetScalar(priv) {
235
153
  */
236
154
  function schnorrSign(message, privateKey, auxRand = randomBytes(32)) {
237
155
  if (message == null)
238
- throw new TypeError(`sign: Expected valid message, not "${message}"`);
156
+ throw new Error(`sign: Expected valid message, not "${message}"`);
239
157
  const m = ensureBytes(message);
240
158
  // checks for isWithinCurveOrder
241
- const { x: px, scalar: d } = schnorrGetScalar(normalizePrivateKey(privateKey));
242
- const rand = ensureBytes(auxRand);
243
- if (rand.length !== 32)
244
- throw new TypeError('sign: Expected 32 bytes of aux randomness');
245
- const tag = taggedHash;
246
- const t0h = tag(TAGS.aux, rand);
247
- const t = numTo32b(d ^ bytesToNumberBE(t0h));
248
- const k0h = tag(TAGS.nonce, t, px, m);
249
- const k0 = mod(bytesToNumberBE(k0h), secp256k1.CURVE.n);
250
- if (k0 === _0n)
251
- throw new Error('sign: Creation of signature failed. k is zero');
252
- const { point: R, x: rx, scalar: k } = schnorrGetScalar(k0);
253
- const e = schnorrChallengeFinalize(tag(TAGS.challenge, rx, px, m));
254
- const sig = new SchnorrSignature(R.x, mod(k + e * d, secp256k1.CURVE.n)).toRawBytes();
159
+ const { x: px, scalar: d } = schnorrGetScalar(bytesToNum(ensureBytes(privateKey, 32)));
160
+ const a = ensureBytes(auxRand, 32); // Auxiliary random data a: a 32-byte array
161
+ // TODO: replace with proper xor?
162
+ const t = numTo32b(d ^ bytesToNum(taggedHash(TAGS.aux, a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
163
+ const rand = taggedHash(TAGS.nonce, t, px, m); // Let rand = hash/nonce(t || bytes(P) || m)
164
+ const k_ = modN(bytesToNum(rand)); // Let k' = int(rand) mod n
165
+ if (k_ === _0n)
166
+ throw new Error('sign failed: k is zero'); // Fail if k' = 0.
167
+ const { point: R, x: rx, scalar: k } = schnorrGetScalar(k_); // Let R = k'⋅G.
168
+ const e = challenge(rx, px, m); // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n.
169
+ const sig = new Uint8Array(64); // Let sig = bytes(R) || bytes((k + ed) mod n).
170
+ sig.set(numTo32b(R.px), 0);
171
+ sig.set(numTo32b(modN(k + e * d)), 32);
172
+ // If Verify(bytes(P), m, sig) (see below) returns failure, abort
255
173
  if (!schnorrVerify(sig, m, px))
256
174
  throw new Error('sign: Invalid signature produced');
257
175
  return sig;
@@ -261,30 +179,76 @@ function schnorrSign(message, privateKey, auxRand = randomBytes(32)) {
261
179
  */
262
180
  function schnorrVerify(signature, message, publicKey) {
263
181
  try {
264
- const raw = signature instanceof SchnorrSignature;
265
- const sig = raw ? signature : SchnorrSignature.fromHex(signature);
266
- if (raw)
267
- sig.assertValidity(); // just in case
268
- const { r, s } = sig;
269
- const m = ensureBytes(message);
270
- const P = normalizePublicKey(publicKey);
271
- const e = schnorrChallengeFinalize(taggedHash(TAGS.challenge, numTo32b(r), toRawX(P), m));
272
- // Finalize
273
- // R = s⋅G - e⋅P
274
- // -eP == (n-e)P
275
- const R = secp256k1.Point.BASE.multiplyAndAddUnsafe(P, normalizePrivateKey(s), mod(-e, secp256k1.CURVE.n));
276
- if (!R || !R.hasEvenY() || R.x !== r)
182
+ const P = lift_x(bytesToNum(ensureBytes(publicKey, 32))); // P = lift_x(int(pk)); fail if that fails
183
+ const sig = ensureBytes(signature, 64);
184
+ const r = bytesToNum(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
185
+ if (!fe(r))
186
+ return false;
187
+ const s = bytesToNum(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
188
+ if (!ge(s))
277
189
  return false;
278
- return true;
190
+ const m = ensureBytes(message);
191
+ const e = challenge(numTo32b(r), toRawX(P), m); // int(challenge(bytes(r)||bytes(P)||m)) mod n
192
+ const R = GmulAdd(P, s, modN(-e)); // R = s⋅G - e⋅P
193
+ if (!R || !R.hasEvenY() || R.toAffine().x !== r)
194
+ return false; // -eP == (n-e)P
195
+ return true; // Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r.
279
196
  }
280
197
  catch (error) {
281
198
  return false;
282
199
  }
283
200
  }
284
201
  export const schnorr = {
285
- Signature: SchnorrSignature,
286
202
  // Schnorr's pubkey is just `x` of Point (BIP340)
287
- getPublicKey: (privateKey) => toRawX(secp256k1.Point.fromPrivateKey(privateKey)),
203
+ getPublicKey: schnorrGetPublicKey,
288
204
  sign: schnorrSign,
289
205
  verify: schnorrVerify,
206
+ utils: { lift_x, int: bytesToNum, taggedHash },
290
207
  };
208
+ const isoMap = htf.isogenyMap(Fp, [
209
+ // xNum
210
+ [
211
+ '0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7',
212
+ '0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581',
213
+ '0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262',
214
+ '0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c',
215
+ ],
216
+ // xDen
217
+ [
218
+ '0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b',
219
+ '0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14',
220
+ '0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
221
+ ],
222
+ // yNum
223
+ [
224
+ '0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c',
225
+ '0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3',
226
+ '0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931',
227
+ '0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84',
228
+ ],
229
+ // yDen
230
+ [
231
+ '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b',
232
+ '0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573',
233
+ '0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f',
234
+ '0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
235
+ ],
236
+ ].map((i) => i.map((j) => BigInt(j))));
237
+ const mapSWU = mapToCurveSimpleSWU(Fp, {
238
+ A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
239
+ B: BigInt('1771'),
240
+ Z: Fp.create(BigInt('-11')),
241
+ });
242
+ const { hashToCurve, encodeToCurve } = htf.hashToCurve(secp256k1.ProjectivePoint, (scalars) => {
243
+ const { x, y } = mapSWU(Fp.create(scalars[0]));
244
+ return isoMap(x, y);
245
+ }, {
246
+ DST: 'secp256k1_XMD:SHA-256_SSWU_RO_',
247
+ encodeDST: 'secp256k1_XMD:SHA-256_SSWU_NU_',
248
+ p: Fp.ORDER,
249
+ m: 1,
250
+ k: 128,
251
+ expand: 'xmd',
252
+ hash: sha256,
253
+ });
254
+ export { hashToCurve, encodeToCurve };