@noble/curves 0.6.0 → 0.6.2
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 +130 -92
- package/lib/_shortw_utils.d.ts +3 -3
- package/lib/abstract/curve.d.ts +3 -4
- package/lib/abstract/curve.js +13 -19
- package/lib/abstract/edwards.d.ts +5 -5
- package/lib/abstract/edwards.js +44 -26
- package/lib/abstract/hash-to-curve.js +1 -1
- package/lib/abstract/modular.d.ts +1 -1
- package/lib/abstract/modular.js +11 -13
- package/lib/abstract/montgomery.js +23 -65
- package/lib/abstract/utils.d.ts +17 -1
- package/lib/abstract/utils.js +57 -27
- package/lib/abstract/weierstrass.d.ts +10 -10
- package/lib/abstract/weierstrass.js +61 -79
- package/lib/esm/abstract/curve.js +11 -17
- package/lib/esm/abstract/edwards.js +46 -28
- package/lib/esm/abstract/hash-to-curve.js +1 -1
- package/lib/esm/abstract/modular.js +12 -14
- package/lib/esm/abstract/montgomery.js +24 -66
- package/lib/esm/abstract/poseidon.js +1 -1
- package/lib/esm/abstract/utils.js +55 -26
- package/lib/esm/abstract/weierstrass.js +62 -80
- package/lib/esm/p224.js +1 -1
- package/lib/esm/p521.js +1 -13
- package/lib/esm/secp256k1.js +34 -36
- package/lib/esm/stark.js +1 -1
- package/lib/p192.d.ts +6 -6
- package/lib/p224.d.ts +6 -6
- package/lib/p224.js +1 -1
- package/lib/p256.d.ts +6 -6
- package/lib/p384.d.ts +6 -6
- package/lib/p521.d.ts +16 -17
- package/lib/p521.js +1 -13
- package/lib/secp256k1.d.ts +17 -14
- package/lib/secp256k1.js +28 -30
- package/lib/stark.d.ts +3 -3
- package/lib/stark.js +1 -1
- package/package.json +4 -4
|
@@ -1,32 +1,27 @@
|
|
|
1
1
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
2
|
// Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
|
|
3
3
|
import { mod } from './modular.js';
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
4
|
+
import * as ut from './utils.js';
|
|
5
|
+
import { ensureBytes } from './utils.js';
|
|
6
|
+
import { wNAF, validateBasic } from './curve.js';
|
|
6
7
|
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
|
7
8
|
const _0n = BigInt(0);
|
|
8
9
|
const _1n = BigInt(1);
|
|
9
10
|
const _2n = BigInt(2);
|
|
10
11
|
const _8n = BigInt(8);
|
|
11
12
|
function validateOpts(curve) {
|
|
12
|
-
const opts =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
for (const fn of ['adjustScalarBytes', 'domain', 'uvRatio', 'mapToCurve']) {
|
|
25
|
-
if (opts[fn] === undefined)
|
|
26
|
-
continue; // Optional
|
|
27
|
-
if (typeof opts[fn] !== 'function')
|
|
28
|
-
throw new Error(`Invalid ${fn} function`);
|
|
29
|
-
}
|
|
13
|
+
const opts = validateBasic(curve);
|
|
14
|
+
ut.validateObject(curve, {
|
|
15
|
+
hash: 'function',
|
|
16
|
+
a: 'bigint',
|
|
17
|
+
d: 'bigint',
|
|
18
|
+
randomBytes: 'function',
|
|
19
|
+
}, {
|
|
20
|
+
adjustScalarBytes: 'function',
|
|
21
|
+
domain: 'function',
|
|
22
|
+
uvRatio: 'function',
|
|
23
|
+
mapToCurve: 'function',
|
|
24
|
+
});
|
|
30
25
|
// Set defaults
|
|
31
26
|
return Object.freeze({ ...opts });
|
|
32
27
|
}
|
|
@@ -111,7 +106,30 @@ export function twistedEdwards(curveDef) {
|
|
|
111
106
|
this._WINDOW_SIZE = windowSize;
|
|
112
107
|
pointPrecomputes.delete(this);
|
|
113
108
|
}
|
|
114
|
-
|
|
109
|
+
// Not required for fromHex(), which always creates valid points.
|
|
110
|
+
// Could be useful for fromAffine().
|
|
111
|
+
assertValidity() {
|
|
112
|
+
const { a, d } = CURVE;
|
|
113
|
+
if (this.is0())
|
|
114
|
+
throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
|
|
115
|
+
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
|
|
116
|
+
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
|
|
117
|
+
const { ex: X, ey: Y, ez: Z, et: T } = this;
|
|
118
|
+
const X2 = modP(X * X); // X²
|
|
119
|
+
const Y2 = modP(Y * Y); // Y²
|
|
120
|
+
const Z2 = modP(Z * Z); // Z²
|
|
121
|
+
const Z4 = modP(Z2 * Z2); // Z⁴
|
|
122
|
+
const aX2 = modP(X2 * a); // aX²
|
|
123
|
+
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
|
|
124
|
+
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
|
|
125
|
+
if (left !== right)
|
|
126
|
+
throw new Error('bad point: equation left != right (1)');
|
|
127
|
+
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
|
|
128
|
+
const XY = modP(X * Y);
|
|
129
|
+
const ZT = modP(Z * T);
|
|
130
|
+
if (XY !== ZT)
|
|
131
|
+
throw new Error('bad point: equation left != right (2)');
|
|
132
|
+
}
|
|
115
133
|
// Compare one point to another.
|
|
116
134
|
equals(other) {
|
|
117
135
|
isPoint(other);
|
|
@@ -261,7 +279,7 @@ export function twistedEdwards(curveDef) {
|
|
|
261
279
|
const normed = hex.slice(); // copy again, we'll manipulate it
|
|
262
280
|
const lastByte = hex[len - 1]; // select last byte
|
|
263
281
|
normed[len - 1] = lastByte & ~0x80; // clear last bit
|
|
264
|
-
const y = bytesToNumberLE(normed);
|
|
282
|
+
const y = ut.bytesToNumberLE(normed);
|
|
265
283
|
if (y === _0n) {
|
|
266
284
|
// y=0 is allowed
|
|
267
285
|
}
|
|
@@ -291,12 +309,12 @@ export function twistedEdwards(curveDef) {
|
|
|
291
309
|
}
|
|
292
310
|
toRawBytes() {
|
|
293
311
|
const { x, y } = this.toAffine();
|
|
294
|
-
const bytes = numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
|
|
312
|
+
const bytes = ut.numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
|
|
295
313
|
bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
|
|
296
314
|
return bytes; // and use the last byte to encode sign of x
|
|
297
315
|
}
|
|
298
316
|
toHex() {
|
|
299
|
-
return bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
|
|
317
|
+
return ut.bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
|
|
300
318
|
}
|
|
301
319
|
}
|
|
302
320
|
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
|
|
@@ -308,7 +326,7 @@ export function twistedEdwards(curveDef) {
|
|
|
308
326
|
}
|
|
309
327
|
// Little-endian SHA512 with modulo n
|
|
310
328
|
function modN_LE(hash) {
|
|
311
|
-
return modN(bytesToNumberLE(hash));
|
|
329
|
+
return modN(ut.bytesToNumberLE(hash));
|
|
312
330
|
}
|
|
313
331
|
function isHex(item, err) {
|
|
314
332
|
if (typeof item !== 'string' && !(item instanceof Uint8Array))
|
|
@@ -334,7 +352,7 @@ export function twistedEdwards(curveDef) {
|
|
|
334
352
|
}
|
|
335
353
|
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
|
|
336
354
|
function hashDomainToScalar(context = new Uint8Array(), ...msgs) {
|
|
337
|
-
const msg = concatBytes(...msgs);
|
|
355
|
+
const msg = ut.concatBytes(...msgs);
|
|
338
356
|
return modN_LE(cHash(domain(msg, ensureBytes(context), !!preHash)));
|
|
339
357
|
}
|
|
340
358
|
/** Signs message with privateKey. RFC8032 5.1.6 */
|
|
@@ -349,7 +367,7 @@ export function twistedEdwards(curveDef) {
|
|
|
349
367
|
const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
|
|
350
368
|
const s = modN(r + k * scalar); // S = (r + k * s) mod L
|
|
351
369
|
assertGE0(s); // 0 <= s < l
|
|
352
|
-
const res = concatBytes(R, numberToBytesLE(s, Fp.BYTES));
|
|
370
|
+
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
|
|
353
371
|
return ensureBytes(res, nByteLength * 2); // 64-byte signature
|
|
354
372
|
}
|
|
355
373
|
function verify(sig, msg, publicKey, context) {
|
|
@@ -362,7 +380,7 @@ export function twistedEdwards(curveDef) {
|
|
|
362
380
|
msg = preHash(msg); // for ed25519ph, etc
|
|
363
381
|
const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
|
|
364
382
|
const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
|
|
365
|
-
const s = bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
|
|
383
|
+
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
|
|
366
384
|
const SB = G.multiplyUnsafe(s);
|
|
367
385
|
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
|
|
368
386
|
const RkA = R.add(A.multiplyUnsafe(k));
|
|
@@ -16,7 +16,7 @@ export function validateOpts(opts) {
|
|
|
16
16
|
}
|
|
17
17
|
export function stringToBytes(str) {
|
|
18
18
|
if (typeof str !== 'string') {
|
|
19
|
-
throw new
|
|
19
|
+
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
|
20
20
|
}
|
|
21
21
|
return new TextEncoder().encode(str);
|
|
22
22
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
2
|
// Utilities for modular arithmetics and finite fields
|
|
3
|
-
import { bitMask, numberToBytesBE, numberToBytesLE, bytesToNumberBE, bytesToNumberLE, ensureBytes, } from './utils.js';
|
|
3
|
+
import { bitMask, numberToBytesBE, numberToBytesLE, bytesToNumberBE, bytesToNumberLE, ensureBytes, validateObject, } from './utils.js';
|
|
4
4
|
// prettier-ignore
|
|
5
5
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
|
6
6
|
// prettier-ignore
|
|
@@ -34,7 +34,6 @@ export function pow(num, power, modulo) {
|
|
|
34
34
|
return res;
|
|
35
35
|
}
|
|
36
36
|
// Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4)
|
|
37
|
-
// TODO: Fp version?
|
|
38
37
|
export function pow2(x, power, modulo) {
|
|
39
38
|
let res = x;
|
|
40
39
|
while (power-- > _0n) {
|
|
@@ -193,18 +192,17 @@ const FIELD_FIELDS = [
|
|
|
193
192
|
'addN', 'subN', 'mulN', 'sqrN'
|
|
194
193
|
];
|
|
195
194
|
export function validateField(field) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
195
|
+
const initial = {
|
|
196
|
+
ORDER: 'bigint',
|
|
197
|
+
MASK: 'bigint',
|
|
198
|
+
BYTES: 'isSafeInteger',
|
|
199
|
+
BITS: 'isSafeInteger',
|
|
200
|
+
};
|
|
201
|
+
const opts = FIELD_FIELDS.reduce((map, val) => {
|
|
202
|
+
map[val] = 'function';
|
|
203
|
+
return map;
|
|
204
|
+
}, initial);
|
|
205
|
+
return validateObject(field, opts);
|
|
208
206
|
}
|
|
209
207
|
// Generic field functions
|
|
210
208
|
export function FpPow(f, num, power) {
|
|
@@ -1,31 +1,19 @@
|
|
|
1
1
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
2
|
import { mod, pow } from './modular.js';
|
|
3
|
-
import { ensureBytes, numberToBytesLE,
|
|
3
|
+
import { bytesToNumberLE, ensureBytes, numberToBytesLE, validateObject } from './utils.js';
|
|
4
4
|
const _0n = BigInt(0);
|
|
5
5
|
const _1n = BigInt(1);
|
|
6
6
|
function validateOpts(curve) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2']) {
|
|
18
|
-
if (curve[fn] === undefined)
|
|
19
|
-
continue; // Optional
|
|
20
|
-
if (typeof curve[fn] !== 'function')
|
|
21
|
-
throw new Error(`Invalid ${fn} function`);
|
|
22
|
-
}
|
|
23
|
-
for (const i of ['Gu']) {
|
|
24
|
-
if (curve[i] === undefined)
|
|
25
|
-
continue; // Optional
|
|
26
|
-
if (typeof curve[i] !== 'string')
|
|
27
|
-
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
|
28
|
-
}
|
|
7
|
+
validateObject(curve, {
|
|
8
|
+
a24: 'bigint',
|
|
9
|
+
}, {
|
|
10
|
+
montgomeryBits: 'isSafeInteger',
|
|
11
|
+
nByteLength: 'isSafeInteger',
|
|
12
|
+
adjustScalarBytes: 'function',
|
|
13
|
+
domain: 'function',
|
|
14
|
+
powPminus2: 'function',
|
|
15
|
+
Gu: 'string',
|
|
16
|
+
});
|
|
29
17
|
// Set defaults
|
|
30
18
|
return Object.freeze({ ...curve });
|
|
31
19
|
}
|
|
@@ -40,31 +28,7 @@ export function montgomery(curveDef) {
|
|
|
40
28
|
const fieldLen = CURVE.nByteLength;
|
|
41
29
|
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes);
|
|
42
30
|
const powPminus2 = CURVE.powPminus2 || ((x) => pow(x, P - BigInt(2), P));
|
|
43
|
-
|
|
44
|
-
* Checks for num to be in range:
|
|
45
|
-
* For strict == true: `0 < num < max`.
|
|
46
|
-
* For strict == false: `0 <= num < max`.
|
|
47
|
-
* Converts non-float safe numbers to bigints.
|
|
48
|
-
*/
|
|
49
|
-
function normalizeScalar(num, max, strict = true) {
|
|
50
|
-
if (!max)
|
|
51
|
-
throw new TypeError('Specify max value');
|
|
52
|
-
if (typeof num === 'number' && Number.isSafeInteger(num))
|
|
53
|
-
num = BigInt(num);
|
|
54
|
-
if (typeof num === 'bigint' && num < max) {
|
|
55
|
-
if (strict) {
|
|
56
|
-
if (_0n < num)
|
|
57
|
-
return num;
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
if (_0n <= num)
|
|
61
|
-
return num;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
throw new TypeError('Expected valid scalar: 0 < scalar < max');
|
|
65
|
-
}
|
|
66
|
-
// cswap from RFC7748
|
|
67
|
-
// NOTE: cswap is not from RFC7748!
|
|
31
|
+
// cswap from RFC7748. But it is not from RFC7748!
|
|
68
32
|
/*
|
|
69
33
|
cswap(swap, x_2, x_3):
|
|
70
34
|
dummy = mask(swap) AND (x_2 XOR x_3)
|
|
@@ -80,6 +44,11 @@ export function montgomery(curveDef) {
|
|
|
80
44
|
x_3 = modP(x_3 + dummy);
|
|
81
45
|
return [x_2, x_3];
|
|
82
46
|
}
|
|
47
|
+
function assertFieldElement(n) {
|
|
48
|
+
if (typeof n === 'bigint' && _0n <= n && n < P)
|
|
49
|
+
return n;
|
|
50
|
+
throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
|
|
51
|
+
}
|
|
83
52
|
// x25519 from 4
|
|
84
53
|
/**
|
|
85
54
|
*
|
|
@@ -88,11 +57,10 @@ export function montgomery(curveDef) {
|
|
|
88
57
|
* @returns new Point on Montgomery curve
|
|
89
58
|
*/
|
|
90
59
|
function montgomeryLadder(pointU, scalar) {
|
|
91
|
-
const
|
|
92
|
-
const u = normalizeScalar(pointU, P);
|
|
60
|
+
const u = assertFieldElement(pointU);
|
|
93
61
|
// Section 5: Implementations MUST accept non-canonical values and process them as
|
|
94
62
|
// if they had been reduced modulo the field prime.
|
|
95
|
-
const k =
|
|
63
|
+
const k = assertFieldElement(scalar);
|
|
96
64
|
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
|
|
97
65
|
const a24 = CURVE.a24;
|
|
98
66
|
const x_1 = u;
|
|
@@ -145,12 +113,14 @@ export function montgomery(curveDef) {
|
|
|
145
113
|
return numberToBytesLE(modP(u), montgomeryBytes);
|
|
146
114
|
}
|
|
147
115
|
function decodeUCoordinate(uEnc) {
|
|
148
|
-
const u = ensureBytes(uEnc, montgomeryBytes);
|
|
149
116
|
// Section 5: When receiving such an array, implementations of X25519
|
|
150
117
|
// MUST mask the most significant bit in the final byte.
|
|
151
118
|
// This is very ugly way, but it works because fieldLen-1 is outside of bounds for X448, so this becomes NOOP
|
|
152
119
|
// fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519
|
|
153
|
-
u
|
|
120
|
+
const u = ensureBytes(uEnc, montgomeryBytes);
|
|
121
|
+
// u[fieldLen-1] crashes QuickJS (TypeError: out-of-bound numeric index)
|
|
122
|
+
if (fieldLen === montgomeryBytes)
|
|
123
|
+
u[fieldLen - 1] &= 127; // 0b0111_1111
|
|
154
124
|
return bytesToNumberLE(u);
|
|
155
125
|
}
|
|
156
126
|
function decodeScalar(n) {
|
|
@@ -159,13 +129,6 @@ export function montgomery(curveDef) {
|
|
|
159
129
|
throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
|
|
160
130
|
return bytesToNumberLE(adjustScalarBytes(bytes));
|
|
161
131
|
}
|
|
162
|
-
/**
|
|
163
|
-
* Computes shared secret between private key "scalar" and public key's "u" (x) coordinate.
|
|
164
|
-
* We can get 'y' coordinate from 'u',
|
|
165
|
-
* but Point.fromHex also wants 'x' coordinate oddity flag,
|
|
166
|
-
* and we cannot get 'x' without knowing 'v'.
|
|
167
|
-
* Need to add generic conversion between twisted edwards and complimentary curve for JubJub.
|
|
168
|
-
*/
|
|
169
132
|
function scalarMult(scalar, u) {
|
|
170
133
|
const pointU = decodeUCoordinate(u);
|
|
171
134
|
const _scalar = decodeScalar(scalar);
|
|
@@ -176,12 +139,7 @@ export function montgomery(curveDef) {
|
|
|
176
139
|
throw new Error('Invalid private or public key received');
|
|
177
140
|
return encodeUCoordinate(pu);
|
|
178
141
|
}
|
|
179
|
-
|
|
180
|
-
* Computes public key from private.
|
|
181
|
-
* Executes scalar multiplication of curve's base point by scalar.
|
|
182
|
-
* @param scalar private key
|
|
183
|
-
* @returns new public key
|
|
184
|
-
*/
|
|
142
|
+
// Computes public key from private. By doing scalar multiplication of base point.
|
|
185
143
|
function scalarMultBase(scalar) {
|
|
186
144
|
return scalarMult(scalar, CURVE.Gu);
|
|
187
145
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
2
|
// Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info
|
|
3
|
-
import {
|
|
3
|
+
import { FpPow, validateField } from './modular.js';
|
|
4
4
|
export function validateOpts(opts) {
|
|
5
5
|
const { Fp } = opts;
|
|
6
6
|
validateField(Fp);
|
|
@@ -6,7 +6,7 @@ const u8a = (a) => a instanceof Uint8Array;
|
|
|
6
6
|
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
|
|
7
7
|
export function bytesToHex(bytes) {
|
|
8
8
|
if (!u8a(bytes))
|
|
9
|
-
throw new Error('
|
|
9
|
+
throw new Error('Uint8Array expected');
|
|
10
10
|
// pre-caching improves the speed 6x
|
|
11
11
|
let hex = '';
|
|
12
12
|
for (let i = 0; i < bytes.length; i++) {
|
|
@@ -20,23 +20,23 @@ export function numberToHexUnpadded(num) {
|
|
|
20
20
|
}
|
|
21
21
|
export function hexToNumber(hex) {
|
|
22
22
|
if (typeof hex !== 'string')
|
|
23
|
-
throw new Error('
|
|
23
|
+
throw new Error('string expected, got ' + typeof hex);
|
|
24
24
|
// Big Endian
|
|
25
|
-
return BigInt(`0x${hex}`);
|
|
25
|
+
return BigInt(hex === '' ? '0' : `0x${hex}`);
|
|
26
26
|
}
|
|
27
27
|
// Caching slows it down 2-3x
|
|
28
28
|
export function hexToBytes(hex) {
|
|
29
29
|
if (typeof hex !== 'string')
|
|
30
|
-
throw new Error('
|
|
30
|
+
throw new Error('string expected, got ' + typeof hex);
|
|
31
31
|
if (hex.length % 2)
|
|
32
|
-
throw new Error('
|
|
32
|
+
throw new Error('hex string is invalid: unpadded ' + hex.length);
|
|
33
33
|
const array = new Uint8Array(hex.length / 2);
|
|
34
34
|
for (let i = 0; i < array.length; i++) {
|
|
35
35
|
const j = i * 2;
|
|
36
36
|
const hexByte = hex.slice(j, j + 2);
|
|
37
37
|
const byte = Number.parseInt(hexByte, 16);
|
|
38
38
|
if (Number.isNaN(byte) || byte < 0)
|
|
39
|
-
throw new Error('
|
|
39
|
+
throw new Error('invalid byte sequence');
|
|
40
40
|
array[i] = byte;
|
|
41
41
|
}
|
|
42
42
|
return array;
|
|
@@ -47,18 +47,13 @@ export function bytesToNumberBE(bytes) {
|
|
|
47
47
|
}
|
|
48
48
|
export function bytesToNumberLE(bytes) {
|
|
49
49
|
if (!u8a(bytes))
|
|
50
|
-
throw new Error('
|
|
50
|
+
throw new Error('Uint8Array expected');
|
|
51
51
|
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
|
|
52
52
|
}
|
|
53
53
|
export const numberToBytesBE = (n, len) => hexToBytes(n.toString(16).padStart(len * 2, '0'));
|
|
54
54
|
export const numberToBytesLE = (n, len) => numberToBytesBE(n, len).reverse();
|
|
55
55
|
// Returns variable number bytes (minimal bigint encoding?)
|
|
56
|
-
export const numberToVarBytesBE = (n) =>
|
|
57
|
-
let hex = n.toString(16);
|
|
58
|
-
if (hex.length & 1)
|
|
59
|
-
hex = '0' + hex;
|
|
60
|
-
return hexToBytes(hex);
|
|
61
|
-
};
|
|
56
|
+
export const numberToVarBytesBE = (n) => hexToBytes(numberToHexUnpadded(n));
|
|
62
57
|
export function ensureBytes(hex, expectedLength) {
|
|
63
58
|
// Uint8Array.from() instead of hash.slice() because node.js Buffer
|
|
64
59
|
// is instance of Uint8Array, and its slice() creates **mutable** copy
|
|
@@ -68,19 +63,16 @@ export function ensureBytes(hex, expectedLength) {
|
|
|
68
63
|
return bytes;
|
|
69
64
|
}
|
|
70
65
|
// Copies several Uint8Arrays into one.
|
|
71
|
-
export function concatBytes(...
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
pad += arr.length;
|
|
82
|
-
}
|
|
83
|
-
return result;
|
|
66
|
+
export function concatBytes(...arrs) {
|
|
67
|
+
const r = new Uint8Array(arrs.reduce((sum, a) => sum + a.length, 0));
|
|
68
|
+
let pad = 0; // walk through each item, ensure they have proper type
|
|
69
|
+
arrs.forEach((a) => {
|
|
70
|
+
if (!u8a(a))
|
|
71
|
+
throw new Error('Uint8Array expected');
|
|
72
|
+
r.set(a, pad);
|
|
73
|
+
pad += a.length;
|
|
74
|
+
});
|
|
75
|
+
return r;
|
|
84
76
|
}
|
|
85
77
|
export function equalBytes(b1, b2) {
|
|
86
78
|
// We don't care about timing attacks here
|
|
@@ -107,3 +99,40 @@ export const bitSet = (n, pos, value) => n | ((value ? _1n : _0n) << BigInt(pos)
|
|
|
107
99
|
// Return mask for N bits (Same as BigInt(`0b${Array(i).fill('1').join('')}`))
|
|
108
100
|
// Not using ** operator with bigints for old engines.
|
|
109
101
|
export const bitMask = (n) => (_2n << BigInt(n - 1)) - _1n;
|
|
102
|
+
const validatorFns = {
|
|
103
|
+
bigint: (val) => typeof val === 'bigint',
|
|
104
|
+
function: (val) => typeof val === 'function',
|
|
105
|
+
boolean: (val) => typeof val === 'boolean',
|
|
106
|
+
string: (val) => typeof val === 'string',
|
|
107
|
+
isSafeInteger: (val) => Number.isSafeInteger(val),
|
|
108
|
+
array: (val) => Array.isArray(val),
|
|
109
|
+
field: (val, object) => object.Fp.isValid(val),
|
|
110
|
+
hash: (val) => typeof val === 'function' && Number.isSafeInteger(val.outputLen),
|
|
111
|
+
};
|
|
112
|
+
// type Record<K extends string | number | symbol, T> = { [P in K]: T; }
|
|
113
|
+
export function validateObject(object, validators, optValidators = {}) {
|
|
114
|
+
const checkField = (fieldName, type, isOptional) => {
|
|
115
|
+
const checkVal = validatorFns[type];
|
|
116
|
+
if (typeof checkVal !== 'function')
|
|
117
|
+
throw new Error(`Invalid validator "${type}", expected function`);
|
|
118
|
+
const val = object[fieldName];
|
|
119
|
+
if (isOptional && val === undefined)
|
|
120
|
+
return;
|
|
121
|
+
if (!checkVal(val, object)) {
|
|
122
|
+
throw new Error(`Invalid param ${String(fieldName)}=${val} (${typeof val}), expected ${type}`);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
for (const [fieldName, type] of Object.entries(validators))
|
|
126
|
+
checkField(fieldName, type, false);
|
|
127
|
+
for (const [fieldName, type] of Object.entries(optValidators))
|
|
128
|
+
checkField(fieldName, type, true);
|
|
129
|
+
return object;
|
|
130
|
+
}
|
|
131
|
+
// validate type tests
|
|
132
|
+
// const o: { a: number; b: number; c: number } = { a: 1, b: 5, c: 6 };
|
|
133
|
+
// const z0 = validateObject(o, { a: 'isSafeInteger' }, { c: 'bigint' }); // Ok!
|
|
134
|
+
// // Should fail type-check
|
|
135
|
+
// const z1 = validateObject(o, { a: 'tmp' }, { c: 'zz' });
|
|
136
|
+
// const z2 = validateObject(o, { a: 'isSafeInteger' }, { c: 'zz' });
|
|
137
|
+
// const z3 = validateObject(o, { test: 'boolean', z: 'bug' });
|
|
138
|
+
// const z4 = validateObject(o, { a: 'boolean', z: 'bug' });
|