@leofcoin/peernet 1.2.30 → 3.0.0
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 +13 -0
- package/docs/automatic-transport.md +37 -0
- package/exports/browser/{browser-DvU1xNFS.js → browser-CCfR9KOM.js} +1 -1
- package/exports/browser/{client-86Tnax66.js → client-C_pBoi1g.js} +254 -78
- package/exports/browser/{identity-CYVPDmkf.js → identity-Cf8KKnho.js} +511 -391
- package/exports/browser/identity.js +1 -1
- package/exports/browser/{index-BD0Anx7_.js → index-dTQVgkpf.js} +1 -1
- package/exports/browser/{messages-D7JxkkDW.js → messages-BGHaihSi.js} +2 -2
- package/exports/browser/{peernet-C4rOvyyU.js → peernet-Db43ag2D.js} +68 -45
- package/exports/browser/peernet.js +2 -2
- package/exports/store.js +0 -3
- package/package.json +10 -6
- package/.esdoc.json +0 -10
- package/.gitattributes +0 -2
- package/.github/workflows/release.yml +0 -34
- package/.prettierrc +0 -7
- package/.travis.yml +0 -27
- package/BREAKING_CHANGES.md +0 -35
- package/deploy.js +0 -8
- package/exports/browser/browser-D-r0O9Qn.js +0 -53
- package/exports/browser/browser-_hiyXwPp.js +0 -147
- package/exports/browser/client-BFxJts6j.js +0 -2224
- package/exports/browser/client-BVhUamQG.js +0 -2200
- package/exports/browser/client-BzvWzzLv.js +0 -2224
- package/exports/browser/client-C9GBz2Tf.js +0 -2224
- package/exports/browser/client-CQzuu6At.js +0 -2224
- package/exports/browser/client-CVkFejdx.js +0 -2224
- package/exports/browser/client-Cj_UUO8W.js +0 -2224
- package/exports/browser/client-CrGaeBjj.js +0 -2224
- package/exports/browser/client-DDC8C510.js +0 -2224
- package/exports/browser/client-ERocgKET.js +0 -2224
- package/exports/browser/client-a8rws3cu.js +0 -2224
- package/exports/browser/identity-ChyyZSve.js +0 -8397
- package/exports/browser/index-Bxr5Iztg.js +0 -37
- package/exports/browser/messages-BFCmg6Vb.js +0 -212
- package/exports/browser/messages-BHIC2mQu.js +0 -212
- package/exports/browser/messages-CimzJ4wr.js +0 -210
- package/exports/browser/messages-Cjoa_LGO.js +0 -210
- package/exports/browser/messages-DH0tgNM9.js +0 -207
- package/exports/browser/messages-DVHcNvs_.js +0 -212
- package/exports/browser/messages-DqL_g2qw.js +0 -210
- package/exports/browser/messages-UKnuelZ7.js +0 -207
- package/exports/browser/messages-XgZZUMma.js +0 -212
- package/exports/browser/messages-kfFyctPL.js +0 -210
- package/exports/browser/messages-psROnfRi.js +0 -212
- package/exports/browser/peernet-B9VL4Xwe.js +0 -9522
- package/exports/browser/peernet-C-HNdrcd.js +0 -9528
- package/exports/browser/peernet-Ca1iM2tN.js +0 -9336
- package/exports/browser/peernet-CbI0-jFn.js +0 -9651
- package/exports/browser/peernet-CtHScDyh.js +0 -9524
- package/exports/browser/peernet-DfuxUK3u.js +0 -9525
- package/exports/browser/peernet-DiEcNDLD.js +0 -9655
- package/exports/browser/peernet-DtuRXOzP.js +0 -9653
- package/exports/browser/peernet-r_9IyzTe.js +0 -9336
- package/exports/browser/peernet-rcD5XL1y.js +0 -9590
- package/exports/browser/peernet-rw8HJGOz.js +0 -9597
- package/exports/messages-CHKVVTVE.js +0 -204
- package/exports/messages-DaUtFfBh.js +0 -207
- package/index.html +0 -19
- package/rollup.config.js +0 -143
- package/src/dht/dht.ts +0 -142
- package/src/discovery/peer-discovery.js +0 -75
- package/src/errors/errors.js +0 -12
- package/src/file-transfer.ts +0 -194
- package/src/handlers/data.js +0 -15
- package/src/handlers/message.js +0 -34
- package/src/identity.ts +0 -172
- package/src/messages/chat.ts +0 -13
- package/src/messages/data-response.ts +0 -13
- package/src/messages/data.ts +0 -17
- package/src/messages/dht-response.ts +0 -13
- package/src/messages/dht.ts +0 -21
- package/src/messages/file-link.ts +0 -17
- package/src/messages/file.ts +0 -17
- package/src/messages/peer-response.ts +0 -13
- package/src/messages/peer.ts +0 -13
- package/src/messages/peernet.ts +0 -13
- package/src/messages/ps.ts +0 -13
- package/src/messages/request.ts +0 -13
- package/src/messages/response.ts +0 -13
- package/src/messages.js +0 -12
- package/src/peer-info.js +0 -9
- package/src/peernet.ts +0 -1113
- package/src/prompts/password/browser.js +0 -1
- package/src/prompts/password/node.js +0 -6
- package/src/prompts/password.js +0 -1
- package/src/proto/chat-message.proto.js +0 -6
- package/src/proto/data-response.proto.js +0 -4
- package/src/proto/data.proto.js +0 -4
- package/src/proto/dht-response.proto.js +0 -4
- package/src/proto/dht.proto.js +0 -4
- package/src/proto/file-link.proto.js +0 -5
- package/src/proto/file.proto.js +0 -10
- package/src/proto/peer-response.proto.js +0 -3
- package/src/proto/peer.proto.js +0 -3
- package/src/proto/peernet.proto.js +0 -7
- package/src/proto/ps.proto.js +0 -4
- package/src/proto/request.proto.js +0 -4
- package/src/proto/response.proto.js +0 -3
- package/src/types.ts +0 -25
- package/src/utils/utils.js +0 -77
- package/test/all.js +0 -5
- package/test/client.js +0 -15
- package/test/codec.js +0 -56
- package/test/file-transfer.test.js +0 -81
- package/test/fresh-empty-store.js +0 -15
- package/test/hash.js +0 -14
- package/test/index.js +0 -6
- package/test/lastBlock.js +0 -7
- package/test/messages.js +0 -26
- package/test/peer-data-exchange.test.js +0 -52
- package/test/peer-file-exchange.test.js +0 -78
- package/test/peernet.js +0 -17
- package/test/peernet.test.js +0 -440
- package/test-output.log +0 -84
- package/test.js +0 -63
- package/test2.js +0 -13
- package/test3.js +0 -16
- package/test4.js +0 -7
- package/tsconfig.json +0 -13
|
@@ -344,13 +344,13 @@ const ALPHABET$1 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
|
344
344
|
const ALPHABET_HEX$1 = "0123456789ABCDEFGHJKLMNPQRSTUVabcdefghijklmnopqrstuv";
|
|
345
345
|
const base58 = base(ALPHABET$1);
|
|
346
346
|
const base58Hex = base(ALPHABET_HEX$1);
|
|
347
|
-
const encode$
|
|
348
|
-
const decode$
|
|
347
|
+
const encode$3 = base58.encode;
|
|
348
|
+
const decode$4 = base58.decode;
|
|
349
349
|
const encodeHex$2 = base58Hex.encode;
|
|
350
350
|
const decodeHex$2 = base58Hex.decode;
|
|
351
351
|
const isBase58 = (string) => {
|
|
352
352
|
try {
|
|
353
|
-
decode$
|
|
353
|
+
decode$4(string);
|
|
354
354
|
return true;
|
|
355
355
|
}
|
|
356
356
|
catch (e) {
|
|
@@ -368,7 +368,7 @@ const isBase58Hex = (string) => {
|
|
|
368
368
|
};
|
|
369
369
|
const whatType = (string) => {
|
|
370
370
|
try {
|
|
371
|
-
decode$
|
|
371
|
+
decode$4(string);
|
|
372
372
|
return "base58";
|
|
373
373
|
}
|
|
374
374
|
catch (e) {
|
|
@@ -382,8 +382,8 @@ const whatType = (string) => {
|
|
|
382
382
|
}
|
|
383
383
|
};
|
|
384
384
|
var base58$1 = {
|
|
385
|
-
encode: encode$
|
|
386
|
-
decode: decode$
|
|
385
|
+
encode: encode$3,
|
|
386
|
+
decode: decode$4,
|
|
387
387
|
isBase58,
|
|
388
388
|
isBase58Hex,
|
|
389
389
|
encodeHex: encodeHex$2,
|
|
@@ -470,7 +470,7 @@ const concatAndDoubleHash = async (input) => {
|
|
|
470
470
|
return new Uint8Array(await createDoubleHash(typedArrayConcat(input), 'SHA-256'));
|
|
471
471
|
};
|
|
472
472
|
|
|
473
|
-
const encode$
|
|
473
|
+
const encode$2 = async (source, prefix = new TextEncoder().encode('00'), hex) => {
|
|
474
474
|
if (!(source instanceof Uint8Array) || !(prefix instanceof Uint8Array)) {
|
|
475
475
|
throw new TypeError('Expected Uint8Array');
|
|
476
476
|
}
|
|
@@ -487,7 +487,7 @@ const encode$3 = async (source, prefix = new TextEncoder().encode('00'), hex) =>
|
|
|
487
487
|
return base58$1.encodeHex(uint8Array);
|
|
488
488
|
return base58$1.encode(uint8Array);
|
|
489
489
|
};
|
|
490
|
-
const decode$
|
|
490
|
+
const decode$3 = async (string, hex) => {
|
|
491
491
|
let uint8Array = hex ? base58$1.decodeHex(string) : base58$1.decode(string);
|
|
492
492
|
const prefix = uint8Array.subarray(0, 2);
|
|
493
493
|
const source = uint8Array.subarray(2, -4);
|
|
@@ -507,129 +507,242 @@ const decode$4 = async (string, hex) => {
|
|
|
507
507
|
};
|
|
508
508
|
const isBase58check = (string, hex) => {
|
|
509
509
|
try {
|
|
510
|
-
hex ? decode$
|
|
510
|
+
hex ? decode$3(string, true) : decode$3(string);
|
|
511
511
|
return true;
|
|
512
512
|
}
|
|
513
513
|
catch (e) {
|
|
514
514
|
return false;
|
|
515
515
|
}
|
|
516
516
|
};
|
|
517
|
-
const encodeHex$1 = (uint8Array, prefix = new TextEncoder().encode('00')) => encode$
|
|
518
|
-
const decodeHex$1 = (string) => decode$
|
|
517
|
+
const encodeHex$1 = (uint8Array, prefix = new TextEncoder().encode('00')) => encode$2(uint8Array, prefix, true);
|
|
518
|
+
const decodeHex$1 = (string) => decode$3(string, true);
|
|
519
519
|
const isBase58checkHex = (string) => isBase58check(string, true);
|
|
520
|
-
var base58check = { encode: encode$
|
|
521
|
-
|
|
522
|
-
const MSB$1 = 0x80;
|
|
523
|
-
const MSBALL = -128;
|
|
524
|
-
const INT = Math.pow(2, 31);
|
|
525
|
-
const encode$2 = (num, out, offset) => {
|
|
526
|
-
if (Number.MAX_SAFE_INTEGER && num > Number.MAX_SAFE_INTEGER) {
|
|
527
|
-
encode$2.bytes = 0;
|
|
528
|
-
throw new RangeError('Could not encode varint');
|
|
529
|
-
}
|
|
530
|
-
out = out || [];
|
|
531
|
-
offset = offset || 0;
|
|
532
|
-
const oldOffset = offset;
|
|
533
|
-
while (num >= INT) {
|
|
534
|
-
out[offset++] = (num & 0xFF) | MSB$1;
|
|
535
|
-
num /= 128;
|
|
536
|
-
}
|
|
537
|
-
while (num & MSBALL) {
|
|
538
|
-
out[offset++] = (num & 0xFF) | MSB$1;
|
|
539
|
-
num >>>= 7;
|
|
540
|
-
}
|
|
541
|
-
out[offset] = num | 0;
|
|
542
|
-
encode$2.bytes = offset - oldOffset + 1;
|
|
543
|
-
return out;
|
|
544
|
-
};
|
|
520
|
+
var base58check = { encode: encode$2, decode: decode$3, encodeHex: encodeHex$1, decodeHex: decodeHex$1, isBase58check, isBase58checkHex };
|
|
545
521
|
|
|
546
|
-
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
522
|
+
/** @leofcoin/binary: dependency-free unsigned framing, with no global byte counters. */
|
|
523
|
+
const MAX_BIGINT = (1n << 256n) - 1n;
|
|
524
|
+
function size(value) {
|
|
525
|
+
if (!Number.isSafeInteger(value) || value < 0)
|
|
526
|
+
throw new RangeError('Expected an unsigned safe integer');
|
|
527
|
+
}
|
|
528
|
+
function bytes(value) {
|
|
529
|
+
if (!(value instanceof Uint8Array))
|
|
530
|
+
throw new TypeError('Expected Uint8Array');
|
|
531
|
+
}
|
|
532
|
+
function varUintLength(value) {
|
|
533
|
+
size(value);
|
|
534
|
+
let length = 1;
|
|
535
|
+
while (value >= 128) {
|
|
536
|
+
value = Math.floor(value / 128);
|
|
537
|
+
length++;
|
|
538
|
+
}
|
|
539
|
+
return length;
|
|
540
|
+
}
|
|
541
|
+
function varBigIntLength(value) {
|
|
542
|
+
if (typeof value !== 'bigint')
|
|
543
|
+
throw new TypeError('Expected BigInt');
|
|
544
|
+
if (value < 0n || value > MAX_BIGINT)
|
|
545
|
+
throw new RangeError('Expected an unsigned 256-bit integer');
|
|
546
|
+
let length = 1;
|
|
547
|
+
while (value >= 128n) {
|
|
548
|
+
value >>= 7n;
|
|
549
|
+
length++;
|
|
550
|
+
}
|
|
551
|
+
return length;
|
|
552
|
+
}
|
|
553
|
+
/** Fixed-capacity writer. finish() is a view; copy it before reusing/resetting the writer. */
|
|
554
|
+
class BinaryWriter {
|
|
555
|
+
#buffer;
|
|
556
|
+
#position = 0;
|
|
557
|
+
constructor(capacity = 64) {
|
|
558
|
+
if (typeof capacity === 'number') {
|
|
559
|
+
size(capacity);
|
|
560
|
+
this.#buffer = new Uint8Array(capacity);
|
|
559
561
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
};
|
|
562
|
+
else {
|
|
563
|
+
bytes(capacity);
|
|
564
|
+
this.#buffer = capacity;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
get position() {
|
|
568
|
+
return this.#position;
|
|
569
|
+
}
|
|
570
|
+
get remaining() {
|
|
571
|
+
return this.#buffer.length - this.#position;
|
|
572
|
+
}
|
|
573
|
+
#require(length) {
|
|
574
|
+
size(length);
|
|
575
|
+
if (length > this.remaining)
|
|
576
|
+
throw new RangeError('Binary output buffer is too small');
|
|
577
|
+
}
|
|
578
|
+
writeVarUint(value) {
|
|
579
|
+
this.#require(varUintLength(value));
|
|
580
|
+
this.#writeVarUint(value);
|
|
581
|
+
return this;
|
|
582
|
+
}
|
|
583
|
+
#writeVarUint(value) {
|
|
584
|
+
while (value >= 128) {
|
|
585
|
+
this.#buffer[this.#position++] = value % 128 | 128;
|
|
586
|
+
value = Math.floor(value / 128);
|
|
587
|
+
}
|
|
588
|
+
this.#buffer[this.#position++] = value;
|
|
589
|
+
}
|
|
590
|
+
writeVarBigInt(value) {
|
|
591
|
+
this.#require(varBigIntLength(value));
|
|
592
|
+
do {
|
|
593
|
+
const byte = Number(value & 127n);
|
|
594
|
+
value >>= 7n;
|
|
595
|
+
this.#buffer[this.#position++] = byte | (value ? 128 : 0);
|
|
596
|
+
} while (value);
|
|
597
|
+
return this;
|
|
598
|
+
}
|
|
599
|
+
writeBytes(value) {
|
|
600
|
+
bytes(value);
|
|
601
|
+
this.#require(value.length);
|
|
602
|
+
this.#buffer.set(value, this.#position);
|
|
603
|
+
this.#position += value.length;
|
|
604
|
+
return this;
|
|
605
|
+
}
|
|
606
|
+
writeFrame(value) {
|
|
607
|
+
bytes(value);
|
|
608
|
+
// Check the complete operation before writing its length prefix.
|
|
609
|
+
const prefixLength = varUintLength(value.length);
|
|
610
|
+
this.#require(prefixLength + value.length);
|
|
611
|
+
const prefixStart = this.#buffer.byteOffset + this.#position;
|
|
612
|
+
if (value.buffer === this.#buffer.buffer &&
|
|
613
|
+
value.byteOffset < prefixStart + prefixLength &&
|
|
614
|
+
value.byteOffset + value.length > prefixStart)
|
|
615
|
+
throw new RangeError('Frame source overlaps its length prefix');
|
|
616
|
+
this.#writeVarUint(value.length);
|
|
617
|
+
this.#buffer.set(value, this.#position);
|
|
618
|
+
this.#position += value.length;
|
|
619
|
+
return this;
|
|
620
|
+
}
|
|
621
|
+
reset() {
|
|
622
|
+
this.#position = 0;
|
|
623
|
+
return this;
|
|
624
|
+
}
|
|
625
|
+
finish(copy = false) {
|
|
626
|
+
const result = this.#buffer.subarray(0, this.#position);
|
|
627
|
+
// Buffer.slice() aliases memory; this constructor copies every Uint8Array subtype.
|
|
628
|
+
return copy ? new Uint8Array(result) : result;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
/** Reads views of the original buffer. Failed operations leave the cursor unchanged. */
|
|
632
|
+
class BinaryReader {
|
|
633
|
+
#buffer;
|
|
634
|
+
#position = 0;
|
|
635
|
+
constructor(buffer) {
|
|
636
|
+
bytes(buffer);
|
|
637
|
+
this.#buffer = buffer;
|
|
638
|
+
}
|
|
639
|
+
get position() {
|
|
640
|
+
return this.#position;
|
|
641
|
+
}
|
|
642
|
+
get remaining() {
|
|
643
|
+
return this.#buffer.length - this.#position;
|
|
644
|
+
}
|
|
645
|
+
readVarUint(canonical = true) {
|
|
646
|
+
let result = 0;
|
|
647
|
+
let multiplier = 1;
|
|
648
|
+
for (let index = 0; index < 8; index++) {
|
|
649
|
+
const cursor = this.#position + index;
|
|
650
|
+
if (cursor >= this.#buffer.length)
|
|
651
|
+
throw new RangeError('Truncated varint');
|
|
652
|
+
const byte = this.#buffer[cursor];
|
|
653
|
+
const payload = byte & 127;
|
|
654
|
+
if (index === 7 && payload > Math.floor((Number.MAX_SAFE_INTEGER - result) / multiplier))
|
|
655
|
+
throw new RangeError('Varint exceeds safe integer range');
|
|
656
|
+
result += payload * multiplier;
|
|
657
|
+
if (byte < 128) {
|
|
658
|
+
if (canonical && index > 0 && payload === 0)
|
|
659
|
+
throw new RangeError('Non-canonical varint');
|
|
660
|
+
this.#position = cursor + 1;
|
|
661
|
+
return result;
|
|
662
|
+
}
|
|
663
|
+
multiplier *= 128;
|
|
664
|
+
}
|
|
665
|
+
throw new RangeError('Varint exceeds safe integer range');
|
|
666
|
+
}
|
|
667
|
+
readVarBigInt(canonical = true) {
|
|
668
|
+
let result = 0n;
|
|
669
|
+
for (let index = 0; index < 37; index++) {
|
|
670
|
+
const cursor = this.#position + index;
|
|
671
|
+
if (cursor >= this.#buffer.length)
|
|
672
|
+
throw new RangeError('Truncated varint');
|
|
673
|
+
const byte = this.#buffer[cursor];
|
|
674
|
+
result |= BigInt(byte & 127) << BigInt(index * 7);
|
|
675
|
+
if (result > MAX_BIGINT)
|
|
676
|
+
throw new RangeError('Varint exceeds 256 bits');
|
|
677
|
+
if (byte < 128) {
|
|
678
|
+
if (canonical && index > 0 && byte === 0)
|
|
679
|
+
throw new RangeError('Non-canonical varint');
|
|
680
|
+
this.#position = cursor + 1;
|
|
681
|
+
return result;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
throw new RangeError('Varint exceeds 256 bits');
|
|
685
|
+
}
|
|
686
|
+
readBytes(length) {
|
|
687
|
+
size(length);
|
|
688
|
+
if (length > this.remaining)
|
|
689
|
+
throw new RangeError('Truncated binary field');
|
|
690
|
+
const start = this.#position;
|
|
691
|
+
this.#position += length;
|
|
692
|
+
return this.#buffer.subarray(start, this.#position);
|
|
693
|
+
}
|
|
694
|
+
readFrame(canonical = true) {
|
|
695
|
+
const start = this.#position;
|
|
696
|
+
try {
|
|
697
|
+
return this.readBytes(this.readVarUint(canonical));
|
|
698
|
+
}
|
|
699
|
+
catch (error) {
|
|
700
|
+
this.#position = start;
|
|
701
|
+
throw error;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
/** Existing smart-concat bytes, with exactly one output allocation and no prefix arrays. */
|
|
706
|
+
function encodeFrames(frames, prefix) {
|
|
707
|
+
if (!Array.isArray(frames))
|
|
708
|
+
throw new TypeError('Expected an array of byte frames');
|
|
709
|
+
let length = 0;
|
|
710
|
+
for (const frame of frames) {
|
|
711
|
+
bytes(frame);
|
|
712
|
+
length += varUintLength(frame.length) + frame.length;
|
|
713
|
+
size(length);
|
|
714
|
+
}
|
|
715
|
+
const writer = new BinaryWriter(length);
|
|
716
|
+
for (const frame of frames)
|
|
717
|
+
writer.writeFrame(frame);
|
|
718
|
+
return writer.finish();
|
|
719
|
+
}
|
|
720
|
+
/** Iterative framing: bounded by the input length, not recursive stack depth. */
|
|
721
|
+
function decodeFrames(buffer, prefixLength = 0, options = {}) {
|
|
722
|
+
const reader = new BinaryReader(buffer);
|
|
723
|
+
reader.readBytes(prefixLength);
|
|
724
|
+
const maxFrames = options.maxFrames ?? buffer.length;
|
|
725
|
+
size(maxFrames);
|
|
726
|
+
const frames = [];
|
|
727
|
+
while (reader.remaining) {
|
|
728
|
+
if (frames.length >= maxFrames)
|
|
729
|
+
throw new RangeError('Too many binary frames');
|
|
730
|
+
frames.push(reader.readFrame(options.canonical ?? true));
|
|
731
|
+
}
|
|
732
|
+
return frames;
|
|
733
|
+
}
|
|
595
734
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
}, 0);
|
|
604
|
-
const typedArray = new Uint8Array(length);
|
|
605
|
-
let currentIndex = 0;
|
|
606
|
-
let index = 0;
|
|
607
|
-
for (const source of input) {
|
|
608
|
-
typedArray.set(encodedArray[index], currentIndex);
|
|
609
|
-
currentIndex += encodedArray[index].length;
|
|
610
|
-
typedArray.set(source, currentIndex);
|
|
611
|
-
currentIndex += source.length;
|
|
612
|
-
index += 1;
|
|
613
|
-
}
|
|
614
|
-
return typedArray;
|
|
735
|
+
const asBytes = (source) => {
|
|
736
|
+
if (source instanceof Uint8Array)
|
|
737
|
+
return source;
|
|
738
|
+
// Keep legacy ArrayLike coercion. Typed-array callers take the zero-copy path.
|
|
739
|
+
const bytes = new Uint8Array(source.length);
|
|
740
|
+
bytes.set(source);
|
|
741
|
+
return bytes;
|
|
615
742
|
};
|
|
743
|
+
var index$3 = (input, prefix) => encodeFrames(input.map(asBytes));
|
|
616
744
|
|
|
617
|
-
var index$2 = (typedArray, prefix) => {
|
|
618
|
-
const set = [];
|
|
619
|
-
const varintAndSub = (typedArray) => {
|
|
620
|
-
const length = index$4.decode(typedArray);
|
|
621
|
-
// remove length
|
|
622
|
-
typedArray = typedArray.subarray(index$4.decode.bytes);
|
|
623
|
-
// push value
|
|
624
|
-
set.push(typedArray.subarray(0, length));
|
|
625
|
-
// remove value
|
|
626
|
-
typedArray = typedArray.subarray(length);
|
|
627
|
-
if (typedArray.length !== 0)
|
|
628
|
-
return varintAndSub(typedArray);
|
|
629
|
-
return set;
|
|
630
|
-
};
|
|
631
|
-
return varintAndSub(typedArray);
|
|
632
|
-
};
|
|
745
|
+
var index$2 = (typedArray, prefix) => decodeFrames(typedArray, 0, { canonical: false });
|
|
633
746
|
|
|
634
747
|
const decode$2 = (multiWif, expectedVersion, expectedCodec) => {
|
|
635
748
|
const decoded = base58$1.decode(multiWif);
|
|
@@ -687,176 +800,190 @@ var index$1 = {
|
|
|
687
800
|
* * b = `7n` // equation param
|
|
688
801
|
* * Gx, Gy are coordinates of Generator / base point
|
|
689
802
|
*/
|
|
803
|
+
const freeze = Object.freeze;
|
|
804
|
+
const P = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn;
|
|
805
|
+
const N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;
|
|
806
|
+
const Gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n;
|
|
807
|
+
const Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n;
|
|
690
808
|
// Mirror noble-curves: Point.CURVE() returns shared params, but those params must stay frozen so
|
|
691
809
|
// callers cannot mutate them out from under the arithmetic constants captured below.
|
|
692
|
-
const secp256k1_CURVE =
|
|
693
|
-
p:
|
|
694
|
-
n:
|
|
810
|
+
const secp256k1_CURVE = freeze({
|
|
811
|
+
p: P,
|
|
812
|
+
n: N,
|
|
695
813
|
h: 1n,
|
|
696
814
|
a: 0n,
|
|
697
815
|
b: 7n,
|
|
698
|
-
Gx
|
|
699
|
-
Gy
|
|
816
|
+
Gx,
|
|
817
|
+
Gy,
|
|
700
818
|
});
|
|
701
|
-
const { p: P, n: N, Gx, Gy, b: _b } = secp256k1_CURVE;
|
|
702
819
|
// 32-byte field / scalar width, and the SHA-256 / HMAC-DRBG output width used
|
|
703
820
|
// by the RFC6979 paths here.
|
|
704
821
|
const L = 32;
|
|
705
|
-
const L2 = 64; // 64-byte compact signatures, and 64 hex chars for zero-padded 32-byte scalars
|
|
706
|
-
const lengths = {
|
|
707
|
-
publicKey: L + 1,
|
|
708
|
-
publicKeyUncompressed: L2 + 1,
|
|
709
|
-
// 48-byte keygen seed floor: 384 bits exceeds FIPS 186-5 Table A.2's
|
|
710
|
-
// 352-bit recommendation for 256-bit prime curves.
|
|
711
|
-
seed: L + L / 2,
|
|
712
|
-
};
|
|
713
822
|
// Helpers and Precomputes sections are reused between libraries
|
|
714
823
|
// ## Helpers
|
|
715
824
|
// ----------
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
825
|
+
/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
|
|
826
|
+
const isBytes = (a) => {
|
|
827
|
+
// Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases.
|
|
828
|
+
// The fallback still requires a real ArrayBuffer view, so plain
|
|
829
|
+
// JSON-deserialized `{ constructor: ... }` spoofing is rejected, and
|
|
830
|
+
// `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.
|
|
831
|
+
return (a instanceof Uint8Array ||
|
|
832
|
+
(ArrayBuffer.isView(a) &&
|
|
833
|
+
a.constructor.name === 'Uint8Array' &&
|
|
834
|
+
a.BYTES_PER_ELEMENT === 1));
|
|
722
835
|
};
|
|
723
|
-
|
|
724
|
-
// fallback still requires a real ArrayBuffer view so plain JSON-deserialized `{ constructor: ... }`
|
|
725
|
-
// spoofing is rejected, and `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.
|
|
726
|
-
const isBytes = (a) => a instanceof Uint8Array ||
|
|
727
|
-
(ArrayBuffer.isView(a) &&
|
|
728
|
-
a.constructor.name === 'Uint8Array' &&
|
|
729
|
-
a.BYTES_PER_ELEMENT === 1);
|
|
730
|
-
/** Asserts something is Bytes. */
|
|
836
|
+
/** Asserts something is Uint8Array. */
|
|
731
837
|
const abytes = (value, length, title = '') => {
|
|
838
|
+
// Success path first: this runs at the start of every update() / digestInto(), and the
|
|
839
|
+
// common `abytes(data)` form must not pay for length handling it does not use.
|
|
840
|
+
if (isBytes(value) && (length === undefined || value.length === length))
|
|
841
|
+
return value;
|
|
842
|
+
// Error path: recompute freely to build the exact message.
|
|
732
843
|
const bytes = isBytes(value);
|
|
733
|
-
const
|
|
734
|
-
const
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
const msg = prefix + 'expected Uint8Array' + ofLen + ', got ' + got;
|
|
740
|
-
return bytes ? err(msg, RangeError) : err(msg, TypeError);
|
|
741
|
-
}
|
|
742
|
-
return value;
|
|
844
|
+
const ofLen = length !== undefined ? ` of length ${length}` : '';
|
|
845
|
+
const got = bytes ? `length=${value.length}` : `type=${typeof value}`;
|
|
846
|
+
const message = (title ? `"${title}" ` : '') + 'expected Uint8Array' + ofLen + ', got ' + got;
|
|
847
|
+
if (!bytes)
|
|
848
|
+
throw new TypeError(message);
|
|
849
|
+
throw new RangeError(message);
|
|
743
850
|
};
|
|
744
|
-
|
|
745
|
-
|
|
851
|
+
// Signing can retain the message across hash callbacks / awaits, and Schnorr hashes it more than
|
|
852
|
+
// once. Take one owned snapshot so caller mutation cannot change the signing transcript.
|
|
853
|
+
// cloneBytes expects an already-validated view; snapshotBytes validates, then copies.
|
|
854
|
+
const cloneBytes = (value) => Uint8Array.from(value);
|
|
855
|
+
const snapshotBytes = (value, title, length) => cloneBytes(abytes(value, length, title));
|
|
746
856
|
// Callers keep values non-negative and within the requested width; padStart() won't truncate over-wide inputs.
|
|
747
857
|
const padh = (n, pad) => n.toString(16).padStart(pad, '0');
|
|
748
|
-
/**
|
|
749
|
-
const bytesToHex = (
|
|
858
|
+
/** Convert byte array to hex string. */
|
|
859
|
+
const bytesToHex = (bytes) => {
|
|
750
860
|
let hex = '';
|
|
751
|
-
for (const
|
|
752
|
-
hex += padh(
|
|
861
|
+
for (const byte of abytes(bytes))
|
|
862
|
+
hex += padh(byte, 2);
|
|
753
863
|
return hex;
|
|
754
864
|
};
|
|
755
|
-
|
|
756
|
-
// Strict ASCII nibble parser: non-ASCII hex lookalikes are rejected as undefined.
|
|
757
|
-
// prettier-ignore
|
|
758
|
-
const _ch = (ch) => ch >= C._0 && ch <= C._9 ? ch - C._0 // '2' => 50-48
|
|
759
|
-
: ch >= C.A && ch <= C.F ? ch - (C.A - 10) // 'B' => 66-(65-10)
|
|
760
|
-
: ch >= C.a && ch <= C.f ? ch - (C.a - 10) // 'b' => 98-(97-10)
|
|
761
|
-
: undefined;
|
|
865
|
+
/** Convert hex string to byte array. */
|
|
762
866
|
const hexToBytes = (hex) => {
|
|
763
|
-
const e = 'hex invalid'; // Strict ASCII hex only, with one generic error for
|
|
867
|
+
const e = 'hex invalid'; // Strict ASCII hex only, with one generic error for parse failures.
|
|
764
868
|
if (typeof hex !== 'string')
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
//
|
|
773
|
-
|
|
774
|
-
const n2 = _ch(hex.charCodeAt(hi + 1)); // parse second char
|
|
775
|
-
if (n1 === undefined || n2 === undefined)
|
|
776
|
-
return err(e);
|
|
777
|
-
array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9
|
|
869
|
+
throw new TypeError(e);
|
|
870
|
+
if (hex.length % 2 || !/^[\da-f]*$/i.test(hex))
|
|
871
|
+
throw new RangeError(e);
|
|
872
|
+
const array = new Uint8Array(hex.length / 2);
|
|
873
|
+
for (let ai = 0, hi = 0; ai < array.length; ai++, hi += 2) {
|
|
874
|
+
const n1 = hex.charCodeAt(hi);
|
|
875
|
+
const n2 = hex.charCodeAt(hi + 1);
|
|
876
|
+
// Regex guarantees ASCII. For 0..9/A..F/a..f, this maps char codes to 0..15.
|
|
877
|
+
array[ai] = ((n1 & 15) + (n1 >> 6) * 9) * 16 + (n2 & 15) + (n2 >> 6) * 9;
|
|
778
878
|
}
|
|
779
879
|
return array;
|
|
780
880
|
};
|
|
781
881
|
// WebCrypto is available in all modern environments
|
|
782
|
-
const subtle = () =>
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
882
|
+
const subtle = () => {
|
|
883
|
+
const s = globalThis?.crypto?.subtle;
|
|
884
|
+
if (s)
|
|
885
|
+
return s;
|
|
886
|
+
throw new Error('crypto.subtle must be defined, consider polyfill');
|
|
887
|
+
};
|
|
888
|
+
/** Copies several Uint8Arrays into one. */
|
|
889
|
+
const concatBytes = (...arrays) => {
|
|
890
|
+
let sum = 0;
|
|
891
|
+
for (const a of arrays)
|
|
892
|
+
sum += abytes(a).length;
|
|
893
|
+
const res = new Uint8Array(sum);
|
|
894
|
+
let pad = 0;
|
|
895
|
+
for (const a of arrays) {
|
|
896
|
+
res.set(a, pad);
|
|
897
|
+
pad += a.length;
|
|
898
|
+
}
|
|
899
|
+
return res;
|
|
793
900
|
};
|
|
794
901
|
/**
|
|
795
902
|
* WebCrypto OS-level CSPRNG (random number generator).
|
|
796
903
|
* Will throw when not available; large-request ceilings are delegated to getRandomValues().
|
|
797
904
|
*/
|
|
798
|
-
const randomBytes = (len = L) =>
|
|
905
|
+
const randomBytes = (len = L) => {
|
|
906
|
+
const c = globalThis?.crypto;
|
|
907
|
+
if (typeof c?.getRandomValues !== 'function')
|
|
908
|
+
throw new Error('crypto.getRandomValues must be defined, consider polyfill');
|
|
909
|
+
return c.getRandomValues(new Uint8Array(len));
|
|
910
|
+
};
|
|
799
911
|
const big = BigInt;
|
|
800
912
|
const arange = (n, min, max, msg = 'bad number: out of range') => {
|
|
801
913
|
if (typeof n !== 'bigint')
|
|
802
|
-
|
|
914
|
+
throw new TypeError(msg);
|
|
803
915
|
if (min <= n && n < max)
|
|
804
916
|
return n;
|
|
805
|
-
|
|
917
|
+
throw new RangeError(msg);
|
|
806
918
|
};
|
|
807
919
|
/** Canonical modular reduction. Callers must provide a positive modulus. */
|
|
808
|
-
const M = (a, b = P) =>
|
|
809
|
-
const r = a % b;
|
|
810
|
-
return r >= 0n ? r : b + r;
|
|
811
|
-
};
|
|
920
|
+
const M = (a, b = P) => ((a %= b) >= 0n ? a : b + a);
|
|
812
921
|
const modN = (a) => M(a, N);
|
|
813
|
-
/** Modular inversion using
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
922
|
+
/** Modular inversion using extended euclidean GCD. Variable-time (non-CT). */
|
|
923
|
+
const invert = (number, modulo) => {
|
|
924
|
+
if (number === 0n)
|
|
925
|
+
throw new Error('invert: expected non-zero number');
|
|
926
|
+
// modulo = 1 is the zero ring: gcd(x, 1) = 1 makes the loop below "succeed" and return the
|
|
927
|
+
// useless inverse 0. Reject it.
|
|
928
|
+
if (modulo <= 1n)
|
|
929
|
+
throw new Error('invert: expected modulus > 1, got ' + modulo);
|
|
930
|
+
// This is variable-time: the loop count depends on `number`.
|
|
931
|
+
let a = M(number, modulo);
|
|
932
|
+
let b = modulo;
|
|
933
|
+
// Only the Bézout coefficient of `number` (x/u chain) is tracked; the coefficient of `modulo`
|
|
934
|
+
// never affects the output, so it is not computed.
|
|
935
|
+
// prettier-ignore
|
|
936
|
+
let x = 0n, u = 1n;
|
|
819
937
|
while (a !== 0n) {
|
|
820
|
-
const q = b / a
|
|
938
|
+
const q = b / a;
|
|
939
|
+
const r = b - a * q;
|
|
821
940
|
const m = x - u * q;
|
|
941
|
+
// prettier-ignore
|
|
822
942
|
b = a, a = r, x = u, u = m;
|
|
823
943
|
}
|
|
824
|
-
|
|
944
|
+
const gcd = b;
|
|
945
|
+
if (gcd !== 1n)
|
|
946
|
+
throw new Error('invert: does not exist');
|
|
947
|
+
return M(x, modulo);
|
|
825
948
|
};
|
|
826
|
-
const
|
|
949
|
+
const _hash = (name) => {
|
|
827
950
|
// @ts-ignore
|
|
828
951
|
const fn = hashes[name];
|
|
829
952
|
if (typeof fn !== 'function')
|
|
830
|
-
|
|
953
|
+
throw new Error('hashes.' + name + ' not set');
|
|
831
954
|
return fn;
|
|
832
955
|
};
|
|
833
956
|
// All exported provider slots are caller-configurable and may be unset or return arbitrary values,
|
|
834
957
|
// so wrapper helpers must enforce the exact 32-byte digest contract instead of trusting providers.
|
|
835
|
-
const
|
|
836
|
-
const
|
|
958
|
+
const callHash = (name, a, b) => abytes(_hash(name)(a, b), L, 'digest');
|
|
959
|
+
const callHashAsync = async (name, a, b) => abytes(await _hash(name)(a, b), L, 'digest');
|
|
837
960
|
// also rejects structurally similar Point values from other realms / bundled copies
|
|
838
|
-
const apoint = (p) =>
|
|
961
|
+
const apoint = (p) => {
|
|
962
|
+
if (p instanceof Point)
|
|
963
|
+
return p;
|
|
964
|
+
throw new TypeError('Point expected');
|
|
965
|
+
};
|
|
839
966
|
// ## End of Helpers
|
|
840
967
|
// -----------------
|
|
968
|
+
const E_BADPOINT = 'bad point: not on curve';
|
|
841
969
|
/**
|
|
842
970
|
* secp256k1 formula. Koblitz curves are subclass of weierstrass curves with a=0,
|
|
843
971
|
* making it x³+b; callers validate x first.
|
|
844
972
|
*/
|
|
845
|
-
const koblitz = (x) => M(M(x * x) * x +
|
|
973
|
+
const koblitz = (x) => M(M(x * x) * x + 7n);
|
|
846
974
|
/** assert is element of field mod P (incl. 0 for projective infinity coordinates) */
|
|
847
975
|
const FpIsValid = (n) => arange(n, 0n, P);
|
|
848
976
|
/** assert is element of field mod P (excl. 0 where current callers need a non-zero coordinate) */
|
|
849
977
|
const FpIsValidNot0 = (n) => arange(n, 1n, P);
|
|
850
|
-
/** assert is element of field mod N (excl. 0), matching the shared BIP340 scalar-failure rule used here
|
|
978
|
+
/** assert is element of field mod N (excl. 0), matching the shared BIP340 scalar-failure rule used here.
|
|
979
|
+
* There is deliberately no FnIsValid: no caller accepts the scalar 0. */
|
|
851
980
|
const FnIsValidNot0 = (n) => arange(n, 1n, N);
|
|
852
981
|
// Shared parity primitive for BIP340 even-y checks and SEC 1 compressed prefixes.
|
|
853
982
|
const isEven = (y) => !(y & 1n);
|
|
854
|
-
/** create Uint8Array of byte n */
|
|
855
|
-
const u8of = (n) => Uint8Array.of(n);
|
|
856
983
|
/** SEC 1 compressed-prefix helper. Parity only: callers validate y before asking for the prefix byte. */
|
|
857
|
-
const getPrefix = (y) =>
|
|
858
|
-
/** lift_x from BIP340 returns the unique even square root
|
|
859
|
-
* SEC 1 callers still
|
|
984
|
+
const getPrefix = (y) => Uint8Array.of(isEven(y) ? 0x02 : 0x03);
|
|
985
|
+
/** lift_x from BIP340 returns the unique point with x and an even square root of x³+7.
|
|
986
|
+
* SEC 1 callers still negate it for the odd-prefix branch. */
|
|
860
987
|
const lift_x = (x) => {
|
|
861
988
|
// Let c = x³ + 7 mod p. Fail if x ≥ p. (also fail if x < 1)
|
|
862
989
|
const c = koblitz(FpIsValidNot0(x));
|
|
@@ -872,8 +999,8 @@ const lift_x = (x) => {
|
|
|
872
999
|
num = (num * num) % P; // Not constant-time.
|
|
873
1000
|
}
|
|
874
1001
|
if (M(r * r) !== c)
|
|
875
|
-
|
|
876
|
-
return isEven(r) ? r : M(-r);
|
|
1002
|
+
throw new Error('sqrt invalid'); // check if result is valid
|
|
1003
|
+
return new Point(x, isEven(r) ? r : M(-r), 1n);
|
|
877
1004
|
};
|
|
878
1005
|
/**
|
|
879
1006
|
* Point in 3d xyz projective coordinates. 3d takes less inversions than 2d.
|
|
@@ -897,7 +1024,7 @@ class Point {
|
|
|
897
1024
|
this.X = FpIsValid(X);
|
|
898
1025
|
this.Y = FpIsValidNot0(Y); // Y can't be 0 in Projective
|
|
899
1026
|
this.Z = FpIsValid(Z);
|
|
900
|
-
|
|
1027
|
+
freeze(this);
|
|
901
1028
|
}
|
|
902
1029
|
/** Returns the shared curve metadata object by reference.
|
|
903
1030
|
* It is readonly only at type level, and mutating it won't retarget arithmetic,
|
|
@@ -913,30 +1040,31 @@ class Point {
|
|
|
913
1040
|
/** Convert Uint8Array or hex string to Point. */
|
|
914
1041
|
static fromBytes(bytes) {
|
|
915
1042
|
abytes(bytes);
|
|
916
|
-
const { publicKey: comp, publicKeyUncompressed: uncomp } = lengths; // e.g. for 32-byte: 33, 65
|
|
917
|
-
let p = undefined;
|
|
918
1043
|
const length = bytes.length;
|
|
919
1044
|
const head = bytes[0];
|
|
920
|
-
const
|
|
921
|
-
const x = sliceBytesNumBE(tail, 0, L);
|
|
1045
|
+
const x = sliceBytesNumBE(bytes, 1, 33);
|
|
922
1046
|
// SEC 1 defines the rare infinity encoding 0x00, but SEC 1 public-key validation rejects
|
|
923
1047
|
// infinity. We keep 0x00 rejected here because this parser is reused by verify(), ECDH,
|
|
924
1048
|
// and public-key validation helpers, so strict handling applies to all callers by default.
|
|
925
1049
|
// Local secp256k1 crosstests show OpenSSL raw point codecs accept 0x00 too.
|
|
926
|
-
// Parse SEC 1 compressed/uncompressed encodings
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
1050
|
+
// Parse and validate SEC 1 compressed/uncompressed encodings before returning.
|
|
1051
|
+
try {
|
|
1052
|
+
if (length === 33 && (head === 0x02 || head === 0x03)) {
|
|
1053
|
+
// Equation is y² == x³ + ax + b. We calculate y from x.
|
|
1054
|
+
// lift_x() returns the even point; SEC 1 0x03 still needs the odd point.
|
|
1055
|
+
const p = lift_x(x);
|
|
1056
|
+
return head === 0x03 ? p.negate() : p;
|
|
1057
|
+
}
|
|
1058
|
+
// Uncompressed 65-byte point, 0x04 prefix
|
|
1059
|
+
if (length === 65 && head === 0x04)
|
|
1060
|
+
return new Point(x, sliceBytesNumBE(bytes, 33, 65), 1n).assertValidity();
|
|
934
1061
|
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
1062
|
+
catch (error) {
|
|
1063
|
+
// Out-of-range coordinates and non-residue x report the same error as wrong
|
|
1064
|
+
// prefixes / off-curve points, instead of generic range-check messages.
|
|
1065
|
+
throw new Error(E_BADPOINT);
|
|
1066
|
+
}
|
|
1067
|
+
throw new Error(E_BADPOINT);
|
|
940
1068
|
}
|
|
941
1069
|
static fromHex(hex) {
|
|
942
1070
|
return Point.fromBytes(hexToBytes(hex));
|
|
@@ -951,14 +1079,10 @@ class Point {
|
|
|
951
1079
|
equals(other) {
|
|
952
1080
|
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
953
1081
|
const { X: X2, Y: Y2, Z: Z2 } = apoint(other); // checks class equality
|
|
954
|
-
|
|
955
|
-
const X2Z1 = M(X2 * Z1);
|
|
956
|
-
const Y1Z2 = M(Y1 * Z2);
|
|
957
|
-
const Y2Z1 = M(Y2 * Z1);
|
|
958
|
-
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
|
|
1082
|
+
return M(X1 * Z2) === M(X2 * Z1) && M(Y1 * Z2) === M(Y2 * Z1);
|
|
959
1083
|
}
|
|
960
1084
|
is0() {
|
|
961
|
-
return this.
|
|
1085
|
+
return this.Z === 0n;
|
|
962
1086
|
}
|
|
963
1087
|
/** Flip point over y coordinate. */
|
|
964
1088
|
negate() {
|
|
@@ -978,7 +1102,7 @@ class Point {
|
|
|
978
1102
|
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
979
1103
|
const { X: X2, Y: Y2, Z: Z2 } = apoint(other);
|
|
980
1104
|
const a = 0n;
|
|
981
|
-
const b =
|
|
1105
|
+
const b = 7n;
|
|
982
1106
|
let X3 = 0n, Y3 = 0n, Z3 = 0n;
|
|
983
1107
|
const b3 = M(b * 3n);
|
|
984
1108
|
let t0 = M(X1 * X2), t1 = M(Y1 * Y2), t2 = M(Z1 * Z2), t3 = M(X1 + Y1); // step 1
|
|
@@ -1042,13 +1166,18 @@ class Point {
|
|
|
1042
1166
|
// init result point & fake point
|
|
1043
1167
|
let p = I;
|
|
1044
1168
|
let f = G;
|
|
1045
|
-
|
|
1169
|
+
let d = this;
|
|
1170
|
+
// Safe mode always runs 256 iterations so ladder length can't leak the scalar's
|
|
1171
|
+
// leading zero bits; unsafe mode stops at the top set bit for speed.
|
|
1172
|
+
for (let i = 0; safe ? i < 256 : n > 0n; i++) {
|
|
1046
1173
|
// if bit is present, add to point
|
|
1047
1174
|
// if not present, add to fake, for timing safety
|
|
1048
1175
|
if (n & 1n)
|
|
1049
1176
|
p = p.add(d);
|
|
1050
1177
|
else if (safe)
|
|
1051
1178
|
f = f.add(d);
|
|
1179
|
+
d = d.double();
|
|
1180
|
+
n >>= 1n;
|
|
1052
1181
|
}
|
|
1053
1182
|
return p;
|
|
1054
1183
|
}
|
|
@@ -1059,14 +1188,14 @@ class Point {
|
|
|
1059
1188
|
toAffine() {
|
|
1060
1189
|
const { X: x, Y: y, Z: z } = this;
|
|
1061
1190
|
// fast-paths for ZERO point OR Z=1
|
|
1062
|
-
if (
|
|
1191
|
+
if (z === 0n)
|
|
1063
1192
|
return { x: 0n, y: 0n };
|
|
1064
1193
|
if (z === 1n)
|
|
1065
1194
|
return { x, y };
|
|
1066
1195
|
const iz = invert(z, P);
|
|
1067
1196
|
// (Z * Z^-1) must be 1, otherwise bad math
|
|
1068
1197
|
if (M(z * iz) !== 1n)
|
|
1069
|
-
|
|
1198
|
+
throw new Error('inverse invalid');
|
|
1070
1199
|
// x = X*Z^-1; y = Y*Z^-1
|
|
1071
1200
|
return { x: M(x * iz), y: M(y * iz) };
|
|
1072
1201
|
}
|
|
@@ -1076,7 +1205,9 @@ class Point {
|
|
|
1076
1205
|
FpIsValidNot0(x); // must be in range 1 <= x,y < P
|
|
1077
1206
|
FpIsValidNot0(y);
|
|
1078
1207
|
// y² == x³ + ax + b, equation sides must be equal
|
|
1079
|
-
|
|
1208
|
+
if (M(y * y) !== koblitz(x))
|
|
1209
|
+
throw new Error(E_BADPOINT);
|
|
1210
|
+
return this;
|
|
1080
1211
|
}
|
|
1081
1212
|
/** Converts point to 33/65-byte Uint8Array. */
|
|
1082
1213
|
toBytes(isCompressed = true) {
|
|
@@ -1086,7 +1217,7 @@ class Point {
|
|
|
1086
1217
|
const x32b = numTo32b(x);
|
|
1087
1218
|
if (isCompressed)
|
|
1088
1219
|
return concatBytes(getPrefix(y), x32b);
|
|
1089
|
-
return concatBytes(
|
|
1220
|
+
return concatBytes(Uint8Array.of(0x04), x32b, numTo32b(y));
|
|
1090
1221
|
}
|
|
1091
1222
|
toHex(isCompressed) {
|
|
1092
1223
|
return bytesToHex(this.toBytes(isCompressed));
|
|
@@ -1101,17 +1232,14 @@ Point.BASE = G;
|
|
|
1101
1232
|
Point.ZERO = I;
|
|
1102
1233
|
/** `Q = u1⋅G + u2⋅R`. Verifies Q is not ZERO. Unsafe: non-CT. */
|
|
1103
1234
|
const doubleScalarMulUns = (R, u1, u2) => {
|
|
1104
|
-
return G.multiply(u1, false)
|
|
1105
|
-
.add(R.multiply(u2, false))
|
|
1106
|
-
.assertValidity();
|
|
1235
|
+
return G.multiply(u1, false).add(R.multiply(u2, false)).assertValidity();
|
|
1107
1236
|
};
|
|
1108
1237
|
// Inherits byte validation from bytesToHex(); the || '0' fallback keeps empty input mapped to 0n.
|
|
1109
1238
|
const bytesToNumBE = (b) => big('0x' + (bytesToHex(b) || '0'));
|
|
1110
1239
|
// Callers provide monotone slice bounds; subarray() would otherwise clamp or reinterpret them silently.
|
|
1111
1240
|
const sliceBytesNumBE = (b, from, to) => bytesToNumBE(b.subarray(from, to));
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
const numTo32b = (num) => hexToBytes(padh(arange(num, 0n, B256), L2));
|
|
1241
|
+
/** Generic 32-byte big-endian encoder. Must be 0 <= num < 2²⁵⁶; call sites need not be field/scalar elements. */
|
|
1242
|
+
const numTo32b = (num) => hexToBytes(padh(arange(num, 0n, 2n ** 256n), L * 2)); // L*2 = 64 hex chars for a zero-padded 32-byte value
|
|
1115
1243
|
/** Normalize private key to scalar (bigint). Verifies scalar is in range 1 <= d < N. */
|
|
1116
1244
|
const secretKeyToScalar = (secretKey) => {
|
|
1117
1245
|
const num = bytesToNumBE(abytes(secretKey, L, 'secret key'));
|
|
@@ -1142,12 +1270,11 @@ const isValidSecretKey = (secretKey) => {
|
|
|
1142
1270
|
}
|
|
1143
1271
|
};
|
|
1144
1272
|
const isValidPublicKey = (publicKey, isCompressed) => {
|
|
1145
|
-
const { publicKey: comp, publicKeyUncompressed } = lengths;
|
|
1146
1273
|
try {
|
|
1147
1274
|
const l = publicKey.length;
|
|
1148
|
-
if (isCompressed === true && l !==
|
|
1275
|
+
if (isCompressed === true && l !== 33)
|
|
1149
1276
|
return false;
|
|
1150
|
-
if (isCompressed === false && l !==
|
|
1277
|
+
if (isCompressed === false && l !== 65)
|
|
1151
1278
|
return false;
|
|
1152
1279
|
return !!Point.fromBytes(publicKey);
|
|
1153
1280
|
}
|
|
@@ -1175,12 +1302,11 @@ const _sha = 'SHA-256';
|
|
|
1175
1302
|
const hashes = {
|
|
1176
1303
|
hmacSha256Async: async (key, message) => {
|
|
1177
1304
|
const s = subtle();
|
|
1178
|
-
const
|
|
1179
|
-
|
|
1180
|
-
return u8n(await s.sign(name, k, message));
|
|
1305
|
+
const k = await s.importKey('raw', key, { name: 'HMAC', hash: _sha }, false, ['sign']);
|
|
1306
|
+
return new Uint8Array(await s.sign('HMAC', k, message));
|
|
1181
1307
|
},
|
|
1182
1308
|
hmacSha256: undefined,
|
|
1183
|
-
sha256Async: async (msg) =>
|
|
1309
|
+
sha256Async: async (msg) => new Uint8Array(await subtle().digest(_sha, msg)),
|
|
1184
1310
|
sha256: undefined,
|
|
1185
1311
|
};
|
|
1186
1312
|
/**
|
|
@@ -1207,20 +1333,17 @@ const getSharedSecret = (secretKeyA, publicKeyB, isCompressed = true) => {
|
|
|
1207
1333
|
// FIPS 186-5 Appendix A.4.1 style key generation reduces a wide random integer mod (n - 1) and adds 1.
|
|
1208
1334
|
// The 48-byte minimum keeps the secp256k1 bias bound below the appendix's epsilon <= 2^-64 target.
|
|
1209
1335
|
const randomSecretKey = (seed) => {
|
|
1210
|
-
seed = seed === undefined ? randomBytes(
|
|
1336
|
+
seed = seed === undefined ? randomBytes(48) : seed;
|
|
1211
1337
|
abytes(seed);
|
|
1212
1338
|
// Keep the public range text aligned with the enforced 48-byte FIPS floor.
|
|
1213
|
-
if (seed.length <
|
|
1214
|
-
|
|
1339
|
+
if (seed.length < 48 || seed.length > 1024)
|
|
1340
|
+
throw new RangeError('expected 48-1024b');
|
|
1215
1341
|
const num = M(bytesToNumBE(seed), N - 1n);
|
|
1216
1342
|
return numTo32b(num + 1n);
|
|
1217
1343
|
};
|
|
1218
1344
|
const createKeygen = (getPublicKey) => (seed) => {
|
|
1219
1345
|
const secretKey = randomSecretKey(seed);
|
|
1220
|
-
return {
|
|
1221
|
-
secretKey,
|
|
1222
|
-
publicKey: getPublicKey(secretKey),
|
|
1223
|
-
};
|
|
1346
|
+
return { secretKey, publicKey: getPublicKey(secretKey) };
|
|
1224
1347
|
};
|
|
1225
1348
|
/**
|
|
1226
1349
|
* Math, hex, byte helpers. Not in `utils` because utils share API with noble-curves.
|
|
@@ -1231,17 +1354,17 @@ const createKeygen = (getPublicKey) => (seed) => {
|
|
|
1231
1354
|
* const hex = etc.bytesToHex(new Uint8Array([1, 2, 3]));
|
|
1232
1355
|
* ```
|
|
1233
1356
|
*/
|
|
1234
|
-
const etc = /* @__PURE__ */
|
|
1357
|
+
const etc = /* @__PURE__ */ freeze({
|
|
1235
1358
|
hexToBytes,
|
|
1236
1359
|
bytesToHex,
|
|
1237
1360
|
concatBytes,
|
|
1238
1361
|
bytesToNumberBE: bytesToNumBE,
|
|
1239
1362
|
numberToBytesBE: numTo32b,
|
|
1240
1363
|
mod: M,
|
|
1241
|
-
invert
|
|
1364
|
+
invert,
|
|
1242
1365
|
randomBytes,
|
|
1243
|
-
secretKeyToScalar
|
|
1244
|
-
abytes
|
|
1366
|
+
secretKeyToScalar,
|
|
1367
|
+
abytes,
|
|
1245
1368
|
});
|
|
1246
1369
|
/**
|
|
1247
1370
|
* Curve-specific key utilities.
|
|
@@ -1253,26 +1376,23 @@ const etc = /* @__PURE__ */ Object.freeze({
|
|
|
1253
1376
|
* const publicKey = secp.getPublicKey(secretKey);
|
|
1254
1377
|
* ```
|
|
1255
1378
|
*/
|
|
1256
|
-
const utils$2 = /* @__PURE__ */
|
|
1257
|
-
isValidSecretKey
|
|
1258
|
-
isValidPublicKey
|
|
1259
|
-
randomSecretKey
|
|
1379
|
+
const utils$2 = /* @__PURE__ */ freeze({
|
|
1380
|
+
isValidSecretKey,
|
|
1381
|
+
isValidPublicKey,
|
|
1382
|
+
randomSecretKey,
|
|
1260
1383
|
});
|
|
1261
1384
|
// Schnorr signatures are superior to ECDSA from above. Below is Schnorr-specific BIP0340 code.
|
|
1262
1385
|
// https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
|
|
1263
1386
|
// Internal BIP340 tag names are ASCII-only here, so charCodeAt() is enough; this is not a general UTF-8 encoder.
|
|
1264
1387
|
const getTag = (tag) => Uint8Array.from('BIP0340/' + tag, (c) => c.charCodeAt(0));
|
|
1265
|
-
const T_AUX = 'aux';
|
|
1266
|
-
const T_NONCE = 'nonce';
|
|
1267
|
-
const T_CHALLENGE = 'challenge';
|
|
1268
1388
|
// Both SHA-256 provider slots are configurable, so tag hashing still goes through the checked
|
|
1269
1389
|
// wrappers even though the built-in defaults are deterministic and the tag bytes are ASCII-only.
|
|
1270
1390
|
const taggedHash = (tag, ...messages) => {
|
|
1271
|
-
const tagH =
|
|
1272
|
-
return
|
|
1391
|
+
const tagH = callHash('sha256', getTag(tag));
|
|
1392
|
+
return callHash('sha256', concatBytes(tagH, tagH, ...messages));
|
|
1273
1393
|
};
|
|
1274
1394
|
// Async twin of taggedHash with the same checked provider boundary.
|
|
1275
|
-
const taggedHashAsync = (tag, ...messages) =>
|
|
1395
|
+
const taggedHashAsync = (tag, ...messages) => callHashAsync('sha256Async', getTag(tag)).then((tagH) => callHashAsync('sha256Async', concatBytes(tagH, tagH, ...messages)));
|
|
1276
1396
|
// BIP340 PubKey(sk) = bytes(d'⋅G), where bytes(P) is bytes(x(P)); signing also normalizes
|
|
1277
1397
|
// d to the equivalent scalar whose point has even y so the x-only public key stays canonical.
|
|
1278
1398
|
const extpubSchnorr = (priv) => {
|
|
@@ -1284,8 +1404,8 @@ const extpubSchnorr = (priv) => {
|
|
|
1284
1404
|
return { d, px };
|
|
1285
1405
|
};
|
|
1286
1406
|
const bytesModN = (bytes) => modN(bytesToNumBE(bytes));
|
|
1287
|
-
const challenge = (...args) => bytesModN(taggedHash(
|
|
1288
|
-
const challengeAsync = async (...args) => bytesModN(await taggedHashAsync(
|
|
1407
|
+
const challenge = (...args) => bytesModN(taggedHash('challenge', ...args));
|
|
1408
|
+
const challengeAsync = async (...args) => bytesModN(await taggedHashAsync('challenge', ...args));
|
|
1289
1409
|
/** Schnorr public key is just `x` coordinate of Point as per BIP340. */
|
|
1290
1410
|
const pubSchnorr = (secretKey) => {
|
|
1291
1411
|
return extpubSchnorr(secretKey).px; // d'=int(sk). Fail if d'=0 or d'≥n. Ret bytes(d'⋅G)
|
|
@@ -1293,13 +1413,14 @@ const pubSchnorr = (secretKey) => {
|
|
|
1293
1413
|
const keygenSchnorr = /* @__PURE__ */ createKeygen(pubSchnorr);
|
|
1294
1414
|
// Common preparation fn for both sync and async signing
|
|
1295
1415
|
const prepSigSchnorr = (message, secretKey, auxRand) => {
|
|
1416
|
+
const m = snapshotBytes(message, 'message');
|
|
1296
1417
|
const { px, d } = extpubSchnorr(secretKey);
|
|
1297
|
-
return { m
|
|
1418
|
+
return { m, px, d, a: abytes(auxRand, L) };
|
|
1298
1419
|
};
|
|
1299
1420
|
const extractK = (rand) => {
|
|
1300
1421
|
const k_ = bytesModN(rand); // Let k' = int(rand) mod n
|
|
1301
1422
|
if (k_ === 0n)
|
|
1302
|
-
|
|
1423
|
+
throw new Error('sign failed: k is zero'); // Fail if k' = 0.
|
|
1303
1424
|
const { px, d } = extpubSchnorr(numTo32b(k_)); // Let R = k'⋅G.
|
|
1304
1425
|
return { rx: px, k: d };
|
|
1305
1426
|
};
|
|
@@ -1315,73 +1436,78 @@ const E_INVSIG = 'invalid signature produced';
|
|
|
1315
1436
|
*/
|
|
1316
1437
|
const signSchnorr = (message, secretKey, auxRand = randomBytes(L)) => {
|
|
1317
1438
|
const { m, px, d, a } = prepSigSchnorr(message, secretKey, auxRand);
|
|
1318
|
-
const aux = taggedHash(T_AUX, a);
|
|
1319
1439
|
// Let t be the byte-wise xor of bytes(d) and hash/aux(a)
|
|
1320
|
-
const t = numTo32b(d ^ bytesToNumBE(aux));
|
|
1440
|
+
const t = numTo32b(d ^ bytesToNumBE(taggedHash('aux', a)));
|
|
1321
1441
|
// Let rand = hash/nonce(t || bytes(P) || m)
|
|
1322
|
-
const
|
|
1323
|
-
const { rx, k } = extractK(rand);
|
|
1442
|
+
const { rx, k } = extractK(taggedHash('nonce', t, px, m));
|
|
1324
1443
|
// Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n.
|
|
1325
|
-
const
|
|
1326
|
-
const sig = createSigSchnorr(k, rx, e, d);
|
|
1444
|
+
const sig = createSigSchnorr(k, rx, challenge(rx, px, m), d);
|
|
1327
1445
|
// If Verify(bytes(P), m, sig) (see below) returns failure, abort
|
|
1328
1446
|
if (!verifySchnorr(sig, m, px))
|
|
1329
|
-
|
|
1447
|
+
throw new Error(E_INVSIG);
|
|
1330
1448
|
return sig;
|
|
1331
1449
|
};
|
|
1332
1450
|
const signSchnorrAsync = async (message, secretKey, auxRand = randomBytes(L)) => {
|
|
1333
1451
|
const { m, px, d, a } = prepSigSchnorr(message, secretKey, auxRand);
|
|
1334
|
-
const aux = await taggedHashAsync(T_AUX, a);
|
|
1335
1452
|
// Let t be the byte-wise xor of bytes(d) and hash/aux(a)
|
|
1336
|
-
const t = numTo32b(d ^ bytesToNumBE(aux));
|
|
1453
|
+
const t = numTo32b(d ^ bytesToNumBE(await taggedHashAsync('aux', a)));
|
|
1337
1454
|
// Let rand = hash/nonce(t || bytes(P) || m)
|
|
1338
|
-
const
|
|
1339
|
-
const { rx, k } = extractK(rand);
|
|
1455
|
+
const { rx, k } = extractK(await taggedHashAsync('nonce', t, px, m));
|
|
1340
1456
|
// Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n.
|
|
1341
|
-
const
|
|
1342
|
-
const sig = createSigSchnorr(k, rx, e, d);
|
|
1457
|
+
const sig = createSigSchnorr(k, rx, await challengeAsync(rx, px, m), d);
|
|
1343
1458
|
// If Verify(bytes(P), m, sig) (see below) returns failure, abort
|
|
1344
1459
|
if (!(await verifySchnorrAsync(sig, m, px)))
|
|
1345
|
-
|
|
1460
|
+
throw new Error(E_INVSIG);
|
|
1346
1461
|
return sig;
|
|
1347
1462
|
};
|
|
1348
1463
|
const callSyncAsyncFn = (res, later) => {
|
|
1349
1464
|
return res instanceof Promise ? res.then(later) : later(res);
|
|
1350
1465
|
};
|
|
1351
1466
|
const _verifSchnorr = (signature, message, publicKey, challengeFn) => {
|
|
1352
|
-
const sig = abytes(signature,
|
|
1467
|
+
const sig = abytes(signature, 64, 'signature');
|
|
1353
1468
|
const msg = abytes(message, undefined, 'message');
|
|
1354
1469
|
const pub = abytes(publicKey, L, 'publicKey');
|
|
1470
|
+
let P_;
|
|
1471
|
+
let r;
|
|
1472
|
+
let s;
|
|
1473
|
+
let chalInput; // challenge preimage bytes(r) || bytes(P) || m
|
|
1355
1474
|
try {
|
|
1356
1475
|
// lift_x from BIP340. Convert 32-byte x coordinate to elliptic curve point.
|
|
1357
1476
|
// Fail if x ≥ p. Let c = x³ + 7 mod p.
|
|
1358
1477
|
const x = bytesToNumBE(pub);
|
|
1359
|
-
|
|
1360
|
-
const P_ = new Point(x, y, 1n).assertValidity();
|
|
1361
|
-
const px = numTo32b(P_.toAffine().x);
|
|
1478
|
+
P_ = lift_x(x); // lift_x returns the validated even-y point required by BIP340.
|
|
1362
1479
|
// P = lift_x(int(pk)); fail if that fails
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
const s = sliceBytesNumBE(sig, L, L2); // Let s = int(sig[32:64]); fail if s ≥ n.
|
|
1480
|
+
r = FpIsValidNot0(sliceBytesNumBE(sig, 0, L)); // Let r = int(sig[0:32]); fail if r ≥ p.
|
|
1481
|
+
// Let s = int(sig[32:64]); fail if s ≥ n.
|
|
1366
1482
|
// Stricter than BIP-340/libsecp256k1, which only reject s >= n. Honest signing reaches
|
|
1367
1483
|
// s = 0 only with negligible probability (k + e*d ≡ 0 mod n), so treat zero-s inputs as
|
|
1368
1484
|
// crafted edge cases and fail closed instead of carrying that extra verification surface.
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
return callSyncAsyncFn(challengeFn(i), (e) => {
|
|
1373
|
-
const { x, y } = doubleScalarMulUns(P_, s, modN(-e)).toAffine(); // R = s⋅G - e⋅P
|
|
1374
|
-
if (!isEven(y) || x !== r)
|
|
1375
|
-
return false; // -eP == (n-e)P
|
|
1376
|
-
return true; // Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r.
|
|
1377
|
-
});
|
|
1485
|
+
s = FnIsValidNot0(sliceBytesNumBE(sig, L, 64));
|
|
1486
|
+
// lift_x validated x < P, so the 32-byte input encoding equals bytes(P.x) exactly.
|
|
1487
|
+
chalInput = concatBytes(numTo32b(r), pub, msg);
|
|
1378
1488
|
}
|
|
1379
1489
|
catch (error) {
|
|
1380
1490
|
return false;
|
|
1381
1491
|
}
|
|
1492
|
+
// Hash-provider errors (unset hashes.sha256, missing crypto.subtle, bad provider digests)
|
|
1493
|
+
// stay outside the catch above: backend misconfiguration is not an "invalid signature"
|
|
1494
|
+
// result, so it throws / rejects instead of becoming false.
|
|
1495
|
+
// int(challenge(bytes(r)||bytes(P)||m))%n
|
|
1496
|
+
return callSyncAsyncFn(challengeFn(chalInput), (e) => {
|
|
1497
|
+
try {
|
|
1498
|
+
const { x, y } = doubleScalarMulUns(P_, s, modN(-e)).toAffine(); // R = s⋅G - e⋅P
|
|
1499
|
+
if (!isEven(y) || x !== r)
|
|
1500
|
+
return false; // -eP == (n-e)P
|
|
1501
|
+
return true; // Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r.
|
|
1502
|
+
}
|
|
1503
|
+
catch (error) {
|
|
1504
|
+
return false; // is_infinite(R) throws inside doubleScalarMulUns
|
|
1505
|
+
}
|
|
1506
|
+
});
|
|
1382
1507
|
};
|
|
1383
|
-
/** Verifies Schnorr signature.
|
|
1384
|
-
*
|
|
1508
|
+
/** Verifies Schnorr signature. Invalid points, scalars and failed curve checks return false;
|
|
1509
|
+
* hash-backend misconfiguration (e.g. unset hashes.sha256) throws instead, since it is a
|
|
1510
|
+
* runtime/backend error, not an "invalid signature" result. */
|
|
1385
1511
|
const verifySchnorr = (s, m, p) => _verifSchnorr(s, m, p, challenge);
|
|
1386
1512
|
/** Async Schnorr verification. Curve/encoding failures after the initial byte checks still
|
|
1387
1513
|
* become false, but async backend failures reject the promise. Missing crypto.subtle is a
|
|
@@ -1403,7 +1529,7 @@ const verifySchnorrAsync = async (s, m, p) => _verifSchnorr(s, m, p, challengeAs
|
|
|
1403
1529
|
* const isValid = secp.schnorr.verify(sig, message, publicKey);
|
|
1404
1530
|
* ```
|
|
1405
1531
|
*/
|
|
1406
|
-
const schnorr = /* @__PURE__ */
|
|
1532
|
+
const schnorr = /* @__PURE__ */ freeze({
|
|
1407
1533
|
keygen: keygenSchnorr,
|
|
1408
1534
|
getPublicKey: pubSchnorr,
|
|
1409
1535
|
sign: signSchnorr,
|
|
@@ -1413,18 +1539,14 @@ const schnorr = /* @__PURE__ */ Object.freeze({
|
|
|
1413
1539
|
});
|
|
1414
1540
|
// ## Precomputes
|
|
1415
1541
|
// --------------
|
|
1416
|
-
const W = 8; // W is window size
|
|
1417
|
-
const scalarBits = 256;
|
|
1418
|
-
const pwindows = Math.ceil(scalarBits / W) + 1; // 33 for W=8, NOT 32 - see wNAF loop
|
|
1419
|
-
const pwindowSize = 2 ** (W - 1); // 128 for W=8
|
|
1420
1542
|
const precompute = () => {
|
|
1421
1543
|
const points = [];
|
|
1422
1544
|
let p = G;
|
|
1423
1545
|
let b = p;
|
|
1424
|
-
for (let w = 0; w <
|
|
1546
|
+
for (let w = 0; w < 33; w++) {
|
|
1425
1547
|
b = p;
|
|
1426
1548
|
points.push(b);
|
|
1427
|
-
for (let i = 1; i <
|
|
1549
|
+
for (let i = 1; i < 128; i++) {
|
|
1428
1550
|
b = b.add(p);
|
|
1429
1551
|
points.push(b);
|
|
1430
1552
|
} // i=1, bc we skip 0
|
|
@@ -1453,37 +1575,32 @@ const wNAF = (n) => {
|
|
|
1453
1575
|
const comp = Gpows || (Gpows = precompute());
|
|
1454
1576
|
let p = I;
|
|
1455
1577
|
let f = G; // f must be G, or could become I in the end
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
const shiftBy = big(W); // 8 for W=8
|
|
1460
|
-
for (let w = 0; w < pwindows; w++) {
|
|
1461
|
-
let wbits = Number(n & mask); // extract W bits.
|
|
1462
|
-
n >>= shiftBy; // shift number by W bits.
|
|
1578
|
+
for (let w = 0; w < 33; w++) {
|
|
1579
|
+
let wbits = Number(n & 255n); // extract W=8 bits.
|
|
1580
|
+
n >>= 8n; // shift number by W=8 bits.
|
|
1463
1581
|
// We use negative indexes to reduce size of precomputed table by 2x.
|
|
1464
1582
|
// Instead of needing precomputes 0..256, we only calculate them for 0..128.
|
|
1465
1583
|
// If an index > 128 is found, we do (256-index) - where 256 is next window.
|
|
1466
1584
|
// Naive: index +127 => 127, +224 => 224
|
|
1467
1585
|
// Optimized: index +127 => 127, +224 => 256-32
|
|
1468
|
-
if (wbits >
|
|
1469
|
-
wbits -=
|
|
1586
|
+
if (wbits > 128) {
|
|
1587
|
+
wbits -= 256;
|
|
1470
1588
|
n += 1n;
|
|
1471
1589
|
}
|
|
1472
|
-
const off = w *
|
|
1473
|
-
const offF = off; // offsets, evaluate both
|
|
1590
|
+
const off = w * 128;
|
|
1474
1591
|
const offP = off + Math.abs(wbits) - 1;
|
|
1475
|
-
const
|
|
1592
|
+
const isOddW = w % 2 !== 0; // conditions, evaluate both; alternates fake-add sign per window
|
|
1476
1593
|
const isNeg = wbits < 0;
|
|
1477
1594
|
if (wbits === 0) {
|
|
1478
|
-
// off == I: can't add it.
|
|
1479
|
-
f = f.add(ctneg(
|
|
1595
|
+
// off == I: can't add it. Add the first point in this window to the fake accumulator.
|
|
1596
|
+
f = f.add(ctneg(isOddW, comp[off])); // bits are 0: add garbage to fake point
|
|
1480
1597
|
}
|
|
1481
1598
|
else {
|
|
1482
1599
|
p = p.add(ctneg(isNeg, comp[offP])); // bits are 1: add to result point
|
|
1483
1600
|
}
|
|
1484
1601
|
}
|
|
1485
1602
|
if (n !== 0n)
|
|
1486
|
-
|
|
1603
|
+
throw new Error('invalid wnaf');
|
|
1487
1604
|
return { p, f }; // return both real and fake points for JIT/leakage-shape symmetry
|
|
1488
1605
|
};
|
|
1489
1606
|
|
|
@@ -2403,10 +2520,10 @@ class HdNode {
|
|
|
2403
2520
|
return (async () => (await this.identifier).subarray(0, 4))();
|
|
2404
2521
|
}
|
|
2405
2522
|
async hash160(data) {
|
|
2406
|
-
const hash = await createHash(data,
|
|
2523
|
+
const hash = await createHash(data, "SHA-256");
|
|
2407
2524
|
return (await createRIPEMD160())
|
|
2408
2525
|
.update(new Uint8Array(hash))
|
|
2409
|
-
.digest(
|
|
2526
|
+
.digest("binary");
|
|
2410
2527
|
}
|
|
2411
2528
|
get isNeutered() {
|
|
2412
2529
|
return this.#privateKey === undefined;
|
|
@@ -2416,24 +2533,24 @@ class HdNode {
|
|
|
2416
2533
|
}
|
|
2417
2534
|
fromPrivateKey(privateKey, chainCode, network) {
|
|
2418
2535
|
if (!privateKeyVerify(privateKey))
|
|
2419
|
-
throw new TypeError(
|
|
2536
|
+
throw new TypeError("Private key not in range [1, n)");
|
|
2420
2537
|
const publicKey = publicKeyCreate(privateKey, true);
|
|
2421
2538
|
return new HdNode(privateKey, publicKey, publicKey.slice(1), network);
|
|
2422
2539
|
}
|
|
2423
2540
|
fromPublicKey(publicKey, chainCode, network) {
|
|
2424
2541
|
// verify the X coordinate is a point on the curve
|
|
2425
2542
|
if (!publicKeyVerify(publicKey))
|
|
2426
|
-
throw new TypeError(
|
|
2543
|
+
throw new TypeError("Point is not on the curve");
|
|
2427
2544
|
return new HdNode(undefined, publicKey, chainCode, network);
|
|
2428
2545
|
}
|
|
2429
2546
|
async fromSeed(seed, network) {
|
|
2430
2547
|
if (seed.length < 16)
|
|
2431
|
-
throw new TypeError(
|
|
2548
|
+
throw new TypeError("Seed should be at least 128 bits");
|
|
2432
2549
|
if (seed.length > 64)
|
|
2433
|
-
throw new TypeError(
|
|
2434
|
-
let hash = (await createHMAC(createSHA512(), new TextEncoder().encode(
|
|
2550
|
+
throw new TypeError("Seed should be at most 512 bits");
|
|
2551
|
+
let hash = (await createHMAC(createSHA512(), new TextEncoder().encode("Bitcoin seed")))
|
|
2435
2552
|
.update(seed)
|
|
2436
|
-
.digest(
|
|
2553
|
+
.digest("binary");
|
|
2437
2554
|
const privateKey = hash.subarray(0, 32);
|
|
2438
2555
|
const chainCode = hash.subarray(32);
|
|
2439
2556
|
return this.fromPrivateKey(privateKey, chainCode, network);
|
|
@@ -2451,7 +2568,7 @@ class HdNode {
|
|
|
2451
2568
|
this.#chainCode,
|
|
2452
2569
|
];
|
|
2453
2570
|
if (!this.isNeutered) {
|
|
2454
|
-
set.push(new TextEncoder().encode(
|
|
2571
|
+
set.push(new TextEncoder().encode("0"));
|
|
2455
2572
|
set.push(new Uint8Array(this.privateKey));
|
|
2456
2573
|
}
|
|
2457
2574
|
else {
|
|
@@ -2461,7 +2578,7 @@ class HdNode {
|
|
|
2461
2578
|
}
|
|
2462
2579
|
toWIF() {
|
|
2463
2580
|
if (!this.#privateKey)
|
|
2464
|
-
throw new TypeError(
|
|
2581
|
+
throw new TypeError("Missing private key");
|
|
2465
2582
|
return wif.encode(this.#network.wif, this.#privateKey, true);
|
|
2466
2583
|
}
|
|
2467
2584
|
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#child-key-derivation-ckd-functions
|
|
@@ -2471,10 +2588,10 @@ class HdNode {
|
|
|
2471
2588
|
// Hardened child
|
|
2472
2589
|
if (isHardened) {
|
|
2473
2590
|
if (this.isNeutered)
|
|
2474
|
-
throw new TypeError(
|
|
2591
|
+
throw new TypeError("Missing private key for hardened child key");
|
|
2475
2592
|
// data = 0x00 || ser256(kpar) || ser32(index)
|
|
2476
2593
|
data = index$3([
|
|
2477
|
-
new TextEncoder().encode(
|
|
2594
|
+
new TextEncoder().encode("0"),
|
|
2478
2595
|
this.privateKey,
|
|
2479
2596
|
new TextEncoder().encode(index.toString()),
|
|
2480
2597
|
]);
|
|
@@ -2487,7 +2604,7 @@ class HdNode {
|
|
|
2487
2604
|
}
|
|
2488
2605
|
const hash = (await createHMAC(createSHA512(), this.#chainCode))
|
|
2489
2606
|
.update(data)
|
|
2490
|
-
.digest(
|
|
2607
|
+
.digest("binary");
|
|
2491
2608
|
const privateKey = hash.subarray(0, 32);
|
|
2492
2609
|
const chainCode = hash.subarray(32);
|
|
2493
2610
|
// if parse256(privateKey) >= n, proceed with the next value for i
|
|
@@ -2514,10 +2631,10 @@ class HdNode {
|
|
|
2514
2631
|
return this.derive(index + HIGHEST_BIT);
|
|
2515
2632
|
}
|
|
2516
2633
|
async derivePath(path) {
|
|
2517
|
-
let splitPath = path.split(
|
|
2518
|
-
if (splitPath[0] ===
|
|
2634
|
+
let splitPath = path.split("/");
|
|
2635
|
+
if (splitPath[0] === "m") {
|
|
2519
2636
|
if (this.#parentFingerprint)
|
|
2520
|
-
throw new TypeError(
|
|
2637
|
+
throw new TypeError("Expected master, got child");
|
|
2521
2638
|
splitPath = splitPath.slice(1);
|
|
2522
2639
|
}
|
|
2523
2640
|
let prevHd = this;
|
|
@@ -2549,16 +2666,16 @@ class HdNode {
|
|
|
2549
2666
|
const k = privateKey ? 0 : result[5];
|
|
2550
2667
|
const chainCode = result[4];
|
|
2551
2668
|
if (version !== network.bip32.private && version !== network.bip32.public)
|
|
2552
|
-
throw new TypeError(
|
|
2669
|
+
throw new TypeError("Invalid network version");
|
|
2553
2670
|
if (depth === 0) {
|
|
2554
2671
|
if (parentFingerprint !== 0)
|
|
2555
|
-
throw new TypeError(
|
|
2672
|
+
throw new TypeError("Invalid parent fingerprint");
|
|
2556
2673
|
}
|
|
2557
2674
|
if (depth === 0 && index !== 0)
|
|
2558
|
-
throw new TypeError(
|
|
2675
|
+
throw new TypeError("Invalid index");
|
|
2559
2676
|
if (version === network.bip32.private) {
|
|
2560
2677
|
if (k !== 0x00)
|
|
2561
|
-
throw new TypeError(
|
|
2678
|
+
throw new TypeError("Invalid private key");
|
|
2562
2679
|
return new HdNode(privateKey, undefined, chainCode, network, depth, index, parentFingerprint);
|
|
2563
2680
|
}
|
|
2564
2681
|
return new HdNode(undefined,
|
|
@@ -4910,11 +5027,8 @@ class MultiSignature {
|
|
|
4910
5027
|
signature = signature || this.signature;
|
|
4911
5028
|
if (!signature)
|
|
4912
5029
|
throw ReferenceError("signature undefined");
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
index$4.encode(this.multiCodec),
|
|
4916
|
-
signature,
|
|
4917
|
-
]);
|
|
5030
|
+
const writer = new BinaryWriter(varUintLength(this.version) + varUintLength(this.multiCodec) + signature.length);
|
|
5031
|
+
this.#multiSignature = writer.writeVarUint(this.version).writeVarUint(this.multiCodec).writeBytes(signature).finish();
|
|
4918
5032
|
return this.multiSignature;
|
|
4919
5033
|
}
|
|
4920
5034
|
/**
|
|
@@ -4927,12 +5041,11 @@ class MultiSignature {
|
|
|
4927
5041
|
this.#multiSignature = multiSignature;
|
|
4928
5042
|
if (!this.multiSignature)
|
|
4929
5043
|
throw ReferenceError("multiSignature undefined");
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
const codec =
|
|
4934
|
-
|
|
4935
|
-
const signature = buffer.subarray(0, buffer.length);
|
|
5044
|
+
const reader = new BinaryReader(this.multiSignature);
|
|
5045
|
+
// Keep legacy padded-header acceptance; do not change signature wire rules.
|
|
5046
|
+
const version = reader.readVarUint(false);
|
|
5047
|
+
const codec = reader.readVarUint(false);
|
|
5048
|
+
const signature = reader.readBytes(reader.remaining);
|
|
4936
5049
|
console.log("[decode] extracted signature length:", signature.length);
|
|
4937
5050
|
console.log("[decode] extracted signature (hex):", Buffer.from(signature).toString("hex"));
|
|
4938
5051
|
if (version !== this.version)
|
|
@@ -4950,7 +5063,7 @@ class MultiSignature {
|
|
|
4950
5063
|
return this.multiSignature.toString();
|
|
4951
5064
|
}
|
|
4952
5065
|
fromString(string) {
|
|
4953
|
-
return this.decode(new Uint8Array(string.split(",")));
|
|
5066
|
+
return this.decode(new Uint8Array(string.split(",").map(Number)));
|
|
4954
5067
|
}
|
|
4955
5068
|
toBs58() {
|
|
4956
5069
|
return base58$1.encode(this.multiSignature);
|
|
@@ -5025,17 +5138,26 @@ class MultiHDNode extends HDWallet {
|
|
|
5025
5138
|
get id() {
|
|
5026
5139
|
return base58check.encode(index$3([
|
|
5027
5140
|
new TextEncoder().encode(this.version.toString()),
|
|
5028
|
-
this.account(0).hdnode.neutered.publicKey
|
|
5141
|
+
this.account(0).hdnode.neutered.publicKey,
|
|
5029
5142
|
]));
|
|
5030
5143
|
}
|
|
5031
5144
|
get multiWIF() {
|
|
5032
5145
|
return this.toMultiWif();
|
|
5033
5146
|
}
|
|
5034
5147
|
async fromId(id) {
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5148
|
+
const buffer = (await base58check.decode(id)).data;
|
|
5149
|
+
const reader = new BinaryReader(buffer);
|
|
5150
|
+
// IDs contain two framed fields: textual version and compressed public key.
|
|
5151
|
+
// Keep legacy padded length prefixes readable without changing generated IDs.
|
|
5152
|
+
const version = reader.readFrame(false);
|
|
5153
|
+
const publicKey = reader.readFrame(false);
|
|
5154
|
+
const expectedVersion = new TextEncoder().encode(this.version.toString());
|
|
5155
|
+
if (reader.remaining !== 0 ||
|
|
5156
|
+
version.length !== expectedVersion.length ||
|
|
5157
|
+
!version.every((byte, index) => byte === expectedVersion[index]) ||
|
|
5158
|
+
publicKey.length !== 33)
|
|
5159
|
+
throw new Error("invalid wallet ID");
|
|
5160
|
+
await this.fromPublicKey(publicKey, null, this.networkName);
|
|
5039
5161
|
}
|
|
5040
5162
|
async import(password, encrypted) {
|
|
5041
5163
|
const { prefix, data } = await base58check.decode(encrypted);
|
|
@@ -5064,22 +5186,20 @@ class MultiHDNode extends HDWallet {
|
|
|
5064
5186
|
return c.testnet;
|
|
5065
5187
|
else
|
|
5066
5188
|
return p;
|
|
5067
|
-
}, networks[
|
|
5189
|
+
}, networks["leofcoin"]);
|
|
5068
5190
|
if (version !== this.network.version)
|
|
5069
|
-
throw new Error(
|
|
5191
|
+
throw new Error("invalid version");
|
|
5070
5192
|
return this.fromPrivateKey(privateKey, undefined, this.network);
|
|
5071
5193
|
}
|
|
5072
5194
|
toMultiWif() {
|
|
5073
5195
|
return index$1.encode(this.network.version, this.network.multiCodec, this.privateKey);
|
|
5074
5196
|
}
|
|
5075
5197
|
sign(hash) {
|
|
5076
|
-
return new MultiSignature(this.version, this.network.multiCodec)
|
|
5077
|
-
.sign(hash, this.privateKey);
|
|
5198
|
+
return new MultiSignature(this.version, this.network.multiCodec).sign(hash, this.privateKey);
|
|
5078
5199
|
}
|
|
5079
5200
|
verify(multiSignature, hash) {
|
|
5080
5201
|
const publicKey = this.publicKey?.length === 33 ? this.publicKey.slice(1) : this.publicKey;
|
|
5081
|
-
return new MultiSignature(this.version, this.network.multiCodec)
|
|
5082
|
-
.verify(multiSignature, hash, publicKey);
|
|
5202
|
+
return new MultiSignature(this.version, this.network.multiCodec).verify(multiSignature, hash, publicKey);
|
|
5083
5203
|
}
|
|
5084
5204
|
}
|
|
5085
5205
|
|
|
@@ -8336,7 +8456,7 @@ class Identity {
|
|
|
8336
8456
|
this.selectedAccount = new TextDecoder().decode(selected);
|
|
8337
8457
|
}
|
|
8338
8458
|
else {
|
|
8339
|
-
const importee = await import(/* webpackChunkName: "generate-account" */ './index-
|
|
8459
|
+
const importee = await import(/* webpackChunkName: "generate-account" */ './index-dTQVgkpf.js');
|
|
8340
8460
|
const { identity, accounts } = await importee.default(password, this.network);
|
|
8341
8461
|
if (fresh) {
|
|
8342
8462
|
await globalThis.accountStore.clear();
|
|
@@ -8399,4 +8519,4 @@ class Identity {
|
|
|
8399
8519
|
}
|
|
8400
8520
|
}
|
|
8401
8521
|
|
|
8402
|
-
export { Identity as I, MultiWallet as M, base58$1 as a, base as b, index as c, index$2 as d,
|
|
8522
|
+
export { BinaryReader as B, Identity as I, MultiWallet as M, base58$1 as a, base as b, index as c, index$2 as d, BinaryWriter as e, createSHA512 as f, createSHA384 as g, createSHA256 as h, index$3 as i, createSHA224 as j, createSHA1 as k, encrypt as l, getDefaultExportFromCjs as m, resolveAccountWallets as r, varUintLength as v };
|