@noble/curves 0.9.0 → 1.0.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 (97) hide show
  1. package/README.md +197 -99
  2. package/abstract/bls.d.ts +43 -31
  3. package/abstract/bls.d.ts.map +1 -1
  4. package/abstract/bls.js +37 -28
  5. package/abstract/bls.js.map +1 -1
  6. package/abstract/edwards.d.ts +6 -2
  7. package/abstract/edwards.d.ts.map +1 -1
  8. package/abstract/edwards.js +37 -26
  9. package/abstract/edwards.js.map +1 -1
  10. package/abstract/hash-to-curve.d.ts +1 -1
  11. package/abstract/hash-to-curve.d.ts.map +1 -1
  12. package/abstract/hash-to-curve.js +3 -2
  13. package/abstract/hash-to-curve.js.map +1 -1
  14. package/abstract/modular.js +1 -1
  15. package/abstract/modular.js.map +1 -1
  16. package/abstract/utils.d.ts.map +1 -1
  17. package/abstract/utils.js +2 -2
  18. package/abstract/utils.js.map +1 -1
  19. package/abstract/weierstrass.d.ts +23 -0
  20. package/abstract/weierstrass.d.ts.map +1 -1
  21. package/abstract/weierstrass.js +44 -31
  22. package/abstract/weierstrass.js.map +1 -1
  23. package/bls12-381.d.ts.map +1 -1
  24. package/bls12-381.js +161 -162
  25. package/bls12-381.js.map +1 -1
  26. package/{bn.d.ts → bn254.d.ts} +1 -1
  27. package/bn254.d.ts.map +1 -0
  28. package/{bn.js → bn254.js} +1 -1
  29. package/bn254.js.map +1 -0
  30. package/ed25519.d.ts +9 -0
  31. package/ed25519.d.ts.map +1 -1
  32. package/ed25519.js +23 -9
  33. package/ed25519.js.map +1 -1
  34. package/ed448.d.ts +9 -0
  35. package/ed448.d.ts.map +1 -1
  36. package/ed448.js +19 -18
  37. package/ed448.js.map +1 -1
  38. package/esm/abstract/bls.js +37 -28
  39. package/esm/abstract/bls.js.map +1 -1
  40. package/esm/abstract/edwards.js +37 -26
  41. package/esm/abstract/edwards.js.map +1 -1
  42. package/esm/abstract/hash-to-curve.js +3 -2
  43. package/esm/abstract/hash-to-curve.js.map +1 -1
  44. package/esm/abstract/modular.js +1 -1
  45. package/esm/abstract/modular.js.map +1 -1
  46. package/esm/abstract/utils.js +2 -2
  47. package/esm/abstract/utils.js.map +1 -1
  48. package/esm/abstract/weierstrass.js +36 -23
  49. package/esm/abstract/weierstrass.js.map +1 -1
  50. package/esm/bls12-381.js +162 -163
  51. package/esm/bls12-381.js.map +1 -1
  52. package/esm/{bn.js → bn254.js} +1 -1
  53. package/esm/bn254.js.map +1 -0
  54. package/esm/ed25519.js +21 -8
  55. package/esm/ed25519.js.map +1 -1
  56. package/esm/ed448.js +17 -17
  57. package/esm/ed448.js.map +1 -1
  58. package/esm/p256.js +5 -6
  59. package/esm/p256.js.map +1 -1
  60. package/esm/p384.js +10 -12
  61. package/esm/p384.js.map +1 -1
  62. package/esm/p521.js +22 -18
  63. package/esm/p521.js.map +1 -1
  64. package/esm/secp256k1.js +1 -1
  65. package/esm/secp256k1.js.map +1 -1
  66. package/p256.d.ts +1 -1
  67. package/p256.d.ts.map +1 -1
  68. package/p256.js +6 -7
  69. package/p256.js.map +1 -1
  70. package/p384.d.ts +1 -1
  71. package/p384.d.ts.map +1 -1
  72. package/p384.js +11 -13
  73. package/p384.js.map +1 -1
  74. package/p521.d.ts +1 -1
  75. package/p521.d.ts.map +1 -1
  76. package/p521.js +23 -19
  77. package/p521.js.map +1 -1
  78. package/package.json +5 -8
  79. package/secp256k1.js +1 -1
  80. package/secp256k1.js.map +1 -1
  81. package/src/abstract/bls.ts +83 -61
  82. package/src/abstract/edwards.ts +49 -24
  83. package/src/abstract/hash-to-curve.ts +4 -3
  84. package/src/abstract/modular.ts +1 -1
  85. package/src/abstract/utils.ts +2 -2
  86. package/src/abstract/weierstrass.ts +36 -23
  87. package/src/bls12-381.ts +252 -171
  88. package/src/ed25519.ts +23 -9
  89. package/src/ed448.ts +18 -17
  90. package/src/p256.ts +15 -19
  91. package/src/p384.ts +17 -21
  92. package/src/p521.ts +34 -22
  93. package/src/secp256k1.ts +1 -1
  94. package/bn.d.ts.map +0 -1
  95. package/bn.js.map +0 -1
  96. package/esm/bn.js.map +0 -1
  97. /package/src/{bn.ts → bn254.ts} +0 -0
@@ -24,13 +24,16 @@ import {
24
24
 
25
25
  type Fp = bigint; // Can be different field?
26
26
 
27
+ // prettier-ignore
28
+ const _2n = BigInt(2), _3n = BigInt(3);
29
+
27
30
  export type SignatureCoder<Fp2> = {
28
- decode(hex: Hex): ProjPointType<Fp2>;
29
- encode(point: ProjPointType<Fp2>): Uint8Array;
31
+ fromHex(hex: Hex): ProjPointType<Fp2>;
32
+ toRawBytes(point: ProjPointType<Fp2>): Uint8Array;
33
+ toHex(point: ProjPointType<Fp2>): string;
30
34
  };
31
35
 
32
36
  export type CurveType<Fp, Fp2, Fp6, Fp12> = {
33
- r: bigint;
34
37
  G1: Omit<CurvePointsType<Fp>, 'n'> & {
35
38
  mapToCurve: htf.MapToCurve<Fp>;
36
39
  htfDefaults: htf.Opts;
@@ -40,20 +43,25 @@ export type CurveType<Fp, Fp2, Fp6, Fp12> = {
40
43
  mapToCurve: htf.MapToCurve<Fp2>;
41
44
  htfDefaults: htf.Opts;
42
45
  };
43
- x: bigint;
44
- Fp: IField<Fp>;
45
- Fr: IField<bigint>;
46
- Fp2: IField<Fp2> & {
47
- reim: (num: Fp2) => { re: bigint; im: bigint };
48
- multiplyByB: (num: Fp2) => Fp2;
49
- frobeniusMap(num: Fp2, power: number): Fp2;
46
+ fields: {
47
+ Fp: IField<Fp>;
48
+ Fr: IField<bigint>;
49
+ Fp2: IField<Fp2> & {
50
+ reim: (num: Fp2) => { re: bigint; im: bigint };
51
+ multiplyByB: (num: Fp2) => Fp2;
52
+ frobeniusMap(num: Fp2, power: number): Fp2;
53
+ };
54
+ Fp6: IField<Fp6>;
55
+ Fp12: IField<Fp12> & {
56
+ frobeniusMap(num: Fp12, power: number): Fp12;
57
+ multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
58
+ conjugate(num: Fp12): Fp12;
59
+ finalExponentiate(num: Fp12): Fp12;
60
+ };
50
61
  };
51
- Fp6: IField<Fp6>;
52
- Fp12: IField<Fp12> & {
53
- frobeniusMap(num: Fp12, power: number): Fp12;
54
- multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
55
- conjugate(num: Fp12): Fp12;
56
- finalExponentiate(num: Fp12): Fp12;
62
+ params: {
63
+ x: bigint;
64
+ r: bigint;
57
65
  };
58
66
  htfDefaults: htf.Opts;
59
67
  hash: CHash; // Because we need outputLen for DRBG
@@ -61,18 +69,6 @@ export type CurveType<Fp, Fp2, Fp6, Fp12> = {
61
69
  };
62
70
 
63
71
  export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
64
- CURVE: CurveType<Fp, Fp2, Fp6, Fp12>;
65
- Fr: IField<bigint>;
66
- Fp: IField<Fp>;
67
- Fp2: IField<Fp2>;
68
- Fp6: IField<Fp6>;
69
- Fp12: IField<Fp12>;
70
- G1: CurvePointsRes<Fp> & ReturnType<typeof htf.createHasher<Fp>>;
71
- G2: CurvePointsRes<Fp2> & ReturnType<typeof htf.createHasher<Fp2>>;
72
- Signature: SignatureCoder<Fp2>;
73
- millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
74
- calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
75
- pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
76
72
  getPublicKey: (privateKey: PrivKey) => Uint8Array;
77
73
  sign: {
78
74
  (message: Hex, privateKey: PrivKey): Uint8Array;
@@ -83,6 +79,11 @@ export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
83
79
  message: Hex | ProjPointType<Fp2>,
84
80
  publicKey: Hex | ProjPointType<Fp>
85
81
  ) => boolean;
82
+ verifyBatch: (
83
+ signature: Hex | ProjPointType<Fp2>,
84
+ messages: (Hex | ProjPointType<Fp2>)[],
85
+ publicKeys: (Hex | ProjPointType<Fp>)[]
86
+ ) => boolean;
86
87
  aggregatePublicKeys: {
87
88
  (publicKeys: Hex[]): Uint8Array;
88
89
  (publicKeys: ProjPointType<Fp>[]): ProjPointType<Fp>;
@@ -91,22 +92,36 @@ export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
91
92
  (signatures: Hex[]): Uint8Array;
92
93
  (signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>;
93
94
  };
94
- verifyBatch: (
95
- signature: Hex | ProjPointType<Fp2>,
96
- messages: (Hex | ProjPointType<Fp2>)[],
97
- publicKeys: (Hex | ProjPointType<Fp>)[]
98
- ) => boolean;
95
+ millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
96
+ pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
97
+ G1: CurvePointsRes<Fp> & ReturnType<typeof htf.createHasher<Fp>>;
98
+ G2: CurvePointsRes<Fp2> & ReturnType<typeof htf.createHasher<Fp2>>;
99
+ Signature: SignatureCoder<Fp2>;
100
+ params: {
101
+ x: bigint;
102
+ r: bigint;
103
+ G1b: bigint;
104
+ G2b: Fp2;
105
+ };
106
+ fields: {
107
+ Fp: IField<Fp>;
108
+ Fp2: IField<Fp2>;
109
+ Fp6: IField<Fp6>;
110
+ Fp12: IField<Fp12>;
111
+ Fr: IField<bigint>;
112
+ };
99
113
  utils: {
100
114
  randomPrivateKey: () => Uint8Array;
115
+ calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
101
116
  };
102
117
  };
103
118
 
104
119
  export function bls<Fp2, Fp6, Fp12>(
105
120
  CURVE: CurveType<Fp, Fp2, Fp6, Fp12>
106
121
  ): CurveFn<Fp, Fp2, Fp6, Fp12> {
107
- // Fields looks pretty specific for curve, so for now we need to pass them with opts
108
- const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE;
109
- const BLS_X_LEN = bitLen(CURVE.x);
122
+ // Fields are specific for curve, so for now we'll need to pass them with opts
123
+ const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE.fields;
124
+ const BLS_X_LEN = bitLen(CURVE.params.x);
110
125
  const groupLen = 32; // TODO: calculate; hardcoded for now
111
126
 
112
127
  // Pre-compute coefficients for sparse multiplication
@@ -122,18 +137,18 @@ export function bls<Fp2, Fp6, Fp12>(
122
137
  // Double
123
138
  let t0 = Fp2.sqr(Ry); // Ry²
124
139
  let t1 = Fp2.sqr(Rz); // Rz²
125
- let t2 = Fp2.multiplyByB(Fp2.mul(t1, 3n)); // 3 * T1 * B
126
- let t3 = Fp2.mul(t2, 3n); // 3 * T2
140
+ let t2 = Fp2.multiplyByB(Fp2.mul(t1, _3n)); // 3 * T1 * B
141
+ let t3 = Fp2.mul(t2, _3n); // 3 * T2
127
142
  let t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
128
143
  ell_coeff.push([
129
144
  Fp2.sub(t2, t0), // T2 - T0
130
- Fp2.mul(Fp2.sqr(Rx), 3n), // 3 * Rx²
145
+ Fp2.mul(Fp2.sqr(Rx), _3n), // 3 * Rx²
131
146
  Fp2.neg(t4), // -T4
132
147
  ]);
133
- Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), 2n); // ((T0 - T3) * Rx * Ry) / 2
134
- Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3), 2n)), Fp2.mul(Fp2.sqr(t2), 3n)); // ((T0 + T3) / 2)² - 3 * T2²
148
+ Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), _2n); // ((T0 - T3) * Rx * Ry) / 2
149
+ Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3), _2n)), Fp2.mul(Fp2.sqr(t2), _3n)); // ((T0 + T3) / 2)² - 3 * T2²
135
150
  Rz = Fp2.mul(t0, t4); // T0 * T4
136
- if (bitGet(CURVE.x, i)) {
151
+ if (bitGet(CURVE.params.x, i)) {
137
152
  // Addition
138
153
  let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
139
154
  let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
@@ -145,7 +160,7 @@ export function bls<Fp2, Fp6, Fp12>(
145
160
  let t2 = Fp2.sqr(t1); // T1²
146
161
  let t3 = Fp2.mul(t2, t1); // T2 * T1
147
162
  let t4 = Fp2.mul(t2, Rx); // T2 * Rx
148
- let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, 2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
163
+ let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, _2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
149
164
  Rx = Fp2.mul(t1, t5); // T1 * T5
150
165
  Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
151
166
  Rz = Fp2.mul(Rz, t3); // Rz * T3
@@ -155,7 +170,7 @@ export function bls<Fp2, Fp6, Fp12>(
155
170
  }
156
171
 
157
172
  function millerLoop(ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]): Fp12 {
158
- const { x } = CURVE;
173
+ const { x } = CURVE.params;
159
174
  const Px = g1[0];
160
175
  const Py = g1[1];
161
176
  let f12 = Fp12.ONE;
@@ -174,8 +189,9 @@ export function bls<Fp2, Fp6, Fp12>(
174
189
 
175
190
  const utils = {
176
191
  randomPrivateKey: (): Uint8Array => {
177
- return Fr.toBytes(hashToPrivateScalar(CURVE.randomBytes(groupLen + 8), CURVE.r));
192
+ return Fr.toBytes(hashToPrivateScalar(CURVE.randomBytes(groupLen + 8), CURVE.params.r));
178
193
  },
194
+ calcPairingPrecomputes,
179
195
  };
180
196
 
181
197
  // Point on G1 curve: (x, y)
@@ -236,7 +252,7 @@ export function bls<Fp2, Fp6, Fp12>(
236
252
  return point instanceof G1.ProjectivePoint ? (point as G1) : G1.ProjectivePoint.fromHex(point);
237
253
  }
238
254
  function normP2(point: G2Hex): G2 {
239
- return point instanceof G2.ProjectivePoint ? point : Signature.decode(point);
255
+ return point instanceof G2.ProjectivePoint ? point : Signature.fromHex(point);
240
256
  }
241
257
  function normP2Hash(point: G2Hex, htfOpts?: htf.htfBasicOpts): G2 {
242
258
  return point instanceof G2.ProjectivePoint
@@ -259,7 +275,7 @@ export function bls<Fp2, Fp6, Fp12>(
259
275
  msgPoint.assertValidity();
260
276
  const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey));
261
277
  if (message instanceof G2.ProjectivePoint) return sigPoint;
262
- return Signature.encode(sigPoint);
278
+ return Signature.toRawBytes(sigPoint);
263
279
  }
264
280
 
265
281
  // Checks if pairing of public key & hash is equal to pairing of generator & signature.
@@ -309,7 +325,7 @@ export function bls<Fp2, Fp6, Fp12>(
309
325
  aggAffine.assertValidity();
310
326
  return aggAffine;
311
327
  }
312
- return Signature.encode(aggAffine);
328
+ return Signature.toRawBytes(aggAffine);
313
329
  }
314
330
 
315
331
  // https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
@@ -353,24 +369,30 @@ export function bls<Fp2, Fp6, Fp12>(
353
369
  G1.ProjectivePoint.BASE._setWindowSize(4);
354
370
 
355
371
  return {
356
- CURVE,
357
- Fr,
358
- Fp,
359
- Fp2,
360
- Fp6,
361
- Fp12,
362
- G1,
363
- G2,
364
- Signature,
365
- millerLoop,
366
- calcPairingPrecomputes,
367
- pairing,
368
372
  getPublicKey,
369
373
  sign,
370
374
  verify,
375
+ verifyBatch,
371
376
  aggregatePublicKeys,
372
377
  aggregateSignatures,
373
- verifyBatch,
378
+ millerLoop,
379
+ pairing,
380
+ G1,
381
+ G2,
382
+ Signature,
383
+ fields: {
384
+ Fr,
385
+ Fp,
386
+ Fp2,
387
+ Fp6,
388
+ Fp12,
389
+ },
390
+ params: {
391
+ x: CURVE.params.x,
392
+ r: CURVE.params.r,
393
+ G1b: CURVE.G1.b,
394
+ G2b: CURVE.G2.b,
395
+ },
374
396
  utils,
375
397
  };
376
398
  }
@@ -5,11 +5,9 @@ import * as ut from './utils.js';
5
5
  import { ensureBytes, FHash, Hex } from './utils.js';
6
6
  import { Group, GroupConstructor, wNAF, BasicCurve, validateBasic, AffinePoint } from './curve.js';
7
7
 
8
- // Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
9
- const _0n = BigInt(0);
10
- const _1n = BigInt(1);
11
- const _2n = BigInt(2);
12
- const _8n = BigInt(8);
8
+ // Be friendly to bad ECMAScript parsers by not using bigint literals
9
+ // prettier-ignore
10
+ const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _8n = BigInt(8);
13
11
 
14
12
  // Edwards curves must declare params a & d.
15
13
  export type CurveType = BasicCurve<bigint> & {
@@ -20,10 +18,13 @@ export type CurveType = BasicCurve<bigint> & {
20
18
  adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array; // clears bits to get valid field elemtn
21
19
  domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array; // Used for hashing
22
20
  uvRatio?: (u: bigint, v: bigint) => { isValid: boolean; value: bigint }; // Ratio √(u/v)
23
- preHash?: FHash; // RFC 8032 pre-hashing of messages to sign() / verify()
21
+ prehash?: FHash; // RFC 8032 pre-hashing of messages to sign() / verify()
24
22
  mapToCurve?: (scalar: bigint[]) => AffinePoint<bigint>; // for hash-to-curve standard
25
23
  };
26
24
 
25
+ // verification rule is either zip215 or rfc8032 / nist186-5. Consult fromHex:
26
+ const VERIFY_DEFAULT = { zip215: true };
27
+
27
28
  function validateOpts(curve: CurveType) {
28
29
  const opts = validateBasic(curve);
29
30
  ut.validateObject(
@@ -51,6 +52,8 @@ export interface ExtPointType extends Group<ExtPointType> {
51
52
  readonly ey: bigint;
52
53
  readonly ez: bigint;
53
54
  readonly et: bigint;
55
+ get x(): bigint;
56
+ get y(): bigint;
54
57
  assertValidity(): void;
55
58
  multiply(scalar: bigint): ExtPointType;
56
59
  multiplyUnsafe(scalar: bigint): ExtPointType;
@@ -58,6 +61,8 @@ export interface ExtPointType extends Group<ExtPointType> {
58
61
  isTorsionFree(): boolean;
59
62
  clearCofactor(): ExtPointType;
60
63
  toAffine(iz?: bigint): AffinePoint<bigint>;
64
+ toRawBytes(isCompressed?: boolean): Uint8Array;
65
+ toHex(isCompressed?: boolean): string;
61
66
  }
62
67
  // Static methods of Extended Point with coordinates in X, Y, Z, T
63
68
  export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
@@ -88,7 +93,15 @@ export type CurveFn = {
88
93
  // It is not generic twisted curve for now, but ed25519/ed448 generic implementation
89
94
  export function twistedEdwards(curveDef: CurveType): CurveFn {
90
95
  const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
91
- const { Fp, n: CURVE_ORDER, preHash, hash: cHash, randomBytes, nByteLength, h: cofactor } = CURVE;
96
+ const {
97
+ Fp,
98
+ n: CURVE_ORDER,
99
+ prehash: prehash,
100
+ hash: cHash,
101
+ randomBytes,
102
+ nByteLength,
103
+ h: cofactor,
104
+ } = CURVE;
92
105
  const MASK = _2n ** BigInt(nByteLength * 8);
93
106
  const modP = Fp.create; // Function overrides
94
107
 
@@ -109,7 +122,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
109
122
  if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
110
123
  return data;
111
124
  }); // NOOP
112
- const inBig = (n: bigint) => typeof n === 'bigint' && 0n < n; // n in [1..]
125
+ const inBig = (n: bigint) => typeof n === 'bigint' && _0n < n; // n in [1..]
113
126
  const inRange = (n: bigint, max: bigint) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
114
127
  const in0MaskRange = (n: bigint) => n === _0n || inRange(n, MASK); // n in [0..MASK-1]
115
128
  function assertInRange(n: bigint, max: bigint) {
@@ -297,8 +310,9 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
297
310
  // Non-constant-time multiplication. Uses double-and-add algorithm.
298
311
  // It's faster, but should only be used when you don't care about
299
312
  // an exposed private key e.g. sig verification.
313
+ // Does NOT allow scalars higher than CURVE.n.
300
314
  multiplyUnsafe(scalar: bigint): Point {
301
- let n = assertGE0(scalar);
315
+ let n = assertGE0(scalar); // 0 <= scalar < CURVE.n
302
316
  if (n === _0n) return I;
303
317
  if (this.equals(I) || n === _1n) return this;
304
318
  if (this.equals(G)) return this.wNAF(n).p;
@@ -341,7 +355,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
341
355
 
342
356
  // Converts hash string or Uint8Array to Point.
343
357
  // Uses algo from RFC8032 5.1.3.
344
- static fromHex(hex: Hex, strict = true): Point {
358
+ static fromHex(hex: Hex, zip215 = false): Point {
345
359
  const { d, a } = CURVE;
346
360
  const len = Fp.BYTES;
347
361
  hex = ensureBytes('pointHex', hex, len); // copy hex to a new array
@@ -353,8 +367,8 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
353
367
  // y=0 is allowed
354
368
  } else {
355
369
  // RFC8032 prohibits >= p, but ZIP215 doesn't
356
- if (strict) assertInRange(y, Fp.ORDER); // strict=true [1..P-1] (2^255-19-1 for ed25519)
357
- else assertInRange(y, MASK); // strict=false [1..MASK-1] (2^256-1 for ed25519)
370
+ if (zip215) assertInRange(y, MASK); // zip215=true [1..P-1] (2^255-19-1 for ed25519)
371
+ else assertInRange(y, Fp.ORDER); // zip215=false [1..MASK-1] (2^256-1 for ed25519)
358
372
  }
359
373
 
360
374
  // Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
@@ -416,32 +430,43 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
416
430
  // int('LE', SHA512(dom2(F, C) || msgs)) mod N
417
431
  function hashDomainToScalar(context: Hex = new Uint8Array(), ...msgs: Uint8Array[]) {
418
432
  const msg = ut.concatBytes(...msgs);
419
- return modN_LE(cHash(domain(msg, ensureBytes('context', context), !!preHash)));
433
+ return modN_LE(cHash(domain(msg, ensureBytes('context', context), !!prehash)));
420
434
  }
421
435
 
422
436
  /** Signs message with privateKey. RFC8032 5.1.6 */
423
- function sign(msg: Hex, privKey: Hex, context?: Hex): Uint8Array {
437
+ function sign(msg: Hex, privKey: Hex, options: { context?: Hex } = {}): Uint8Array {
424
438
  msg = ensureBytes('message', msg);
425
- if (preHash) msg = preHash(msg); // for ed25519ph etc.
439
+ if (prehash) msg = prehash(msg); // for ed25519ph etc.
426
440
  const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
427
- const r = hashDomainToScalar(context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
441
+ const r = hashDomainToScalar(options.context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
428
442
  const R = G.multiply(r).toRawBytes(); // R = rG
429
- const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
443
+ const k = hashDomainToScalar(options.context, R, pointBytes, msg); // R || A || PH(M)
430
444
  const s = modN(r + k * scalar); // S = (r + k * s) mod L
431
445
  assertGE0(s); // 0 <= s < l
432
446
  const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
433
447
  return ensureBytes('result', res, nByteLength * 2); // 64-byte signature
434
448
  }
435
449
 
436
- function verify(sig: Hex, msg: Hex, publicKey: Hex, context?: Hex): boolean {
450
+ const verifyOpts: { context?: Hex; zip215?: boolean } = VERIFY_DEFAULT;
451
+ function verify(sig: Hex, msg: Hex, publicKey: Hex, options = verifyOpts): boolean {
452
+ const { context, zip215 } = options;
437
453
  const len = Fp.BYTES; // Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
438
454
  sig = ensureBytes('signature', sig, 2 * len); // An extended group equation is checked.
439
- msg = ensureBytes('message', msg); // ZIP215 compliant, which means not fully RFC8032 compliant.
440
- if (preHash) msg = preHash(msg); // for ed25519ph, etc
441
- const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
442
- const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
443
- const s = ut.bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
444
- const SB = G.multiplyUnsafe(s);
455
+ msg = ensureBytes('message', msg);
456
+ if (prehash) msg = prehash(msg); // for ed25519ph, etc
457
+
458
+ const s = ut.bytesToNumberLE(sig.slice(len, 2 * len));
459
+ // zip215: true is good for consensus-critical apps and allows points < 2^256
460
+ // zip215: false follows RFC8032 / NIST186-5 and restricts points to CURVE.p
461
+ let A, R, SB;
462
+ try {
463
+ A = Point.fromHex(publicKey, zip215);
464
+ R = Point.fromHex(sig.slice(0, len), zip215);
465
+ SB = G.multiplyUnsafe(s); // 0 <= s < l is done inside
466
+ } catch (error) {
467
+ return false;
468
+ }
469
+
445
470
  const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
446
471
  const RkA = R.add(A.multiplyUnsafe(k));
447
472
  // [8][S]B = [8]R + [8][k]A'
@@ -17,7 +17,7 @@ export type Opts = {
17
17
  p: bigint;
18
18
  m: number;
19
19
  k: number;
20
- expand?: 'xmd' | 'xof';
20
+ expand: 'xmd' | 'xof';
21
21
  hash: CHash;
22
22
  };
23
23
 
@@ -145,10 +145,11 @@ export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bi
145
145
  prb = expand_message_xmd(msg, DST, len_in_bytes, hash);
146
146
  } else if (expand === 'xof') {
147
147
  prb = expand_message_xof(msg, DST, len_in_bytes, k, hash);
148
- } else if (expand === undefined) {
148
+ } else if (expand === '_internal_pass') {
149
+ // for internal tests only
149
150
  prb = msg;
150
151
  } else {
151
- throw new Error('expand must be "xmd", "xof" or undefined');
152
+ throw new Error('expand must be "xmd" or "xof"');
152
153
  }
153
154
  const u = new Array(count);
154
155
  for (let i = 0; i < count; i++) {
@@ -275,7 +275,7 @@ export function FpPow<T>(f: IField<T>, num: T, power: bigint): T {
275
275
  while (power > _0n) {
276
276
  if (power & _1n) p = f.mul(p, d);
277
277
  d = f.sqr(d);
278
- power >>= 1n;
278
+ power >>= _1n;
279
279
  }
280
280
  return p;
281
281
  }
@@ -123,12 +123,12 @@ export function utf8ToBytes(str: string): Uint8Array {
123
123
  // Amount of bits inside bigint (Same as n.toString(2).length)
124
124
  export function bitLen(n: bigint) {
125
125
  let len;
126
- for (len = 0; n > 0n; n >>= _1n, len += 1);
126
+ for (len = 0; n > _0n; n >>= _1n, len += 1);
127
127
  return len;
128
128
  }
129
129
  // Gets single bit at position. NOTE: first bit position is 0 (same as arrays)
130
130
  // Same as !!+Array.from(n.toString(2)).reverse()[pos]
131
- export const bitGet = (n: bigint, pos: number) => (n >> BigInt(pos)) & 1n;
131
+ export const bitGet = (n: bigint, pos: number) => (n >> BigInt(pos)) & _1n;
132
132
  // Sets single bit at position
133
133
  export const bitSet = (n: bigint, pos: number, value: boolean) =>
134
134
  n | ((value ? _1n : _0n) << BigInt(pos));
@@ -58,6 +58,8 @@ export interface ProjPointType<T> extends Group<ProjPointType<T>> {
58
58
  readonly px: T;
59
59
  readonly py: T;
60
60
  readonly pz: T;
61
+ get x(): T;
62
+ get y(): T;
61
63
  multiply(scalar: bigint): ProjPointType<T>;
62
64
  toAffine(iz?: T): AffinePoint<T>;
63
65
  isTorsionFree(): boolean;
@@ -129,7 +131,7 @@ export type CurvePointsRes<T> = {
129
131
 
130
132
  // ASN.1 DER encoding utilities
131
133
  const { bytesToNumberBE: b2n, hexToBytes: h2b } = ut;
132
- const DER = {
134
+ export const DER = {
133
135
  // asn.1 DER encoding utils
134
136
  Err: class DERErr extends Error {
135
137
  constructor(m = '') {
@@ -142,9 +144,13 @@ const DER = {
142
144
  const len = data[1];
143
145
  const res = data.subarray(2, len + 2);
144
146
  if (!len || res.length !== len) throw new E('Invalid signature integer: wrong length');
145
- if (res[0] === 0x00 && res[1] <= 0x7f)
146
- throw new E('Invalid signature integer: trailing length');
147
- // ^ Weird condition: not about length, but about first bytes of number.
147
+ // https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag,
148
+ // since we always use positive integers here. It must always be empty:
149
+ // - add zero byte if exists
150
+ // - if next byte doesn't have a flag, leading zero is not allowed (minimal encoding)
151
+ if (res[0] & 0b10000000) throw new E('Invalid signature integer: negative');
152
+ if (res[0] === 0x00 && !(res[1] & 0b10000000))
153
+ throw new E('Invalid signature integer: unnecessary leading zero');
148
154
  return { d: b2n(res), l: data.subarray(len + 2) }; // d is data, l is left
149
155
  },
150
156
  toSig(hex: string | Uint8Array): { r: bigint; s: bigint } {
@@ -161,7 +167,8 @@ const DER = {
161
167
  return { r, s };
162
168
  },
163
169
  hexFromSig(sig: { r: bigint; s: bigint }): string {
164
- const slice = (s: string): string => (Number.parseInt(s[0], 16) >= 8 ? '00' + s : s); // slice DER
170
+ // Add leading zero if first byte has negative bit enabled. More details in '_parseInt'
171
+ const slice = (s: string): string => (Number.parseInt(s[0], 16) & 0b1000 ? '00' + s : s);
165
172
  const h = (num: number | bigint) => {
166
173
  const hex = num.toString(16);
167
174
  return hex.length & 1 ? `0${hex}` : hex;
@@ -176,9 +183,9 @@ const DER = {
176
183
  },
177
184
  };
178
185
 
179
- // Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
180
- const _0n = BigInt(0);
181
- const _1n = BigInt(1);
186
+ // Be friendly to bad ECMAScript parsers by not using bigint literals
187
+ // prettier-ignore
188
+ const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
182
189
 
183
190
  export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
184
191
  const CURVE = validatePointOpts(opts);
@@ -211,6 +218,12 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
211
218
  const x3 = Fp.mul(x2, x); // x2 * x
212
219
  return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x3 + a * x + b
213
220
  }
221
+ // Validate whether the passed curve params are valid.
222
+ // We check if curve equation works for generator point.
223
+ // `assertValidity()` won't work: `isTorsionFree()` is not available at this point in bls12-381.
224
+ // ProjectivePoint class has not been initialized yet.
225
+ if (!Fp.eql(Fp.sqr(CURVE.Gy), weierstrassEquation(CURVE.Gx)))
226
+ throw new Error('bad generator point: equation left != right');
214
227
 
215
228
  // Valid group elements reside in range 1..n-1
216
229
  function isWithinCurveOrder(num: bigint): boolean {
@@ -365,7 +378,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
365
378
  // Cost: 8M + 3S + 3*a + 2*b3 + 15add.
366
379
  double() {
367
380
  const { a, b } = CURVE;
368
- const b3 = Fp.mul(b, 3n);
381
+ const b3 = Fp.mul(b, _3n);
369
382
  const { px: X1, py: Y1, pz: Z1 } = this;
370
383
  let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
371
384
  let t0 = Fp.mul(X1, X1); // step 1
@@ -412,7 +425,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
412
425
  const { px: X2, py: Y2, pz: Z2 } = other;
413
426
  let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
414
427
  const a = CURVE.a;
415
- const b3 = Fp.mul(CURVE.b, 3n);
428
+ const b3 = Fp.mul(CURVE.b, _3n);
416
429
  let t0 = Fp.mul(X1, X2); // step 1
417
430
  let t1 = Fp.mul(Y1, Y2);
418
431
  let t2 = Fp.mul(Z1, Z2);
@@ -589,7 +602,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
589
602
  }
590
603
  const _bits = CURVE.nBitLength;
591
604
  const wnaf = wNAF(Point, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
592
-
605
+ // Validate if generator point is on curve
593
606
  return {
594
607
  CURVE,
595
608
  ProjectivePoint: Point as ProjConstructor<T>,
@@ -1078,15 +1091,15 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1078
1091
  export function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T) {
1079
1092
  // Generic implementation
1080
1093
  const q = Fp.ORDER;
1081
- let l = 0n;
1082
- for (let o = q - 1n; o % 2n === 0n; o /= 2n) l += 1n;
1094
+ let l = _0n;
1095
+ for (let o = q - _1n; o % _2n === _0n; o /= _2n) l += _1n;
1083
1096
  const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
1084
- const c2 = (q - 1n) / 2n ** c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
1085
- const c3 = (c2 - 1n) / 2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
1086
- const c4 = 2n ** c1 - 1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
1087
- const c5 = 2n ** (c1 - 1n); // 5. c5 = 2^(c1 - 1) # Integer arithmetic
1097
+ const c2 = (q - _1n) / _2n ** c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
1098
+ const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
1099
+ const c4 = _2n ** c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
1100
+ const c5 = _2n ** (c1 - _1n); // 5. c5 = 2^(c1 - 1) # Integer arithmetic
1088
1101
  const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
1089
- const c7 = Fp.pow(Z, (c2 + 1n) / 2n); // 7. c7 = Z^((c2 + 1) / 2)
1102
+ const c7 = Fp.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
1090
1103
  let sqrtRatio = (u: T, v: T): { isValid: boolean; value: T } => {
1091
1104
  let tv1 = c6; // 1. tv1 = c6
1092
1105
  let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
@@ -1105,8 +1118,8 @@ export function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T) {
1105
1118
  tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
1106
1119
  tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
1107
1120
  // 17. for i in (c1, c1 - 1, ..., 2):
1108
- for (let i = c1; i > 1; i--) {
1109
- let tv5 = 2n ** (i - 2n); // 18. tv5 = i - 2; 19. tv5 = 2^tv5
1121
+ for (let i = c1; i > _1n; i--) {
1122
+ let tv5 = _2n ** (i - _2n); // 18. tv5 = i - 2; 19. tv5 = 2^tv5
1110
1123
  let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
1111
1124
  const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
1112
1125
  tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
@@ -1117,9 +1130,9 @@ export function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T) {
1117
1130
  }
1118
1131
  return { isValid: isQR, value: tv3 };
1119
1132
  };
1120
- if (Fp.ORDER % 4n === 3n) {
1133
+ if (Fp.ORDER % _4n === _3n) {
1121
1134
  // sqrt_ratio_3mod4(u, v)
1122
- const c1 = (Fp.ORDER - 3n) / 4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
1135
+ const c1 = (Fp.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
1123
1136
  const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
1124
1137
  sqrtRatio = (u: T, v: T) => {
1125
1138
  let tv1 = Fp.sqr(v); // 1. tv1 = v^2
@@ -1135,7 +1148,7 @@ export function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T) {
1135
1148
  };
1136
1149
  }
1137
1150
  // No curves uses that
1138
- // if (Fp.ORDER % 8n === 5n) // sqrt_ratio_5mod8
1151
+ // if (Fp.ORDER % _8n === _5n) // sqrt_ratio_5mod8
1139
1152
  return sqrtRatio;
1140
1153
  }
1141
1154
  // From draft-irtf-cfrg-hash-to-curve-16