@noble/curves 0.5.0 → 0.5.1

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.
package/lib/ed25519.js CHANGED
@@ -81,7 +81,74 @@ exports.ED25519_TORSION_SUBGROUP = [
81
81
  '0000000000000000000000000000000000000000000000000000000000000000',
82
82
  'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa',
83
83
  ];
84
- const Fp = (0, modular_js_1.Fp)(ED25519_P);
84
+ const Fp = (0, modular_js_1.Fp)(ED25519_P, undefined, true);
85
+ // Hash To Curve Elligator2 Map (NOTE: different from ristretto255 elligator)
86
+ // NOTE: very important part is usage of FpSqrtEven for ELL2_C1_EDWARDS, since
87
+ // SageMath returns different root first and everything falls apart
88
+ const ELL2_C1 = (Fp.ORDER + BigInt(3)) / BigInt(8); // 1. c1 = (q + 3) / 8 # Integer arithmetic
89
+ const ELL2_C2 = Fp.pow(_2n, ELL2_C1); // 2. c2 = 2^c1
90
+ const ELL2_C3 = Fp.sqrt(Fp.negate(Fp.ONE)); // 3. c3 = sqrt(-1)
91
+ const ELL2_C4 = (Fp.ORDER - BigInt(5)) / BigInt(8); // 4. c4 = (q - 5) / 8 # Integer arithmetic
92
+ const ELL2_J = BigInt(486662);
93
+ // prettier-ignore
94
+ function map_to_curve_elligator2_curve25519(u) {
95
+ let tv1 = Fp.square(u); // 1. tv1 = u^2
96
+ tv1 = Fp.mul(tv1, _2n); // 2. tv1 = 2 * tv1
97
+ let xd = Fp.add(tv1, Fp.ONE); // 3. xd = tv1 + 1 # Nonzero: -1 is square (mod p), tv1 is not
98
+ let x1n = Fp.negate(ELL2_J); // 4. x1n = -J # x1 = x1n / xd = -J / (1 + 2 * u^2)
99
+ let tv2 = Fp.square(xd); // 5. tv2 = xd^2
100
+ let gxd = Fp.mul(tv2, xd); // 6. gxd = tv2 * xd # gxd = xd^3
101
+ let gx1 = Fp.mul(tv1, ELL2_J); // 7. gx1 = J * tv1 # x1n + J * xd
102
+ gx1 = Fp.mul(gx1, x1n); // 8. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
103
+ gx1 = Fp.add(gx1, tv2); // 9. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
104
+ gx1 = Fp.mul(gx1, x1n); // 10. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
105
+ let tv3 = Fp.square(gxd); // 11. tv3 = gxd^2
106
+ tv2 = Fp.square(tv3); // 12. tv2 = tv3^2 # gxd^4
107
+ tv3 = Fp.mul(tv3, gxd); // 13. tv3 = tv3 * gxd # gxd^3
108
+ tv3 = Fp.mul(tv3, gx1); // 14. tv3 = tv3 * gx1 # gx1 * gxd^3
109
+ tv2 = Fp.mul(tv2, tv3); // 15. tv2 = tv2 * tv3 # gx1 * gxd^7
110
+ let y11 = Fp.pow(tv2, ELL2_C4); // 16. y11 = tv2^c4 # (gx1 * gxd^7)^((p - 5) / 8)
111
+ y11 = Fp.mul(y11, tv3); // 17. y11 = y11 * tv3 # gx1*gxd^3*(gx1*gxd^7)^((p-5)/8)
112
+ let y12 = Fp.mul(y11, ELL2_C3); // 18. y12 = y11 * c3
113
+ tv2 = Fp.square(y11); // 19. tv2 = y11^2
114
+ tv2 = Fp.mul(tv2, gxd); // 20. tv2 = tv2 * gxd
115
+ let e1 = Fp.equals(tv2, gx1); // 21. e1 = tv2 == gx1
116
+ let y1 = Fp.cmov(y12, y11, e1); // 22. y1 = CMOV(y12, y11, e1) # If g(x1) is square, this is its sqrt
117
+ let x2n = Fp.mul(x1n, tv1); // 23. x2n = x1n * tv1 # x2 = x2n / xd = 2 * u^2 * x1n / xd
118
+ let y21 = Fp.mul(y11, u); // 24. y21 = y11 * u
119
+ y21 = Fp.mul(y21, ELL2_C2); // 25. y21 = y21 * c2
120
+ let y22 = Fp.mul(y21, ELL2_C3); // 26. y22 = y21 * c3
121
+ let gx2 = Fp.mul(gx1, tv1); // 27. gx2 = gx1 * tv1 # g(x2) = gx2 / gxd = 2 * u^2 * g(x1)
122
+ tv2 = Fp.square(y21); // 28. tv2 = y21^2
123
+ tv2 = Fp.mul(tv2, gxd); // 29. tv2 = tv2 * gxd
124
+ let e2 = Fp.equals(tv2, gx2); // 30. e2 = tv2 == gx2
125
+ let y2 = Fp.cmov(y22, y21, e2); // 31. y2 = CMOV(y22, y21, e2) # If g(x2) is square, this is its sqrt
126
+ tv2 = Fp.square(y1); // 32. tv2 = y1^2
127
+ tv2 = Fp.mul(tv2, gxd); // 33. tv2 = tv2 * gxd
128
+ let e3 = Fp.equals(tv2, gx1); // 34. e3 = tv2 == gx1
129
+ let xn = Fp.cmov(x2n, x1n, e3); // 35. xn = CMOV(x2n, x1n, e3) # If e3, x = x1, else x = x2
130
+ let y = Fp.cmov(y2, y1, e3); // 36. y = CMOV(y2, y1, e3) # If e3, y = y1, else y = y2
131
+ let e4 = Fp.isOdd(y); // 37. e4 = sgn0(y) == 1 # Fix sign of y
132
+ y = Fp.cmov(y, Fp.negate(y), e3 !== e4); // 38. y = CMOV(y, -y, e3 XOR e4)
133
+ return { xMn: xn, xMd: xd, yMn: y, yMd: 1n }; // 39. return (xn, xd, y, 1)
134
+ }
135
+ const ELL2_C1_EDWARDS = (0, modular_js_1.FpSqrtEven)(Fp, Fp.negate(BigInt(486664))); // sgn0(c1) MUST equal 0
136
+ function map_to_curve_elligator2_edwards25519(u) {
137
+ const { xMn, xMd, yMn, yMd } = map_to_curve_elligator2_curve25519(u); // 1. (xMn, xMd, yMn, yMd) = map_to_curve_elligator2_curve25519(u)
138
+ let xn = Fp.mul(xMn, yMd); // 2. xn = xMn * yMd
139
+ xn = Fp.mul(xn, ELL2_C1_EDWARDS); // 3. xn = xn * c1
140
+ let xd = Fp.mul(xMd, yMn); // 4. xd = xMd * yMn # xn / xd = c1 * xM / yM
141
+ let yn = Fp.sub(xMn, xMd); // 5. yn = xMn - xMd
142
+ let yd = Fp.add(xMn, xMd); // 6. yd = xMn + xMd # (n / d - 1) / (n / d + 1) = (n - d) / (n + d)
143
+ let tv1 = Fp.mul(xd, yd); // 7. tv1 = xd * yd
144
+ let e = Fp.equals(tv1, Fp.ZERO); // 8. e = tv1 == 0
145
+ xn = Fp.cmov(xn, Fp.ZERO, e); // 9. xn = CMOV(xn, 0, e)
146
+ xd = Fp.cmov(xd, Fp.ONE, e); // 10. xd = CMOV(xd, 1, e)
147
+ yn = Fp.cmov(yn, Fp.ONE, e); // 11. yn = CMOV(yn, 1, e)
148
+ yd = Fp.cmov(yd, Fp.ONE, e); // 12. yd = CMOV(yd, 1, e)
149
+ const inv = Fp.invertBatch([xd, yd]); // batch division
150
+ return { x: Fp.mul(xn, inv[0]), y: Fp.mul(yn, inv[1]) }; // 13. return (xn, xd, yn, yd)
151
+ }
85
152
  const ED25519_DEF = {
86
153
  // Param: a
87
154
  a: BigInt(-1),
@@ -110,14 +177,10 @@ const ED25519_DEF = {
110
177
  p: Fp.ORDER,
111
178
  m: 1,
112
179
  k: 128,
113
- expand: true,
180
+ expand: 'xmd',
114
181
  hash: sha512_1.sha512,
115
182
  },
116
- mapToCurve: (scalars) => {
117
- throw new Error('Not supported yet');
118
- // const { x, y } = calcElligatorRistrettoMap(scalars[0]).toAffine();
119
- // return { x, y };
120
- },
183
+ mapToCurve: (scalars) => map_to_curve_elligator2_edwards25519(scalars[0]),
121
184
  };
122
185
  exports.ed25519 = (0, edwards_js_1.twistedEdwards)(ED25519_DEF);
123
186
  function ed25519_domain(data, ctx, phflag) {
package/lib/ed448.js CHANGED
@@ -48,13 +48,87 @@ function adjustScalarBytes(bytes) {
48
48
  bytes[56] = 0; // Byte outside of group (456 buts vs 448 bits)
49
49
  return bytes;
50
50
  }
51
+ const Fp = (0, modular_js_1.Fp)(ed448P, 456, true);
52
+ // Hash To Curve Elligator2 Map
53
+ const ELL2_C1 = (Fp.ORDER - BigInt(3)) / BigInt(4); // 1. c1 = (q - 3) / 4 # Integer arithmetic
54
+ const ELL2_J = BigInt(156326);
55
+ function map_to_curve_elligator2_curve448(u) {
56
+ let tv1 = Fp.square(u); // 1. tv1 = u^2
57
+ let e1 = Fp.equals(tv1, Fp.ONE); // 2. e1 = tv1 == 1
58
+ tv1 = Fp.cmov(tv1, Fp.ZERO, e1); // 3. tv1 = CMOV(tv1, 0, e1) # If Z * u^2 == -1, set tv1 = 0
59
+ let xd = Fp.sub(Fp.ONE, tv1); // 4. xd = 1 - tv1
60
+ let x1n = Fp.negate(ELL2_J); // 5. x1n = -J
61
+ let tv2 = Fp.square(xd); // 6. tv2 = xd^2
62
+ let gxd = Fp.mul(tv2, xd); // 7. gxd = tv2 * xd # gxd = xd^3
63
+ let gx1 = Fp.mul(tv1, Fp.negate(ELL2_J)); // 8. gx1 = -J * tv1 # x1n + J * xd
64
+ gx1 = Fp.mul(gx1, x1n); // 9. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
65
+ gx1 = Fp.add(gx1, tv2); // 10. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
66
+ gx1 = Fp.mul(gx1, x1n); // 11. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
67
+ let tv3 = Fp.square(gxd); // 12. tv3 = gxd^2
68
+ tv2 = Fp.mul(gx1, gxd); // 13. tv2 = gx1 * gxd # gx1 * gxd
69
+ tv3 = Fp.mul(tv3, tv2); // 14. tv3 = tv3 * tv2 # gx1 * gxd^3
70
+ let y1 = Fp.pow(tv3, ELL2_C1); // 15. y1 = tv3^c1 # (gx1 * gxd^3)^((p - 3) / 4)
71
+ y1 = Fp.mul(y1, tv2); // 16. y1 = y1 * tv2 # gx1 * gxd * (gx1 * gxd^3)^((p - 3) / 4)
72
+ let x2n = Fp.mul(x1n, Fp.negate(tv1)); // 17. x2n = -tv1 * x1n # x2 = x2n / xd = -1 * u^2 * x1n / xd
73
+ let y2 = Fp.mul(y1, u); // 18. y2 = y1 * u
74
+ y2 = Fp.cmov(y2, Fp.ZERO, e1); // 19. y2 = CMOV(y2, 0, e1)
75
+ tv2 = Fp.square(y1); // 20. tv2 = y1^2
76
+ tv2 = Fp.mul(tv2, gxd); // 21. tv2 = tv2 * gxd
77
+ let e2 = Fp.equals(tv2, gx1); // 22. e2 = tv2 == gx1
78
+ let xn = Fp.cmov(x2n, x1n, e2); // 23. xn = CMOV(x2n, x1n, e2) # If e2, x = x1, else x = x2
79
+ let y = Fp.cmov(y2, y1, e2); // 24. y = CMOV(y2, y1, e2) # If e2, y = y1, else y = y2
80
+ let e3 = Fp.isOdd(y); // 25. e3 = sgn0(y) == 1 # Fix sign of y
81
+ y = Fp.cmov(y, Fp.negate(y), e2 !== e3); // 26. y = CMOV(y, -y, e2 XOR e3)
82
+ return { xn, xd, yn: y, yd: Fp.ONE }; // 27. return (xn, xd, y, 1)
83
+ }
84
+ function map_to_curve_elligator2_edwards448(u) {
85
+ let { xn, xd, yn, yd } = map_to_curve_elligator2_curve448(u); // 1. (xn, xd, yn, yd) = map_to_curve_elligator2_curve448(u)
86
+ let xn2 = Fp.square(xn); // 2. xn2 = xn^2
87
+ let xd2 = Fp.square(xd); // 3. xd2 = xd^2
88
+ let xd4 = Fp.square(xd2); // 4. xd4 = xd2^2
89
+ let yn2 = Fp.square(yn); // 5. yn2 = yn^2
90
+ let yd2 = Fp.square(yd); // 6. yd2 = yd^2
91
+ let xEn = Fp.sub(xn2, xd2); // 7. xEn = xn2 - xd2
92
+ let tv2 = Fp.sub(xEn, xd2); // 8. tv2 = xEn - xd2
93
+ xEn = Fp.mul(xEn, xd2); // 9. xEn = xEn * xd2
94
+ xEn = Fp.mul(xEn, yd); // 10. xEn = xEn * yd
95
+ xEn = Fp.mul(xEn, yn); // 11. xEn = xEn * yn
96
+ xEn = Fp.mul(xEn, 4n); // 12. xEn = xEn * 4
97
+ tv2 = Fp.mul(tv2, xn2); // 13. tv2 = tv2 * xn2
98
+ tv2 = Fp.mul(tv2, yd2); // 14. tv2 = tv2 * yd2
99
+ let tv3 = Fp.mul(yn2, 4n); // 15. tv3 = 4 * yn2
100
+ let tv1 = Fp.add(tv3, yd2); // 16. tv1 = tv3 + yd2
101
+ tv1 = Fp.mul(tv1, xd4); // 17. tv1 = tv1 * xd4
102
+ let xEd = Fp.add(tv1, tv2); // 18. xEd = tv1 + tv2
103
+ tv2 = Fp.mul(tv2, xn); // 19. tv2 = tv2 * xn
104
+ let tv4 = Fp.mul(xn, xd4); // 20. tv4 = xn * xd4
105
+ let yEn = Fp.sub(tv3, yd2); // 21. yEn = tv3 - yd2
106
+ yEn = Fp.mul(yEn, tv4); // 22. yEn = yEn * tv4
107
+ yEn = Fp.sub(yEn, tv2); // 23. yEn = yEn - tv2
108
+ tv1 = Fp.add(xn2, xd2); // 24. tv1 = xn2 + xd2
109
+ tv1 = Fp.mul(tv1, xd2); // 25. tv1 = tv1 * xd2
110
+ tv1 = Fp.mul(tv1, xd); // 26. tv1 = tv1 * xd
111
+ tv1 = Fp.mul(tv1, yn2); // 27. tv1 = tv1 * yn2
112
+ tv1 = Fp.mul(tv1, BigInt(-2)); // 28. tv1 = -2 * tv1
113
+ let yEd = Fp.add(tv2, tv1); // 29. yEd = tv2 + tv1
114
+ tv4 = Fp.mul(tv4, yd2); // 30. tv4 = tv4 * yd2
115
+ yEd = Fp.add(yEd, tv4); // 31. yEd = yEd + tv4
116
+ tv1 = Fp.mul(xEd, yEd); // 32. tv1 = xEd * yEd
117
+ let e = Fp.equals(tv1, Fp.ZERO); // 33. e = tv1 == 0
118
+ xEn = Fp.cmov(xEn, Fp.ZERO, e); // 34. xEn = CMOV(xEn, 0, e)
119
+ xEd = Fp.cmov(xEd, Fp.ONE, e); // 35. xEd = CMOV(xEd, 1, e)
120
+ yEn = Fp.cmov(yEn, Fp.ONE, e); // 36. yEn = CMOV(yEn, 1, e)
121
+ yEd = Fp.cmov(yEd, Fp.ONE, e); // 37. yEd = CMOV(yEd, 1, e)
122
+ const inv = Fp.invertBatch([xEd, yEd]); // batch division
123
+ return { x: Fp.mul(xEn, inv[0]), y: Fp.mul(yEn, inv[1]) }; // 38. return (xEn, xEd, yEn, yEd)
124
+ }
51
125
  const ED448_DEF = {
52
126
  // Param: a
53
127
  a: BigInt(1),
54
128
  // -39081. Negative number is P - number
55
129
  d: BigInt('726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018326358'),
56
130
  // Finite field 𝔽p over which we'll do calculations; 2n ** 448n - 2n ** 224n - 1n
57
- Fp: (0, modular_js_1.Fp)(ed448P, 456),
131
+ Fp,
58
132
  // Subgroup order: how many points ed448 has; 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n
59
133
  n: BigInt('181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779'),
60
134
  nBitLength: 456,
@@ -94,6 +168,15 @@ const ED448_DEF = {
94
168
  // square root exists, and the decoding fails.
95
169
  return { isValid: (0, modular_js_1.mod)(x2 * v, P) === u, value: x };
96
170
  },
171
+ htfDefaults: {
172
+ DST: 'edwards448_XOF:SHAKE256_ELL2_RO_',
173
+ p: Fp.ORDER,
174
+ m: 1,
175
+ k: 224,
176
+ expand: 'xof',
177
+ hash: sha3_1.shake256,
178
+ },
179
+ mapToCurve: (scalars) => map_to_curve_elligator2_edwards448(scalars[0]),
97
180
  };
98
181
  exports.ed448 = (0, edwards_js_1.twistedEdwards)(ED448_DEF);
99
182
  // NOTE: there is no ed448ctx, since ed448 supports ctx by default
@@ -10,7 +10,7 @@ export function validateHTFOpts(opts) {
10
10
  throw new Error('Invalid htf/m');
11
11
  if (typeof opts.k !== 'number')
12
12
  throw new Error('Invalid htf/k');
13
- if (typeof opts.expand !== 'boolean')
13
+ if (opts.expand !== 'xmd' && opts.expand !== 'xof' && opts.expand !== undefined)
14
14
  throw new Error('Invalid htf/expand');
15
15
  if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
16
16
  throw new Error('Invalid htf/hash function');
@@ -75,13 +75,31 @@ export function expand_message_xmd(msg, DST, lenInBytes, H) {
75
75
  const pseudo_random_bytes = concatBytes(...b);
76
76
  return pseudo_random_bytes.slice(0, lenInBytes);
77
77
  }
78
- // hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
79
- // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
80
- // Inputs:
81
- // msg - a byte string containing the message to hash.
82
- // count - the number of elements of F to output.
83
- // Outputs:
84
- // [u_0, ..., u_(count - 1)], a list of field elements.
78
+ export function expand_message_xof(msg, DST, lenInBytes, k, H) {
79
+ // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
80
+ // DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
81
+ if (DST.length > 255) {
82
+ const dkLen = Math.ceil((2 * k) / 8);
83
+ DST = H.create({ dkLen }).update(stringToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
84
+ }
85
+ if (lenInBytes > 65535 || DST.length > 255)
86
+ throw new Error('expand_message_xof: invalid lenInBytes');
87
+ return (H.create({ dkLen: lenInBytes })
88
+ .update(msg)
89
+ .update(i2osp(lenInBytes, 2))
90
+ // 2. DST_prime = DST || I2OSP(len(DST), 1)
91
+ .update(DST)
92
+ .update(i2osp(DST.length, 1))
93
+ .digest());
94
+ }
95
+ /**
96
+ * Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
97
+ * https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
98
+ * @param msg a byte string containing the message to hash
99
+ * @param count the number of elements of F to output
100
+ * @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
101
+ * @returns [u_0, ..., u_(count - 1)], a list of field elements.
102
+ */
85
103
  export function hash_to_field(msg, count, options) {
86
104
  // if options is provided but incomplete, fill any missing fields with the
87
105
  // value in hftDefaults (ie hash to G2).
@@ -90,9 +108,12 @@ export function hash_to_field(msg, count, options) {
90
108
  const len_in_bytes = count * options.m * L;
91
109
  const DST = stringToBytes(options.DST);
92
110
  let pseudo_random_bytes = msg;
93
- if (options.expand) {
111
+ if (options.expand === 'xmd') {
94
112
  pseudo_random_bytes = expand_message_xmd(msg, DST, len_in_bytes, options.hash);
95
113
  }
114
+ else if (options.expand === 'xof') {
115
+ pseudo_random_bytes = expand_message_xof(msg, DST, len_in_bytes, options.k, options.hash);
116
+ }
96
117
  const u = new Array(count);
97
118
  for (let i = 0; i < count; i++) {
98
119
  const e = new Array(options.m);
@@ -1,10 +1,11 @@
1
1
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
+ // TODO: remove circular imports
2
3
  import * as utils from './utils.js';
3
4
  // Utilities for modular arithmetics and finite fields
4
5
  // prettier-ignore
5
6
  const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
6
7
  // prettier-ignore
7
- const _4n = BigInt(4), _5n = BigInt(5), _7n = BigInt(7), _8n = BigInt(8);
8
+ const _4n = BigInt(4), _5n = BigInt(5), _8n = BigInt(8);
8
9
  // prettier-ignore
9
10
  const _9n = BigInt(9), _16n = BigInt(16);
10
11
  // Calculates a modulo b
@@ -66,25 +67,66 @@ export function invert(number, modulo) {
66
67
  throw new Error('invert: does not exist');
67
68
  return mod(x, modulo);
68
69
  }
69
- /**
70
- * Calculates Legendre symbol (a | p), which denotes the value of a^((p-1)/2) (mod p).
71
- * * (a | p) ≡ 1 if a is a square (mod p)
72
- * * (a | p) -1 if a is not a square (mod p)
73
- * * (a | p) ≡ 0 if a ≡ 0 (mod p)
74
- */
75
- export function legendre(num, fieldPrime) {
76
- return pow(num, (fieldPrime - _1n) / _2n, fieldPrime);
70
+ // Tonelli-Shanks algorithm
71
+ // https://eprint.iacr.org/2012/685.pdf (page 12)
72
+ export function tonelliShanks(P) {
73
+ // Legendre constant: used to calculate Legendre symbol (a | p),
74
+ // which denotes the value of a^((p-1)/2) (mod p).
75
+ // (a | p) ≡ 1 if a is a square (mod p)
76
+ // (a | p) ≡ -1 if a is not a square (mod p)
77
+ // (a | p) 0 if a ≡ 0 (mod p)
78
+ const legendreC = (P - _1n) / _2n;
79
+ let Q, S, Z;
80
+ // Step 1: By factoring out powers of 2 from p - 1,
81
+ // find q and s such that p - 1 = q2s with q odd
82
+ for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++)
83
+ ;
84
+ // Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
85
+ for (Z = _2n; Z < P && pow(Z, legendreC, P) !== P - _1n; Z++)
86
+ ;
87
+ // Fast-path
88
+ if (S === 1) {
89
+ const p1div4 = (P + _1n) / _4n;
90
+ return function tonelliFast(Fp, n) {
91
+ const root = Fp.pow(n, p1div4);
92
+ if (!Fp.equals(Fp.square(root), n))
93
+ throw new Error('Cannot find square root');
94
+ return root;
95
+ };
96
+ }
97
+ // Slow-path
98
+ const Q1div2 = (Q + _1n) / _2n;
99
+ return function tonelliSlow(Fp, n) {
100
+ // Step 0: Check that n is indeed a square: (n | p) must be ≡ 1
101
+ if (Fp.pow(n, legendreC) !== Fp.ONE)
102
+ throw new Error('Cannot find square root');
103
+ let s = S;
104
+ let c = pow(Z, Q, P);
105
+ let r = Fp.pow(n, Q1div2);
106
+ let t = Fp.pow(n, Q);
107
+ let t2 = Fp.ZERO;
108
+ while (!Fp.equals(Fp.sub(t, Fp.ONE), Fp.ZERO)) {
109
+ t2 = Fp.square(t);
110
+ let i;
111
+ for (i = 1; i < s; i++) {
112
+ // stop if t2-1 == 0
113
+ if (Fp.equals(Fp.sub(t2, Fp.ONE), Fp.ZERO))
114
+ break;
115
+ // t2 *= t2
116
+ t2 = Fp.square(t2);
117
+ }
118
+ let b = pow(c, BigInt(1 << (s - i - 1)), P);
119
+ r = Fp.mul(r, b);
120
+ c = mod(b * b, P);
121
+ t = Fp.mul(t, c);
122
+ s = i;
123
+ }
124
+ return r;
125
+ };
77
126
  }
78
- /**
79
- * Calculates square root of a number in a finite field.
80
- * √a mod P
81
- */
82
- // TODO: rewrite as generic Fp function && remove bls versions
83
- export function sqrt(number, modulo) {
84
- // prettier-ignore
85
- const n = number;
86
- const P = modulo;
87
- const p1div4 = (P + _1n) / _4n;
127
+ export function FpSqrt(P) {
128
+ // NOTE: different algorithms can give different roots, it is up to user to decide which one they want.
129
+ // For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
88
130
  // P ≡ 3 (mod 4)
89
131
  // √n = n^((P+1)/4)
90
132
  if (P % _4n === _3n) {
@@ -92,50 +134,53 @@ export function sqrt(number, modulo) {
92
134
  // const ORDER =
93
135
  // 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;
94
136
  // const NUM = 72057594037927816n;
95
- // TODO: fix sqrtMod in secp256k1
96
- const root = pow(n, p1div4, P);
97
- if (mod(root * root, modulo) !== number)
98
- throw new Error('Cannot find square root');
99
- return root;
137
+ const p1div4 = (P + _1n) / _4n;
138
+ return function sqrt3mod4(Fp, n) {
139
+ const root = Fp.pow(n, p1div4);
140
+ // Throw if root**2 != n
141
+ if (!Fp.equals(Fp.square(root), n))
142
+ throw new Error('Cannot find square root');
143
+ return root;
144
+ };
100
145
  }
101
- // P ≡ 5 (mod 8)
146
+ // Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10)
102
147
  if (P % _8n === _5n) {
103
- const n2 = mod(n * _2n, P);
104
- const v = pow(n2, (P - _5n) / _8n, P);
105
- const nv = mod(n * v, P);
106
- const i = mod(_2n * nv * v, P);
107
- const r = mod(nv * (i - _1n), P);
108
- return r;
148
+ const c1 = (P - _5n) / _8n;
149
+ return function sqrt5mod8(Fp, n) {
150
+ const n2 = Fp.mul(n, _2n);
151
+ const v = Fp.pow(n2, c1);
152
+ const nv = Fp.mul(n, v);
153
+ const i = Fp.mul(Fp.mul(nv, _2n), v);
154
+ const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
155
+ if (!Fp.equals(Fp.square(root), n))
156
+ throw new Error('Cannot find square root');
157
+ return root;
158
+ };
109
159
  }
110
- // Other cases: Tonelli-Shanks algorithm
111
- if (legendre(n, P) !== _1n)
112
- throw new Error('Cannot find square root');
113
- let q, s, z;
114
- for (q = P - _1n, s = 0; q % _2n === _0n; q /= _2n, s++)
115
- ;
116
- if (s === 1)
117
- return pow(n, p1div4, P);
118
- for (z = _2n; z < P && legendre(z, P) !== P - _1n; z++)
119
- ;
120
- let c = pow(z, q, P);
121
- let r = pow(n, (q + _1n) / _2n, P);
122
- let t = pow(n, q, P);
123
- let t2 = _0n;
124
- while (mod(t - _1n, P) !== _0n) {
125
- t2 = mod(t * t, P);
126
- let i;
127
- for (i = 1; i < s; i++) {
128
- if (mod(t2 - _1n, P) === _0n)
129
- break;
130
- t2 = mod(t2 * t2, P);
131
- }
132
- let b = pow(c, BigInt(1 << (s - i - 1)), P);
133
- r = mod(r * b, P);
134
- c = mod(b * b, P);
135
- t = mod(t * c, P);
136
- s = i;
160
+ // P 9 (mod 16)
161
+ if (P % _16n === _9n) {
162
+ // NOTE: tonelli is too slow for bls-Fp2 calculations even on start
163
+ // Means we cannot use sqrt for constants at all!
164
+ //
165
+ // const c1 = Fp.sqrt(Fp.negate(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
166
+ // const c2 = Fp.sqrt(c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
167
+ // const c3 = Fp.sqrt(Fp.negate(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
168
+ // const c4 = (P + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
169
+ // sqrt = (x) => {
170
+ // let tv1 = Fp.pow(x, c4); // 1. tv1 = x^c4
171
+ // let tv2 = Fp.mul(c1, tv1); // 2. tv2 = c1 * tv1
172
+ // const tv3 = Fp.mul(c2, tv1); // 3. tv3 = c2 * tv1
173
+ // let tv4 = Fp.mul(c3, tv1); // 4. tv4 = c3 * tv1
174
+ // const e1 = Fp.equals(Fp.square(tv2), x); // 5. e1 = (tv2^2) == x
175
+ // const e2 = Fp.equals(Fp.square(tv3), x); // 6. e2 = (tv3^2) == x
176
+ // tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
177
+ // tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
178
+ // const e3 = Fp.equals(Fp.square(tv2), x); // 9. e3 = (tv2^2) == x
179
+ // return Fp.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select the sqrt from tv1 and tv2
180
+ // }
137
181
  }
138
- return r;
182
+ // Other cases: Tonelli-Shanks algorithm
183
+ return tonelliShanks(P);
139
184
  }
140
185
  // Little-endian check for first LE bit (last BE bit);
141
186
  export const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n;
@@ -202,17 +247,21 @@ export function FpInvertBatch(f, nums) {
202
247
  export function FpDiv(f, lhs, rhs) {
203
248
  return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.invert(rhs));
204
249
  }
205
- // NOTE: very fragile, always bench. Major performance points:
206
- // - NonNormalized ops
207
- // - Object.freeze
208
- // - same shape of object (don't add/remove keys)
250
+ // This function returns True whenever the value x is a square in the field F.
251
+ export function FpIsSquare(f) {
252
+ const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
253
+ return (x) => {
254
+ const p = f.pow(x, legendreConst);
255
+ return f.equals(p, f.ZERO) || f.equals(p, f.ONE);
256
+ };
257
+ }
209
258
  export function Fp(ORDER, bitLen, isLE = false, redef = {}) {
210
259
  if (ORDER <= _0n)
211
260
  throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
212
261
  const { nBitLength: BITS, nByteLength: BYTES } = utils.nLength(ORDER, bitLen);
213
262
  if (BYTES > 2048)
214
263
  throw new Error('Field lengths over 2048 bytes are not supported');
215
- const sqrtP = (num) => sqrt(num, ORDER);
264
+ const sqrtP = FpSqrt(ORDER);
216
265
  const f = Object.freeze({
217
266
  ORDER,
218
267
  BITS,
@@ -242,7 +291,7 @@ export function Fp(ORDER, bitLen, isLE = false, redef = {}) {
242
291
  subN: (lhs, rhs) => lhs - rhs,
243
292
  mulN: (lhs, rhs) => lhs * rhs,
244
293
  invert: (num) => invert(num, ORDER),
245
- sqrt: redef.sqrt || sqrtP,
294
+ sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
246
295
  invertBatch: (lst) => FpInvertBatch(f, lst),
247
296
  // TODO: do we really need constant cmov?
248
297
  // We don't have const-time bigints anyway, so probably will be not very useful
@@ -256,87 +305,15 @@ export function Fp(ORDER, bitLen, isLE = false, redef = {}) {
256
305
  });
257
306
  return Object.freeze(f);
258
307
  }
259
- // TODO: re-use in bls/generic sqrt for field/etc?
260
- // Something like sqrtUnsafe which always returns value, but sqrt throws exception if non-square
261
- // From draft-irtf-cfrg-hash-to-curve-16
262
- export function FpSqrt(Fp) {
263
- // NOTE: it requires another sqrt for constant precomputes, but no need for roots of unity,
264
- // probably we can simply bls code using it
265
- const q = Fp.ORDER;
266
- const squareConst = (q - _1n) / _2n;
267
- // is_square(x) := { True, if x^((q - 1) / 2) is 0 or 1 in F;
268
- // { False, otherwise.
269
- let isSquare = (x) => {
270
- const p = Fp.pow(x, squareConst);
271
- return Fp.equals(p, Fp.ZERO) || Fp.equals(p, Fp.ONE);
272
- };
273
- // Constant-time Tonelli-Shanks algorithm
274
- let l = _0n;
275
- for (let o = q - _1n; o % _2n === _0n; o /= _2n)
276
- l += _1n;
277
- const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
278
- const c2 = (q - _1n) / _2n ** c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
279
- const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
280
- // 4. c4, a non-square value in F
281
- // 5. c5 = c4^c2 in F
282
- let c4 = Fp.ONE;
283
- while (isSquare(c4))
284
- c4 = Fp.add(c4, Fp.ONE);
285
- const c5 = Fp.pow(c4, c2);
286
- let sqrt = (x) => {
287
- let z = Fp.pow(x, c3); // 1. z = x^c3
288
- let t = Fp.square(z); // 2. t = z * z
289
- t = Fp.mul(t, x); // 3. t = t * x
290
- z = Fp.mul(z, x); // 4. z = z * x
291
- let b = t; // 5. b = t
292
- let c = c5; // 6. c = c5
293
- // 7. for i in (c1, c1 - 1, ..., 2):
294
- for (let i = c1; i > 1; i--) {
295
- // 8. for j in (1, 2, ..., i - 2):
296
- // 9. b = b * b
297
- for (let j = _1n; j < i - _1n; i++)
298
- b = Fp.square(b);
299
- const e = Fp.equals(b, Fp.ONE); // 10. e = b == 1
300
- const zt = Fp.mul(z, c); // 11. zt = z * c
301
- z = Fp.cmov(zt, z, e); // 12. z = CMOV(zt, z, e)
302
- c = Fp.square(c); // 13. c = c * c
303
- let tt = Fp.mul(t, c); // 14. tt = t * c
304
- t = Fp.cmov(tt, t, e); // 15. t = CMOV(tt, t, e)
305
- b = t; // 16. b = t
306
- }
307
- return z; // 17. return z
308
- };
309
- if (q % _4n === _3n) {
310
- const c1 = (q + _1n) / _4n; // 1. c1 = (q + 1) / 4 # Integer arithmetic
311
- sqrt = (x) => Fp.pow(x, c1);
312
- }
313
- else if (q % _8n === _5n) {
314
- const c1 = Fp.sqrt(Fp.negate(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
315
- const c2 = (q + _3n) / _8n; // 2. c2 = (q + 3) / 8 # Integer arithmetic
316
- sqrt = (x) => {
317
- let tv1 = Fp.pow(x, c2); // 1. tv1 = x^c2
318
- let tv2 = Fp.mul(tv1, c1); // 2. tv2 = tv1 * c1
319
- let e = Fp.equals(Fp.square(tv1), x); // 3. e = (tv1^2) == x
320
- return Fp.cmov(tv2, tv1, e); // 4. z = CMOV(tv2, tv1, e)
321
- };
322
- }
323
- else if (Fp.ORDER % _16n === _9n) {
324
- const c1 = Fp.sqrt(Fp.negate(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
325
- const c2 = Fp.sqrt(c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
326
- const c3 = Fp.sqrt(Fp.negate(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
327
- const c4 = (Fp.ORDER + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
328
- sqrt = (x) => {
329
- let tv1 = Fp.pow(x, c4); // 1. tv1 = x^c4
330
- let tv2 = Fp.mul(c1, tv1); // 2. tv2 = c1 * tv1
331
- const tv3 = Fp.mul(c2, tv1); // 3. tv3 = c2 * tv1
332
- let tv4 = Fp.mul(c3, tv1); // 4. tv4 = c3 * tv1
333
- const e1 = Fp.equals(Fp.square(tv2), x); // 5. e1 = (tv2^2) == x
334
- const e2 = Fp.equals(Fp.square(tv3), x); // 6. e2 = (tv3^2) == x
335
- tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
336
- tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
337
- const e3 = Fp.equals(Fp.square(tv2), x); // 9. e3 = (tv2^2) == x
338
- return Fp.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select the sqrt from tv1 and tv2
339
- };
340
- }
341
- return { sqrt, isSquare };
308
+ export function FpSqrtOdd(Fp, elm) {
309
+ if (!Fp.isOdd)
310
+ throw new Error(`Field doesn't have isOdd`);
311
+ const root = Fp.sqrt(elm);
312
+ return Fp.isOdd(root) ? root : Fp.negate(root);
313
+ }
314
+ export function FpSqrtEven(Fp, elm) {
315
+ if (!Fp.isOdd)
316
+ throw new Error(`Field doesn't have isOdd`);
317
+ const root = Fp.sqrt(elm);
318
+ return Fp.isOdd(root) ? Fp.negate(root) : root;
342
319
  }
@@ -815,7 +815,7 @@ const htfDefaults = {
815
815
  k: 128,
816
816
  // option to use a message that has already been processed by
817
817
  // expand_message_xmd
818
- expand: true,
818
+ expand: 'xmd',
819
819
  // Hash functions for: expand_message_xmd is appropriate for use with a
820
820
  // wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
821
821
  // BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247