@noble/curves 1.9.2 → 1.9.3
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 +186 -206
- package/_shortw_utils.d.ts +1 -0
- package/_shortw_utils.d.ts.map +1 -1
- package/_shortw_utils.js +1 -0
- package/_shortw_utils.js.map +1 -1
- package/abstract/bls.d.ts +87 -62
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +170 -163
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +109 -23
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +158 -156
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +124 -70
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +212 -62
- package/abstract/edwards.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +8 -4
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +23 -11
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +8 -3
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +79 -35
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +17 -4
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +19 -3
- package/abstract/montgomery.js.map +1 -1
- package/abstract/tower.d.ts +3 -3
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js.map +1 -1
- package/abstract/weierstrass.d.ts +142 -116
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +414 -335
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +4 -4
- package/bls12-381.js.map +1 -1
- package/ed25519.d.ts +52 -66
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +128 -155
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +57 -58
- package/ed448.d.ts.map +1 -1
- package/ed448.js +114 -131
- package/ed448.js.map +1 -1
- package/esm/_shortw_utils.d.ts +1 -0
- package/esm/_shortw_utils.d.ts.map +1 -1
- package/esm/_shortw_utils.js +1 -0
- package/esm/_shortw_utils.js.map +1 -1
- package/esm/abstract/bls.d.ts +87 -62
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +171 -164
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +109 -23
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +156 -155
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +124 -70
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +210 -62
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/hash-to-curve.d.ts +8 -4
- package/esm/abstract/hash-to-curve.d.ts.map +1 -1
- package/esm/abstract/hash-to-curve.js +22 -11
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.d.ts +8 -3
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +79 -35
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts +17 -4
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +19 -3
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/tower.d.ts +3 -3
- package/esm/abstract/tower.d.ts.map +1 -1
- package/esm/abstract/tower.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +142 -116
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +411 -333
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +4 -4
- package/esm/bls12-381.js.map +1 -1
- package/esm/ed25519.d.ts +52 -66
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +131 -157
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +57 -58
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +116 -132
- package/esm/ed448.js.map +1 -1
- package/esm/index.js +7 -9
- package/esm/index.js.map +1 -1
- package/esm/jubjub.d.ts +3 -3
- package/esm/jubjub.d.ts.map +1 -1
- package/esm/jubjub.js +3 -3
- package/esm/jubjub.js.map +1 -1
- package/esm/misc.d.ts +3 -5
- package/esm/misc.d.ts.map +1 -1
- package/esm/misc.js +0 -3
- package/esm/misc.js.map +1 -1
- package/esm/nist.d.ts +0 -6
- package/esm/nist.d.ts.map +1 -1
- package/esm/nist.js +31 -15
- package/esm/nist.js.map +1 -1
- package/esm/p256.d.ts +4 -0
- package/esm/p256.d.ts.map +1 -1
- package/esm/p256.js +4 -0
- package/esm/p256.js.map +1 -1
- package/esm/p384.d.ts +4 -1
- package/esm/p384.d.ts.map +1 -1
- package/esm/p384.js +4 -1
- package/esm/p384.js.map +1 -1
- package/esm/p521.d.ts +4 -0
- package/esm/p521.d.ts.map +1 -1
- package/esm/p521.js +4 -0
- package/esm/p521.js.map +1 -1
- package/esm/secp256k1.d.ts +32 -15
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +72 -67
- package/esm/secp256k1.js.map +1 -1
- package/esm/utils.d.ts +1 -1
- package/esm/utils.js +1 -1
- package/index.js +7 -9
- package/index.js.map +1 -1
- package/jubjub.d.ts +3 -3
- package/jubjub.d.ts.map +1 -1
- package/jubjub.js +3 -3
- package/jubjub.js.map +1 -1
- package/misc.d.ts +3 -5
- package/misc.d.ts.map +1 -1
- package/misc.js +0 -3
- package/misc.js.map +1 -1
- package/nist.d.ts +0 -6
- package/nist.d.ts.map +1 -1
- package/nist.js +31 -15
- package/nist.js.map +1 -1
- package/p256.d.ts +4 -0
- package/p256.d.ts.map +1 -1
- package/p256.js +4 -0
- package/p256.js.map +1 -1
- package/p384.d.ts +4 -1
- package/p384.d.ts.map +1 -1
- package/p384.js +4 -1
- package/p384.js.map +1 -1
- package/p521.d.ts +4 -0
- package/p521.d.ts.map +1 -1
- package/p521.js +4 -0
- package/p521.js.map +1 -1
- package/package.json +4 -2
- package/secp256k1.d.ts +32 -15
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +70 -65
- package/secp256k1.js.map +1 -1
- package/src/_shortw_utils.ts +1 -0
- package/src/abstract/bls.ts +319 -257
- package/src/abstract/curve.ts +226 -170
- package/src/abstract/edwards.ts +350 -139
- package/src/abstract/hash-to-curve.ts +33 -16
- package/src/abstract/modular.ts +86 -35
- package/src/abstract/montgomery.ts +36 -9
- package/src/abstract/tower.ts +4 -4
- package/src/abstract/weierstrass.ts +567 -474
- package/src/bls12-381.ts +28 -20
- package/src/ed25519.ts +161 -179
- package/src/ed448.ts +150 -156
- package/src/index.ts +7 -9
- package/src/jubjub.ts +3 -3
- package/src/misc.ts +3 -7
- package/src/nist.ts +40 -16
- package/src/p256.ts +4 -0
- package/src/p384.ts +4 -2
- package/src/p521.ts +4 -0
- package/src/secp256k1.ts +91 -73
- package/src/utils.ts +1 -1
- package/utils.d.ts +1 -1
- package/utils.js +1 -1
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
utf8ToBytes,
|
|
17
17
|
} from '../utils.ts';
|
|
18
18
|
import type { AffinePoint, Group, GroupConstructor } from './curve.ts';
|
|
19
|
-
import { FpInvertBatch, type IField
|
|
19
|
+
import { FpInvertBatch, mod, type IField } from './modular.ts';
|
|
20
20
|
|
|
21
21
|
export type UnicodeOrBytes = string | Uint8Array;
|
|
22
22
|
|
|
@@ -71,19 +71,24 @@ function anum(item: unknown): void {
|
|
|
71
71
|
if (!Number.isSafeInteger(item)) throw new Error('number expected');
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
function normDST(DST: UnicodeOrBytes): Uint8Array {
|
|
75
|
+
if (!isBytes(DST) && typeof DST !== 'string') throw new Error('DST must be Uint8Array or string');
|
|
76
|
+
return typeof DST === 'string' ? utf8ToBytes(DST) : DST;
|
|
77
|
+
}
|
|
78
|
+
|
|
74
79
|
/**
|
|
75
80
|
* Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits.
|
|
76
81
|
* [RFC 9380 5.3.1](https://www.rfc-editor.org/rfc/rfc9380#section-5.3.1).
|
|
77
82
|
*/
|
|
78
83
|
export function expand_message_xmd(
|
|
79
84
|
msg: Uint8Array,
|
|
80
|
-
DST:
|
|
85
|
+
DST: UnicodeOrBytes,
|
|
81
86
|
lenInBytes: number,
|
|
82
87
|
H: CHash
|
|
83
88
|
): Uint8Array {
|
|
84
89
|
abytes(msg);
|
|
85
|
-
abytes(DST);
|
|
86
90
|
anum(lenInBytes);
|
|
91
|
+
DST = normDST(DST);
|
|
87
92
|
// https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
|
|
88
93
|
if (DST.length > 255) DST = H(concatBytes(utf8ToBytes('H2C-OVERSIZE-DST-'), DST));
|
|
89
94
|
const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H;
|
|
@@ -112,14 +117,14 @@ export function expand_message_xmd(
|
|
|
112
117
|
*/
|
|
113
118
|
export function expand_message_xof(
|
|
114
119
|
msg: Uint8Array,
|
|
115
|
-
DST:
|
|
120
|
+
DST: UnicodeOrBytes,
|
|
116
121
|
lenInBytes: number,
|
|
117
122
|
k: number,
|
|
118
123
|
H: CHash
|
|
119
124
|
): Uint8Array {
|
|
120
125
|
abytes(msg);
|
|
121
|
-
abytes(DST);
|
|
122
126
|
anum(lenInBytes);
|
|
127
|
+
DST = normDST(DST);
|
|
123
128
|
// https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
|
|
124
129
|
// DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
|
|
125
130
|
if (DST.length > 255) {
|
|
@@ -154,13 +159,10 @@ export function hash_to_field(msg: Uint8Array, count: number, options: H2COpts):
|
|
|
154
159
|
k: 'number',
|
|
155
160
|
hash: 'function',
|
|
156
161
|
});
|
|
157
|
-
const { p, k, m, hash, expand, DST
|
|
158
|
-
if (!isBytes(_DST) && typeof _DST !== 'string')
|
|
159
|
-
throw new Error('DST must be string or uint8array');
|
|
162
|
+
const { p, k, m, hash, expand, DST } = options;
|
|
160
163
|
if (!isHash(options.hash)) throw new Error('expected valid hash');
|
|
161
164
|
abytes(msg);
|
|
162
165
|
anum(count);
|
|
163
|
-
const DST = typeof _DST === 'string' ? utf8ToBytes(_DST) : _DST;
|
|
164
166
|
const log2p = p.toString(2).length;
|
|
165
167
|
const L = Math.ceil((log2p + k) / 8); // section 5.1 of ietf draft link above
|
|
166
168
|
const len_in_bytes = count * m * L;
|
|
@@ -229,6 +231,10 @@ export type H2CMethod<T> = (msg: Uint8Array, options?: htfBasicOpts) => H2CPoint
|
|
|
229
231
|
// TODO: remove
|
|
230
232
|
export type HTFMethod<T> = H2CMethod<T>;
|
|
231
233
|
export type MapMethod<T> = (scalars: bigint[]) => H2CPoint<T>;
|
|
234
|
+
export type H2CHasherBase<T> = {
|
|
235
|
+
hashToCurve: H2CMethod<T>;
|
|
236
|
+
hashToScalar: (msg: Uint8Array, options: htfBasicOpts) => bigint;
|
|
237
|
+
};
|
|
232
238
|
/**
|
|
233
239
|
* RFC 9380 methods, with cofactor clearing. See https://www.rfc-editor.org/rfc/rfc9380#section-3.
|
|
234
240
|
*
|
|
@@ -236,8 +242,7 @@ export type MapMethod<T> = (scalars: bigint[]) => H2CPoint<T>;
|
|
|
236
242
|
* * encodeToCurve: `map(hash(input))`, encodes NON-UNIFORM bytes to curve (WITH hashing)
|
|
237
243
|
* * mapToCurve: `map(scalars)`, encodes NON-UNIFORM scalars to curve (NO hashing)
|
|
238
244
|
*/
|
|
239
|
-
export type H2CHasher<T> = {
|
|
240
|
-
hashToCurve: H2CMethod<T>;
|
|
245
|
+
export type H2CHasher<T> = H2CHasherBase<T> & {
|
|
241
246
|
encodeToCurve: H2CMethod<T>;
|
|
242
247
|
mapToCurve: MapMethod<T>;
|
|
243
248
|
defaults: H2COpts & { encodeDST?: UnicodeOrBytes };
|
|
@@ -245,6 +250,8 @@ export type H2CHasher<T> = {
|
|
|
245
250
|
// TODO: remove
|
|
246
251
|
export type Hasher<T> = H2CHasher<T>;
|
|
247
252
|
|
|
253
|
+
export const _DST_scalar: Uint8Array = utf8ToBytes('HashToScalar-');
|
|
254
|
+
|
|
248
255
|
/** Creates hash-to-curve methods from EC Point and mapToCurve function. See {@link H2CHasher}. */
|
|
249
256
|
export function createHasher<T>(
|
|
250
257
|
Point: H2CPointConstructor<T>,
|
|
@@ -264,19 +271,20 @@ export function createHasher<T>(
|
|
|
264
271
|
|
|
265
272
|
return {
|
|
266
273
|
defaults,
|
|
274
|
+
|
|
267
275
|
hashToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
|
|
268
|
-
const
|
|
269
|
-
const opts = Object.assign({}, defaults, dst, options);
|
|
276
|
+
const opts = Object.assign({}, defaults, options);
|
|
270
277
|
const u = hash_to_field(msg, 2, opts);
|
|
271
278
|
const u0 = map(u[0]);
|
|
272
279
|
const u1 = map(u[1]);
|
|
273
280
|
return clear(u0.add(u1));
|
|
274
281
|
},
|
|
275
282
|
encodeToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
|
|
276
|
-
const
|
|
277
|
-
const opts = Object.assign({}, defaults,
|
|
283
|
+
const optsDst = defaults.encodeDST ? { DST: defaults.encodeDST } : {};
|
|
284
|
+
const opts = Object.assign({}, defaults, optsDst, options);
|
|
278
285
|
const u = hash_to_field(msg, 1, opts);
|
|
279
|
-
|
|
286
|
+
const u0 = map(u[0]);
|
|
287
|
+
return clear(u0);
|
|
280
288
|
},
|
|
281
289
|
/** See {@link H2CHasher} */
|
|
282
290
|
mapToCurve(scalars: bigint[]): H2CPoint<T> {
|
|
@@ -285,5 +293,14 @@ export function createHasher<T>(
|
|
|
285
293
|
if (typeof i !== 'bigint') throw new Error('expected array of bigints');
|
|
286
294
|
return clear(map(scalars));
|
|
287
295
|
},
|
|
296
|
+
|
|
297
|
+
// hash_to_scalar can produce 0: https://www.rfc-editor.org/errata/eid8393
|
|
298
|
+
// RFC 9380, draft-irtf-cfrg-bbs-signatures-08
|
|
299
|
+
hashToScalar(msg: Uint8Array, options?: htfBasicOpts): bigint {
|
|
300
|
+
// @ts-ignore
|
|
301
|
+
const N = Point.Fn.ORDER;
|
|
302
|
+
const opts = Object.assign({}, defaults, { p: N, m: 1, DST: _DST_scalar }, options);
|
|
303
|
+
return hash_to_field(msg, 1, opts)[0][0];
|
|
304
|
+
},
|
|
288
305
|
};
|
|
289
306
|
}
|
package/src/abstract/modular.ts
CHANGED
|
@@ -19,8 +19,9 @@ import {
|
|
|
19
19
|
// prettier-ignore
|
|
20
20
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _3n = /* @__PURE__ */ BigInt(3);
|
|
21
21
|
// prettier-ignore
|
|
22
|
-
const _4n = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5);
|
|
23
|
-
|
|
22
|
+
const _4n = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5), _7n = /* @__PURE__ */ BigInt(7);
|
|
23
|
+
// prettier-ignore
|
|
24
|
+
const _8n = /* @__PURE__ */ BigInt(8), _9n = /* @__PURE__ */ BigInt(9), _16n = /* @__PURE__ */ BigInt(16);
|
|
24
25
|
|
|
25
26
|
// Calculates a modulo b
|
|
26
27
|
export function mod(a: bigint, b: bigint): bigint {
|
|
@@ -73,6 +74,10 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
|
|
73
74
|
return mod(x, modulo);
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
function assertIsSquare<T>(Fp: IField<T>, root: T, n: T): void {
|
|
78
|
+
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
|
79
|
+
}
|
|
80
|
+
|
|
76
81
|
// Not all roots are possible! Example which will throw:
|
|
77
82
|
// const NUM =
|
|
78
83
|
// n = 72057594037927816n;
|
|
@@ -80,8 +85,7 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
|
|
80
85
|
function sqrt3mod4<T>(Fp: IField<T>, n: T) {
|
|
81
86
|
const p1div4 = (Fp.ORDER + _1n) / _4n;
|
|
82
87
|
const root = Fp.pow(n, p1div4);
|
|
83
|
-
|
|
84
|
-
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
|
88
|
+
assertIsSquare(Fp, root, n);
|
|
85
89
|
return root;
|
|
86
90
|
}
|
|
87
91
|
|
|
@@ -92,32 +96,34 @@ function sqrt5mod8<T>(Fp: IField<T>, n: T) {
|
|
|
92
96
|
const nv = Fp.mul(n, v);
|
|
93
97
|
const i = Fp.mul(Fp.mul(nv, _2n), v);
|
|
94
98
|
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
|
|
95
|
-
|
|
99
|
+
assertIsSquare(Fp, root, n);
|
|
96
100
|
return root;
|
|
97
101
|
}
|
|
98
102
|
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
103
|
+
// Based on RFC9380, Kong algorithm
|
|
104
|
+
// prettier-ignore
|
|
105
|
+
function sqrt9mod16(P: bigint): <T>(Fp: IField<T>, n: T) => T {
|
|
106
|
+
const Fp_ = Field(P);
|
|
107
|
+
const tn = tonelliShanks(P);
|
|
108
|
+
const c1 = tn(Fp_, Fp_.neg(Fp_.ONE));// 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
|
|
109
|
+
const c2 = tn(Fp_, c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
|
|
110
|
+
const c3 = tn(Fp_, Fp_.neg(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
|
|
111
|
+
const c4 = (P + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
|
|
112
|
+
return <T>(Fp: IField<T>, n: T) => {
|
|
113
|
+
let tv1 = Fp.pow(n, c4); // 1. tv1 = x^c4
|
|
114
|
+
let tv2 = Fp.mul(tv1, c1); // 2. tv2 = c1 * tv1
|
|
115
|
+
const tv3 = Fp.mul(tv1, c2); // 3. tv3 = c2 * tv1
|
|
116
|
+
const tv4 = Fp.mul(tv1, c3); // 4. tv4 = c3 * tv1
|
|
117
|
+
const e1 = Fp.eql(Fp.sqr(tv2), n); // 5. e1 = (tv2^2) == x
|
|
118
|
+
const e2 = Fp.eql(Fp.sqr(tv3), n); // 6. e2 = (tv3^2) == x
|
|
119
|
+
tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
|
|
120
|
+
tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
|
|
121
|
+
const e3 = Fp.eql(Fp.sqr(tv2), n); // 9. e3 = (tv2^2) == x
|
|
122
|
+
const root = Fp.cmov(tv1, tv2, e3);// 10. z = CMOV(tv1, tv2, e3) # Select sqrt from tv1 & tv2
|
|
123
|
+
assertIsSquare(Fp, root, n);
|
|
124
|
+
return root;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
121
127
|
|
|
122
128
|
/**
|
|
123
129
|
* Tonelli-Shanks square root search algorithm.
|
|
@@ -129,7 +135,7 @@ function sqrt5mod8<T>(Fp: IField<T>, n: T) {
|
|
|
129
135
|
export function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T {
|
|
130
136
|
// Initialization (precomputation).
|
|
131
137
|
// Caching initialization could boost perf by 7%.
|
|
132
|
-
if (P <
|
|
138
|
+
if (P < _3n) throw new Error('sqrt is not defined for small field');
|
|
133
139
|
// Factor P - 1 = Q * 2^S, where Q is odd
|
|
134
140
|
let Q = P - _1n;
|
|
135
141
|
let S = 0;
|
|
@@ -197,7 +203,8 @@ export function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T {
|
|
|
197
203
|
*
|
|
198
204
|
* 1. P ≡ 3 (mod 4)
|
|
199
205
|
* 2. P ≡ 5 (mod 8)
|
|
200
|
-
* 3.
|
|
206
|
+
* 3. P ≡ 9 (mod 16)
|
|
207
|
+
* 4. Tonelli-Shanks algorithm
|
|
201
208
|
*
|
|
202
209
|
* Different algorithms can give different roots, it is up to user to decide which one they want.
|
|
203
210
|
* For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
|
|
@@ -207,7 +214,8 @@ export function FpSqrt(P: bigint): <T>(Fp: IField<T>, n: T) => T {
|
|
|
207
214
|
if (P % _4n === _3n) return sqrt3mod4;
|
|
208
215
|
// P ≡ 5 (mod 8) => Atkin algorithm, page 10 of https://eprint.iacr.org/2012/685.pdf
|
|
209
216
|
if (P % _8n === _5n) return sqrt5mod8;
|
|
210
|
-
// P ≡ 9 (mod 16)
|
|
217
|
+
// P ≡ 9 (mod 16) => Kong algorithm, page 11 of https://eprint.iacr.org/2012/685.pdf (algorithm 4)
|
|
218
|
+
if (P % _16n === _9n) return sqrt9mod16(P);
|
|
211
219
|
// Tonelli-Shanks algorithm
|
|
212
220
|
return tonelliShanks(P);
|
|
213
221
|
}
|
|
@@ -252,10 +260,11 @@ export interface IField<T> {
|
|
|
252
260
|
// [RFC9380](https://www.rfc-editor.org/rfc/rfc9380#section-4.1).
|
|
253
261
|
// NOTE: sgn0 is 'negative in LE', which is same as odd. And negative in LE is kinda strange definition anyway.
|
|
254
262
|
isOdd?(num: T): boolean; // Odd instead of even since we have it for Fp2
|
|
263
|
+
allowedLengths?: number[];
|
|
255
264
|
// legendre?(num: T): T;
|
|
256
265
|
invertBatch: (lst: T[]) => T[];
|
|
257
266
|
toBytes(num: T): Uint8Array;
|
|
258
|
-
fromBytes(bytes: Uint8Array): T;
|
|
267
|
+
fromBytes(bytes: Uint8Array, skipValidation?: boolean): T;
|
|
259
268
|
// If c is False, CMOV returns a, otherwise it returns b.
|
|
260
269
|
cmov(a: T, b: T, c: boolean): T;
|
|
261
270
|
}
|
|
@@ -371,7 +380,13 @@ export function nLength(n: bigint, nBitLength?: number): NLength {
|
|
|
371
380
|
|
|
372
381
|
type FpField = IField<bigint> & Required<Pick<IField<bigint>, 'isOdd'>>;
|
|
373
382
|
type SqrtFn = (n: bigint) => bigint;
|
|
374
|
-
type FieldOpts = Partial<{
|
|
383
|
+
type FieldOpts = Partial<{
|
|
384
|
+
sqrt: SqrtFn;
|
|
385
|
+
isLE: boolean;
|
|
386
|
+
BITS: number;
|
|
387
|
+
modOnDecode: boolean; // bls12-381 requires mod(n) instead of rejecting keys >= n
|
|
388
|
+
allowedLengths?: readonly number[]; // for P521 (adds padding for smaller sizes)
|
|
389
|
+
}>;
|
|
375
390
|
/**
|
|
376
391
|
* Creates a finite field. Major performance optimizations:
|
|
377
392
|
* * 1. Denormalized operations like mulN instead of mul.
|
|
@@ -393,19 +408,23 @@ type FieldOpts = Partial<{ sqrt: SqrtFn; isLE: boolean; BITS: number }>;
|
|
|
393
408
|
*/
|
|
394
409
|
export function Field(
|
|
395
410
|
ORDER: bigint,
|
|
396
|
-
bitLenOrOpts?: number | FieldOpts,
|
|
411
|
+
bitLenOrOpts?: number | FieldOpts, // TODO: use opts only in v2?
|
|
397
412
|
isLE = false,
|
|
398
413
|
opts: { sqrt?: SqrtFn } = {}
|
|
399
414
|
): Readonly<FpField> {
|
|
400
415
|
if (ORDER <= _0n) throw new Error('invalid field: expected ORDER > 0, got ' + ORDER);
|
|
401
416
|
let _nbitLength: number | undefined = undefined;
|
|
402
417
|
let _sqrt: SqrtFn | undefined = undefined;
|
|
418
|
+
let modOnDecode: boolean = false;
|
|
419
|
+
let allowedLengths: undefined | readonly number[] = undefined;
|
|
403
420
|
if (typeof bitLenOrOpts === 'object' && bitLenOrOpts != null) {
|
|
404
421
|
if (opts.sqrt || isLE) throw new Error('cannot specify opts in two arguments');
|
|
405
422
|
const _opts = bitLenOrOpts;
|
|
406
423
|
if (_opts.BITS) _nbitLength = _opts.BITS;
|
|
407
424
|
if (_opts.sqrt) _sqrt = _opts.sqrt;
|
|
408
425
|
if (typeof _opts.isLE === 'boolean') isLE = _opts.isLE;
|
|
426
|
+
if (typeof _opts.modOnDecode === 'boolean') modOnDecode = _opts.modOnDecode;
|
|
427
|
+
allowedLengths = _opts.allowedLengths;
|
|
409
428
|
} else {
|
|
410
429
|
if (typeof bitLenOrOpts === 'number') _nbitLength = bitLenOrOpts;
|
|
411
430
|
if (opts.sqrt) _sqrt = opts.sqrt;
|
|
@@ -421,6 +440,7 @@ export function Field(
|
|
|
421
440
|
MASK: bitMask(BITS),
|
|
422
441
|
ZERO: _0n,
|
|
423
442
|
ONE: _1n,
|
|
443
|
+
allowedLengths: allowedLengths,
|
|
424
444
|
create: (num) => mod(num, ORDER),
|
|
425
445
|
isValid: (num) => {
|
|
426
446
|
if (typeof num !== 'bigint')
|
|
@@ -455,10 +475,27 @@ export function Field(
|
|
|
455
475
|
return sqrtP(f, n);
|
|
456
476
|
}),
|
|
457
477
|
toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
|
|
458
|
-
fromBytes: (bytes) => {
|
|
478
|
+
fromBytes: (bytes, skipValidation = true) => {
|
|
479
|
+
if (allowedLengths) {
|
|
480
|
+
if (!allowedLengths.includes(bytes.length) || bytes.length > BYTES) {
|
|
481
|
+
throw new Error(
|
|
482
|
+
'Field.fromBytes: expected ' + allowedLengths + ' bytes, got ' + bytes.length
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
const padded = new Uint8Array(BYTES);
|
|
486
|
+
// isLE add 0 to right, !isLE to the left.
|
|
487
|
+
padded.set(bytes, isLE ? 0 : padded.length - bytes.length);
|
|
488
|
+
bytes = padded;
|
|
489
|
+
}
|
|
459
490
|
if (bytes.length !== BYTES)
|
|
460
491
|
throw new Error('Field.fromBytes: expected ' + BYTES + ' bytes, got ' + bytes.length);
|
|
461
|
-
|
|
492
|
+
let scalar = isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
|
493
|
+
if (modOnDecode) scalar = mod(scalar, ORDER);
|
|
494
|
+
if (!skipValidation)
|
|
495
|
+
if (!f.isValid(scalar)) throw new Error('invalid field element: outside of range 0..ORDER');
|
|
496
|
+
// NOTE: we don't validate scalar here, please use isValid. This done such way because some
|
|
497
|
+
// protocol may allow non-reduced scalar that reduced later or changed some other way.
|
|
498
|
+
return scalar;
|
|
462
499
|
},
|
|
463
500
|
// TODO: we don't need it here, move out to separate fn
|
|
464
501
|
invertBatch: (lst) => FpInvertBatch(f, lst),
|
|
@@ -469,6 +506,20 @@ export function Field(
|
|
|
469
506
|
return Object.freeze(f);
|
|
470
507
|
}
|
|
471
508
|
|
|
509
|
+
// Generic random scalar, we can do same for other fields if via Fp2.mul(Fp2.ONE, Fp2.random)?
|
|
510
|
+
// This allows unsafe methods like ignore bias or zero. These unsafe, but often used in different protocols (if deterministic RNG).
|
|
511
|
+
// which mean we cannot force this via opts.
|
|
512
|
+
// Not sure what to do with randomBytes, we can accept it inside opts if wanted.
|
|
513
|
+
// Probably need to export getMinHashLength somewhere?
|
|
514
|
+
// random(bytes?: Uint8Array, unsafeAllowZero = false, unsafeAllowBias = false) {
|
|
515
|
+
// const LEN = !unsafeAllowBias ? getMinHashLength(ORDER) : BYTES;
|
|
516
|
+
// if (bytes === undefined) bytes = randomBytes(LEN); // _opts.randomBytes?
|
|
517
|
+
// const num = isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
|
518
|
+
// // `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0
|
|
519
|
+
// const reduced = unsafeAllowZero ? mod(num, ORDER) : mod(num, ORDER - _1n) + _1n;
|
|
520
|
+
// return reduced;
|
|
521
|
+
// },
|
|
522
|
+
|
|
472
523
|
export function FpSqrtOdd<T>(Fp: IField<T>, elm: T): T {
|
|
473
524
|
if (!Fp.isOdd) throw new Error("Field doesn't have isOdd");
|
|
474
525
|
const root = Fp.sqrt(elm);
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
numberToBytesLE,
|
|
14
14
|
randomBytes,
|
|
15
15
|
} from '../utils.ts';
|
|
16
|
+
import type { CurveInfo } from './curve.ts';
|
|
16
17
|
import { mod } from './modular.ts';
|
|
17
18
|
|
|
18
19
|
const _0n = BigInt(0);
|
|
@@ -28,14 +29,25 @@ export type CurveType = {
|
|
|
28
29
|
randomBytes?: (bytesLength?: number) => Uint8Array;
|
|
29
30
|
};
|
|
30
31
|
|
|
31
|
-
export type
|
|
32
|
+
export type MontgomeryECDH = {
|
|
32
33
|
scalarMult: (scalar: Hex, u: Hex) => Uint8Array;
|
|
33
34
|
scalarMultBase: (scalar: Hex) => Uint8Array;
|
|
34
|
-
getSharedSecret: (
|
|
35
|
-
getPublicKey: (
|
|
36
|
-
utils: {
|
|
35
|
+
getSharedSecret: (secretKeyA: Hex, publicKeyB: Hex) => Uint8Array;
|
|
36
|
+
getPublicKey: (secretKey: Hex) => Uint8Array;
|
|
37
|
+
utils: {
|
|
38
|
+
randomSecretKey: () => Uint8Array;
|
|
39
|
+
/** @deprecated use `randomSecretKey` */
|
|
40
|
+
randomPrivateKey: () => Uint8Array;
|
|
41
|
+
};
|
|
37
42
|
GuBytes: Uint8Array;
|
|
43
|
+
info: {
|
|
44
|
+
type: 'montgomery';
|
|
45
|
+
lengths: Omit<CurveInfo['lengths'], 'signature'>;
|
|
46
|
+
publicKeyHasPrefix?: boolean;
|
|
47
|
+
};
|
|
48
|
+
keygen: (seed?: Uint8Array) => { secretKey: Uint8Array; publicKey: Uint8Array };
|
|
38
49
|
};
|
|
50
|
+
export type CurveFn = MontgomeryECDH;
|
|
39
51
|
|
|
40
52
|
function validateOpts(curve: CurveType) {
|
|
41
53
|
_validateObject(curve, {
|
|
@@ -45,7 +57,7 @@ function validateOpts(curve: CurveType) {
|
|
|
45
57
|
return Object.freeze({ ...curve } as const);
|
|
46
58
|
}
|
|
47
59
|
|
|
48
|
-
export function montgomery(curveDef: CurveType):
|
|
60
|
+
export function montgomery(curveDef: CurveType): MontgomeryECDH {
|
|
49
61
|
const CURVE = validateOpts(curveDef);
|
|
50
62
|
const { P, type, adjustScalarBytes, powPminus2, randomBytes: rand } = CURVE;
|
|
51
63
|
const is25519 = type === 'x25519';
|
|
@@ -155,13 +167,28 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|
|
155
167
|
const z2 = powPminus2(z_2); // `Fp.pow(x, P - _2n)` is much slower equivalent
|
|
156
168
|
return modP(x_2 * z2); // Return x_2 * (z_2^(p - 2))
|
|
157
169
|
}
|
|
158
|
-
|
|
170
|
+
const randomSecretKey = (seed = randomBytes_(fieldLen)) => seed;
|
|
171
|
+
const utils = {
|
|
172
|
+
randomSecretKey,
|
|
173
|
+
randomPrivateKey: randomSecretKey,
|
|
174
|
+
};
|
|
175
|
+
function keygen(seed?: Uint8Array) {
|
|
176
|
+
const secretKey = utils.randomSecretKey(seed);
|
|
177
|
+
return { secretKey, publicKey: scalarMultBase(secretKey) };
|
|
178
|
+
}
|
|
179
|
+
const lengths = {
|
|
180
|
+
secret: fieldLen,
|
|
181
|
+
public: fieldLen,
|
|
182
|
+
seed: fieldLen,
|
|
183
|
+
};
|
|
159
184
|
return {
|
|
185
|
+
keygen,
|
|
186
|
+
getSharedSecret: (secretKey: Hex, publicKey: Hex) => scalarMult(secretKey, publicKey),
|
|
187
|
+
getPublicKey: (secretKey: Hex): Uint8Array => scalarMultBase(secretKey),
|
|
160
188
|
scalarMult,
|
|
161
189
|
scalarMultBase,
|
|
162
|
-
|
|
163
|
-
getPublicKey: (privateKey: Hex): Uint8Array => scalarMultBase(privateKey),
|
|
164
|
-
utils: { randomPrivateKey: () => randomBytes_(fieldLen) },
|
|
190
|
+
utils,
|
|
165
191
|
GuBytes: GuBytes.slice(),
|
|
192
|
+
info: { type: 'montgomery' as const, lengths },
|
|
166
193
|
};
|
|
167
194
|
}
|
package/src/abstract/tower.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
13
13
|
import { bitLen, bitMask, concatBytes, notImplemented } from '../utils.ts';
|
|
14
14
|
import * as mod from './modular.ts';
|
|
15
|
-
import type {
|
|
15
|
+
import type { WeierstrassPoint, WeierstrassPointCons } from './weierstrass.ts';
|
|
16
16
|
|
|
17
17
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
18
18
|
// prettier-ignore
|
|
@@ -95,8 +95,8 @@ export function psiFrobenius(
|
|
|
95
95
|
): {
|
|
96
96
|
psi: (x: Fp2, y: Fp2) => [Fp2, Fp2];
|
|
97
97
|
psi2: (x: Fp2, y: Fp2) => [Fp2, Fp2];
|
|
98
|
-
G2psi: (c:
|
|
99
|
-
G2psi2: (c:
|
|
98
|
+
G2psi: (c: WeierstrassPointCons<Fp2>, P: WeierstrassPoint<Fp2>) => WeierstrassPoint<Fp2>;
|
|
99
|
+
G2psi2: (c: WeierstrassPointCons<Fp2>, P: WeierstrassPoint<Fp2>) => WeierstrassPoint<Fp2>;
|
|
100
100
|
PSI_X: Fp2;
|
|
101
101
|
PSI_Y: Fp2;
|
|
102
102
|
PSI2_X: Fp2;
|
|
@@ -123,7 +123,7 @@ export function psiFrobenius(
|
|
|
123
123
|
// Map points
|
|
124
124
|
const mapAffine =
|
|
125
125
|
<T>(fn: (x: T, y: T) => [T, T]) =>
|
|
126
|
-
(c:
|
|
126
|
+
(c: WeierstrassPointCons<T>, P: WeierstrassPoint<T>) => {
|
|
127
127
|
const affine = P.toAffine();
|
|
128
128
|
const p = fn(affine.x, affine.y);
|
|
129
129
|
return c.fromAffine({ x: p[0], y: p[1] });
|