@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/esm/bls12-381.js CHANGED
@@ -1,14 +1,8 @@
1
1
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
- // bls12-381 pairing-friendly Barreto-Lynn-Scott elliptic curve construction allows to:
3
- // - Construct zk-SNARKs at the 128-bit security
4
- // - Use threshold signatures, which allows a user to sign lots of messages with one signature and
5
- // verify them swiftly in a batch, using Boneh-Lynn-Shacham signature scheme.
6
- //
7
- // The library uses G1 for public keys and G2 for signatures. Support for G1 signatures is planned.
8
- // Compatible with Algorand, Chia, Dfinity, Ethereum, FIL, Zcash. Matches specs
9
- // [pairing-curves-11](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11),
10
- // [bls-sigs-04](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04),
11
- // [hash-to-curve-12](https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-12).
2
+ // bls12-381 is pairing-friendly Barreto-Lynn-Scott elliptic curve construction allowing to:
3
+ // - Construct zk-SNARKs at the 120-bit security
4
+ // - Efficiently verify N aggregate signatures with 1 pairing and N ec additions:
5
+ // the Boneh-Lynn-Shacham signature scheme is orders of magnitude more efficient than Schnorr
12
6
  //
13
7
  // ### Summary
14
8
  // 1. BLS Relies on Bilinear Pairing (expensive)
@@ -24,13 +18,22 @@
24
18
  // - `S = pk x H(m)` - signing
25
19
  // - `e(P, H(m)) == e(G, S)` - verification using pairings
26
20
  // - `e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))` - signature aggregation
27
- // Filecoin uses little endian byte arrays for private keys -
28
- // so ensure to reverse byte order if you'll use it with FIL.
21
+ //
22
+ // ### Compatibility and notes
23
+ // 1. It is compatible with Algorand, Chia, Dfinity, Ethereum, Filecoin, ZEC
24
+ // Filecoin uses little endian byte arrays for private keys - make sure to reverse byte order.
25
+ // 2. Some projects use G2 for public keys and G1 for signatures. It's called "short signature"
26
+ // 3. Curve security level is about 120 bits as per Barbulescu-Duquesne 2017
27
+ // https://hal.science/hal-01534101/file/main.pdf
28
+ // 4. Compatible with specs:
29
+ // [cfrg-pairing-friendly-curves-11](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11),
30
+ // [cfrg-bls-signature-05](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-05),
31
+ // [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380).
29
32
  import { sha256 } from '@noble/hashes/sha256';
30
33
  import { randomBytes } from '@noble/hashes/utils';
31
34
  import { bls } from './abstract/bls.js';
32
35
  import * as mod from './abstract/modular.js';
33
- import { concatBytes as concatB, ensureBytes, numberToBytesBE, bytesToNumberBE, bitLen, bitSet, bitGet, bitMask, bytesToHex, } from './abstract/utils.js';
36
+ import { concatBytes as concatB, ensureBytes, numberToBytesBE, bytesToNumberBE, bitLen, bitGet, bitMask, bytesToHex, } from './abstract/utils.js';
34
37
  // Types
35
38
  import { mapToCurveSimpleSWU, } from './abstract/weierstrass.js';
36
39
  import { isogenyMap } from './abstract/hash-to-curve.js';
@@ -148,7 +151,7 @@ const Fp2 = {
148
151
  return x1;
149
152
  return x2;
150
153
  },
151
- // Same as sgn0_fp2 in draft-irtf-cfrg-hash-to-curve-16
154
+ // Same as sgn0_m_eq_2 in RFC 9380
152
155
  isOdd: (x) => {
153
156
  const { re: x0, im: x1 } = Fp2.reim(x);
154
157
  const sign_0 = x0 % _2n;
@@ -257,14 +260,14 @@ const Fp6Square = ({ c0, c1, c2 }) => {
257
260
  let t3 = Fp2.mul(Fp2.mul(c1, c2), _2n); // 2 * c1 * c2
258
261
  let t4 = Fp2.sqr(c2); // c2²
259
262
  return {
260
- c0: Fp2.add(Fp2.mulByNonresidue(t3), t0),
261
- c1: Fp2.add(Fp2.mulByNonresidue(t4), t1),
263
+ c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
264
+ c1: Fp2.add(Fp2.mulByNonresidue(t4), t1), // T4 * (u + 1) + T1
262
265
  // T1 + (c0 - c1 + c2)² + T3 - T0 - T4
263
266
  c2: Fp2.sub(Fp2.sub(Fp2.add(Fp2.add(t1, Fp2.sqr(Fp2.add(Fp2.sub(c0, c1), c2))), t3), t0), t4),
264
267
  };
265
268
  };
266
269
  const Fp6 = {
267
- ORDER: Fp2.ORDER,
270
+ ORDER: Fp2.ORDER, // TODO: unused, but need to verify
268
271
  BITS: 3 * Fp2.BITS,
269
272
  BYTES: 3 * Fp2.BYTES,
270
273
  MASK: bitMask(3 * Fp2.BITS),
@@ -424,7 +427,7 @@ const Fp12Multiply = ({ c0, c1 }, rhs) => {
424
427
  let t1 = Fp6.mul(c0, r0); // c0 * r0
425
428
  let t2 = Fp6.mul(c1, r1); // c1 * r1
426
429
  return {
427
- c0: Fp6.add(t1, Fp6.mulByNonresidue(t2)),
430
+ c0: Fp6.add(t1, Fp6.mulByNonresidue(t2)), // T1 + T2 * v
428
431
  // (c0 + c1) * (r0 + r1) - (T1 + T2)
429
432
  c1: Fp6.sub(Fp6.mul(Fp6.add(c0, c1), Fp6.add(r0, r1)), Fp6.add(t1, t2)),
430
433
  };
@@ -441,12 +444,12 @@ function Fp4Square(a, b) {
441
444
  const a2 = Fp2.sqr(a);
442
445
  const b2 = Fp2.sqr(b);
443
446
  return {
444
- first: Fp2.add(Fp2.mulByNonresidue(b2), a2),
447
+ first: Fp2.add(Fp2.mulByNonresidue(b2), a2), // b² * Nonresidue + a²
445
448
  second: Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(a, b)), a2), b2), // (a + b)² - a² - b²
446
449
  };
447
450
  }
448
451
  const Fp12 = {
449
- ORDER: Fp2.ORDER,
452
+ ORDER: Fp2.ORDER, // TODO: unused, but need to verify
450
453
  BITS: 2 * Fp2.BITS,
451
454
  BYTES: 2 * Fp2.BYTES,
452
455
  MASK: bitMask(2 * Fp2.BITS),
@@ -521,7 +524,7 @@ const Fp12 = {
521
524
  let t0 = Fp6.multiplyBy01(c0, o0, o1);
522
525
  let t1 = Fp6.multiplyBy1(c1, o4);
523
526
  return {
524
- c0: Fp6.add(Fp6.mulByNonresidue(t1), t0),
527
+ c0: Fp6.add(Fp6.mulByNonresidue(t1), t0), // T1 * v + T0
525
528
  // (c1 + c0) * [o0, o1+o4] - T0 - T1
526
529
  c1: Fp6.sub(Fp6.sub(Fp6.multiplyBy01(Fp6.add(c1, c0), o0, Fp2.add(o1, o4)), t0), t1),
527
530
  };
@@ -544,13 +547,13 @@ const Fp12 = {
544
547
  let t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
545
548
  return {
546
549
  c0: Fp6.create({
547
- c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), _2n), t3),
548
- c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), _2n), t5),
550
+ c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), _2n), t3), // 2 * (T3 - c0c0) + T3
551
+ c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), _2n), t5), // 2 * (T5 - c0c1) + T5
549
552
  c2: Fp2.add(Fp2.mul(Fp2.sub(t7, c0c2), _2n), t7),
550
- }),
553
+ }), // 2 * (T7 - c0c2) + T7
551
554
  c1: Fp6.create({
552
- c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), _2n), t9),
553
- c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), _2n), t4),
555
+ c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), _2n), t9), // 2 * (T9 + c1c0) + T9
556
+ c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), _2n), t4), // 2 * (T4 + c1c1) + T4
554
557
  c2: Fp2.add(Fp2.mul(Fp2.add(t6, c1c2), _2n), t6),
555
558
  }),
556
559
  }; // 2 * (T6 + c1c2) + T6
@@ -635,8 +638,7 @@ const FP12_FROBENIUS_COEFFICIENTS = [
635
638
  ].map((n) => Fp2.fromBigTuple(n));
636
639
  // END OF CURVE FIELDS
637
640
  // HashToCurve
638
- // 3-isogeny map from E' to E
639
- // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-E.3
641
+ // 3-isogeny map from E' to E https://www.rfc-editor.org/rfc/rfc9380#appendix-E.3
640
642
  const isogenyMapG2 = isogenyMap(Fp2, [
641
643
  // xNum
642
644
  [
@@ -777,8 +779,8 @@ const isogenyMapG1 = isogenyMap(Fp, [
777
779
  ].map((i) => i.map((j) => BigInt(j))));
778
780
  // SWU Map - Fp2 to G2': y² = x³ + 240i * x + 1012 + 1012i
779
781
  const G2_SWU = mapToCurveSimpleSWU(Fp2, {
780
- A: Fp2.create({ c0: Fp.create(_0n), c1: Fp.create(BigInt(240)) }),
781
- B: Fp2.create({ c0: Fp.create(BigInt(1012)), c1: Fp.create(BigInt(1012)) }),
782
+ A: Fp2.create({ c0: Fp.create(_0n), c1: Fp.create(BigInt(240)) }), // A' = 240 * I
783
+ B: Fp2.create({ c0: Fp.create(BigInt(1012)), c1: Fp.create(BigInt(1012)) }), // B' = 1012 * (1 + I)
782
784
  Z: Fp2.create({ c0: Fp.create(BigInt(-2)), c1: Fp.create(BigInt(-1)) }), // Z: -(2 + I)
783
785
  });
784
786
  // Optimized SWU Map - Fp to G1
@@ -820,7 +822,7 @@ function G2psi2(c, P) {
820
822
  //
821
823
  // Parameter definitions are in section 5.3 of the spec unless otherwise noted.
822
824
  // Parameter values come from section 8.8.2 of the spec.
823
- // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.8.2
825
+ // https://www.rfc-editor.org/rfc/rfc9380#section-8.8.2
824
826
  //
825
827
  // Base field F is GF(p^m)
826
828
  // p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
@@ -851,11 +853,39 @@ const htfDefaults = Object.freeze({
851
853
  });
852
854
  // Encoding utils
853
855
  // Point on G1 curve: (x, y)
854
- const C_BIT_POS = Fp.BITS; // C_bit, compression bit for serialization flag
855
- const I_BIT_POS = Fp.BITS + 1; // I_bit, point-at-infinity bit for serialization flag
856
- const S_BIT_POS = Fp.BITS + 2; // S_bit, sign bit for serialization flag
857
856
  // Compressed point of infinity
858
- const COMPRESSED_ZERO = Fp.toBytes(bitSet(bitSet(_0n, I_BIT_POS, true), S_BIT_POS, true)); // set compressed & point-at-infinity bits
857
+ const COMPRESSED_ZERO = setMask(Fp.toBytes(_0n), { infinity: true, compressed: true }); // set compressed & point-at-infinity bits
858
+ function parseMask(bytes) {
859
+ // Copy, so we can remove mask data. It will be removed also later, when Fp.create will call modulo.
860
+ bytes = bytes.slice();
861
+ const mask = bytes[0] & 224;
862
+ const compressed = !!((mask >> 7) & 1); // compression bit (0b1000_0000)
863
+ const infinity = !!((mask >> 6) & 1); // point at infinity bit (0b0100_0000)
864
+ const sort = !!((mask >> 5) & 1); // sort bit (0b0010_0000)
865
+ bytes[0] &= 31; // clear mask (zero first 3 bits)
866
+ return { compressed, infinity, sort, value: bytes };
867
+ }
868
+ function setMask(bytes, mask) {
869
+ if (bytes[0] & 224)
870
+ throw new Error('setMask: non-empty mask');
871
+ if (mask.compressed)
872
+ bytes[0] |= 128;
873
+ if (mask.infinity)
874
+ bytes[0] |= 64;
875
+ if (mask.sort)
876
+ bytes[0] |= 32;
877
+ return bytes;
878
+ }
879
+ function signatureG1ToRawBytes(point) {
880
+ point.assertValidity();
881
+ const isZero = point.equals(bls12_381.G1.ProjectivePoint.ZERO);
882
+ const { x, y } = point.toAffine();
883
+ if (isZero)
884
+ return COMPRESSED_ZERO.slice();
885
+ const P = Fp.ORDER;
886
+ const sort = Boolean((y * _2n) / P);
887
+ return setMask(numberToBytesBE(x, Fp.BYTES), { compressed: true, sort });
888
+ }
859
889
  function signatureG2ToRawBytes(point) {
860
890
  // NOTE: by some reasons it was missed in bls12-381, looks like bug
861
891
  point.assertValidity();
@@ -866,13 +896,12 @@ function signatureG2ToRawBytes(point) {
866
896
  const { re: x0, im: x1 } = Fp2.reim(x);
867
897
  const { re: y0, im: y1 } = Fp2.reim(y);
868
898
  const tmp = y1 > _0n ? y1 * _2n : y0 * _2n;
869
- const aflag1 = Boolean((tmp / Fp.ORDER) & _1n);
870
- const z1 = bitSet(bitSet(x1, 381, aflag1), S_BIT_POS, true);
899
+ const sort = Boolean((tmp / Fp.ORDER) & _1n);
871
900
  const z2 = x0;
872
- return concatB(numberToBytesBE(z1, len), numberToBytesBE(z2, len));
901
+ return concatB(setMask(numberToBytesBE(x1, len), { sort, compressed: true }), numberToBytesBE(z2, len));
873
902
  }
874
903
  // To verify curve parameters, see pairing-friendly-curves spec:
875
- // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-09
904
+ // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11
876
905
  // Basic math is done over finite fields over p.
877
906
  // More complicated math is done over polynominal extension fields.
878
907
  // To simplify calculations in Fp12, we construct extension tower:
@@ -903,7 +932,7 @@ export const bls12_381 = bls({
903
932
  Gy: BigInt('0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'),
904
933
  a: Fp.ZERO,
905
934
  b: _4n,
906
- htfDefaults: { ...htfDefaults, m: 1 },
935
+ htfDefaults: { ...htfDefaults, m: 1, DST: 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_' },
907
936
  wrapPrivateKey: true,
908
937
  allowInfinityPoint: true,
909
938
  // Checks is the point resides in prime-order subgroup.
@@ -932,7 +961,7 @@ export const bls12_381 = bls({
932
961
  },
933
962
  // Clear cofactor of G1
934
963
  // https://eprint.iacr.org/2019/403
935
- clearCofactor: (c, point) => {
964
+ clearCofactor: (_c, point) => {
936
965
  // return this.multiplyUnsafe(CURVE.h);
937
966
  return point.multiplyUnsafe(bls12_381.params.x).add(point); // x*P + P
938
967
  },
@@ -941,31 +970,35 @@ export const bls12_381 = bls({
941
970
  return isogenyMapG1(x, y);
942
971
  },
943
972
  fromBytes: (bytes) => {
944
- bytes = bytes.slice();
945
- if (bytes.length === 48) {
973
+ const { compressed, infinity, sort, value } = parseMask(bytes);
974
+ if (value.length === 48 && compressed) {
946
975
  // TODO: Fp.bytes
947
976
  const P = Fp.ORDER;
948
- const compressedValue = bytesToNumberBE(bytes);
949
- const bflag = bitGet(compressedValue, I_BIT_POS);
977
+ const compressedValue = bytesToNumberBE(value);
950
978
  // Zero
951
- if (bflag === _1n)
952
- return { x: _0n, y: _0n };
953
979
  const x = Fp.create(compressedValue & Fp.MASK);
980
+ if (infinity) {
981
+ if (x !== _0n)
982
+ throw new Error('G1: non-empty compressed point at infinity');
983
+ return { x: _0n, y: _0n };
984
+ }
954
985
  const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.params.G1b)); // y² = x³ + b
955
986
  let y = Fp.sqrt(right);
956
987
  if (!y)
957
988
  throw new Error('Invalid compressed G1 point');
958
- const aflag = bitGet(compressedValue, C_BIT_POS);
959
- if ((y * _2n) / P !== aflag)
989
+ if ((y * _2n) / P !== BigInt(sort))
960
990
  y = Fp.neg(y);
961
991
  return { x: Fp.create(x), y: Fp.create(y) };
962
992
  }
963
- else if (bytes.length === 96) {
993
+ else if (value.length === 96 && !compressed) {
964
994
  // Check if the infinity flag is set
965
- if ((bytes[0] & (1 << 6)) !== 0)
995
+ const x = bytesToNumberBE(value.subarray(0, Fp.BYTES));
996
+ const y = bytesToNumberBE(value.subarray(Fp.BYTES));
997
+ if (infinity) {
998
+ if (x !== _0n || y !== _0n)
999
+ throw new Error('G1: non-empty point at infinity');
966
1000
  return bls12_381.G1.ProjectivePoint.ZERO.toAffine();
967
- const x = bytesToNumberBE(bytes.subarray(0, Fp.BYTES));
968
- const y = bytesToNumberBE(bytes.subarray(Fp.BYTES));
1001
+ }
969
1002
  return { x: Fp.create(x), y: Fp.create(y) };
970
1003
  }
971
1004
  else {
@@ -979,10 +1012,8 @@ export const bls12_381 = bls({
979
1012
  if (isZero)
980
1013
  return COMPRESSED_ZERO.slice();
981
1014
  const P = Fp.ORDER;
982
- let num;
983
- num = bitSet(x, C_BIT_POS, Boolean((y * _2n) / P)); // set aflag
984
- num = bitSet(num, S_BIT_POS, true);
985
- return numberToBytesBE(num, Fp.BYTES);
1015
+ const sort = Boolean((y * _2n) / P);
1016
+ return setMask(numberToBytesBE(x, Fp.BYTES), { compressed: true, sort });
986
1017
  }
987
1018
  else {
988
1019
  if (isZero) {
@@ -995,6 +1026,33 @@ export const bls12_381 = bls({
995
1026
  }
996
1027
  }
997
1028
  },
1029
+ ShortSignature: {
1030
+ fromHex(hex) {
1031
+ const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex, 48));
1032
+ const P = Fp.ORDER;
1033
+ const compressedValue = bytesToNumberBE(value);
1034
+ // Zero
1035
+ if (infinity)
1036
+ return bls12_381.G1.ProjectivePoint.ZERO;
1037
+ const x = Fp.create(compressedValue & Fp.MASK);
1038
+ const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.params.G1b)); // y² = x³ + b
1039
+ let y = Fp.sqrt(right);
1040
+ if (!y)
1041
+ throw new Error('Invalid compressed G1 point');
1042
+ const aflag = BigInt(sort);
1043
+ if ((y * _2n) / P !== aflag)
1044
+ y = Fp.neg(y);
1045
+ const point = bls12_381.G1.ProjectivePoint.fromAffine({ x, y });
1046
+ point.assertValidity();
1047
+ return point;
1048
+ },
1049
+ toRawBytes(point) {
1050
+ return signatureG1ToRawBytes(point);
1051
+ },
1052
+ toHex(point) {
1053
+ return bytesToHex(signatureG1ToRawBytes(point));
1054
+ },
1055
+ },
998
1056
  },
999
1057
  // G2 is the order-q subgroup of E2(Fp²) : y² = x³+4(1+√−1),
1000
1058
  // where Fp2 is Fp[√−1]/(x2+1). #E2(Fp2 ) = h2q, where
@@ -1054,45 +1112,45 @@ export const bls12_381 = bls({
1054
1112
  return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P)
1055
1113
  },
1056
1114
  fromBytes: (bytes) => {
1057
- bytes = bytes.slice();
1058
- const m_byte = bytes[0] & 0xe0;
1059
- if (m_byte === 0x20 || m_byte === 0x60 || m_byte === 0xe0) {
1060
- throw new Error('Invalid encoding flag: ' + m_byte);
1115
+ const { compressed, infinity, sort, value } = parseMask(bytes);
1116
+ if ((!compressed && !infinity && sort) || // 00100000
1117
+ (!compressed && infinity && sort) || // 01100000
1118
+ (sort && infinity && compressed) // 11100000
1119
+ ) {
1120
+ throw new Error('Invalid encoding flag: ' + (bytes[0] & 224));
1061
1121
  }
1062
- const bitC = m_byte & 0x80; // compression bit
1063
- const bitI = m_byte & 0x40; // point at infinity bit
1064
- const bitS = m_byte & 0x20; // sign bit
1065
1122
  const L = Fp.BYTES;
1066
1123
  const slc = (b, from, to) => bytesToNumberBE(b.slice(from, to));
1067
- if (bytes.length === 96 && bitC) {
1124
+ if (value.length === 96 && compressed) {
1068
1125
  const b = bls12_381.params.G2b;
1069
1126
  const P = Fp.ORDER;
1070
- bytes[0] = bytes[0] & 0x1f; // clear flags
1071
- if (bitI) {
1127
+ if (infinity) {
1072
1128
  // check that all bytes are 0
1073
- if (bytes.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
1129
+ if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
1074
1130
  throw new Error('Invalid compressed G2 point');
1075
1131
  }
1076
1132
  return { x: Fp2.ZERO, y: Fp2.ZERO };
1077
1133
  }
1078
- const x_1 = slc(bytes, 0, L);
1079
- const x_0 = slc(bytes, L, 2 * L);
1134
+ const x_1 = slc(value, 0, L);
1135
+ const x_0 = slc(value, L, 2 * L);
1080
1136
  const x = Fp2.create({ c0: Fp.create(x_0), c1: Fp.create(x_1) });
1081
1137
  const right = Fp2.add(Fp2.pow(x, _3n), b); // y² = x³ + 4 * (u+1) = x³ + b
1082
1138
  let y = Fp2.sqrt(right);
1083
1139
  const Y_bit = y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P ? _1n : _0n;
1084
- y = bitS > 0 && Y_bit > 0 ? y : Fp2.neg(y);
1140
+ y = sort && Y_bit > 0 ? y : Fp2.neg(y);
1085
1141
  return { x, y };
1086
1142
  }
1087
- else if (bytes.length === 192 && !bitC) {
1088
- // Check if the infinity flag is set
1089
- if ((bytes[0] & (1 << 6)) !== 0) {
1143
+ else if (value.length === 192 && !compressed) {
1144
+ if (infinity) {
1145
+ if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
1146
+ throw new Error('Invalid uncompressed G2 point');
1147
+ }
1090
1148
  return { x: Fp2.ZERO, y: Fp2.ZERO };
1091
1149
  }
1092
- const x1 = slc(bytes, 0, L);
1093
- const x0 = slc(bytes, L, 2 * L);
1094
- const y1 = slc(bytes, 2 * L, 3 * L);
1095
- const y0 = slc(bytes, 3 * L, 4 * L);
1150
+ const x1 = slc(value, 0, L);
1151
+ const x0 = slc(value, L, 2 * L);
1152
+ const y1 = slc(value, 2 * L, 3 * L);
1153
+ const y0 = slc(value, 3 * L, 4 * L);
1096
1154
  return { x: Fp2.fromBigTuple([x0, x1]), y: Fp2.fromBigTuple([y0, y1]) };
1097
1155
  }
1098
1156
  else {
@@ -1107,10 +1165,7 @@ export const bls12_381 = bls({
1107
1165
  if (isZero)
1108
1166
  return concatB(COMPRESSED_ZERO, numberToBytesBE(_0n, len));
1109
1167
  const flag = Boolean(y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P);
1110
- // set compressed & sign bits (looks like different offsets than for G1/Fp?)
1111
- let x_1 = bitSet(x.c1, C_BIT_POS, flag);
1112
- x_1 = bitSet(x_1, S_BIT_POS, true);
1113
- return concatB(numberToBytesBE(x_1, len), numberToBytesBE(x.c0, len));
1168
+ return concatB(setMask(numberToBytesBE(x.c1, len), { compressed: true, sort: flag }), numberToBytesBE(x.c0, len));
1114
1169
  }
1115
1170
  else {
1116
1171
  if (isZero)
@@ -1123,16 +1178,15 @@ export const bls12_381 = bls({
1123
1178
  Signature: {
1124
1179
  // TODO: Optimize, it's very slow because of sqrt.
1125
1180
  fromHex(hex) {
1126
- hex = ensureBytes('signatureHex', hex);
1181
+ const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex));
1127
1182
  const P = Fp.ORDER;
1128
1183
  const half = hex.length / 2;
1129
1184
  if (half !== 48 && half !== 96)
1130
1185
  throw new Error('Invalid compressed signature length, must be 96 or 192');
1131
- const z1 = bytesToNumberBE(hex.slice(0, half));
1132
- const z2 = bytesToNumberBE(hex.slice(half));
1186
+ const z1 = bytesToNumberBE(value.slice(0, half));
1187
+ const z2 = bytesToNumberBE(value.slice(half));
1133
1188
  // Indicates the infinity point
1134
- const bflag1 = bitGet(z1, I_BIT_POS);
1135
- if (bflag1 === _1n)
1189
+ if (infinity)
1136
1190
  return bls12_381.G2.ProjectivePoint.ZERO;
1137
1191
  const x1 = Fp.create(z1 & Fp.MASK);
1138
1192
  const x2 = Fp.create(z2);
@@ -1145,7 +1199,7 @@ export const bls12_381 = bls({
1145
1199
  // Choose the y whose leftmost bit of the imaginary part is equal to the a_flag1
1146
1200
  // If y1 happens to be zero, then use the bit of y0
1147
1201
  const { re: y0, im: y1 } = Fp2.reim(y);
1148
- const aflag1 = bitGet(z1, 381);
1202
+ const aflag1 = BigInt(sort);
1149
1203
  const isGreater = y1 > _0n && (y1 * _2n) / P !== aflag1;
1150
1204
  const isZero = y1 === _0n && (y0 * _2n) / P !== aflag1;
1151
1205
  if (isGreater || isZero)
@@ -1163,7 +1217,7 @@ export const bls12_381 = bls({
1163
1217
  },
1164
1218
  },
1165
1219
  params: {
1166
- x: BLS_X,
1220
+ x: BLS_X, // The BLS parameter x for BLS12-381
1167
1221
  r: Fr.ORDER, // order; z⁴ − z² + 1; CURVE.n from other curves
1168
1222
  },
1169
1223
  htfDefaults,