@noble/curves 1.1.0 → 1.3.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 (101) hide show
  1. package/README.md +295 -258
  2. package/abstract/bls.d.ts +27 -10
  3. package/abstract/bls.d.ts.map +1 -1
  4. package/abstract/bls.js +60 -10
  5. package/abstract/bls.js.map +1 -1
  6. package/abstract/curve.js.map +1 -1
  7. package/abstract/edwards.js.map +1 -1
  8. package/abstract/hash-to-curve.d.ts +2 -2
  9. package/abstract/hash-to-curve.d.ts.map +1 -1
  10. package/abstract/hash-to-curve.js +22 -16
  11. package/abstract/hash-to-curve.js.map +1 -1
  12. package/abstract/modular.d.ts +51 -11
  13. package/abstract/modular.d.ts.map +1 -1
  14. package/abstract/modular.js +79 -21
  15. package/abstract/modular.js.map +1 -1
  16. package/abstract/montgomery.d.ts.map +1 -1
  17. package/abstract/montgomery.js +5 -7
  18. package/abstract/montgomery.js.map +1 -1
  19. package/abstract/poseidon.d.ts.map +1 -1
  20. package/abstract/poseidon.js +39 -41
  21. package/abstract/poseidon.js.map +1 -1
  22. package/abstract/utils.d.ts +3 -1
  23. package/abstract/utils.d.ts.map +1 -1
  24. package/abstract/utils.js +56 -31
  25. package/abstract/utils.js.map +1 -1
  26. package/abstract/weierstrass.d.ts +25 -28
  27. package/abstract/weierstrass.d.ts.map +1 -1
  28. package/abstract/weierstrass.js +17 -15
  29. package/abstract/weierstrass.js.map +1 -1
  30. package/bls12-381.d.ts.map +1 -1
  31. package/bls12-381.js +142 -88
  32. package/bls12-381.js.map +1 -1
  33. package/bn254.d.ts +3 -2
  34. package/bn254.d.ts.map +1 -1
  35. package/bn254.js +3 -2
  36. package/bn254.js.map +1 -1
  37. package/ed25519.d.ts +5 -2
  38. package/ed25519.d.ts.map +1 -1
  39. package/ed25519.js +17 -8
  40. package/ed25519.js.map +1 -1
  41. package/ed448.d.ts +53 -2
  42. package/ed448.d.ts.map +1 -1
  43. package/ed448.js +216 -29
  44. package/ed448.js.map +1 -1
  45. package/esm/abstract/bls.js +61 -11
  46. package/esm/abstract/bls.js.map +1 -1
  47. package/esm/abstract/curve.js.map +1 -1
  48. package/esm/abstract/edwards.js.map +1 -1
  49. package/esm/abstract/hash-to-curve.js +23 -17
  50. package/esm/abstract/hash-to-curve.js.map +1 -1
  51. package/esm/abstract/modular.js +75 -20
  52. package/esm/abstract/modular.js.map +1 -1
  53. package/esm/abstract/montgomery.js +5 -7
  54. package/esm/abstract/montgomery.js.map +1 -1
  55. package/esm/abstract/poseidon.js +39 -41
  56. package/esm/abstract/poseidon.js.map +1 -1
  57. package/esm/abstract/utils.js +54 -30
  58. package/esm/abstract/utils.js.map +1 -1
  59. package/esm/abstract/weierstrass.js +17 -15
  60. package/esm/abstract/weierstrass.js.map +1 -1
  61. package/esm/bls12-381.js +143 -89
  62. package/esm/bls12-381.js.map +1 -1
  63. package/esm/bn254.js +3 -2
  64. package/esm/bn254.js.map +1 -1
  65. package/esm/ed25519.js +17 -8
  66. package/esm/ed25519.js.map +1 -1
  67. package/esm/ed448.js +218 -32
  68. package/esm/ed448.js.map +1 -1
  69. package/esm/jubjub.js +1 -1
  70. package/esm/jubjub.js.map +1 -1
  71. package/esm/p256.js +2 -2
  72. package/esm/p256.js.map +1 -1
  73. package/esm/p384.js +2 -2
  74. package/esm/p384.js.map +1 -1
  75. package/esm/p521.js +3 -3
  76. package/esm/p521.js.map +1 -1
  77. package/esm/package.json +1 -4
  78. package/esm/secp256k1.js +6 -6
  79. package/esm/secp256k1.js.map +1 -1
  80. package/jubjub.js.map +1 -1
  81. package/p256.js +2 -2
  82. package/p256.js.map +1 -1
  83. package/p384.js +2 -2
  84. package/p384.js.map +1 -1
  85. package/p521.js +3 -3
  86. package/p521.js.map +1 -1
  87. package/package.json +7 -6
  88. package/secp256k1.js +6 -6
  89. package/secp256k1.js.map +1 -1
  90. package/src/abstract/bls.ts +120 -22
  91. package/src/abstract/hash-to-curve.ts +24 -17
  92. package/src/abstract/modular.ts +81 -22
  93. package/src/abstract/montgomery.ts +4 -6
  94. package/src/abstract/poseidon.ts +39 -40
  95. package/src/abstract/utils.ts +55 -26
  96. package/src/abstract/weierstrass.ts +29 -18
  97. package/src/bls12-381.ts +132 -75
  98. package/src/bn254.ts +3 -2
  99. package/src/ed25519.ts +19 -8
  100. package/src/ed448.ts +267 -34
  101. package/src/jubjub.ts +1 -1
package/src/bls12-381.ts CHANGED
@@ -1,15 +1,9 @@
1
1
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
2
 
3
- // bls12-381 pairing-friendly Barreto-Lynn-Scott elliptic curve construction allows to:
4
- // - Construct zk-SNARKs at the 128-bit security
5
- // - Use threshold signatures, which allows a user to sign lots of messages with one signature and
6
- // verify them swiftly in a batch, using Boneh-Lynn-Shacham signature scheme.
7
- //
8
- // The library uses G1 for public keys and G2 for signatures. Support for G1 signatures is planned.
9
- // Compatible with Algorand, Chia, Dfinity, Ethereum, FIL, Zcash. Matches specs
10
- // [pairing-curves-11](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11),
11
- // [bls-sigs-04](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04),
12
- // [hash-to-curve-12](https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-12).
3
+ // bls12-381 is pairing-friendly Barreto-Lynn-Scott elliptic curve construction allowing to:
4
+ // - Construct zk-SNARKs at the 120-bit security
5
+ // - Efficiently verify N aggregate signatures with 1 pairing and N ec additions:
6
+ // the Boneh-Lynn-Shacham signature scheme is orders of magnitude more efficient than Schnorr
13
7
  //
14
8
  // ### Summary
15
9
  // 1. BLS Relies on Bilinear Pairing (expensive)
@@ -25,8 +19,17 @@
25
19
  // - `S = pk x H(m)` - signing
26
20
  // - `e(P, H(m)) == e(G, S)` - verification using pairings
27
21
  // - `e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))` - signature aggregation
28
- // Filecoin uses little endian byte arrays for private keys -
29
- // so ensure to reverse byte order if you'll use it with FIL.
22
+ //
23
+ // ### Compatibility and notes
24
+ // 1. It is compatible with Algorand, Chia, Dfinity, Ethereum, Filecoin, ZEC
25
+ // Filecoin uses little endian byte arrays for private keys - make sure to reverse byte order.
26
+ // 2. Some projects use G2 for public keys and G1 for signatures. It's called "short signature"
27
+ // 3. Curve security level is about 120 bits as per Barbulescu-Duquesne 2017
28
+ // https://hal.science/hal-01534101/file/main.pdf
29
+ // 4. Compatible with specs:
30
+ // [cfrg-pairing-friendly-curves-11](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11),
31
+ // [cfrg-bls-signature-05](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-05),
32
+ // [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380).
30
33
  import { sha256 } from '@noble/hashes/sha256';
31
34
  import { randomBytes } from '@noble/hashes/utils';
32
35
  import { bls, CurveFn } from './abstract/bls.js';
@@ -37,7 +40,6 @@ import {
37
40
  numberToBytesBE,
38
41
  bytesToNumberBE,
39
42
  bitLen,
40
- bitSet,
41
43
  bitGet,
42
44
  Hex,
43
45
  bitMask,
@@ -177,7 +179,7 @@ const Fp2: mod.IField<Fp2> & Fp2Utils = {
177
179
  if (im1 > im2 || (im1 === im2 && re1 > re2)) return x1;
178
180
  return x2;
179
181
  },
180
- // Same as sgn0_fp2 in draft-irtf-cfrg-hash-to-curve-16
182
+ // Same as sgn0_m_eq_2 in RFC 9380
181
183
  isOdd: (x: Fp2) => {
182
184
  const { re: x0, im: x1 } = Fp2.reim(x);
183
185
  const sign_0 = x0 % _2n;
@@ -780,8 +782,7 @@ const FP12_FROBENIUS_COEFFICIENTS = [
780
782
 
781
783
  // HashToCurve
782
784
 
783
- // 3-isogeny map from E' to E
784
- // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-E.3
785
+ // 3-isogeny map from E' to E https://www.rfc-editor.org/rfc/rfc9380#appendix-E.3
785
786
  const isogenyMapG2 = isogenyMap(
786
787
  Fp2,
787
788
  [
@@ -985,7 +986,7 @@ function G2psi2(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
985
986
  //
986
987
  // Parameter definitions are in section 5.3 of the spec unless otherwise noted.
987
988
  // Parameter values come from section 8.8.2 of the spec.
988
- // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.8.2
989
+ // https://www.rfc-editor.org/rfc/rfc9380#section-8.8.2
989
990
  //
990
991
  // Base field F is GF(p^m)
991
992
  // p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
@@ -1017,11 +1018,41 @@ const htfDefaults = Object.freeze({
1017
1018
 
1018
1019
  // Encoding utils
1019
1020
  // Point on G1 curve: (x, y)
1020
- const C_BIT_POS = Fp.BITS; // C_bit, compression bit for serialization flag
1021
- const I_BIT_POS = Fp.BITS + 1; // I_bit, point-at-infinity bit for serialization flag
1022
- const S_BIT_POS = Fp.BITS + 2; // S_bit, sign bit for serialization flag
1021
+
1023
1022
  // Compressed point of infinity
1024
- const COMPRESSED_ZERO = Fp.toBytes(bitSet(bitSet(_0n, I_BIT_POS, true), S_BIT_POS, true)); // set compressed & point-at-infinity bits
1023
+ const COMPRESSED_ZERO = setMask(Fp.toBytes(_0n), { infinity: true, compressed: true }); // set compressed & point-at-infinity bits
1024
+
1025
+ function parseMask(bytes: Uint8Array) {
1026
+ // Copy, so we can remove mask data. It will be removed also later, when Fp.create will call modulo.
1027
+ bytes = bytes.slice();
1028
+ const mask = bytes[0] & 0b1110_0000;
1029
+ const compressed = !!((mask >> 7) & 1); // compression bit (0b1000_0000)
1030
+ const infinity = !!((mask >> 6) & 1); // point at infinity bit (0b0100_0000)
1031
+ const sort = !!((mask >> 5) & 1); // sort bit (0b0010_0000)
1032
+ bytes[0] &= 0b0001_1111; // clear mask (zero first 3 bits)
1033
+ return { compressed, infinity, sort, value: bytes };
1034
+ }
1035
+
1036
+ function setMask(
1037
+ bytes: Uint8Array,
1038
+ mask: { compressed?: boolean; infinity?: boolean; sort?: boolean }
1039
+ ) {
1040
+ if (bytes[0] & 0b1110_0000) throw new Error('setMask: non-empty mask');
1041
+ if (mask.compressed) bytes[0] |= 0b1000_0000;
1042
+ if (mask.infinity) bytes[0] |= 0b0100_0000;
1043
+ if (mask.sort) bytes[0] |= 0b0010_0000;
1044
+ return bytes;
1045
+ }
1046
+
1047
+ function signatureG1ToRawBytes(point: ProjPointType<Fp>) {
1048
+ point.assertValidity();
1049
+ const isZero = point.equals(bls12_381.G1.ProjectivePoint.ZERO);
1050
+ const { x, y } = point.toAffine();
1051
+ if (isZero) return COMPRESSED_ZERO.slice();
1052
+ const P = Fp.ORDER;
1053
+ const sort = Boolean((y * _2n) / P);
1054
+ return setMask(numberToBytesBE(x, Fp.BYTES), { compressed: true, sort });
1055
+ }
1025
1056
 
1026
1057
  function signatureG2ToRawBytes(point: ProjPointType<Fp2>) {
1027
1058
  // NOTE: by some reasons it was missed in bls12-381, looks like bug
@@ -1033,14 +1064,16 @@ function signatureG2ToRawBytes(point: ProjPointType<Fp2>) {
1033
1064
  const { re: x0, im: x1 } = Fp2.reim(x);
1034
1065
  const { re: y0, im: y1 } = Fp2.reim(y);
1035
1066
  const tmp = y1 > _0n ? y1 * _2n : y0 * _2n;
1036
- const aflag1 = Boolean((tmp / Fp.ORDER) & _1n);
1037
- const z1 = bitSet(bitSet(x1, 381, aflag1), S_BIT_POS, true);
1067
+ const sort = Boolean((tmp / Fp.ORDER) & _1n);
1038
1068
  const z2 = x0;
1039
- return concatB(numberToBytesBE(z1, len), numberToBytesBE(z2, len));
1069
+ return concatB(
1070
+ setMask(numberToBytesBE(x1, len), { sort, compressed: true }),
1071
+ numberToBytesBE(z2, len)
1072
+ );
1040
1073
  }
1041
1074
 
1042
1075
  // To verify curve parameters, see pairing-friendly-curves spec:
1043
- // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-09
1076
+ // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11
1044
1077
  // Basic math is done over finite fields over p.
1045
1078
  // More complicated math is done over polynominal extension fields.
1046
1079
  // To simplify calculations in Fp12, we construct extension tower:
@@ -1075,7 +1108,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
1075
1108
  ),
1076
1109
  a: Fp.ZERO,
1077
1110
  b: _4n,
1078
- htfDefaults: { ...htfDefaults, m: 1 },
1111
+ htfDefaults: { ...htfDefaults, m: 1, DST: 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_' },
1079
1112
  wrapPrivateKey: true,
1080
1113
  allowInfinityPoint: true,
1081
1114
  // Checks is the point resides in prime-order subgroup.
@@ -1108,7 +1141,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
1108
1141
  },
1109
1142
  // Clear cofactor of G1
1110
1143
  // https://eprint.iacr.org/2019/403
1111
- clearCofactor: (c, point) => {
1144
+ clearCofactor: (_c, point) => {
1112
1145
  // return this.multiplyUnsafe(CURVE.h);
1113
1146
  return point.multiplyUnsafe(bls12_381.params.x).add(point); // x*P + P
1114
1147
  },
@@ -1117,26 +1150,30 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
1117
1150
  return isogenyMapG1(x, y);
1118
1151
  },
1119
1152
  fromBytes: (bytes: Uint8Array): AffinePoint<Fp> => {
1120
- bytes = bytes.slice();
1121
- if (bytes.length === 48) {
1153
+ const { compressed, infinity, sort, value } = parseMask(bytes);
1154
+ if (value.length === 48 && compressed) {
1122
1155
  // TODO: Fp.bytes
1123
1156
  const P = Fp.ORDER;
1124
- const compressedValue = bytesToNumberBE(bytes);
1125
- const bflag = bitGet(compressedValue, I_BIT_POS);
1157
+ const compressedValue = bytesToNumberBE(value);
1126
1158
  // Zero
1127
- if (bflag === _1n) return { x: _0n, y: _0n };
1128
1159
  const x = Fp.create(compressedValue & Fp.MASK);
1160
+ if (infinity) {
1161
+ if (x !== _0n) throw new Error('G1: non-empty compressed point at infinity');
1162
+ return { x: _0n, y: _0n };
1163
+ }
1129
1164
  const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.params.G1b)); // y² = x³ + b
1130
1165
  let y = Fp.sqrt(right);
1131
1166
  if (!y) throw new Error('Invalid compressed G1 point');
1132
- const aflag = bitGet(compressedValue, C_BIT_POS);
1133
- if ((y * _2n) / P !== aflag) y = Fp.neg(y);
1167
+ if ((y * _2n) / P !== BigInt(sort)) y = Fp.neg(y);
1134
1168
  return { x: Fp.create(x), y: Fp.create(y) };
1135
- } else if (bytes.length === 96) {
1169
+ } else if (value.length === 96 && !compressed) {
1136
1170
  // Check if the infinity flag is set
1137
- if ((bytes[0] & (1 << 6)) !== 0) return bls12_381.G1.ProjectivePoint.ZERO.toAffine();
1138
- const x = bytesToNumberBE(bytes.subarray(0, Fp.BYTES));
1139
- const y = bytesToNumberBE(bytes.subarray(Fp.BYTES));
1171
+ const x = bytesToNumberBE(value.subarray(0, Fp.BYTES));
1172
+ const y = bytesToNumberBE(value.subarray(Fp.BYTES));
1173
+ if (infinity) {
1174
+ if (x !== _0n || y !== _0n) throw new Error('G1: non-empty point at infinity');
1175
+ return bls12_381.G1.ProjectivePoint.ZERO.toAffine();
1176
+ }
1140
1177
  return { x: Fp.create(x), y: Fp.create(y) };
1141
1178
  } else {
1142
1179
  throw new Error('Invalid point G1, expected 48/96 bytes');
@@ -1148,10 +1185,8 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
1148
1185
  if (isCompressed) {
1149
1186
  if (isZero) return COMPRESSED_ZERO.slice();
1150
1187
  const P = Fp.ORDER;
1151
- let num;
1152
- num = bitSet(x, C_BIT_POS, Boolean((y * _2n) / P)); // set aflag
1153
- num = bitSet(num, S_BIT_POS, true);
1154
- return numberToBytesBE(num, Fp.BYTES);
1188
+ const sort = Boolean((y * _2n) / P);
1189
+ return setMask(numberToBytesBE(x, Fp.BYTES), { compressed: true, sort });
1155
1190
  } else {
1156
1191
  if (isZero) {
1157
1192
  // 2x PUBLIC_KEY_LENGTH
@@ -1162,6 +1197,30 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
1162
1197
  }
1163
1198
  }
1164
1199
  },
1200
+ ShortSignature: {
1201
+ fromHex(hex: Hex): ProjPointType<Fp> {
1202
+ const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex, 48));
1203
+ const P = Fp.ORDER;
1204
+ const compressedValue = bytesToNumberBE(value);
1205
+ // Zero
1206
+ if (infinity) return bls12_381.G1.ProjectivePoint.ZERO;
1207
+ const x = Fp.create(compressedValue & Fp.MASK);
1208
+ const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.params.G1b)); // y² = x³ + b
1209
+ let y = Fp.sqrt(right);
1210
+ if (!y) throw new Error('Invalid compressed G1 point');
1211
+ const aflag = BigInt(sort);
1212
+ if ((y * _2n) / P !== aflag) y = Fp.neg(y);
1213
+ const point = bls12_381.G1.ProjectivePoint.fromAffine({ x, y });
1214
+ point.assertValidity();
1215
+ return point;
1216
+ },
1217
+ toRawBytes(point: ProjPointType<Fp>) {
1218
+ return signatureG1ToRawBytes(point);
1219
+ },
1220
+ toHex(point: ProjPointType<Fp>) {
1221
+ return bytesToHex(signatureG1ToRawBytes(point));
1222
+ },
1223
+ },
1165
1224
  },
1166
1225
  // G2 is the order-q subgroup of E2(Fp²) : y² = x³+4(1+√−1),
1167
1226
  // where Fp2 is Fp[√−1]/(x2+1). #E2(Fp2 ) = h2q, where
@@ -1233,45 +1292,45 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
1233
1292
  return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P)
1234
1293
  },
1235
1294
  fromBytes: (bytes: Uint8Array): AffinePoint<Fp2> => {
1236
- bytes = bytes.slice();
1237
- const m_byte = bytes[0] & 0xe0;
1238
- if (m_byte === 0x20 || m_byte === 0x60 || m_byte === 0xe0) {
1239
- throw new Error('Invalid encoding flag: ' + m_byte);
1295
+ const { compressed, infinity, sort, value } = parseMask(bytes);
1296
+ if (
1297
+ (!compressed && !infinity && sort) || // 00100000
1298
+ (!compressed && infinity && sort) || // 01100000
1299
+ (sort && infinity && compressed) // 11100000
1300
+ ) {
1301
+ throw new Error('Invalid encoding flag: ' + (bytes[0] & 0b1110_0000));
1240
1302
  }
1241
- const bitC = m_byte & 0x80; // compression bit
1242
- const bitI = m_byte & 0x40; // point at infinity bit
1243
- const bitS = m_byte & 0x20; // sign bit
1244
1303
  const L = Fp.BYTES;
1245
1304
  const slc = (b: Uint8Array, from: number, to?: number) => bytesToNumberBE(b.slice(from, to));
1246
- if (bytes.length === 96 && bitC) {
1305
+ if (value.length === 96 && compressed) {
1247
1306
  const b = bls12_381.params.G2b;
1248
1307
  const P = Fp.ORDER;
1249
-
1250
- bytes[0] = bytes[0] & 0x1f; // clear flags
1251
- if (bitI) {
1308
+ if (infinity) {
1252
1309
  // check that all bytes are 0
1253
- if (bytes.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
1310
+ if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
1254
1311
  throw new Error('Invalid compressed G2 point');
1255
1312
  }
1256
1313
  return { x: Fp2.ZERO, y: Fp2.ZERO };
1257
1314
  }
1258
- const x_1 = slc(bytes, 0, L);
1259
- const x_0 = slc(bytes, L, 2 * L);
1315
+ const x_1 = slc(value, 0, L);
1316
+ const x_0 = slc(value, L, 2 * L);
1260
1317
  const x = Fp2.create({ c0: Fp.create(x_0), c1: Fp.create(x_1) });
1261
1318
  const right = Fp2.add(Fp2.pow(x, _3n), b); // y² = x³ + 4 * (u+1) = x³ + b
1262
1319
  let y = Fp2.sqrt(right);
1263
1320
  const Y_bit = y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P ? _1n : _0n;
1264
- y = bitS > 0 && Y_bit > 0 ? y : Fp2.neg(y);
1321
+ y = sort && Y_bit > 0 ? y : Fp2.neg(y);
1265
1322
  return { x, y };
1266
- } else if (bytes.length === 192 && !bitC) {
1267
- // Check if the infinity flag is set
1268
- if ((bytes[0] & (1 << 6)) !== 0) {
1323
+ } else if (value.length === 192 && !compressed) {
1324
+ if (infinity) {
1325
+ if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
1326
+ throw new Error('Invalid uncompressed G2 point');
1327
+ }
1269
1328
  return { x: Fp2.ZERO, y: Fp2.ZERO };
1270
1329
  }
1271
- const x1 = slc(bytes, 0, L);
1272
- const x0 = slc(bytes, L, 2 * L);
1273
- const y1 = slc(bytes, 2 * L, 3 * L);
1274
- const y0 = slc(bytes, 3 * L, 4 * L);
1330
+ const x1 = slc(value, 0, L);
1331
+ const x0 = slc(value, L, 2 * L);
1332
+ const y1 = slc(value, 2 * L, 3 * L);
1333
+ const y0 = slc(value, 3 * L, 4 * L);
1275
1334
  return { x: Fp2.fromBigTuple([x0, x1]), y: Fp2.fromBigTuple([y0, y1]) };
1276
1335
  } else {
1277
1336
  throw new Error('Invalid point G2, expected 96/192 bytes');
@@ -1284,10 +1343,10 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
1284
1343
  if (isCompressed) {
1285
1344
  if (isZero) return concatB(COMPRESSED_ZERO, numberToBytesBE(_0n, len));
1286
1345
  const flag = Boolean(y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P);
1287
- // set compressed & sign bits (looks like different offsets than for G1/Fp?)
1288
- let x_1 = bitSet(x.c1, C_BIT_POS, flag);
1289
- x_1 = bitSet(x_1, S_BIT_POS, true);
1290
- return concatB(numberToBytesBE(x_1, len), numberToBytesBE(x.c0, len));
1346
+ return concatB(
1347
+ setMask(numberToBytesBE(x.c1, len), { compressed: true, sort: flag }),
1348
+ numberToBytesBE(x.c0, len)
1349
+ );
1291
1350
  } else {
1292
1351
  if (isZero) return concatB(new Uint8Array([0x40]), new Uint8Array(4 * len - 1)); // bytes[0] |= 1 << 6;
1293
1352
  const { re: x0, im: x1 } = Fp2.reim(x);
@@ -1303,17 +1362,15 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
1303
1362
  Signature: {
1304
1363
  // TODO: Optimize, it's very slow because of sqrt.
1305
1364
  fromHex(hex: Hex): ProjPointType<Fp2> {
1306
- hex = ensureBytes('signatureHex', hex);
1365
+ const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex));
1307
1366
  const P = Fp.ORDER;
1308
1367
  const half = hex.length / 2;
1309
1368
  if (half !== 48 && half !== 96)
1310
1369
  throw new Error('Invalid compressed signature length, must be 96 or 192');
1311
- const z1 = bytesToNumberBE(hex.slice(0, half));
1312
- const z2 = bytesToNumberBE(hex.slice(half));
1370
+ const z1 = bytesToNumberBE(value.slice(0, half));
1371
+ const z2 = bytesToNumberBE(value.slice(half));
1313
1372
  // Indicates the infinity point
1314
- const bflag1 = bitGet(z1, I_BIT_POS);
1315
- if (bflag1 === _1n) return bls12_381.G2.ProjectivePoint.ZERO;
1316
-
1373
+ if (infinity) return bls12_381.G2.ProjectivePoint.ZERO;
1317
1374
  const x1 = Fp.create(z1 & Fp.MASK);
1318
1375
  const x2 = Fp.create(z2);
1319
1376
  const x = Fp2.create({ c0: x2, c1: x1 });
@@ -1325,7 +1382,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
1325
1382
  // Choose the y whose leftmost bit of the imaginary part is equal to the a_flag1
1326
1383
  // If y1 happens to be zero, then use the bit of y0
1327
1384
  const { re: y0, im: y1 } = Fp2.reim(y);
1328
- const aflag1 = bitGet(z1, 381);
1385
+ const aflag1 = BigInt(sort);
1329
1386
  const isGreater = y1 > _0n && (y1 * _2n) / P !== aflag1;
1330
1387
  const isZero = y1 === _0n && (y0 * _2n) / P !== aflag1;
1331
1388
  if (isGreater || isZero) y = Fp2.neg(y);
package/src/bn254.ts CHANGED
@@ -6,8 +6,9 @@ import { Field } from './abstract/modular.js';
6
6
  /**
7
7
  * bn254 pairing-friendly curve.
8
8
  * Previously known as alt_bn_128, when it had 128-bit security.
9
- * Recent research shown it's weaker, the naming has been adjusted to its prime bit count.
10
- * https://github.com/zcash/zcash/issues/2502
9
+ * Barbulescu-Duquesne 2017 shown it's weaker: just about 100 bits,
10
+ * so the naming has been adjusted to its prime bit count
11
+ * https://hal.science/hal-01534101/file/main.pdf
11
12
  */
12
13
  export const bn254 = weierstrass({
13
14
  a: BigInt(0),
package/src/ed25519.ts CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  numberToBytesLE,
14
14
  } from './abstract/utils.js';
15
15
  import { createHasher, htfBasicOpts, expand_message_xmd } from './abstract/hash-to-curve.js';
16
- import { AffinePoint } from './abstract/curve.js';
16
+ import { AffinePoint, Group } from './abstract/curve.js';
17
17
 
18
18
  /**
19
19
  * ed25519 Twisted Edwards curve with following addons:
@@ -123,7 +123,7 @@ const ed25519Defaults = {
123
123
  uvRatio,
124
124
  } as const;
125
125
 
126
- export const ed25519 = twistedEdwards(ed25519Defaults);
126
+ export const ed25519 = /* @__PURE__ */ twistedEdwards(ed25519Defaults);
127
127
 
128
128
  function ed25519_domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {
129
129
  if (ctx.length > 255) throw new Error('Context is too big');
@@ -135,8 +135,11 @@ function ed25519_domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {
135
135
  );
136
136
  }
137
137
 
138
- export const ed25519ctx = twistedEdwards({ ...ed25519Defaults, domain: ed25519_domain });
139
- export const ed25519ph = twistedEdwards({
138
+ export const ed25519ctx = /* @__PURE__ */ twistedEdwards({
139
+ ...ed25519Defaults,
140
+ domain: ed25519_domain,
141
+ });
142
+ export const ed25519ph = /* @__PURE__ */ twistedEdwards({
140
143
  ...ed25519Defaults,
141
144
  domain: ed25519_domain,
142
145
  prehash: sha512,
@@ -340,7 +343,7 @@ function calcElligatorRistrettoMap(r0: bigint): ExtendedPoint {
340
343
  * but it should work in its own namespace: do not combine those two.
341
344
  * https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448
342
345
  */
343
- class RistPoint {
346
+ class RistPoint implements Group<RistPoint> {
344
347
  static BASE: RistPoint;
345
348
  static ZERO: RistPoint;
346
349
  // Private property to discourage combining ExtendedPoint + RistrettoPoint
@@ -468,6 +471,14 @@ class RistPoint {
468
471
  multiplyUnsafe(scalar: bigint): RistPoint {
469
472
  return new RistPoint(this.ep.multiplyUnsafe(scalar));
470
473
  }
474
+
475
+ double(): RistPoint {
476
+ return new RistPoint(this.ep.double());
477
+ }
478
+
479
+ negate(): RistPoint {
480
+ return new RistPoint(this.ep.negate());
481
+ }
471
482
  }
472
483
  export const RistrettoPoint = /* @__PURE__ */ (() => {
473
484
  if (!RistPoint.BASE) RistPoint.BASE = new RistPoint(ed25519.ExtendedPoint.BASE);
@@ -475,12 +486,12 @@ export const RistrettoPoint = /* @__PURE__ */ (() => {
475
486
  return RistPoint;
476
487
  })();
477
488
 
478
- // https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/14/
479
- // Appendix B. Hashing to ristretto255
480
- export const hash_to_ristretto255 = (msg: Uint8Array, options: htfBasicOpts) => {
489
+ // Hashing to ristretto255. https://www.rfc-editor.org/rfc/rfc9380#appendix-B
490
+ export const hashToRistretto255 = (msg: Uint8Array, options: htfBasicOpts) => {
481
491
  const d = options.DST;
482
492
  const DST = typeof d === 'string' ? utf8ToBytes(d) : d;
483
493
  const uniform_bytes = expand_message_xmd(msg, DST, 64, sha512);
484
494
  const P = RistPoint.hashToCurve(uniform_bytes);
485
495
  return P;
486
496
  };
497
+ export const hash_to_ristretto255 = hashToRistretto255; // legacy