@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/README.md CHANGED
@@ -7,8 +7,8 @@ Minimal, auditable JS implementation of elliptic curve cryptography.
7
7
  - [hash to curve](https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/)
8
8
  for encoding or hashing an arbitrary string to a point on an elliptic curve
9
9
  - Auditable, [fast](#speed)
10
- - 🔻 Tree-shaking-friendly: there is no entry point, which ensures small size of your app
11
10
  - 🔍 Unique tests ensure correctness. Wycheproof vectors included
11
+ - 🔻 Tree-shaking-friendly: there is no entry point, which ensures small size of your app
12
12
 
13
13
  There are two parts of the package:
14
14
 
@@ -84,11 +84,12 @@ To define a custom curve, check out API below.
84
84
  ## API
85
85
 
86
86
  - [Overview](#overview)
87
- - [abstract/edwards: Twisted Edwards curve](#abstract/edwards-twisted-edwards-curve)
88
- - [abstract/montgomery: Montgomery curve](#abstract/montgomery-montgomery-curve)
89
- - [abstract/weierstrass: Short Weierstrass curve](#abstract/weierstrass-short-weierstrass-curve)
90
- - [abstract/modular](#abstract/modular)
91
- - [abstract/utils](#abstract/utils)
87
+ - [abstract/edwards: Twisted Edwards curve](#abstractedwards-twisted-edwards-curve)
88
+ - [abstract/montgomery: Montgomery curve](#abstractmontgomery-montgomery-curve)
89
+ - [abstract/weierstrass: Short Weierstrass curve](#abstractweierstrass-short-weierstrass-curve)
90
+ - [abstract/hash-to-curve: Hashing strings to curve points](#abstracthash-to-curve-hashing-strings-to-curve-points)
91
+ - [abstract/modular](#abstractmodular)
92
+ - [abstract/utils](#abstractutils)
92
93
 
93
94
  ### Overview
94
95
 
@@ -324,20 +325,70 @@ export type CurveFn = {
324
325
  };
325
326
  ```
326
327
 
328
+ ### abstract/hash-to-curve: Hashing strings to curve points
329
+
330
+ The module allows to hash arbitrary strings to elliptic curve points.
331
+
332
+ - `expand_message_xmd` [(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1) produces a uniformly random byte string using a cryptographic hash function H that outputs b bits..
333
+
334
+ ```ts
335
+ function expand_message_xmd(
336
+ msg: Uint8Array, DST: Uint8Array, lenInBytes: number, H: CHash
337
+ ): Uint8Array;
338
+ function expand_message_xof(
339
+ msg: Uint8Array, DST: Uint8Array, lenInBytes: number, k: number, H: CHash
340
+ ): Uint8Array;
341
+ ```
342
+
343
+ - `hash_to_field(msg, count, options)` [(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3)
344
+ hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
345
+ * `msg` a byte string containing the message to hash
346
+ * `count` the number of elements of F to output
347
+ * `options` `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
348
+ * Returns `[u_0, ..., u_(count - 1)]`, a list of field elements.
349
+
350
+ ```ts
351
+ function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][];
352
+ type htfOpts = {
353
+ // DST: a domain separation tag
354
+ // defined in section 2.2.5
355
+ DST: string;
356
+ // p: the characteristic of F
357
+ // where F is a finite field of characteristic p and order q = p^m
358
+ p: bigint;
359
+ // m: the extension degree of F, m >= 1
360
+ // where F is a finite field of characteristic p and order q = p^m
361
+ m: number;
362
+ // k: the target security level for the suite in bits
363
+ // defined in section 5.1
364
+ k: number;
365
+ // option to use a message that has already been processed by
366
+ // expand_message_xmd
367
+ expand?: 'xmd' | 'xof';
368
+ // Hash functions for: expand_message_xmd is appropriate for use with a
369
+ // wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
370
+ // BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
371
+ // TODO: verify that hash is shake if expand==='xof' via types
372
+ hash: CHash;
373
+ };
374
+ ```
375
+
327
376
  ### abstract/modular
328
377
 
329
378
  Modular arithmetics utilities.
330
379
 
331
380
  ```typescript
332
- import { mod, invert, div, invertBatch, sqrt, Fp } from '@noble/curves/abstract/modular';
381
+ import { Fp, mod, invert, div, invertBatch, sqrt } from '@noble/curves/abstract/modular';
382
+ const fp = Fp(2n ** 255n - 19n); // Finite field over 2^255-19
383
+ fp.mul(591n, 932n);
384
+ fp.pow(481n, 11024858120n);
385
+
386
+ // Generic non-FP utils are also available
333
387
  mod(21n, 10n); // 21 mod 10 == 1n; fixed version of 21 % 10
334
388
  invert(17n, 10n); // invert(17) mod 10; modular multiplicative inverse
335
389
  div(5n, 17n, 10n); // 5/17 mod 10 == 5 * invert(17) mod 10; division
336
390
  invertBatch([1n, 2n, 4n], 21n); // => [1n, 11n, 16n] in one inversion
337
391
  sqrt(21n, 73n); // √21 mod 73; square root
338
- const fp = Fp(2n ** 255n - 19n); // Finite field over 2^255-19
339
- fp.mul(591n, 932n);
340
- fp.pow(481n, 11024858120n);
341
392
  ```
342
393
 
343
394
  ### abstract/utils
@@ -6,12 +6,21 @@ export declare type htfOpts = {
6
6
  p: bigint;
7
7
  m: number;
8
8
  k: number;
9
- expand: boolean;
9
+ expand?: 'xmd' | 'xof';
10
10
  hash: CHash;
11
11
  };
12
12
  export declare function validateHTFOpts(opts: htfOpts): void;
13
13
  export declare function stringToBytes(str: string): Uint8Array;
14
14
  export declare function expand_message_xmd(msg: Uint8Array, DST: Uint8Array, lenInBytes: number, H: CHash): Uint8Array;
15
+ export declare function expand_message_xof(msg: Uint8Array, DST: Uint8Array, lenInBytes: number, k: number, H: CHash): Uint8Array;
16
+ /**
17
+ * Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
18
+ * https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
19
+ * @param msg a byte string containing the message to hash
20
+ * @param count the number of elements of F to output
21
+ * @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
22
+ * @returns [u_0, ..., u_(count - 1)], a list of field elements.
23
+ */
15
24
  export declare function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][];
16
25
  export declare function isogenyMap<T, F extends mod.Field<T>>(field: F, map: [T[], T[], T[], T[]]): (x: T, y: T) => {
17
26
  x: T;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isogenyMap = exports.hash_to_field = exports.expand_message_xmd = exports.stringToBytes = exports.validateHTFOpts = void 0;
3
+ exports.isogenyMap = exports.hash_to_field = exports.expand_message_xof = exports.expand_message_xmd = exports.stringToBytes = exports.validateHTFOpts = void 0;
4
4
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
5
5
  const utils_js_1 = require("./utils.js");
6
6
  const mod = require("./modular.js");
@@ -13,7 +13,7 @@ function validateHTFOpts(opts) {
13
13
  throw new Error('Invalid htf/m');
14
14
  if (typeof opts.k !== 'number')
15
15
  throw new Error('Invalid htf/k');
16
- if (typeof opts.expand !== 'boolean')
16
+ if (opts.expand !== 'xmd' && opts.expand !== 'xof' && opts.expand !== undefined)
17
17
  throw new Error('Invalid htf/expand');
18
18
  if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
19
19
  throw new Error('Invalid htf/hash function');
@@ -81,13 +81,32 @@ function expand_message_xmd(msg, DST, lenInBytes, H) {
81
81
  return pseudo_random_bytes.slice(0, lenInBytes);
82
82
  }
83
83
  exports.expand_message_xmd = expand_message_xmd;
84
- // hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
85
- // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
86
- // Inputs:
87
- // msg - a byte string containing the message to hash.
88
- // count - the number of elements of F to output.
89
- // Outputs:
90
- // [u_0, ..., u_(count - 1)], a list of field elements.
84
+ function expand_message_xof(msg, DST, lenInBytes, k, H) {
85
+ // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
86
+ // DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
87
+ if (DST.length > 255) {
88
+ const dkLen = Math.ceil((2 * k) / 8);
89
+ DST = H.create({ dkLen }).update(stringToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
90
+ }
91
+ if (lenInBytes > 65535 || DST.length > 255)
92
+ throw new Error('expand_message_xof: invalid lenInBytes');
93
+ return (H.create({ dkLen: lenInBytes })
94
+ .update(msg)
95
+ .update(i2osp(lenInBytes, 2))
96
+ // 2. DST_prime = DST || I2OSP(len(DST), 1)
97
+ .update(DST)
98
+ .update(i2osp(DST.length, 1))
99
+ .digest());
100
+ }
101
+ exports.expand_message_xof = expand_message_xof;
102
+ /**
103
+ * Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
104
+ * https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
105
+ * @param msg a byte string containing the message to hash
106
+ * @param count the number of elements of F to output
107
+ * @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
108
+ * @returns [u_0, ..., u_(count - 1)], a list of field elements.
109
+ */
91
110
  function hash_to_field(msg, count, options) {
92
111
  // if options is provided but incomplete, fill any missing fields with the
93
112
  // value in hftDefaults (ie hash to G2).
@@ -96,9 +115,12 @@ function hash_to_field(msg, count, options) {
96
115
  const len_in_bytes = count * options.m * L;
97
116
  const DST = stringToBytes(options.DST);
98
117
  let pseudo_random_bytes = msg;
99
- if (options.expand) {
118
+ if (options.expand === 'xmd') {
100
119
  pseudo_random_bytes = expand_message_xmd(msg, DST, len_in_bytes, options.hash);
101
120
  }
121
+ else if (options.expand === 'xof') {
122
+ pseudo_random_bytes = expand_message_xof(msg, DST, len_in_bytes, options.k, options.hash);
123
+ }
102
124
  const u = new Array(count);
103
125
  for (let i = 0; i < count; i++) {
104
126
  const e = new Array(options.m);
@@ -8,18 +8,8 @@ export declare function mod(a: bigint, b: bigint): bigint;
8
8
  export declare function pow(num: bigint, power: bigint, modulo: bigint): bigint;
9
9
  export declare function pow2(x: bigint, power: bigint, modulo: bigint): bigint;
10
10
  export declare function invert(number: bigint, modulo: bigint): bigint;
11
- /**
12
- * Calculates Legendre symbol (a | p), which denotes the value of a^((p-1)/2) (mod p).
13
- * * (a | p) ≡ 1 if a is a square (mod p)
14
- * * (a | p) ≡ -1 if a is not a square (mod p)
15
- * * (a | p) ≡ 0 if a ≡ 0 (mod p)
16
- */
17
- export declare function legendre(num: bigint, fieldPrime: bigint): bigint;
18
- /**
19
- * Calculates square root of a number in a finite field.
20
- * √a mod P
21
- */
22
- export declare function sqrt(number: bigint, modulo: bigint): bigint;
11
+ export declare function tonelliShanks(P: bigint): <T>(Fp: Field<T>, n: T) => T;
12
+ export declare function FpSqrt(P: bigint): <T>(Fp: Field<T>, n: T) => T;
23
13
  export declare const isNegativeLE: (num: bigint, modulo: bigint) => boolean;
24
14
  export interface Field<T> {
25
15
  ORDER: bigint;
@@ -57,8 +47,9 @@ export declare function validateField<T>(field: Field<T>): void;
57
47
  export declare function FpPow<T>(f: Field<T>, num: T, power: bigint): T;
58
48
  export declare function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[];
59
49
  export declare function FpDiv<T>(f: Field<T>, lhs: T, rhs: T | bigint): T;
60
- export declare function Fp(ORDER: bigint, bitLen?: number, isLE?: boolean, redef?: Partial<Field<bigint>>): Readonly<Field<bigint>>;
61
- export declare function FpSqrt<T>(Fp: Field<T>): {
62
- sqrt: (x: T) => T;
63
- isSquare: (x: T) => boolean;
64
- };
50
+ export declare function FpIsSquare<T>(f: Field<T>): (x: T) => boolean;
51
+ declare type FpField = Field<bigint> & Required<Pick<Field<bigint>, 'isOdd'>>;
52
+ export declare function Fp(ORDER: bigint, bitLen?: number, isLE?: boolean, redef?: Partial<Field<bigint>>): Readonly<FpField>;
53
+ export declare function FpSqrtOdd<T>(Fp: Field<T>, elm: T): T;
54
+ export declare function FpSqrtEven<T>(Fp: Field<T>, elm: T): T;
55
+ export {};
@@ -1,13 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FpSqrt = exports.Fp = exports.FpDiv = exports.FpInvertBatch = exports.FpPow = exports.validateField = exports.isNegativeLE = exports.sqrt = exports.legendre = exports.invert = exports.pow2 = exports.pow = exports.mod = void 0;
3
+ exports.FpSqrtEven = exports.FpSqrtOdd = exports.Fp = exports.FpIsSquare = exports.FpDiv = exports.FpInvertBatch = exports.FpPow = exports.validateField = exports.isNegativeLE = exports.FpSqrt = exports.tonelliShanks = exports.invert = exports.pow2 = exports.pow = exports.mod = void 0;
4
4
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
5
+ // TODO: remove circular imports
5
6
  const utils = require("./utils.js");
6
7
  // Utilities for modular arithmetics and finite fields
7
8
  // prettier-ignore
8
9
  const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
9
10
  // prettier-ignore
10
- const _4n = BigInt(4), _5n = BigInt(5), _7n = BigInt(7), _8n = BigInt(8);
11
+ const _4n = BigInt(4), _5n = BigInt(5), _8n = BigInt(8);
11
12
  // prettier-ignore
12
13
  const _9n = BigInt(9), _16n = BigInt(16);
13
14
  // Calculates a modulo b
@@ -73,26 +74,67 @@ function invert(number, modulo) {
73
74
  return mod(x, modulo);
74
75
  }
75
76
  exports.invert = invert;
76
- /**
77
- * Calculates Legendre symbol (a | p), which denotes the value of a^((p-1)/2) (mod p).
78
- * * (a | p) ≡ 1 if a is a square (mod p)
79
- * * (a | p) -1 if a is not a square (mod p)
80
- * * (a | p) ≡ 0 if a ≡ 0 (mod p)
81
- */
82
- function legendre(num, fieldPrime) {
83
- return pow(num, (fieldPrime - _1n) / _2n, fieldPrime);
77
+ // Tonelli-Shanks algorithm
78
+ // https://eprint.iacr.org/2012/685.pdf (page 12)
79
+ function tonelliShanks(P) {
80
+ // Legendre constant: used to calculate Legendre symbol (a | p),
81
+ // which denotes the value of a^((p-1)/2) (mod p).
82
+ // (a | p) ≡ 1 if a is a square (mod p)
83
+ // (a | p) ≡ -1 if a is not a square (mod p)
84
+ // (a | p) 0 if a ≡ 0 (mod p)
85
+ const legendreC = (P - _1n) / _2n;
86
+ let Q, S, Z;
87
+ // Step 1: By factoring out powers of 2 from p - 1,
88
+ // find q and s such that p - 1 = q2s with q odd
89
+ for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++)
90
+ ;
91
+ // Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
92
+ for (Z = _2n; Z < P && pow(Z, legendreC, P) !== P - _1n; Z++)
93
+ ;
94
+ // Fast-path
95
+ if (S === 1) {
96
+ const p1div4 = (P + _1n) / _4n;
97
+ return function tonelliFast(Fp, n) {
98
+ const root = Fp.pow(n, p1div4);
99
+ if (!Fp.equals(Fp.square(root), n))
100
+ throw new Error('Cannot find square root');
101
+ return root;
102
+ };
103
+ }
104
+ // Slow-path
105
+ const Q1div2 = (Q + _1n) / _2n;
106
+ return function tonelliSlow(Fp, n) {
107
+ // Step 0: Check that n is indeed a square: (n | p) must be ≡ 1
108
+ if (Fp.pow(n, legendreC) !== Fp.ONE)
109
+ throw new Error('Cannot find square root');
110
+ let s = S;
111
+ let c = pow(Z, Q, P);
112
+ let r = Fp.pow(n, Q1div2);
113
+ let t = Fp.pow(n, Q);
114
+ let t2 = Fp.ZERO;
115
+ while (!Fp.equals(Fp.sub(t, Fp.ONE), Fp.ZERO)) {
116
+ t2 = Fp.square(t);
117
+ let i;
118
+ for (i = 1; i < s; i++) {
119
+ // stop if t2-1 == 0
120
+ if (Fp.equals(Fp.sub(t2, Fp.ONE), Fp.ZERO))
121
+ break;
122
+ // t2 *= t2
123
+ t2 = Fp.square(t2);
124
+ }
125
+ let b = pow(c, BigInt(1 << (s - i - 1)), P);
126
+ r = Fp.mul(r, b);
127
+ c = mod(b * b, P);
128
+ t = Fp.mul(t, c);
129
+ s = i;
130
+ }
131
+ return r;
132
+ };
84
133
  }
85
- exports.legendre = legendre;
86
- /**
87
- * Calculates square root of a number in a finite field.
88
- * √a mod P
89
- */
90
- // TODO: rewrite as generic Fp function && remove bls versions
91
- function sqrt(number, modulo) {
92
- // prettier-ignore
93
- const n = number;
94
- const P = modulo;
95
- const p1div4 = (P + _1n) / _4n;
134
+ exports.tonelliShanks = tonelliShanks;
135
+ function FpSqrt(P) {
136
+ // NOTE: different algorithms can give different roots, it is up to user to decide which one they want.
137
+ // For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
96
138
  // P ≡ 3 (mod 4)
97
139
  // √n = n^((P+1)/4)
98
140
  if (P % _4n === _3n) {
@@ -100,52 +142,55 @@ function sqrt(number, modulo) {
100
142
  // const ORDER =
101
143
  // 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;
102
144
  // const NUM = 72057594037927816n;
103
- // TODO: fix sqrtMod in secp256k1
104
- const root = pow(n, p1div4, P);
105
- if (mod(root * root, modulo) !== number)
106
- throw new Error('Cannot find square root');
107
- return root;
145
+ const p1div4 = (P + _1n) / _4n;
146
+ return function sqrt3mod4(Fp, n) {
147
+ const root = Fp.pow(n, p1div4);
148
+ // Throw if root**2 != n
149
+ if (!Fp.equals(Fp.square(root), n))
150
+ throw new Error('Cannot find square root');
151
+ return root;
152
+ };
108
153
  }
109
- // P ≡ 5 (mod 8)
154
+ // Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10)
110
155
  if (P % _8n === _5n) {
111
- const n2 = mod(n * _2n, P);
112
- const v = pow(n2, (P - _5n) / _8n, P);
113
- const nv = mod(n * v, P);
114
- const i = mod(_2n * nv * v, P);
115
- const r = mod(nv * (i - _1n), P);
116
- return r;
156
+ const c1 = (P - _5n) / _8n;
157
+ return function sqrt5mod8(Fp, n) {
158
+ const n2 = Fp.mul(n, _2n);
159
+ const v = Fp.pow(n2, c1);
160
+ const nv = Fp.mul(n, v);
161
+ const i = Fp.mul(Fp.mul(nv, _2n), v);
162
+ const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
163
+ if (!Fp.equals(Fp.square(root), n))
164
+ throw new Error('Cannot find square root');
165
+ return root;
166
+ };
117
167
  }
118
- // Other cases: Tonelli-Shanks algorithm
119
- if (legendre(n, P) !== _1n)
120
- throw new Error('Cannot find square root');
121
- let q, s, z;
122
- for (q = P - _1n, s = 0; q % _2n === _0n; q /= _2n, s++)
123
- ;
124
- if (s === 1)
125
- return pow(n, p1div4, P);
126
- for (z = _2n; z < P && legendre(z, P) !== P - _1n; z++)
127
- ;
128
- let c = pow(z, q, P);
129
- let r = pow(n, (q + _1n) / _2n, P);
130
- let t = pow(n, q, P);
131
- let t2 = _0n;
132
- while (mod(t - _1n, P) !== _0n) {
133
- t2 = mod(t * t, P);
134
- let i;
135
- for (i = 1; i < s; i++) {
136
- if (mod(t2 - _1n, P) === _0n)
137
- break;
138
- t2 = mod(t2 * t2, P);
139
- }
140
- let b = pow(c, BigInt(1 << (s - i - 1)), P);
141
- r = mod(r * b, P);
142
- c = mod(b * b, P);
143
- t = mod(t * c, P);
144
- s = i;
168
+ // P 9 (mod 16)
169
+ if (P % _16n === _9n) {
170
+ // NOTE: tonelli is too slow for bls-Fp2 calculations even on start
171
+ // Means we cannot use sqrt for constants at all!
172
+ //
173
+ // const c1 = Fp.sqrt(Fp.negate(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
174
+ // const c2 = Fp.sqrt(c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
175
+ // const c3 = Fp.sqrt(Fp.negate(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
176
+ // const c4 = (P + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
177
+ // sqrt = (x) => {
178
+ // let tv1 = Fp.pow(x, c4); // 1. tv1 = x^c4
179
+ // let tv2 = Fp.mul(c1, tv1); // 2. tv2 = c1 * tv1
180
+ // const tv3 = Fp.mul(c2, tv1); // 3. tv3 = c2 * tv1
181
+ // let tv4 = Fp.mul(c3, tv1); // 4. tv4 = c3 * tv1
182
+ // const e1 = Fp.equals(Fp.square(tv2), x); // 5. e1 = (tv2^2) == x
183
+ // const e2 = Fp.equals(Fp.square(tv3), x); // 6. e2 = (tv3^2) == x
184
+ // tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
185
+ // tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
186
+ // const e3 = Fp.equals(Fp.square(tv2), x); // 9. e3 = (tv2^2) == x
187
+ // return Fp.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select the sqrt from tv1 and tv2
188
+ // }
145
189
  }
146
- return r;
190
+ // Other cases: Tonelli-Shanks algorithm
191
+ return tonelliShanks(P);
147
192
  }
148
- exports.sqrt = sqrt;
193
+ exports.FpSqrt = FpSqrt;
149
194
  // Little-endian check for first LE bit (last BE bit);
150
195
  const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n;
151
196
  exports.isNegativeLE = isNegativeLE;
@@ -216,17 +261,22 @@ function FpDiv(f, lhs, rhs) {
216
261
  return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.invert(rhs));
217
262
  }
218
263
  exports.FpDiv = FpDiv;
219
- // NOTE: very fragile, always bench. Major performance points:
220
- // - NonNormalized ops
221
- // - Object.freeze
222
- // - same shape of object (don't add/remove keys)
264
+ // This function returns True whenever the value x is a square in the field F.
265
+ function FpIsSquare(f) {
266
+ const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
267
+ return (x) => {
268
+ const p = f.pow(x, legendreConst);
269
+ return f.equals(p, f.ZERO) || f.equals(p, f.ONE);
270
+ };
271
+ }
272
+ exports.FpIsSquare = FpIsSquare;
223
273
  function Fp(ORDER, bitLen, isLE = false, redef = {}) {
224
274
  if (ORDER <= _0n)
225
275
  throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
226
276
  const { nBitLength: BITS, nByteLength: BYTES } = utils.nLength(ORDER, bitLen);
227
277
  if (BYTES > 2048)
228
278
  throw new Error('Field lengths over 2048 bytes are not supported');
229
- const sqrtP = (num) => sqrt(num, ORDER);
279
+ const sqrtP = FpSqrt(ORDER);
230
280
  const f = Object.freeze({
231
281
  ORDER,
232
282
  BITS,
@@ -256,7 +306,7 @@ function Fp(ORDER, bitLen, isLE = false, redef = {}) {
256
306
  subN: (lhs, rhs) => lhs - rhs,
257
307
  mulN: (lhs, rhs) => lhs * rhs,
258
308
  invert: (num) => invert(num, ORDER),
259
- sqrt: redef.sqrt || sqrtP,
309
+ sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
260
310
  invertBatch: (lst) => FpInvertBatch(f, lst),
261
311
  // TODO: do we really need constant cmov?
262
312
  // We don't have const-time bigints anyway, so probably will be not very useful
@@ -271,88 +321,17 @@ function Fp(ORDER, bitLen, isLE = false, redef = {}) {
271
321
  return Object.freeze(f);
272
322
  }
273
323
  exports.Fp = Fp;
274
- // TODO: re-use in bls/generic sqrt for field/etc?
275
- // Something like sqrtUnsafe which always returns value, but sqrt throws exception if non-square
276
- // From draft-irtf-cfrg-hash-to-curve-16
277
- function FpSqrt(Fp) {
278
- // NOTE: it requires another sqrt for constant precomputes, but no need for roots of unity,
279
- // probably we can simply bls code using it
280
- const q = Fp.ORDER;
281
- const squareConst = (q - _1n) / _2n;
282
- // is_square(x) := { True, if x^((q - 1) / 2) is 0 or 1 in F;
283
- // { False, otherwise.
284
- let isSquare = (x) => {
285
- const p = Fp.pow(x, squareConst);
286
- return Fp.equals(p, Fp.ZERO) || Fp.equals(p, Fp.ONE);
287
- };
288
- // Constant-time Tonelli-Shanks algorithm
289
- let l = _0n;
290
- for (let o = q - _1n; o % _2n === _0n; o /= _2n)
291
- l += _1n;
292
- const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
293
- const c2 = (q - _1n) / _2n ** c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
294
- const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
295
- // 4. c4, a non-square value in F
296
- // 5. c5 = c4^c2 in F
297
- let c4 = Fp.ONE;
298
- while (isSquare(c4))
299
- c4 = Fp.add(c4, Fp.ONE);
300
- const c5 = Fp.pow(c4, c2);
301
- let sqrt = (x) => {
302
- let z = Fp.pow(x, c3); // 1. z = x^c3
303
- let t = Fp.square(z); // 2. t = z * z
304
- t = Fp.mul(t, x); // 3. t = t * x
305
- z = Fp.mul(z, x); // 4. z = z * x
306
- let b = t; // 5. b = t
307
- let c = c5; // 6. c = c5
308
- // 7. for i in (c1, c1 - 1, ..., 2):
309
- for (let i = c1; i > 1; i--) {
310
- // 8. for j in (1, 2, ..., i - 2):
311
- // 9. b = b * b
312
- for (let j = _1n; j < i - _1n; i++)
313
- b = Fp.square(b);
314
- const e = Fp.equals(b, Fp.ONE); // 10. e = b == 1
315
- const zt = Fp.mul(z, c); // 11. zt = z * c
316
- z = Fp.cmov(zt, z, e); // 12. z = CMOV(zt, z, e)
317
- c = Fp.square(c); // 13. c = c * c
318
- let tt = Fp.mul(t, c); // 14. tt = t * c
319
- t = Fp.cmov(tt, t, e); // 15. t = CMOV(tt, t, e)
320
- b = t; // 16. b = t
321
- }
322
- return z; // 17. return z
323
- };
324
- if (q % _4n === _3n) {
325
- const c1 = (q + _1n) / _4n; // 1. c1 = (q + 1) / 4 # Integer arithmetic
326
- sqrt = (x) => Fp.pow(x, c1);
327
- }
328
- else if (q % _8n === _5n) {
329
- const c1 = Fp.sqrt(Fp.negate(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
330
- const c2 = (q + _3n) / _8n; // 2. c2 = (q + 3) / 8 # Integer arithmetic
331
- sqrt = (x) => {
332
- let tv1 = Fp.pow(x, c2); // 1. tv1 = x^c2
333
- let tv2 = Fp.mul(tv1, c1); // 2. tv2 = tv1 * c1
334
- let e = Fp.equals(Fp.square(tv1), x); // 3. e = (tv1^2) == x
335
- return Fp.cmov(tv2, tv1, e); // 4. z = CMOV(tv2, tv1, e)
336
- };
337
- }
338
- else if (Fp.ORDER % _16n === _9n) {
339
- const c1 = Fp.sqrt(Fp.negate(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
340
- const c2 = Fp.sqrt(c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
341
- const c3 = Fp.sqrt(Fp.negate(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
342
- const c4 = (Fp.ORDER + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
343
- sqrt = (x) => {
344
- let tv1 = Fp.pow(x, c4); // 1. tv1 = x^c4
345
- let tv2 = Fp.mul(c1, tv1); // 2. tv2 = c1 * tv1
346
- const tv3 = Fp.mul(c2, tv1); // 3. tv3 = c2 * tv1
347
- let tv4 = Fp.mul(c3, tv1); // 4. tv4 = c3 * tv1
348
- const e1 = Fp.equals(Fp.square(tv2), x); // 5. e1 = (tv2^2) == x
349
- const e2 = Fp.equals(Fp.square(tv3), x); // 6. e2 = (tv3^2) == x
350
- tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
351
- tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
352
- const e3 = Fp.equals(Fp.square(tv2), x); // 9. e3 = (tv2^2) == x
353
- return Fp.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select the sqrt from tv1 and tv2
354
- };
355
- }
356
- return { sqrt, isSquare };
324
+ function FpSqrtOdd(Fp, elm) {
325
+ if (!Fp.isOdd)
326
+ throw new Error(`Field doesn't have isOdd`);
327
+ const root = Fp.sqrt(elm);
328
+ return Fp.isOdd(root) ? root : Fp.negate(root);
357
329
  }
358
- exports.FpSqrt = FpSqrt;
330
+ exports.FpSqrtOdd = FpSqrtOdd;
331
+ function FpSqrtEven(Fp, elm) {
332
+ if (!Fp.isOdd)
333
+ throw new Error(`Field doesn't have isOdd`);
334
+ const root = Fp.sqrt(elm);
335
+ return Fp.isOdd(root) ? Fp.negate(root) : root;
336
+ }
337
+ exports.FpSqrtEven = FpSqrtEven;
@@ -6,7 +6,9 @@ export declare type CHash = {
6
6
  (message: Uint8Array | string): Uint8Array;
7
7
  blockLen: number;
8
8
  outputLen: number;
9
- create(): any;
9
+ create(opts?: {
10
+ dkLen?: number;
11
+ }): any;
10
12
  };
11
13
  export declare type BasicCurve<T> = {
12
14
  Fp: mod.Field<T>;
@@ -1,6 +1,6 @@
1
1
  import { CurveFn } from './abstract/bls.js';
2
2
  import * as mod from './abstract/modular.js';
3
- declare const Fp: Readonly<mod.Field<bigint>>;
3
+ declare const Fp: Readonly<mod.Field<bigint> & Required<Pick<mod.Field<bigint>, "isOdd">>>;
4
4
  declare type Fp = bigint;
5
5
  declare type BigintTuple = [bigint, bigint];
6
6
  declare type Fp2 = {
package/lib/bls12-381.js CHANGED
@@ -818,7 +818,7 @@ const htfDefaults = {
818
818
  k: 128,
819
819
  // option to use a message that has already been processed by
820
820
  // expand_message_xmd
821
- expand: true,
821
+ expand: 'xmd',
822
822
  // Hash functions for: expand_message_xmd is appropriate for use with a
823
823
  // wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
824
824
  // BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247