@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 +61 -10
- package/lib/abstract/hash-to-curve.d.ts +10 -1
- package/lib/abstract/hash-to-curve.js +32 -10
- package/lib/abstract/modular.d.ts +8 -17
- package/lib/abstract/modular.js +131 -152
- package/lib/abstract/utils.d.ts +3 -1
- package/lib/bls12-381.d.ts +1 -1
- package/lib/bls12-381.js +1 -1
- package/lib/ed25519.js +70 -7
- package/lib/ed448.js +84 -1
- package/lib/esm/abstract/hash-to-curve.js +30 -9
- package/lib/esm/abstract/modular.js +125 -148
- package/lib/esm/bls12-381.js +1 -1
- package/lib/esm/ed25519.js +71 -8
- package/lib/esm/ed448.js +85 -2
- package/lib/esm/p256.js +1 -1
- package/lib/esm/p384.js +1 -1
- package/lib/esm/p521.js +1 -1
- package/lib/esm/secp256k1.js +5 -2
- package/lib/esm/stark.js +2 -0
- package/lib/p256.js +1 -1
- package/lib/p384.js +1 -1
- package/lib/p521.js +1 -1
- package/lib/secp256k1.js +5 -2
- package/lib/stark.d.ts +1 -1
- package/lib/stark.js +2 -0
- package/package.json +1 -1
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](#
|
|
88
|
-
- [abstract/montgomery: Montgomery curve](#
|
|
89
|
-
- [abstract/weierstrass: Short Weierstrass curve](#
|
|
90
|
-
- [abstract/
|
|
91
|
-
- [abstract/
|
|
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
|
|
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
|
|
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 (
|
|
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
|
-
|
|
85
|
-
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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 {};
|
package/lib/abstract/modular.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
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),
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
//
|
|
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
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
//
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
190
|
+
// Other cases: Tonelli-Shanks algorithm
|
|
191
|
+
return tonelliShanks(P);
|
|
147
192
|
}
|
|
148
|
-
exports.
|
|
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
|
-
//
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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 = (
|
|
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
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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.
|
|
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;
|
package/lib/abstract/utils.d.ts
CHANGED
package/lib/bls12-381.d.ts
CHANGED
|
@@ -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:
|
|
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
|