@leofcoin/chain 1.10.21 → 1.10.22
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 +21 -0
- package/exports/browser/beacon-wire.js +1 -1
- package/exports/browser/{browser-CCfR9KOM-BGMMnVsR.js → browser-CCfR9KOM-UO9mzy-j.js} +2 -2
- package/exports/browser/chain.js +55 -125
- package/exports/browser/client-DbZBfpG6-CYDyUGUC.js +5943 -0
- package/exports/browser/{connection-monitor-D-krNiRH.js → connection-monitor-DMGAhcC0.js} +111 -2
- package/exports/browser/{index-dTQVgkpf-DOL2rIjp.js → index-dTQVgkpf-C6SJ0ZYW.js} +2 -2
- package/exports/browser/{index-DCbLvMKL.js → index-dxD17Fom.js} +231 -227
- package/exports/browser/{messages-DCePyqKC-BSvJxDWZ.js → messages-DZYCxoZI-D6kIiLKF.js} +2 -2
- package/exports/browser/{node-browser-Ha4Lvntj.js → node-browser-BMfelxXV.js} +23 -7
- package/exports/browser/node-browser.js +2 -2
- package/exports/chain.js +54 -124
- package/exports/connection-monitor-h2L8vsS7.js +435 -0
- package/exports/connection-monitor.js +2 -324
- package/exports/node.js +3 -1
- package/package.json +5 -5
- package/exports/browser/client-DKKzPSlb-C_1ug6ux.js +0 -2401
|
@@ -1282,176 +1282,190 @@ var index = {
|
|
|
1282
1282
|
* * b = `7n` // equation param
|
|
1283
1283
|
* * Gx, Gy are coordinates of Generator / base point
|
|
1284
1284
|
*/
|
|
1285
|
+
const freeze = Object.freeze;
|
|
1286
|
+
const P = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn;
|
|
1287
|
+
const N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;
|
|
1288
|
+
const Gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n;
|
|
1289
|
+
const Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n;
|
|
1285
1290
|
// Mirror noble-curves: Point.CURVE() returns shared params, but those params must stay frozen so
|
|
1286
1291
|
// callers cannot mutate them out from under the arithmetic constants captured below.
|
|
1287
|
-
const secp256k1_CURVE =
|
|
1288
|
-
p:
|
|
1289
|
-
n:
|
|
1292
|
+
const secp256k1_CURVE = freeze({
|
|
1293
|
+
p: P,
|
|
1294
|
+
n: N,
|
|
1290
1295
|
h: 1n,
|
|
1291
1296
|
a: 0n,
|
|
1292
1297
|
b: 7n,
|
|
1293
|
-
Gx
|
|
1294
|
-
Gy
|
|
1298
|
+
Gx,
|
|
1299
|
+
Gy,
|
|
1295
1300
|
});
|
|
1296
|
-
const { p: P, n: N, Gx, Gy, b: _b } = secp256k1_CURVE;
|
|
1297
1301
|
// 32-byte field / scalar width, and the SHA-256 / HMAC-DRBG output width used
|
|
1298
1302
|
// by the RFC6979 paths here.
|
|
1299
1303
|
const L = 32;
|
|
1300
|
-
const L2 = 64; // 64-byte compact signatures, and 64 hex chars for zero-padded 32-byte scalars
|
|
1301
|
-
const lengths = {
|
|
1302
|
-
publicKey: L + 1,
|
|
1303
|
-
publicKeyUncompressed: L2 + 1,
|
|
1304
|
-
// 48-byte keygen seed floor: 384 bits exceeds FIPS 186-5 Table A.2's
|
|
1305
|
-
// 352-bit recommendation for 256-bit prime curves.
|
|
1306
|
-
seed: L + L / 2,
|
|
1307
|
-
};
|
|
1308
1304
|
// Helpers and Precomputes sections are reused between libraries
|
|
1309
1305
|
// ## Helpers
|
|
1310
1306
|
// ----------
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1307
|
+
/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
|
|
1308
|
+
const isBytes = (a) => {
|
|
1309
|
+
// Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases.
|
|
1310
|
+
// The fallback still requires a real ArrayBuffer view, so plain
|
|
1311
|
+
// JSON-deserialized `{ constructor: ... }` spoofing is rejected, and
|
|
1312
|
+
// `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.
|
|
1313
|
+
return (a instanceof Uint8Array ||
|
|
1314
|
+
(ArrayBuffer.isView(a) &&
|
|
1315
|
+
a.constructor.name === 'Uint8Array' &&
|
|
1316
|
+
a.BYTES_PER_ELEMENT === 1));
|
|
1317
1317
|
};
|
|
1318
|
-
|
|
1319
|
-
// fallback still requires a real ArrayBuffer view so plain JSON-deserialized `{ constructor: ... }`
|
|
1320
|
-
// spoofing is rejected, and `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.
|
|
1321
|
-
const isBytes = (a) => a instanceof Uint8Array ||
|
|
1322
|
-
(ArrayBuffer.isView(a) &&
|
|
1323
|
-
a.constructor.name === 'Uint8Array' &&
|
|
1324
|
-
a.BYTES_PER_ELEMENT === 1);
|
|
1325
|
-
/** Asserts something is Bytes. */
|
|
1318
|
+
/** Asserts something is Uint8Array. */
|
|
1326
1319
|
const abytes = (value, length, title = '') => {
|
|
1320
|
+
// Success path first: this runs at the start of every update() / digestInto(), and the
|
|
1321
|
+
// common `abytes(data)` form must not pay for length handling it does not use.
|
|
1322
|
+
if (isBytes(value) && (length === undefined || value.length === length))
|
|
1323
|
+
return value;
|
|
1324
|
+
// Error path: recompute freely to build the exact message.
|
|
1327
1325
|
const bytes = isBytes(value);
|
|
1328
|
-
const
|
|
1329
|
-
const
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
const msg = prefix + 'expected Uint8Array' + ofLen + ', got ' + got;
|
|
1335
|
-
return bytes ? err(msg, RangeError) : err(msg, TypeError);
|
|
1336
|
-
}
|
|
1337
|
-
return value;
|
|
1326
|
+
const ofLen = length !== undefined ? ` of length ${length}` : '';
|
|
1327
|
+
const got = bytes ? `length=${value.length}` : `type=${typeof value}`;
|
|
1328
|
+
const message = (title ? `"${title}" ` : '') + 'expected Uint8Array' + ofLen + ', got ' + got;
|
|
1329
|
+
if (!bytes)
|
|
1330
|
+
throw new TypeError(message);
|
|
1331
|
+
throw new RangeError(message);
|
|
1338
1332
|
};
|
|
1339
|
-
|
|
1340
|
-
|
|
1333
|
+
// Signing can retain the message across hash callbacks / awaits, and Schnorr hashes it more than
|
|
1334
|
+
// once. Take one owned snapshot so caller mutation cannot change the signing transcript.
|
|
1335
|
+
// cloneBytes expects an already-validated view; snapshotBytes validates, then copies.
|
|
1336
|
+
const cloneBytes = (value) => Uint8Array.from(value);
|
|
1337
|
+
const snapshotBytes = (value, title, length) => cloneBytes(abytes(value, length, title));
|
|
1341
1338
|
// Callers keep values non-negative and within the requested width; padStart() won't truncate over-wide inputs.
|
|
1342
1339
|
const padh = (n, pad) => n.toString(16).padStart(pad, '0');
|
|
1343
|
-
/**
|
|
1344
|
-
const bytesToHex = (
|
|
1340
|
+
/** Convert byte array to hex string. */
|
|
1341
|
+
const bytesToHex = (bytes) => {
|
|
1345
1342
|
let hex = '';
|
|
1346
|
-
for (const
|
|
1347
|
-
hex += padh(
|
|
1343
|
+
for (const byte of abytes(bytes))
|
|
1344
|
+
hex += padh(byte, 2);
|
|
1348
1345
|
return hex;
|
|
1349
1346
|
};
|
|
1350
|
-
|
|
1351
|
-
// Strict ASCII nibble parser: non-ASCII hex lookalikes are rejected as undefined.
|
|
1352
|
-
// prettier-ignore
|
|
1353
|
-
const _ch = (ch) => ch >= C._0 && ch <= C._9 ? ch - C._0 // '2' => 50-48
|
|
1354
|
-
: ch >= C.A && ch <= C.F ? ch - (C.A - 10) // 'B' => 66-(65-10)
|
|
1355
|
-
: ch >= C.a && ch <= C.f ? ch - (C.a - 10) // 'b' => 98-(97-10)
|
|
1356
|
-
: undefined;
|
|
1347
|
+
/** Convert hex string to byte array. */
|
|
1357
1348
|
const hexToBytes = (hex) => {
|
|
1358
|
-
const e = 'hex invalid'; // Strict ASCII hex only, with one generic error for
|
|
1349
|
+
const e = 'hex invalid'; // Strict ASCII hex only, with one generic error for parse failures.
|
|
1359
1350
|
if (typeof hex !== 'string')
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
//
|
|
1368
|
-
|
|
1369
|
-
const n2 = _ch(hex.charCodeAt(hi + 1)); // parse second char
|
|
1370
|
-
if (n1 === undefined || n2 === undefined)
|
|
1371
|
-
return err(e);
|
|
1372
|
-
array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9
|
|
1351
|
+
throw new TypeError(e);
|
|
1352
|
+
if (hex.length % 2 || !/^[\da-f]*$/i.test(hex))
|
|
1353
|
+
throw new RangeError(e);
|
|
1354
|
+
const array = new Uint8Array(hex.length / 2);
|
|
1355
|
+
for (let ai = 0, hi = 0; ai < array.length; ai++, hi += 2) {
|
|
1356
|
+
const n1 = hex.charCodeAt(hi);
|
|
1357
|
+
const n2 = hex.charCodeAt(hi + 1);
|
|
1358
|
+
// Regex guarantees ASCII. For 0..9/A..F/a..f, this maps char codes to 0..15.
|
|
1359
|
+
array[ai] = ((n1 & 15) + (n1 >> 6) * 9) * 16 + (n2 & 15) + (n2 >> 6) * 9;
|
|
1373
1360
|
}
|
|
1374
1361
|
return array;
|
|
1375
1362
|
};
|
|
1376
1363
|
// WebCrypto is available in all modern environments
|
|
1377
|
-
const subtle = () =>
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1364
|
+
const subtle = () => {
|
|
1365
|
+
const s = globalThis?.crypto?.subtle;
|
|
1366
|
+
if (s)
|
|
1367
|
+
return s;
|
|
1368
|
+
throw new Error('crypto.subtle must be defined, consider polyfill');
|
|
1369
|
+
};
|
|
1370
|
+
/** Copies several Uint8Arrays into one. */
|
|
1371
|
+
const concatBytes = (...arrays) => {
|
|
1372
|
+
let sum = 0;
|
|
1373
|
+
for (const a of arrays)
|
|
1374
|
+
sum += abytes(a).length;
|
|
1375
|
+
const res = new Uint8Array(sum);
|
|
1376
|
+
let pad = 0;
|
|
1377
|
+
for (const a of arrays) {
|
|
1378
|
+
res.set(a, pad);
|
|
1379
|
+
pad += a.length;
|
|
1380
|
+
}
|
|
1381
|
+
return res;
|
|
1388
1382
|
};
|
|
1389
1383
|
/**
|
|
1390
1384
|
* WebCrypto OS-level CSPRNG (random number generator).
|
|
1391
1385
|
* Will throw when not available; large-request ceilings are delegated to getRandomValues().
|
|
1392
1386
|
*/
|
|
1393
|
-
const randomBytes = (len = L) =>
|
|
1387
|
+
const randomBytes = (len = L) => {
|
|
1388
|
+
const c = globalThis?.crypto;
|
|
1389
|
+
if (typeof c?.getRandomValues !== 'function')
|
|
1390
|
+
throw new Error('crypto.getRandomValues must be defined, consider polyfill');
|
|
1391
|
+
return c.getRandomValues(new Uint8Array(len));
|
|
1392
|
+
};
|
|
1394
1393
|
const big = BigInt;
|
|
1395
1394
|
const arange = (n, min, max, msg = 'bad number: out of range') => {
|
|
1396
1395
|
if (typeof n !== 'bigint')
|
|
1397
|
-
|
|
1396
|
+
throw new TypeError(msg);
|
|
1398
1397
|
if (min <= n && n < max)
|
|
1399
1398
|
return n;
|
|
1400
|
-
|
|
1399
|
+
throw new RangeError(msg);
|
|
1401
1400
|
};
|
|
1402
1401
|
/** Canonical modular reduction. Callers must provide a positive modulus. */
|
|
1403
|
-
const M = (a, b = P) =>
|
|
1404
|
-
const r = a % b;
|
|
1405
|
-
return r >= 0n ? r : b + r;
|
|
1406
|
-
};
|
|
1402
|
+
const M = (a, b = P) => ((a %= b) >= 0n ? a : b + a);
|
|
1407
1403
|
const modN = (a) => M(a, N);
|
|
1408
|
-
/** Modular inversion using
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1404
|
+
/** Modular inversion using extended euclidean GCD. Variable-time (non-CT). */
|
|
1405
|
+
const invert = (number, modulo) => {
|
|
1406
|
+
if (number === 0n)
|
|
1407
|
+
throw new Error('invert: expected non-zero number');
|
|
1408
|
+
// modulo = 1 is the zero ring: gcd(x, 1) = 1 makes the loop below "succeed" and return the
|
|
1409
|
+
// useless inverse 0. Reject it.
|
|
1410
|
+
if (modulo <= 1n)
|
|
1411
|
+
throw new Error('invert: expected modulus > 1, got ' + modulo);
|
|
1412
|
+
// This is variable-time: the loop count depends on `number`.
|
|
1413
|
+
let a = M(number, modulo);
|
|
1414
|
+
let b = modulo;
|
|
1415
|
+
// Only the Bézout coefficient of `number` (x/u chain) is tracked; the coefficient of `modulo`
|
|
1416
|
+
// never affects the output, so it is not computed.
|
|
1417
|
+
// prettier-ignore
|
|
1418
|
+
let x = 0n, u = 1n;
|
|
1414
1419
|
while (a !== 0n) {
|
|
1415
|
-
const q = b / a
|
|
1420
|
+
const q = b / a;
|
|
1421
|
+
const r = b - a * q;
|
|
1416
1422
|
const m = x - u * q;
|
|
1423
|
+
// prettier-ignore
|
|
1417
1424
|
b = a, a = r, x = u, u = m;
|
|
1418
1425
|
}
|
|
1419
|
-
|
|
1426
|
+
const gcd = b;
|
|
1427
|
+
if (gcd !== 1n)
|
|
1428
|
+
throw new Error('invert: does not exist');
|
|
1429
|
+
return M(x, modulo);
|
|
1420
1430
|
};
|
|
1421
|
-
const
|
|
1431
|
+
const _hash = (name) => {
|
|
1422
1432
|
// @ts-ignore
|
|
1423
1433
|
const fn = hashes[name];
|
|
1424
1434
|
if (typeof fn !== 'function')
|
|
1425
|
-
|
|
1435
|
+
throw new Error('hashes.' + name + ' not set');
|
|
1426
1436
|
return fn;
|
|
1427
1437
|
};
|
|
1428
1438
|
// All exported provider slots are caller-configurable and may be unset or return arbitrary values,
|
|
1429
1439
|
// so wrapper helpers must enforce the exact 32-byte digest contract instead of trusting providers.
|
|
1430
|
-
const
|
|
1431
|
-
const
|
|
1440
|
+
const callHash = (name, a, b) => abytes(_hash(name)(a, b), L, 'digest');
|
|
1441
|
+
const callHashAsync = async (name, a, b) => abytes(await _hash(name)(a, b), L, 'digest');
|
|
1432
1442
|
// also rejects structurally similar Point values from other realms / bundled copies
|
|
1433
|
-
const apoint = (p) =>
|
|
1443
|
+
const apoint = (p) => {
|
|
1444
|
+
if (p instanceof Point)
|
|
1445
|
+
return p;
|
|
1446
|
+
throw new TypeError('Point expected');
|
|
1447
|
+
};
|
|
1434
1448
|
// ## End of Helpers
|
|
1435
1449
|
// -----------------
|
|
1450
|
+
const E_BADPOINT = 'bad point: not on curve';
|
|
1436
1451
|
/**
|
|
1437
1452
|
* secp256k1 formula. Koblitz curves are subclass of weierstrass curves with a=0,
|
|
1438
1453
|
* making it x³+b; callers validate x first.
|
|
1439
1454
|
*/
|
|
1440
|
-
const koblitz = (x) => M(M(x * x) * x +
|
|
1455
|
+
const koblitz = (x) => M(M(x * x) * x + 7n);
|
|
1441
1456
|
/** assert is element of field mod P (incl. 0 for projective infinity coordinates) */
|
|
1442
1457
|
const FpIsValid = (n) => arange(n, 0n, P);
|
|
1443
1458
|
/** assert is element of field mod P (excl. 0 where current callers need a non-zero coordinate) */
|
|
1444
1459
|
const FpIsValidNot0 = (n) => arange(n, 1n, P);
|
|
1445
|
-
/** assert is element of field mod N (excl. 0), matching the shared BIP340 scalar-failure rule used here
|
|
1460
|
+
/** assert is element of field mod N (excl. 0), matching the shared BIP340 scalar-failure rule used here.
|
|
1461
|
+
* There is deliberately no FnIsValid: no caller accepts the scalar 0. */
|
|
1446
1462
|
const FnIsValidNot0 = (n) => arange(n, 1n, N);
|
|
1447
1463
|
// Shared parity primitive for BIP340 even-y checks and SEC 1 compressed prefixes.
|
|
1448
1464
|
const isEven = (y) => !(y & 1n);
|
|
1449
|
-
/** create Uint8Array of byte n */
|
|
1450
|
-
const u8of = (n) => Uint8Array.of(n);
|
|
1451
1465
|
/** SEC 1 compressed-prefix helper. Parity only: callers validate y before asking for the prefix byte. */
|
|
1452
|
-
const getPrefix = (y) =>
|
|
1453
|
-
/** lift_x from BIP340 returns the unique even square root
|
|
1454
|
-
* SEC 1 callers still
|
|
1466
|
+
const getPrefix = (y) => Uint8Array.of(isEven(y) ? 0x02 : 0x03);
|
|
1467
|
+
/** lift_x from BIP340 returns the unique point with x and an even square root of x³+7.
|
|
1468
|
+
* SEC 1 callers still negate it for the odd-prefix branch. */
|
|
1455
1469
|
const lift_x = (x) => {
|
|
1456
1470
|
// Let c = x³ + 7 mod p. Fail if x ≥ p. (also fail if x < 1)
|
|
1457
1471
|
const c = koblitz(FpIsValidNot0(x));
|
|
@@ -1467,8 +1481,8 @@ const lift_x = (x) => {
|
|
|
1467
1481
|
num = (num * num) % P; // Not constant-time.
|
|
1468
1482
|
}
|
|
1469
1483
|
if (M(r * r) !== c)
|
|
1470
|
-
|
|
1471
|
-
return isEven(r) ? r : M(-r);
|
|
1484
|
+
throw new Error('sqrt invalid'); // check if result is valid
|
|
1485
|
+
return new Point(x, isEven(r) ? r : M(-r), 1n);
|
|
1472
1486
|
};
|
|
1473
1487
|
/**
|
|
1474
1488
|
* Point in 3d xyz projective coordinates. 3d takes less inversions than 2d.
|
|
@@ -1492,7 +1506,7 @@ class Point {
|
|
|
1492
1506
|
this.X = FpIsValid(X);
|
|
1493
1507
|
this.Y = FpIsValidNot0(Y); // Y can't be 0 in Projective
|
|
1494
1508
|
this.Z = FpIsValid(Z);
|
|
1495
|
-
|
|
1509
|
+
freeze(this);
|
|
1496
1510
|
}
|
|
1497
1511
|
/** Returns the shared curve metadata object by reference.
|
|
1498
1512
|
* It is readonly only at type level, and mutating it won't retarget arithmetic,
|
|
@@ -1508,30 +1522,31 @@ class Point {
|
|
|
1508
1522
|
/** Convert Uint8Array or hex string to Point. */
|
|
1509
1523
|
static fromBytes(bytes) {
|
|
1510
1524
|
abytes(bytes);
|
|
1511
|
-
const { publicKey: comp, publicKeyUncompressed: uncomp } = lengths; // e.g. for 32-byte: 33, 65
|
|
1512
|
-
let p = undefined;
|
|
1513
1525
|
const length = bytes.length;
|
|
1514
1526
|
const head = bytes[0];
|
|
1515
|
-
const
|
|
1516
|
-
const x = sliceBytesNumBE(tail, 0, L);
|
|
1527
|
+
const x = sliceBytesNumBE(bytes, 1, 33);
|
|
1517
1528
|
// SEC 1 defines the rare infinity encoding 0x00, but SEC 1 public-key validation rejects
|
|
1518
1529
|
// infinity. We keep 0x00 rejected here because this parser is reused by verify(), ECDH,
|
|
1519
1530
|
// and public-key validation helpers, so strict handling applies to all callers by default.
|
|
1520
1531
|
// Local secp256k1 crosstests show OpenSSL raw point codecs accept 0x00 too.
|
|
1521
|
-
// Parse SEC 1 compressed/uncompressed encodings
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1532
|
+
// Parse and validate SEC 1 compressed/uncompressed encodings before returning.
|
|
1533
|
+
try {
|
|
1534
|
+
if (length === 33 && (head === 0x02 || head === 0x03)) {
|
|
1535
|
+
// Equation is y² == x³ + ax + b. We calculate y from x.
|
|
1536
|
+
// lift_x() returns the even point; SEC 1 0x03 still needs the odd point.
|
|
1537
|
+
const p = lift_x(x);
|
|
1538
|
+
return head === 0x03 ? p.negate() : p;
|
|
1539
|
+
}
|
|
1540
|
+
// Uncompressed 65-byte point, 0x04 prefix
|
|
1541
|
+
if (length === 65 && head === 0x04)
|
|
1542
|
+
return new Point(x, sliceBytesNumBE(bytes, 33, 65), 1n).assertValidity();
|
|
1543
|
+
}
|
|
1544
|
+
catch (error) {
|
|
1545
|
+
// Out-of-range coordinates and non-residue x report the same error as wrong
|
|
1546
|
+
// prefixes / off-curve points, instead of generic range-check messages.
|
|
1547
|
+
throw new Error(E_BADPOINT);
|
|
1529
1548
|
}
|
|
1530
|
-
|
|
1531
|
-
if (length === uncomp && head === 0x04)
|
|
1532
|
-
p = new Point(x, sliceBytesNumBE(tail, L, L2), 1n);
|
|
1533
|
-
// Validate point
|
|
1534
|
-
return p ? p.assertValidity() : err('bad point: not on curve');
|
|
1549
|
+
throw new Error(E_BADPOINT);
|
|
1535
1550
|
}
|
|
1536
1551
|
static fromHex(hex) {
|
|
1537
1552
|
return Point.fromBytes(hexToBytes(hex));
|
|
@@ -1546,14 +1561,10 @@ class Point {
|
|
|
1546
1561
|
equals(other) {
|
|
1547
1562
|
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
1548
1563
|
const { X: X2, Y: Y2, Z: Z2 } = apoint(other); // checks class equality
|
|
1549
|
-
|
|
1550
|
-
const X2Z1 = M(X2 * Z1);
|
|
1551
|
-
const Y1Z2 = M(Y1 * Z2);
|
|
1552
|
-
const Y2Z1 = M(Y2 * Z1);
|
|
1553
|
-
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
|
|
1564
|
+
return M(X1 * Z2) === M(X2 * Z1) && M(Y1 * Z2) === M(Y2 * Z1);
|
|
1554
1565
|
}
|
|
1555
1566
|
is0() {
|
|
1556
|
-
return this.
|
|
1567
|
+
return this.Z === 0n;
|
|
1557
1568
|
}
|
|
1558
1569
|
/** Flip point over y coordinate. */
|
|
1559
1570
|
negate() {
|
|
@@ -1573,7 +1584,7 @@ class Point {
|
|
|
1573
1584
|
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
1574
1585
|
const { X: X2, Y: Y2, Z: Z2 } = apoint(other);
|
|
1575
1586
|
const a = 0n;
|
|
1576
|
-
const b =
|
|
1587
|
+
const b = 7n;
|
|
1577
1588
|
let X3 = 0n, Y3 = 0n, Z3 = 0n;
|
|
1578
1589
|
const b3 = M(b * 3n);
|
|
1579
1590
|
let t0 = M(X1 * X2), t1 = M(Y1 * Y2), t2 = M(Z1 * Z2), t3 = M(X1 + Y1); // step 1
|
|
@@ -1637,13 +1648,18 @@ class Point {
|
|
|
1637
1648
|
// init result point & fake point
|
|
1638
1649
|
let p = I;
|
|
1639
1650
|
let f = G;
|
|
1640
|
-
|
|
1651
|
+
let d = this;
|
|
1652
|
+
// Safe mode always runs 256 iterations so ladder length can't leak the scalar's
|
|
1653
|
+
// leading zero bits; unsafe mode stops at the top set bit for speed.
|
|
1654
|
+
for (let i = 0; safe ? i < 256 : n > 0n; i++) {
|
|
1641
1655
|
// if bit is present, add to point
|
|
1642
1656
|
// if not present, add to fake, for timing safety
|
|
1643
1657
|
if (n & 1n)
|
|
1644
1658
|
p = p.add(d);
|
|
1645
1659
|
else if (safe)
|
|
1646
1660
|
f = f.add(d);
|
|
1661
|
+
d = d.double();
|
|
1662
|
+
n >>= 1n;
|
|
1647
1663
|
}
|
|
1648
1664
|
return p;
|
|
1649
1665
|
}
|
|
@@ -1654,14 +1670,14 @@ class Point {
|
|
|
1654
1670
|
toAffine() {
|
|
1655
1671
|
const { X: x, Y: y, Z: z } = this;
|
|
1656
1672
|
// fast-paths for ZERO point OR Z=1
|
|
1657
|
-
if (
|
|
1673
|
+
if (z === 0n)
|
|
1658
1674
|
return { x: 0n, y: 0n };
|
|
1659
1675
|
if (z === 1n)
|
|
1660
1676
|
return { x, y };
|
|
1661
1677
|
const iz = invert(z, P);
|
|
1662
1678
|
// (Z * Z^-1) must be 1, otherwise bad math
|
|
1663
1679
|
if (M(z * iz) !== 1n)
|
|
1664
|
-
|
|
1680
|
+
throw new Error('inverse invalid');
|
|
1665
1681
|
// x = X*Z^-1; y = Y*Z^-1
|
|
1666
1682
|
return { x: M(x * iz), y: M(y * iz) };
|
|
1667
1683
|
}
|
|
@@ -1671,7 +1687,9 @@ class Point {
|
|
|
1671
1687
|
FpIsValidNot0(x); // must be in range 1 <= x,y < P
|
|
1672
1688
|
FpIsValidNot0(y);
|
|
1673
1689
|
// y² == x³ + ax + b, equation sides must be equal
|
|
1674
|
-
|
|
1690
|
+
if (M(y * y) !== koblitz(x))
|
|
1691
|
+
throw new Error(E_BADPOINT);
|
|
1692
|
+
return this;
|
|
1675
1693
|
}
|
|
1676
1694
|
/** Converts point to 33/65-byte Uint8Array. */
|
|
1677
1695
|
toBytes(isCompressed = true) {
|
|
@@ -1681,7 +1699,7 @@ class Point {
|
|
|
1681
1699
|
const x32b = numTo32b(x);
|
|
1682
1700
|
if (isCompressed)
|
|
1683
1701
|
return concatBytes(getPrefix(y), x32b);
|
|
1684
|
-
return concatBytes(
|
|
1702
|
+
return concatBytes(Uint8Array.of(0x04), x32b, numTo32b(y));
|
|
1685
1703
|
}
|
|
1686
1704
|
toHex(isCompressed) {
|
|
1687
1705
|
return bytesToHex(this.toBytes(isCompressed));
|
|
@@ -1696,17 +1714,14 @@ Point.BASE = G;
|
|
|
1696
1714
|
Point.ZERO = I;
|
|
1697
1715
|
/** `Q = u1⋅G + u2⋅R`. Verifies Q is not ZERO. Unsafe: non-CT. */
|
|
1698
1716
|
const doubleScalarMulUns = (R, u1, u2) => {
|
|
1699
|
-
return G.multiply(u1, false)
|
|
1700
|
-
.add(R.multiply(u2, false))
|
|
1701
|
-
.assertValidity();
|
|
1717
|
+
return G.multiply(u1, false).add(R.multiply(u2, false)).assertValidity();
|
|
1702
1718
|
};
|
|
1703
1719
|
// Inherits byte validation from bytesToHex(); the || '0' fallback keeps empty input mapped to 0n.
|
|
1704
1720
|
const bytesToNumBE = (b) => big('0x' + (bytesToHex(b) || '0'));
|
|
1705
1721
|
// Callers provide monotone slice bounds; subarray() would otherwise clamp or reinterpret them silently.
|
|
1706
1722
|
const sliceBytesNumBE = (b, from, to) => bytesToNumBE(b.subarray(from, to));
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
const numTo32b = (num) => hexToBytes(padh(arange(num, 0n, B256), L2));
|
|
1723
|
+
/** Generic 32-byte big-endian encoder. Must be 0 <= num < 2²⁵⁶; call sites need not be field/scalar elements. */
|
|
1724
|
+
const numTo32b = (num) => hexToBytes(padh(arange(num, 0n, 2n ** 256n), L * 2)); // L*2 = 64 hex chars for a zero-padded 32-byte value
|
|
1710
1725
|
/** Normalize private key to scalar (bigint). Verifies scalar is in range 1 <= d < N. */
|
|
1711
1726
|
const secretKeyToScalar = (secretKey) => {
|
|
1712
1727
|
const num = bytesToNumBE(abytes(secretKey, L, 'secret key'));
|
|
@@ -1737,12 +1752,11 @@ const isValidSecretKey = (secretKey) => {
|
|
|
1737
1752
|
}
|
|
1738
1753
|
};
|
|
1739
1754
|
const isValidPublicKey = (publicKey, isCompressed) => {
|
|
1740
|
-
const { publicKey: comp, publicKeyUncompressed } = lengths;
|
|
1741
1755
|
try {
|
|
1742
1756
|
const l = publicKey.length;
|
|
1743
|
-
if (isCompressed === true && l !==
|
|
1757
|
+
if (isCompressed === true && l !== 33)
|
|
1744
1758
|
return false;
|
|
1745
|
-
if (isCompressed === false && l !==
|
|
1759
|
+
if (isCompressed === false && l !== 65)
|
|
1746
1760
|
return false;
|
|
1747
1761
|
return !!Point.fromBytes(publicKey);
|
|
1748
1762
|
}
|
|
@@ -1770,12 +1784,11 @@ const _sha = 'SHA-256';
|
|
|
1770
1784
|
const hashes = {
|
|
1771
1785
|
hmacSha256Async: async (key, message) => {
|
|
1772
1786
|
const s = subtle();
|
|
1773
|
-
const
|
|
1774
|
-
|
|
1775
|
-
return u8n(await s.sign(name, k, message));
|
|
1787
|
+
const k = await s.importKey('raw', key, { name: 'HMAC', hash: _sha }, false, ['sign']);
|
|
1788
|
+
return new Uint8Array(await s.sign('HMAC', k, message));
|
|
1776
1789
|
},
|
|
1777
1790
|
hmacSha256: undefined,
|
|
1778
|
-
sha256Async: async (msg) =>
|
|
1791
|
+
sha256Async: async (msg) => new Uint8Array(await subtle().digest(_sha, msg)),
|
|
1779
1792
|
sha256: undefined,
|
|
1780
1793
|
};
|
|
1781
1794
|
/**
|
|
@@ -1802,20 +1815,17 @@ const getSharedSecret = (secretKeyA, publicKeyB, isCompressed = true) => {
|
|
|
1802
1815
|
// FIPS 186-5 Appendix A.4.1 style key generation reduces a wide random integer mod (n - 1) and adds 1.
|
|
1803
1816
|
// The 48-byte minimum keeps the secp256k1 bias bound below the appendix's epsilon <= 2^-64 target.
|
|
1804
1817
|
const randomSecretKey = (seed) => {
|
|
1805
|
-
seed = seed === undefined ? randomBytes(
|
|
1818
|
+
seed = seed === undefined ? randomBytes(48) : seed;
|
|
1806
1819
|
abytes(seed);
|
|
1807
1820
|
// Keep the public range text aligned with the enforced 48-byte FIPS floor.
|
|
1808
|
-
if (seed.length <
|
|
1809
|
-
|
|
1821
|
+
if (seed.length < 48 || seed.length > 1024)
|
|
1822
|
+
throw new RangeError('expected 48-1024b');
|
|
1810
1823
|
const num = M(bytesToNumBE(seed), N - 1n);
|
|
1811
1824
|
return numTo32b(num + 1n);
|
|
1812
1825
|
};
|
|
1813
1826
|
const createKeygen = (getPublicKey) => (seed) => {
|
|
1814
1827
|
const secretKey = randomSecretKey(seed);
|
|
1815
|
-
return {
|
|
1816
|
-
secretKey,
|
|
1817
|
-
publicKey: getPublicKey(secretKey),
|
|
1818
|
-
};
|
|
1828
|
+
return { secretKey, publicKey: getPublicKey(secretKey) };
|
|
1819
1829
|
};
|
|
1820
1830
|
/**
|
|
1821
1831
|
* Math, hex, byte helpers. Not in `utils` because utils share API with noble-curves.
|
|
@@ -1826,17 +1836,17 @@ const createKeygen = (getPublicKey) => (seed) => {
|
|
|
1826
1836
|
* const hex = etc.bytesToHex(new Uint8Array([1, 2, 3]));
|
|
1827
1837
|
* ```
|
|
1828
1838
|
*/
|
|
1829
|
-
const etc = /* @__PURE__ */
|
|
1839
|
+
const etc = /* @__PURE__ */ freeze({
|
|
1830
1840
|
hexToBytes,
|
|
1831
1841
|
bytesToHex,
|
|
1832
1842
|
concatBytes,
|
|
1833
1843
|
bytesToNumberBE: bytesToNumBE,
|
|
1834
1844
|
numberToBytesBE: numTo32b,
|
|
1835
1845
|
mod: M,
|
|
1836
|
-
invert
|
|
1846
|
+
invert,
|
|
1837
1847
|
randomBytes,
|
|
1838
|
-
secretKeyToScalar
|
|
1839
|
-
abytes
|
|
1848
|
+
secretKeyToScalar,
|
|
1849
|
+
abytes,
|
|
1840
1850
|
});
|
|
1841
1851
|
/**
|
|
1842
1852
|
* Curve-specific key utilities.
|
|
@@ -1848,26 +1858,23 @@ const etc = /* @__PURE__ */ Object.freeze({
|
|
|
1848
1858
|
* const publicKey = secp.getPublicKey(secretKey);
|
|
1849
1859
|
* ```
|
|
1850
1860
|
*/
|
|
1851
|
-
const utils = /* @__PURE__ */
|
|
1852
|
-
isValidSecretKey
|
|
1853
|
-
isValidPublicKey
|
|
1854
|
-
randomSecretKey
|
|
1861
|
+
const utils = /* @__PURE__ */ freeze({
|
|
1862
|
+
isValidSecretKey,
|
|
1863
|
+
isValidPublicKey,
|
|
1864
|
+
randomSecretKey,
|
|
1855
1865
|
});
|
|
1856
1866
|
// Schnorr signatures are superior to ECDSA from above. Below is Schnorr-specific BIP0340 code.
|
|
1857
1867
|
// https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
|
|
1858
1868
|
// Internal BIP340 tag names are ASCII-only here, so charCodeAt() is enough; this is not a general UTF-8 encoder.
|
|
1859
1869
|
const getTag = (tag) => Uint8Array.from('BIP0340/' + tag, (c) => c.charCodeAt(0));
|
|
1860
|
-
const T_AUX = 'aux';
|
|
1861
|
-
const T_NONCE = 'nonce';
|
|
1862
|
-
const T_CHALLENGE = 'challenge';
|
|
1863
1870
|
// Both SHA-256 provider slots are configurable, so tag hashing still goes through the checked
|
|
1864
1871
|
// wrappers even though the built-in defaults are deterministic and the tag bytes are ASCII-only.
|
|
1865
1872
|
const taggedHash = (tag, ...messages) => {
|
|
1866
|
-
const tagH =
|
|
1867
|
-
return
|
|
1873
|
+
const tagH = callHash('sha256', getTag(tag));
|
|
1874
|
+
return callHash('sha256', concatBytes(tagH, tagH, ...messages));
|
|
1868
1875
|
};
|
|
1869
1876
|
// Async twin of taggedHash with the same checked provider boundary.
|
|
1870
|
-
const taggedHashAsync = (tag, ...messages) =>
|
|
1877
|
+
const taggedHashAsync = (tag, ...messages) => callHashAsync('sha256Async', getTag(tag)).then((tagH) => callHashAsync('sha256Async', concatBytes(tagH, tagH, ...messages)));
|
|
1871
1878
|
// BIP340 PubKey(sk) = bytes(d'⋅G), where bytes(P) is bytes(x(P)); signing also normalizes
|
|
1872
1879
|
// d to the equivalent scalar whose point has even y so the x-only public key stays canonical.
|
|
1873
1880
|
const extpubSchnorr = (priv) => {
|
|
@@ -1879,8 +1886,8 @@ const extpubSchnorr = (priv) => {
|
|
|
1879
1886
|
return { d, px };
|
|
1880
1887
|
};
|
|
1881
1888
|
const bytesModN = (bytes) => modN(bytesToNumBE(bytes));
|
|
1882
|
-
const challenge = (...args) => bytesModN(taggedHash(
|
|
1883
|
-
const challengeAsync = async (...args) => bytesModN(await taggedHashAsync(
|
|
1889
|
+
const challenge = (...args) => bytesModN(taggedHash('challenge', ...args));
|
|
1890
|
+
const challengeAsync = async (...args) => bytesModN(await taggedHashAsync('challenge', ...args));
|
|
1884
1891
|
/** Schnorr public key is just `x` coordinate of Point as per BIP340. */
|
|
1885
1892
|
const pubSchnorr = (secretKey) => {
|
|
1886
1893
|
return extpubSchnorr(secretKey).px; // d'=int(sk). Fail if d'=0 or d'≥n. Ret bytes(d'⋅G)
|
|
@@ -1888,13 +1895,14 @@ const pubSchnorr = (secretKey) => {
|
|
|
1888
1895
|
const keygenSchnorr = /* @__PURE__ */ createKeygen(pubSchnorr);
|
|
1889
1896
|
// Common preparation fn for both sync and async signing
|
|
1890
1897
|
const prepSigSchnorr = (message, secretKey, auxRand) => {
|
|
1898
|
+
const m = snapshotBytes(message, 'message');
|
|
1891
1899
|
const { px, d } = extpubSchnorr(secretKey);
|
|
1892
|
-
return { m
|
|
1900
|
+
return { m, px, d, a: abytes(auxRand, L) };
|
|
1893
1901
|
};
|
|
1894
1902
|
const extractK = (rand) => {
|
|
1895
1903
|
const k_ = bytesModN(rand); // Let k' = int(rand) mod n
|
|
1896
1904
|
if (k_ === 0n)
|
|
1897
|
-
|
|
1905
|
+
throw new Error('sign failed: k is zero'); // Fail if k' = 0.
|
|
1898
1906
|
const { px, d } = extpubSchnorr(numTo32b(k_)); // Let R = k'⋅G.
|
|
1899
1907
|
return { rx: px, k: d };
|
|
1900
1908
|
};
|
|
@@ -1910,73 +1918,78 @@ const E_INVSIG = 'invalid signature produced';
|
|
|
1910
1918
|
*/
|
|
1911
1919
|
const signSchnorr = (message, secretKey, auxRand = randomBytes(L)) => {
|
|
1912
1920
|
const { m, px, d, a } = prepSigSchnorr(message, secretKey, auxRand);
|
|
1913
|
-
const aux = taggedHash(T_AUX, a);
|
|
1914
1921
|
// Let t be the byte-wise xor of bytes(d) and hash/aux(a)
|
|
1915
|
-
const t = numTo32b(d ^ bytesToNumBE(aux));
|
|
1922
|
+
const t = numTo32b(d ^ bytesToNumBE(taggedHash('aux', a)));
|
|
1916
1923
|
// Let rand = hash/nonce(t || bytes(P) || m)
|
|
1917
|
-
const
|
|
1918
|
-
const { rx, k } = extractK(rand);
|
|
1924
|
+
const { rx, k } = extractK(taggedHash('nonce', t, px, m));
|
|
1919
1925
|
// Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n.
|
|
1920
|
-
const
|
|
1921
|
-
const sig = createSigSchnorr(k, rx, e, d);
|
|
1926
|
+
const sig = createSigSchnorr(k, rx, challenge(rx, px, m), d);
|
|
1922
1927
|
// If Verify(bytes(P), m, sig) (see below) returns failure, abort
|
|
1923
1928
|
if (!verifySchnorr(sig, m, px))
|
|
1924
|
-
|
|
1929
|
+
throw new Error(E_INVSIG);
|
|
1925
1930
|
return sig;
|
|
1926
1931
|
};
|
|
1927
1932
|
const signSchnorrAsync = async (message, secretKey, auxRand = randomBytes(L)) => {
|
|
1928
1933
|
const { m, px, d, a } = prepSigSchnorr(message, secretKey, auxRand);
|
|
1929
|
-
const aux = await taggedHashAsync(T_AUX, a);
|
|
1930
1934
|
// Let t be the byte-wise xor of bytes(d) and hash/aux(a)
|
|
1931
|
-
const t = numTo32b(d ^ bytesToNumBE(aux));
|
|
1935
|
+
const t = numTo32b(d ^ bytesToNumBE(await taggedHashAsync('aux', a)));
|
|
1932
1936
|
// Let rand = hash/nonce(t || bytes(P) || m)
|
|
1933
|
-
const
|
|
1934
|
-
const { rx, k } = extractK(rand);
|
|
1937
|
+
const { rx, k } = extractK(await taggedHashAsync('nonce', t, px, m));
|
|
1935
1938
|
// Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n.
|
|
1936
|
-
const
|
|
1937
|
-
const sig = createSigSchnorr(k, rx, e, d);
|
|
1939
|
+
const sig = createSigSchnorr(k, rx, await challengeAsync(rx, px, m), d);
|
|
1938
1940
|
// If Verify(bytes(P), m, sig) (see below) returns failure, abort
|
|
1939
1941
|
if (!(await verifySchnorrAsync(sig, m, px)))
|
|
1940
|
-
|
|
1942
|
+
throw new Error(E_INVSIG);
|
|
1941
1943
|
return sig;
|
|
1942
1944
|
};
|
|
1943
1945
|
const callSyncAsyncFn = (res, later) => {
|
|
1944
1946
|
return res instanceof Promise ? res.then(later) : later(res);
|
|
1945
1947
|
};
|
|
1946
1948
|
const _verifSchnorr = (signature, message, publicKey, challengeFn) => {
|
|
1947
|
-
const sig = abytes(signature,
|
|
1949
|
+
const sig = abytes(signature, 64, 'signature');
|
|
1948
1950
|
const msg = abytes(message, undefined, 'message');
|
|
1949
1951
|
const pub = abytes(publicKey, L, 'publicKey');
|
|
1952
|
+
let P_;
|
|
1953
|
+
let r;
|
|
1954
|
+
let s;
|
|
1955
|
+
let chalInput; // challenge preimage bytes(r) || bytes(P) || m
|
|
1950
1956
|
try {
|
|
1951
1957
|
// lift_x from BIP340. Convert 32-byte x coordinate to elliptic curve point.
|
|
1952
1958
|
// Fail if x ≥ p. Let c = x³ + 7 mod p.
|
|
1953
1959
|
const x = bytesToNumBE(pub);
|
|
1954
|
-
|
|
1955
|
-
const P_ = new Point(x, y, 1n).assertValidity();
|
|
1956
|
-
const px = numTo32b(P_.toAffine().x);
|
|
1960
|
+
P_ = lift_x(x); // lift_x returns the validated even-y point required by BIP340.
|
|
1957
1961
|
// P = lift_x(int(pk)); fail if that fails
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
const s = sliceBytesNumBE(sig, L, L2); // Let s = int(sig[32:64]); fail if s ≥ n.
|
|
1962
|
+
r = FpIsValidNot0(sliceBytesNumBE(sig, 0, L)); // Let r = int(sig[0:32]); fail if r ≥ p.
|
|
1963
|
+
// Let s = int(sig[32:64]); fail if s ≥ n.
|
|
1961
1964
|
// Stricter than BIP-340/libsecp256k1, which only reject s >= n. Honest signing reaches
|
|
1962
1965
|
// s = 0 only with negligible probability (k + e*d ≡ 0 mod n), so treat zero-s inputs as
|
|
1963
1966
|
// crafted edge cases and fail closed instead of carrying that extra verification surface.
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
return callSyncAsyncFn(challengeFn(i), (e) => {
|
|
1968
|
-
const { x, y } = doubleScalarMulUns(P_, s, modN(-e)).toAffine(); // R = s⋅G - e⋅P
|
|
1969
|
-
if (!isEven(y) || x !== r)
|
|
1970
|
-
return false; // -eP == (n-e)P
|
|
1971
|
-
return true; // Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r.
|
|
1972
|
-
});
|
|
1967
|
+
s = FnIsValidNot0(sliceBytesNumBE(sig, L, 64));
|
|
1968
|
+
// lift_x validated x < P, so the 32-byte input encoding equals bytes(P.x) exactly.
|
|
1969
|
+
chalInput = concatBytes(numTo32b(r), pub, msg);
|
|
1973
1970
|
}
|
|
1974
1971
|
catch (error) {
|
|
1975
1972
|
return false;
|
|
1976
1973
|
}
|
|
1974
|
+
// Hash-provider errors (unset hashes.sha256, missing crypto.subtle, bad provider digests)
|
|
1975
|
+
// stay outside the catch above: backend misconfiguration is not an "invalid signature"
|
|
1976
|
+
// result, so it throws / rejects instead of becoming false.
|
|
1977
|
+
// int(challenge(bytes(r)||bytes(P)||m))%n
|
|
1978
|
+
return callSyncAsyncFn(challengeFn(chalInput), (e) => {
|
|
1979
|
+
try {
|
|
1980
|
+
const { x, y } = doubleScalarMulUns(P_, s, modN(-e)).toAffine(); // R = s⋅G - e⋅P
|
|
1981
|
+
if (!isEven(y) || x !== r)
|
|
1982
|
+
return false; // -eP == (n-e)P
|
|
1983
|
+
return true; // Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r.
|
|
1984
|
+
}
|
|
1985
|
+
catch (error) {
|
|
1986
|
+
return false; // is_infinite(R) throws inside doubleScalarMulUns
|
|
1987
|
+
}
|
|
1988
|
+
});
|
|
1977
1989
|
};
|
|
1978
|
-
/** Verifies Schnorr signature.
|
|
1979
|
-
*
|
|
1990
|
+
/** Verifies Schnorr signature. Invalid points, scalars and failed curve checks return false;
|
|
1991
|
+
* hash-backend misconfiguration (e.g. unset hashes.sha256) throws instead, since it is a
|
|
1992
|
+
* runtime/backend error, not an "invalid signature" result. */
|
|
1980
1993
|
const verifySchnorr = (s, m, p) => _verifSchnorr(s, m, p, challenge);
|
|
1981
1994
|
/** Async Schnorr verification. Curve/encoding failures after the initial byte checks still
|
|
1982
1995
|
* become false, but async backend failures reject the promise. Missing crypto.subtle is a
|
|
@@ -1998,7 +2011,7 @@ const verifySchnorrAsync = async (s, m, p) => _verifSchnorr(s, m, p, challengeAs
|
|
|
1998
2011
|
* const isValid = secp.schnorr.verify(sig, message, publicKey);
|
|
1999
2012
|
* ```
|
|
2000
2013
|
*/
|
|
2001
|
-
const schnorr = /* @__PURE__ */
|
|
2014
|
+
const schnorr = /* @__PURE__ */ freeze({
|
|
2002
2015
|
keygen: keygenSchnorr,
|
|
2003
2016
|
getPublicKey: pubSchnorr,
|
|
2004
2017
|
sign: signSchnorr,
|
|
@@ -2008,18 +2021,14 @@ const schnorr = /* @__PURE__ */ Object.freeze({
|
|
|
2008
2021
|
});
|
|
2009
2022
|
// ## Precomputes
|
|
2010
2023
|
// --------------
|
|
2011
|
-
const W = 8; // W is window size
|
|
2012
|
-
const scalarBits = 256;
|
|
2013
|
-
const pwindows = Math.ceil(scalarBits / W) + 1; // 33 for W=8, NOT 32 - see wNAF loop
|
|
2014
|
-
const pwindowSize = 2 ** (W - 1); // 128 for W=8
|
|
2015
2024
|
const precompute = () => {
|
|
2016
2025
|
const points = [];
|
|
2017
2026
|
let p = G;
|
|
2018
2027
|
let b = p;
|
|
2019
|
-
for (let w = 0; w <
|
|
2028
|
+
for (let w = 0; w < 33; w++) {
|
|
2020
2029
|
b = p;
|
|
2021
2030
|
points.push(b);
|
|
2022
|
-
for (let i = 1; i <
|
|
2031
|
+
for (let i = 1; i < 128; i++) {
|
|
2023
2032
|
b = b.add(p);
|
|
2024
2033
|
points.push(b);
|
|
2025
2034
|
} // i=1, bc we skip 0
|
|
@@ -2048,37 +2057,32 @@ const wNAF = (n) => {
|
|
|
2048
2057
|
const comp = Gpows || (Gpows = precompute());
|
|
2049
2058
|
let p = I;
|
|
2050
2059
|
let f = G; // f must be G, or could become I in the end
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
const shiftBy = big(W); // 8 for W=8
|
|
2055
|
-
for (let w = 0; w < pwindows; w++) {
|
|
2056
|
-
let wbits = Number(n & mask); // extract W bits.
|
|
2057
|
-
n >>= shiftBy; // shift number by W bits.
|
|
2060
|
+
for (let w = 0; w < 33; w++) {
|
|
2061
|
+
let wbits = Number(n & 255n); // extract W=8 bits.
|
|
2062
|
+
n >>= 8n; // shift number by W=8 bits.
|
|
2058
2063
|
// We use negative indexes to reduce size of precomputed table by 2x.
|
|
2059
2064
|
// Instead of needing precomputes 0..256, we only calculate them for 0..128.
|
|
2060
2065
|
// If an index > 128 is found, we do (256-index) - where 256 is next window.
|
|
2061
2066
|
// Naive: index +127 => 127, +224 => 224
|
|
2062
2067
|
// Optimized: index +127 => 127, +224 => 256-32
|
|
2063
|
-
if (wbits >
|
|
2064
|
-
wbits -=
|
|
2068
|
+
if (wbits > 128) {
|
|
2069
|
+
wbits -= 256;
|
|
2065
2070
|
n += 1n;
|
|
2066
2071
|
}
|
|
2067
|
-
const off = w *
|
|
2068
|
-
const offF = off; // offsets, evaluate both
|
|
2072
|
+
const off = w * 128;
|
|
2069
2073
|
const offP = off + Math.abs(wbits) - 1;
|
|
2070
|
-
const
|
|
2074
|
+
const isOddW = w % 2 !== 0; // conditions, evaluate both; alternates fake-add sign per window
|
|
2071
2075
|
const isNeg = wbits < 0;
|
|
2072
2076
|
if (wbits === 0) {
|
|
2073
|
-
// off == I: can't add it.
|
|
2074
|
-
f = f.add(ctneg(
|
|
2077
|
+
// off == I: can't add it. Add the first point in this window to the fake accumulator.
|
|
2078
|
+
f = f.add(ctneg(isOddW, comp[off])); // bits are 0: add garbage to fake point
|
|
2075
2079
|
}
|
|
2076
2080
|
else {
|
|
2077
2081
|
p = p.add(ctneg(isNeg, comp[offP])); // bits are 1: add to result point
|
|
2078
2082
|
}
|
|
2079
2083
|
}
|
|
2080
2084
|
if (n !== 0n)
|
|
2081
|
-
|
|
2085
|
+
throw new Error('invalid wnaf');
|
|
2082
2086
|
return { p, f }; // return both real and fake points for JIT/leakage-shape symmetry
|
|
2083
2087
|
};
|
|
2084
2088
|
|