@cardanowall/sdk-ts 0.2.0 → 0.3.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/dist/client/index.cjs +1608 -1525
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.js +1608 -1525
- package/dist/client/index.js.map +1 -1
- package/dist/conformance/cli.cjs +2334 -2073
- package/dist/conformance/cli.cjs.map +1 -1
- package/dist/conformance/cli.js +2334 -2073
- package/dist/conformance/cli.js.map +1 -1
- package/dist/identity/index.cjs +219 -104
- package/dist/identity/index.cjs.map +1 -1
- package/dist/identity/index.js +219 -104
- package/dist/identity/index.js.map +1 -1
- package/dist/index.cjs +2762 -2484
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2762 -2484
- package/dist/index.js.map +1 -1
- package/dist/verifier/index.cjs +2335 -2074
- package/dist/verifier/index.cjs.map +1 -1
- package/dist/verifier/index.js +2336 -2075
- package/dist/verifier/index.js.map +1 -1
- package/package.json +3 -3
package/dist/conformance/cli.js
CHANGED
|
@@ -5,11 +5,10 @@ import { sortCoreDeterministic } from 'cbor2/sorts';
|
|
|
5
5
|
import { blake2b } from '@noble/hashes/blake2.js';
|
|
6
6
|
import * as ed from '@noble/ed25519';
|
|
7
7
|
import { sha512, sha256 } from '@noble/hashes/sha2.js';
|
|
8
|
-
import { hkdf } from '@noble/hashes/hkdf.js';
|
|
9
|
-
import { argon2id } from 'hash-wasm';
|
|
10
|
-
import { xchacha20poly1305, chacha20poly1305 } from '@noble/ciphers/chacha.js';
|
|
11
8
|
import '@noble/ciphers/utils.js';
|
|
12
9
|
import { hmac } from '@noble/hashes/hmac.js';
|
|
10
|
+
import { xchacha20poly1305, chacha20poly1305 } from '@noble/ciphers/chacha.js';
|
|
11
|
+
import { hkdf } from '@noble/hashes/hkdf.js';
|
|
13
12
|
import '@noble/curves/abstract/edwards.js';
|
|
14
13
|
import '@noble/curves/abstract/montgomery.js';
|
|
15
14
|
import '@noble/curves/abstract/weierstrass.js';
|
|
@@ -19,6 +18,7 @@ import { concatBytes as concatBytes$1, asciiToBytes, bytesToNumberLE, bytesToNum
|
|
|
19
18
|
import { sha3_256, shake256, sha3_512, shake128 } from '@noble/hashes/sha3.js';
|
|
20
19
|
import { anumber, abytes, randomBytes as randomBytes$1, u32, swap32IfBE } from '@noble/hashes/utils.js';
|
|
21
20
|
import { FFTCore, reverseBits } from '@noble/curves/abstract/fft.js';
|
|
21
|
+
import { argon2id } from 'hash-wasm';
|
|
22
22
|
|
|
23
23
|
// src/verifier/types.ts
|
|
24
24
|
var PROFILE_RANK = Object.freeze({
|
|
@@ -573,2231 +573,2431 @@ function parseCoseKeyEd25519(blob) {
|
|
|
573
573
|
if (!(x instanceof Uint8Array) || x.length !== ED25519_PUBLIC_KEY_LENGTH) return null;
|
|
574
574
|
return x;
|
|
575
575
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
out.set(c, offset);
|
|
586
|
-
offset += c.length;
|
|
587
|
-
}
|
|
588
|
-
return out;
|
|
576
|
+
var abytesDoc = abytes;
|
|
577
|
+
var randomBytes = randomBytes$1;
|
|
578
|
+
function equalBytes(a, b) {
|
|
579
|
+
if (a.length !== b.length)
|
|
580
|
+
return false;
|
|
581
|
+
let diff = 0;
|
|
582
|
+
for (let i = 0; i < a.length; i++)
|
|
583
|
+
diff |= a[i] ^ b[i];
|
|
584
|
+
return diff === 0;
|
|
589
585
|
}
|
|
590
|
-
function
|
|
591
|
-
|
|
592
|
-
try {
|
|
593
|
-
const uri = new TextDecoder("utf-8", { fatal: true }).decode(merged);
|
|
594
|
-
return { ok: true, uri };
|
|
595
|
-
} catch (cause) {
|
|
596
|
-
return {
|
|
597
|
-
ok: false,
|
|
598
|
-
code: "INVALID_URI",
|
|
599
|
-
reason: cause instanceof Error ? cause.message : String(cause)
|
|
600
|
-
};
|
|
601
|
-
}
|
|
586
|
+
function copyBytes(bytes) {
|
|
587
|
+
return Uint8Array.from(abytes(bytes));
|
|
602
588
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
ENC_REQUIRES_CONTENT_HASH: "error",
|
|
633
|
-
ENC_PASSPHRASE_ALG_UNSUPPORTED: "error",
|
|
634
|
-
ENC_PASSPHRASE_SALT_TOO_SHORT: "error",
|
|
635
|
-
ENC_PASSPHRASE_SALT_TOO_LONG: "error",
|
|
636
|
-
ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW: "error",
|
|
637
|
-
ENC_PASSPHRASE_PARAMS_EXCEED_POLICY: "error",
|
|
638
|
-
MALFORMED_SIG_COSE_SIGN1: "error",
|
|
639
|
-
SIGNATURE_UNSUPPORTED: "info",
|
|
640
|
-
SIG_ENTRY_INVALID_SHAPE: "error",
|
|
641
|
-
SIG_ENTRY_KID_COSE_KEY_CONFLICT: "error",
|
|
642
|
-
SIG_PRIVATE_KEY_LEAKED: "error",
|
|
643
|
-
SUPERSEDES_TX_INVALID_LENGTH: "error",
|
|
644
|
-
EXTENSION_UNSUPPORTED_CRITICAL: "error",
|
|
645
|
-
CRIT_SHAPE_INVALID: "error",
|
|
646
|
-
// --- Part B ---
|
|
647
|
-
METADATA_NOT_FOUND: "error",
|
|
648
|
-
INSUFFICIENT_CONFIRMATIONS: "info",
|
|
649
|
-
SIGNATURE_INVALID: "error",
|
|
650
|
-
SIGNER_KEY_UNRESOLVED: "error",
|
|
651
|
-
WALLET_ADDRESS_MISMATCH: "error",
|
|
652
|
-
URI_TARGET_FORBIDDEN: "error",
|
|
653
|
-
URI_INTEGRITY_MISMATCH: "error",
|
|
654
|
-
URI_FETCH_FAILED: "warning",
|
|
655
|
-
CONTENT_UNAVAILABLE: "error",
|
|
656
|
-
CIPHERTEXT_UNAVAILABLE: "error",
|
|
657
|
-
PROVIDER_UNAVAILABLE: "error",
|
|
658
|
-
SERVICE_INDEPENDENCE_VIOLATION: "error",
|
|
659
|
-
WRONG_DECRYPTION_INPUT_SHAPE: "error",
|
|
660
|
-
WRONG_RECIPIENT_KEY: "error",
|
|
661
|
-
TAMPERED_HEADER: "error",
|
|
662
|
-
TAMPERED_CIPHERTEXT: "error",
|
|
663
|
-
KDF_DERIVATION_FAILED: "error",
|
|
664
|
-
SCHEMA_MERKLE_LEAF_COUNT_MISMATCH: "error",
|
|
665
|
-
SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED: "error",
|
|
666
|
-
SCHEMA_MERKLE_LEAVES_MALFORMED: "error",
|
|
667
|
-
MERKLE_ROOT_MISMATCH: "error",
|
|
668
|
-
MERKLE_LEAVES_UNAVAILABLE: "warning",
|
|
669
|
-
MERKLE_LEAVES_INFORMATIVE_FORM: "info",
|
|
670
|
-
// Dual-severity — default reading is `info`; the verifier promotes to
|
|
671
|
-
// `error` for merkle-only records (no `items[]` content claim was
|
|
672
|
-
// validated in the same record).
|
|
673
|
-
MERKLE_UNSUPPORTED: "info",
|
|
674
|
-
// Dual-severity — default reading is `info` (render mode); strict
|
|
675
|
-
// end-to-end verifiers promote to `error`.
|
|
676
|
-
OUT_OF_PROFILE_SKIPPED: "info"
|
|
677
|
-
});
|
|
678
|
-
|
|
679
|
-
// ../poe-standard/src/validator.ts
|
|
680
|
-
var HASH_ALG_LENGTHS = {
|
|
681
|
-
"sha2-256": 32,
|
|
682
|
-
"blake2b-256": 32
|
|
683
|
-
};
|
|
684
|
-
var MERKLE_COMMIT_ALG_LENGTHS = {
|
|
685
|
-
"rfc9162-sha256": 32
|
|
686
|
-
};
|
|
687
|
-
var AEAD_NONCE_LENGTHS = {
|
|
688
|
-
"xchacha20-poly1305": 24
|
|
689
|
-
};
|
|
690
|
-
var UNAUTHENTICATED_CIPHER_RE = /(?:^|[-_])(?:cbc|ctr|ecb|cfb|ofb)(?:[-_]|\n?$)|^(?:rc4|des|3des)(?:[-_]|\n?$)/i;
|
|
691
|
-
var KEM_SLOT_DESCRIPTORS = {
|
|
692
|
-
x25519: { field: "epk", fieldLength: 32, wrapLength: 48 },
|
|
693
|
-
mlkem768x25519: { field: "kem_ct", fieldLength: 1120, wrapLength: 48 }
|
|
694
|
-
};
|
|
695
|
-
var KEM_FIELD_LENGTH_CODE = {
|
|
696
|
-
epk: "KEM_EPK_LENGTH_MISMATCH",
|
|
697
|
-
kem_ct: "KEM_CT_LENGTH_MISMATCH"
|
|
698
|
-
};
|
|
699
|
-
var PASSPHRASE_KDF_ALGS = /* @__PURE__ */ new Set(["argon2id"]);
|
|
700
|
-
var KNOWN_SIG_ALG_IDS = /* @__PURE__ */ new Set([-8, -19]);
|
|
701
|
-
function validatePoeRecord(bytes) {
|
|
702
|
-
let decoded;
|
|
703
|
-
try {
|
|
704
|
-
decoded = decodeCanonicalCbor(bytes);
|
|
705
|
-
} catch (cause) {
|
|
706
|
-
return {
|
|
707
|
-
ok: false,
|
|
708
|
-
issues: [
|
|
709
|
-
{
|
|
710
|
-
code: "MALFORMED_CBOR",
|
|
711
|
-
path: [],
|
|
712
|
-
message: cause instanceof Error ? cause.message : String(cause),
|
|
713
|
-
severity: "error"
|
|
714
|
-
}
|
|
715
|
-
]
|
|
716
|
-
};
|
|
717
|
-
}
|
|
718
|
-
const parse = PoeRecordSchema.safeParse(decoded);
|
|
719
|
-
if (!parse.success) {
|
|
720
|
-
const issues = parse.error.issues.map((issue2) => mapZodIssue(issue2, decoded)).sort(compareIssuePath);
|
|
721
|
-
return { ok: false, issues };
|
|
722
|
-
}
|
|
723
|
-
const record = parse.data;
|
|
724
|
-
const errors = [];
|
|
725
|
-
const warnings = [];
|
|
726
|
-
const info = [];
|
|
727
|
-
const itemsLen = Array.isArray(record.items) ? record.items.length : 0;
|
|
728
|
-
const merkleLen = Array.isArray(record.merkle) ? record.merkle.length : 0;
|
|
729
|
-
if (itemsLen === 0 && merkleLen === 0) {
|
|
730
|
-
errors.push(
|
|
731
|
-
issue(
|
|
732
|
-
"SCHEMA_EMPTY_RECORD",
|
|
733
|
-
[],
|
|
734
|
-
"record must carry at least one of items[] or merkle[] non-empty"
|
|
735
|
-
)
|
|
736
|
-
);
|
|
737
|
-
}
|
|
738
|
-
const decodedTopKeys = topLevelKeysOf(decoded);
|
|
739
|
-
const critShapeInvalidIndices = checkCritShape(record, decodedTopKeys, errors);
|
|
740
|
-
for (const k of decodedTopKeys) {
|
|
741
|
-
if (TOP_LEVEL_BASE_KEYS.has(k)) continue;
|
|
742
|
-
if (isExtensionKey(k)) continue;
|
|
743
|
-
errors.push(issue("SCHEMA_UNKNOWN_FIELD", [k], `unknown top-level field: ${k}`));
|
|
744
|
-
}
|
|
745
|
-
if (Array.isArray(record.crit)) {
|
|
746
|
-
for (let i = 0; i < record.crit.length; i++) {
|
|
747
|
-
if (critShapeInvalidIndices.has(i)) continue;
|
|
748
|
-
const critName = record.crit[i];
|
|
749
|
-
errors.push(
|
|
750
|
-
issue(
|
|
751
|
-
"EXTENSION_UNSUPPORTED_CRITICAL",
|
|
752
|
-
["crit", i],
|
|
753
|
-
`crit lists extension '${critName}' that this validator does not implement`
|
|
754
|
-
)
|
|
755
|
-
);
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
for (let i = 0; i < (record.items ?? []).length; i++) {
|
|
759
|
-
const item = record.items[i];
|
|
760
|
-
checkItemHashes(item, i, errors);
|
|
761
|
-
if (item.uris) checkItemUris(item.uris, ["items", i, "uris"], errors);
|
|
762
|
-
if (item.enc !== void 0) checkItemEnc(item, i, errors);
|
|
763
|
-
}
|
|
764
|
-
for (let i = 0; i < (record.merkle ?? []).length; i++) {
|
|
765
|
-
const commit = record.merkle[i];
|
|
766
|
-
checkMerkleCommit(commit, i, errors);
|
|
767
|
-
}
|
|
768
|
-
if (record.sigs) {
|
|
769
|
-
for (let i = 0; i < record.sigs.length; i++) {
|
|
770
|
-
checkSigEntry(record.sigs[i], i, errors, info);
|
|
589
|
+
function splitCoder(label, ...lengths) {
|
|
590
|
+
const getLength = (c) => typeof c === "number" ? c : c.bytesLen;
|
|
591
|
+
const bytesLen = lengths.reduce((sum, a) => sum + getLength(a), 0);
|
|
592
|
+
return {
|
|
593
|
+
bytesLen,
|
|
594
|
+
encode: (bufs) => {
|
|
595
|
+
const res = new Uint8Array(bytesLen);
|
|
596
|
+
for (let i = 0, pos = 0; i < lengths.length; i++) {
|
|
597
|
+
const c = lengths[i];
|
|
598
|
+
const l = getLength(c);
|
|
599
|
+
const b = typeof c === "number" ? bufs[i] : c.encode(bufs[i]);
|
|
600
|
+
abytes(b, l, label);
|
|
601
|
+
res.set(b, pos);
|
|
602
|
+
if (typeof c !== "number")
|
|
603
|
+
b.fill(0);
|
|
604
|
+
pos += l;
|
|
605
|
+
}
|
|
606
|
+
return res;
|
|
607
|
+
},
|
|
608
|
+
decode: (buf) => {
|
|
609
|
+
abytes(buf, bytesLen, label);
|
|
610
|
+
const res = [];
|
|
611
|
+
for (const c of lengths) {
|
|
612
|
+
const l = getLength(c);
|
|
613
|
+
const b = buf.subarray(0, l);
|
|
614
|
+
res.push(typeof c === "number" ? b : c.decode(b));
|
|
615
|
+
buf = buf.subarray(l);
|
|
616
|
+
}
|
|
617
|
+
return res;
|
|
771
618
|
}
|
|
772
|
-
}
|
|
773
|
-
if (errors.length > 0) {
|
|
774
|
-
return { ok: false, issues: errors.sort(compareIssuePath) };
|
|
775
|
-
}
|
|
776
|
-
const result = {
|
|
777
|
-
ok: true,
|
|
778
|
-
record
|
|
779
619
|
};
|
|
780
|
-
if (warnings.length > 0) result.warnings = warnings.sort(compareIssuePath);
|
|
781
|
-
if (info.length > 0) result.info = info.sort(compareIssuePath);
|
|
782
|
-
return result;
|
|
783
620
|
}
|
|
784
|
-
function
|
|
785
|
-
const
|
|
786
|
-
const
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
return false;
|
|
799
|
-
})();
|
|
800
|
-
const valueAtIssue = valueAtPath(decoded, path);
|
|
801
|
-
const isMissing = valueAtIssue === void 0;
|
|
802
|
-
switch (zissue.code) {
|
|
803
|
-
case "invalid_type":
|
|
804
|
-
if (isInSlotEntry) return issue("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
|
|
805
|
-
if (isMissing) {
|
|
806
|
-
if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
807
|
-
return issue("SCHEMA_MISSING_REQUIRED", path, zissue.message);
|
|
808
|
-
}
|
|
809
|
-
if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
810
|
-
return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
|
|
811
|
-
case "invalid_value":
|
|
812
|
-
if (path.length === 1 && path[0] === "v") {
|
|
813
|
-
return issue(
|
|
814
|
-
isMissing ? "SCHEMA_MISSING_REQUIRED" : "SCHEMA_INVALID_LITERAL",
|
|
815
|
-
path,
|
|
816
|
-
zissue.message
|
|
817
|
-
);
|
|
621
|
+
function vecCoder(c, vecLen) {
|
|
622
|
+
const coder = c;
|
|
623
|
+
const bytesLen = vecLen * coder.bytesLen;
|
|
624
|
+
return {
|
|
625
|
+
bytesLen,
|
|
626
|
+
encode: (u) => {
|
|
627
|
+
if (u.length !== vecLen)
|
|
628
|
+
throw new RangeError(`vecCoder.encode: wrong length=${u.length}. Expected: ${vecLen}`);
|
|
629
|
+
const res = new Uint8Array(bytesLen);
|
|
630
|
+
for (let i = 0, pos = 0; i < u.length; i++) {
|
|
631
|
+
const b = coder.encode(u[i]);
|
|
632
|
+
res.set(b, pos);
|
|
633
|
+
b.fill(0);
|
|
634
|
+
pos += b.length;
|
|
818
635
|
}
|
|
819
|
-
return
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
828
|
-
return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
|
|
829
|
-
case "invalid_union":
|
|
830
|
-
case "invalid_key":
|
|
831
|
-
case "invalid_element":
|
|
832
|
-
case "custom":
|
|
833
|
-
default:
|
|
834
|
-
if (isInSlotEntry) return issue("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
|
|
835
|
-
if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
836
|
-
return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
function checkItemHashes(item, idx, errors) {
|
|
840
|
-
const entries = Object.entries(item.hashes);
|
|
841
|
-
if (entries.length === 0) {
|
|
842
|
-
errors.push(
|
|
843
|
-
issue(
|
|
844
|
-
"SCHEMA_TYPE_MISMATCH",
|
|
845
|
-
["items", idx, "hashes"],
|
|
846
|
-
"hashes must be a non-empty CBOR map of <alg-id> -> <digest>"
|
|
847
|
-
)
|
|
848
|
-
);
|
|
849
|
-
return;
|
|
850
|
-
}
|
|
851
|
-
for (const [alg, digest] of entries) {
|
|
852
|
-
if (!(alg in HASH_ALG_LENGTHS)) {
|
|
853
|
-
errors.push(
|
|
854
|
-
issue("UNSUPPORTED_HASH_ALG", ["items", idx, "hashes", alg], `unknown hash alg: ${alg}`)
|
|
855
|
-
);
|
|
856
|
-
continue;
|
|
857
|
-
}
|
|
858
|
-
const expected = HASH_ALG_LENGTHS[alg];
|
|
859
|
-
if (digest.length !== expected) {
|
|
860
|
-
errors.push(
|
|
861
|
-
issue(
|
|
862
|
-
"HASH_DIGEST_LENGTH_MISMATCH",
|
|
863
|
-
["items", idx, "hashes", alg],
|
|
864
|
-
`hashes['${alg}'] digest length ${digest.length} != ${expected}`
|
|
865
|
-
)
|
|
866
|
-
);
|
|
636
|
+
return res;
|
|
637
|
+
},
|
|
638
|
+
decode: (a) => {
|
|
639
|
+
abytes(a, bytesLen);
|
|
640
|
+
const r = [];
|
|
641
|
+
for (let i = 0; i < a.length; i += coder.bytesLen)
|
|
642
|
+
r.push(coder.decode(a.subarray(i, i + coder.bytesLen)));
|
|
643
|
+
return r;
|
|
867
644
|
}
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
function cleanBytes(...list) {
|
|
648
|
+
for (const t of list) {
|
|
649
|
+
if (Array.isArray(t))
|
|
650
|
+
for (const b of t)
|
|
651
|
+
b.fill(0);
|
|
652
|
+
else
|
|
653
|
+
t.fill(0);
|
|
868
654
|
}
|
|
869
655
|
}
|
|
870
|
-
function
|
|
871
|
-
|
|
656
|
+
function getMask(bits) {
|
|
657
|
+
if (!Number.isSafeInteger(bits) || bits < 0 || bits > 32)
|
|
658
|
+
throw new RangeError(`expected bits in [0..32], got ${bits}`);
|
|
659
|
+
return bits === 32 ? 4294967295 : ~(-1 << bits) >>> 0;
|
|
872
660
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
);
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
return;
|
|
892
|
-
}
|
|
893
|
-
const scheme = uri.slice(0, sepIdx).toLowerCase();
|
|
894
|
-
const rest = uri.slice(sepIdx + "://".length);
|
|
895
|
-
if (scheme === "ar") {
|
|
896
|
-
if (!/^ar:\/\/[A-Za-z0-9_-]{43}$/.test("ar://" + rest)) {
|
|
897
|
-
errors.push(
|
|
898
|
-
issue(
|
|
899
|
-
"INVALID_URI",
|
|
900
|
-
path,
|
|
901
|
-
"ar:// URI does not match `^ar://[A-Za-z0-9_-]{43}$` (43-char base64url txid, no path/query/fragment)"
|
|
902
|
-
)
|
|
903
|
-
);
|
|
661
|
+
|
|
662
|
+
// ../../node_modules/.pnpm/@noble+post-quantum@0.6.1/node_modules/@noble/post-quantum/_crystals.js
|
|
663
|
+
var genCrystals = (opts2) => {
|
|
664
|
+
const { newPoly, N: N2, Q: Q2, F: F2, ROOT_OF_UNITY: ROOT_OF_UNITY2, brvBits} = opts2;
|
|
665
|
+
const mod = (a, modulo = Q2) => {
|
|
666
|
+
const result = a % modulo | 0;
|
|
667
|
+
return (result >= 0 ? result | 0 : modulo + result | 0) | 0;
|
|
668
|
+
};
|
|
669
|
+
const smod = (a, modulo = Q2) => {
|
|
670
|
+
const r = mod(a, modulo) | 0;
|
|
671
|
+
return (r > modulo >> 1 ? r - modulo | 0 : r) | 0;
|
|
672
|
+
};
|
|
673
|
+
function getZettas() {
|
|
674
|
+
const out = newPoly(N2);
|
|
675
|
+
for (let i = 0; i < N2; i++) {
|
|
676
|
+
const b = reverseBits(i, brvBits);
|
|
677
|
+
const p = BigInt(ROOT_OF_UNITY2) ** BigInt(b) % BigInt(Q2);
|
|
678
|
+
out[i] = Number(p) | 0;
|
|
904
679
|
}
|
|
905
|
-
return;
|
|
680
|
+
return out;
|
|
906
681
|
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
);
|
|
682
|
+
const nttZetas = getZettas();
|
|
683
|
+
const field = {
|
|
684
|
+
add: (a, b) => mod((a | 0) + (b | 0)) | 0,
|
|
685
|
+
sub: (a, b) => mod((a | 0) - (b | 0)) | 0,
|
|
686
|
+
mul: (a, b) => mod((a | 0) * (b | 0)) | 0,
|
|
687
|
+
inv: (_a) => {
|
|
688
|
+
throw new Error("not implemented");
|
|
914
689
|
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
if (!encParse.success) {
|
|
935
|
-
for (const zissue of encParse.error.issues) {
|
|
936
|
-
const mapped = mapZodIssue(zissue, item.enc);
|
|
937
|
-
errors.push({
|
|
938
|
-
...mapped,
|
|
939
|
-
path: ["items", idx, "enc", ...mapped.path]
|
|
940
|
-
});
|
|
690
|
+
};
|
|
691
|
+
const nttOpts = {
|
|
692
|
+
N: N2,
|
|
693
|
+
roots: nttZetas,
|
|
694
|
+
invertButterflies: true,
|
|
695
|
+
skipStages: 1 ,
|
|
696
|
+
brp: false
|
|
697
|
+
};
|
|
698
|
+
const dif = FFTCore(field, { dit: false, ...nttOpts });
|
|
699
|
+
const dit = FFTCore(field, { dit: true, ...nttOpts });
|
|
700
|
+
const NTT = {
|
|
701
|
+
encode: (r) => {
|
|
702
|
+
return dif(r);
|
|
703
|
+
},
|
|
704
|
+
decode: (r) => {
|
|
705
|
+
dit(r);
|
|
706
|
+
for (let i = 0; i < r.length; i++)
|
|
707
|
+
r[i] = mod(F2 * r[i]);
|
|
708
|
+
return r;
|
|
941
709
|
}
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
errors.push(
|
|
957
|
-
issue(
|
|
958
|
-
"UNAUTHENTICATED_CIPHER_FORBIDDEN",
|
|
959
|
-
[...basePath, "aead"],
|
|
960
|
-
`'${enc.aead}' is an unauthenticated cipher; Label 309 mandates an authenticated (AEAD) cipher`
|
|
961
|
-
)
|
|
962
|
-
);
|
|
963
|
-
return;
|
|
964
|
-
}
|
|
965
|
-
if (!(enc.aead in AEAD_NONCE_LENGTHS)) {
|
|
966
|
-
errors.push(
|
|
967
|
-
issue("UNSUPPORTED_AEAD_ALG", [...basePath, "aead"], `unknown aead alg: ${enc.aead}`)
|
|
968
|
-
);
|
|
969
|
-
return;
|
|
970
|
-
}
|
|
971
|
-
const expectedNonceLen = AEAD_NONCE_LENGTHS[enc.aead];
|
|
972
|
-
if (enc.nonce.length !== expectedNonceLen) {
|
|
973
|
-
errors.push(
|
|
974
|
-
issue(
|
|
975
|
-
"NONCE_LENGTH_MISMATCH",
|
|
976
|
-
[...basePath, "nonce"],
|
|
977
|
-
`nonce length ${enc.nonce.length} != ${expectedNonceLen} for ${enc.aead}`
|
|
978
|
-
)
|
|
979
|
-
);
|
|
980
|
-
}
|
|
981
|
-
if (enc.kem !== void 0 && !(enc.kem in KEM_SLOT_DESCRIPTORS)) {
|
|
982
|
-
errors.push(issue("UNSUPPORTED_KEM_ALG", [...basePath, "kem"], `unknown kem alg: ${enc.kem}`));
|
|
983
|
-
}
|
|
984
|
-
const hasSlots = enc.slots !== void 0;
|
|
985
|
-
const hasSlotsMac = enc.slots_mac !== void 0;
|
|
986
|
-
const hasPassphrase = enc.passphrase !== void 0;
|
|
987
|
-
if (hasSlots && hasPassphrase) {
|
|
988
|
-
errors.push(
|
|
989
|
-
issue("ENC_EXCLUSIVITY_VIOLATION", basePath, "enc combines slots with passphrase; pick one")
|
|
990
|
-
);
|
|
991
|
-
}
|
|
992
|
-
if (hasSlots && !hasSlotsMac) {
|
|
993
|
-
errors.push(
|
|
994
|
-
issue("ENC_SLOTS_MAC_REQUIRED", basePath, "enc.slots present but enc.slots_mac absent")
|
|
995
|
-
);
|
|
996
|
-
}
|
|
997
|
-
if (hasSlotsMac && !hasSlots) {
|
|
998
|
-
errors.push(
|
|
999
|
-
issue("ENC_SLOTS_REQUIRED", basePath, "enc.slots_mac present but enc.slots absent")
|
|
1000
|
-
);
|
|
1001
|
-
}
|
|
1002
|
-
if (hasSlots && enc.kem === void 0) {
|
|
1003
|
-
errors.push(issue("ENC_KEM_REQUIRED", basePath, "enc.slots present but enc.kem absent"));
|
|
1004
|
-
}
|
|
1005
|
-
if (!hasSlots && !hasPassphrase) {
|
|
1006
|
-
errors.push(
|
|
1007
|
-
issue(
|
|
1008
|
-
"ENC_NO_KEY_PATH",
|
|
1009
|
-
basePath,
|
|
1010
|
-
"enc requires either slots or passphrase \u2014 no on-chain key path otherwise"
|
|
1011
|
-
)
|
|
1012
|
-
);
|
|
1013
|
-
}
|
|
1014
|
-
if (hasSlots) {
|
|
1015
|
-
if (enc.slots.length < 1) {
|
|
1016
|
-
errors.push(
|
|
1017
|
-
issue("ENC_SLOTS_EMPTY", [...basePath, "slots"], `slots length ${enc.slots.length} < 1`)
|
|
1018
|
-
);
|
|
1019
|
-
}
|
|
1020
|
-
const descriptor = enc.kem !== void 0 ? KEM_SLOT_DESCRIPTORS[enc.kem] : void 0;
|
|
1021
|
-
if (descriptor !== void 0) {
|
|
1022
|
-
const rawSlotKeys = rawSlotKeySets(item.enc);
|
|
1023
|
-
enc.slots.forEach((slot, si) => {
|
|
1024
|
-
checkSlotShape(
|
|
1025
|
-
slot,
|
|
1026
|
-
rawSlotKeys[si] ?? /* @__PURE__ */ new Set(),
|
|
1027
|
-
descriptor,
|
|
1028
|
-
enc.kem,
|
|
1029
|
-
[...basePath, "slots", si],
|
|
1030
|
-
errors
|
|
1031
|
-
);
|
|
1032
|
-
});
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
if (hasPassphrase) {
|
|
1036
|
-
const pp = enc.passphrase;
|
|
1037
|
-
const ppPath = [...basePath, "passphrase"];
|
|
1038
|
-
if (!PASSPHRASE_KDF_ALGS.has(pp.alg)) {
|
|
1039
|
-
errors.push(
|
|
1040
|
-
issue(
|
|
1041
|
-
"ENC_PASSPHRASE_ALG_UNSUPPORTED",
|
|
1042
|
-
[...ppPath, "alg"],
|
|
1043
|
-
`unknown passphrase kdf alg: ${pp.alg}`
|
|
1044
|
-
)
|
|
1045
|
-
);
|
|
1046
|
-
return;
|
|
1047
|
-
}
|
|
1048
|
-
if (pp.alg === "argon2id") {
|
|
1049
|
-
const allowed = /* @__PURE__ */ new Set(["m", "t", "p"]);
|
|
1050
|
-
for (const k of Object.keys(pp.params)) {
|
|
1051
|
-
if (!allowed.has(k)) {
|
|
1052
|
-
errors.push(
|
|
1053
|
-
issue(
|
|
1054
|
-
"SCHEMA_UNKNOWN_FIELD",
|
|
1055
|
-
[...ppPath, "params", k],
|
|
1056
|
-
`unknown argon2id params field: ${k}`
|
|
1057
|
-
)
|
|
1058
|
-
);
|
|
710
|
+
};
|
|
711
|
+
const bitsCoder = (d, c) => {
|
|
712
|
+
const mask = getMask(d);
|
|
713
|
+
const bytesLen = d * (N2 / 8);
|
|
714
|
+
return {
|
|
715
|
+
bytesLen,
|
|
716
|
+
encode: (poly_) => {
|
|
717
|
+
const poly = poly_;
|
|
718
|
+
const r = new Uint8Array(bytesLen);
|
|
719
|
+
for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < poly.length; i++) {
|
|
720
|
+
buf |= (c.encode(poly[i]) & mask) << bufLen;
|
|
721
|
+
bufLen += d;
|
|
722
|
+
for (; bufLen >= 8; bufLen -= 8, buf >>= 8)
|
|
723
|
+
r[pos++] = buf & getMask(bufLen);
|
|
1059
724
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
)
|
|
1070
|
-
);
|
|
1071
|
-
return null;
|
|
725
|
+
return r;
|
|
726
|
+
},
|
|
727
|
+
decode: (bytes) => {
|
|
728
|
+
const r = newPoly(N2);
|
|
729
|
+
for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < bytes.length; i++) {
|
|
730
|
+
buf |= bytes[i] << bufLen;
|
|
731
|
+
bufLen += 8;
|
|
732
|
+
for (; bufLen >= d; bufLen -= d, buf >>= d)
|
|
733
|
+
r[pos++] = c.decode(buf & mask);
|
|
1072
734
|
}
|
|
1073
|
-
return
|
|
1074
|
-
};
|
|
1075
|
-
const mVal = argonInt(p.m, "m");
|
|
1076
|
-
const tVal = argonInt(p.t, "t");
|
|
1077
|
-
const pVal = argonInt(p.p, "p");
|
|
1078
|
-
if (mVal !== null && mVal < 65536) {
|
|
1079
|
-
errors.push(
|
|
1080
|
-
issue(
|
|
1081
|
-
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
1082
|
-
[...ppPath, "params", "m"],
|
|
1083
|
-
"argon2id requires m >= 65536 KiB"
|
|
1084
|
-
)
|
|
1085
|
-
);
|
|
1086
|
-
}
|
|
1087
|
-
if (tVal !== null && tVal < 3) {
|
|
1088
|
-
errors.push(
|
|
1089
|
-
issue(
|
|
1090
|
-
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
1091
|
-
[...ppPath, "params", "t"],
|
|
1092
|
-
"argon2id requires t >= 3"
|
|
1093
|
-
)
|
|
1094
|
-
);
|
|
1095
|
-
}
|
|
1096
|
-
if (pVal !== null && pVal < 1) {
|
|
1097
|
-
errors.push(
|
|
1098
|
-
issue(
|
|
1099
|
-
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
1100
|
-
[...ppPath, "params", "p"],
|
|
1101
|
-
"argon2id requires p >= 1"
|
|
1102
|
-
)
|
|
1103
|
-
);
|
|
735
|
+
return r;
|
|
1104
736
|
}
|
|
737
|
+
};
|
|
738
|
+
};
|
|
739
|
+
return {
|
|
740
|
+
mod,
|
|
741
|
+
smod,
|
|
742
|
+
nttZetas,
|
|
743
|
+
NTT: {
|
|
744
|
+
encode: (r) => NTT.encode(r),
|
|
745
|
+
decode: (r) => NTT.decode(r)
|
|
746
|
+
},
|
|
747
|
+
bitsCoder
|
|
748
|
+
};
|
|
749
|
+
};
|
|
750
|
+
var createXofShake = (shake) => (seed, blockLen) => {
|
|
751
|
+
if (!blockLen)
|
|
752
|
+
blockLen = shake.blockLen;
|
|
753
|
+
const _seed = new Uint8Array(seed.length + 2);
|
|
754
|
+
_seed.set(seed);
|
|
755
|
+
const seedLen = seed.length;
|
|
756
|
+
const buf = new Uint8Array(blockLen);
|
|
757
|
+
let h = shake.create({});
|
|
758
|
+
let calls = 0;
|
|
759
|
+
let xofs = 0;
|
|
760
|
+
return {
|
|
761
|
+
stats: () => ({ calls, xofs }),
|
|
762
|
+
get: (x, y) => {
|
|
763
|
+
_seed[seedLen + 0] = x;
|
|
764
|
+
_seed[seedLen + 1] = y;
|
|
765
|
+
h.destroy();
|
|
766
|
+
h = shake.create({}).update(_seed);
|
|
767
|
+
calls++;
|
|
768
|
+
return () => {
|
|
769
|
+
xofs++;
|
|
770
|
+
return h.xofInto(buf);
|
|
771
|
+
};
|
|
772
|
+
},
|
|
773
|
+
clean: () => {
|
|
774
|
+
h.destroy();
|
|
775
|
+
cleanBytes(buf, _seed);
|
|
1105
776
|
}
|
|
1106
|
-
}
|
|
777
|
+
};
|
|
778
|
+
};
|
|
779
|
+
var XOF128 = /* @__PURE__ */ createXofShake(shake128);
|
|
780
|
+
|
|
781
|
+
// ../../node_modules/.pnpm/@noble+post-quantum@0.6.1/node_modules/@noble/post-quantum/ml-kem.js
|
|
782
|
+
var N = 256;
|
|
783
|
+
var Q = 3329;
|
|
784
|
+
var F = 3303;
|
|
785
|
+
var ROOT_OF_UNITY = 17;
|
|
786
|
+
var crystals = /* @__PURE__ */ genCrystals({
|
|
787
|
+
N,
|
|
788
|
+
Q,
|
|
789
|
+
F,
|
|
790
|
+
ROOT_OF_UNITY,
|
|
791
|
+
newPoly: (n) => new Uint16Array(n),
|
|
792
|
+
brvBits: 7});
|
|
793
|
+
var PARAMS = /* @__PURE__ */ (() => Object.freeze({
|
|
794
|
+
512: Object.freeze({ N, Q, K: 2, ETA1: 3, ETA2: 2, du: 10, dv: 4, RBGstrength: 128 }),
|
|
795
|
+
768: Object.freeze({ N, Q, K: 3, ETA1: 2, ETA2: 2, du: 10, dv: 4, RBGstrength: 192 }),
|
|
796
|
+
1024: Object.freeze({ N, Q, K: 4, ETA1: 2, ETA2: 2, du: 11, dv: 5, RBGstrength: 256 })
|
|
797
|
+
}))();
|
|
798
|
+
var compress = (d) => {
|
|
799
|
+
if (d >= 12)
|
|
800
|
+
return { encode: (i) => i, decode: (i) => i >= Q ? i - Q : i };
|
|
801
|
+
const a = 2 ** (d - 1);
|
|
802
|
+
return {
|
|
803
|
+
// This only matches standalone Compress_d after bitsCoder masks the result into Z_(2^d).
|
|
804
|
+
encode: (i) => ((i << d) + Q / 2) / Q,
|
|
805
|
+
// const decompress = (i: number) => round((Q / 2 ** d) * i);
|
|
806
|
+
decode: (i) => i * Q + a >>> d
|
|
807
|
+
};
|
|
808
|
+
};
|
|
809
|
+
var byteCoder = (d) => crystals.bitsCoder(d, { encode: (i) => i, decode: (i) => i >= Q ? i - Q : i } );
|
|
810
|
+
var polyCoder = (d) => d === 12 ? byteCoder(12) : crystals.bitsCoder(d, compress(d));
|
|
811
|
+
function polyAdd(a_, b_) {
|
|
812
|
+
const a = a_;
|
|
813
|
+
const b = b_;
|
|
814
|
+
for (let i = 0; i < N; i++)
|
|
815
|
+
a[i] = crystals.mod(a[i] + b[i]);
|
|
1107
816
|
}
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
const
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
817
|
+
function polySub(a_, b_) {
|
|
818
|
+
const a = a_;
|
|
819
|
+
const b = b_;
|
|
820
|
+
for (let i = 0; i < N; i++)
|
|
821
|
+
a[i] = crystals.mod(a[i] - b[i]);
|
|
822
|
+
}
|
|
823
|
+
function BaseCaseMultiply(a0, a1, b0, b1, zeta) {
|
|
824
|
+
const c0 = crystals.mod(a1 * b1 * zeta + a0 * b0);
|
|
825
|
+
const c1 = crystals.mod(a0 * b1 + a1 * b0);
|
|
826
|
+
return { c0, c1 };
|
|
827
|
+
}
|
|
828
|
+
function MultiplyNTTs(f_, g_) {
|
|
829
|
+
const f = f_;
|
|
830
|
+
const g = g_;
|
|
831
|
+
for (let i = 0; i < N / 2; i++) {
|
|
832
|
+
let z3 = crystals.nttZetas[64 + (i >> 1)];
|
|
833
|
+
if (i & 1)
|
|
834
|
+
z3 = -z3;
|
|
835
|
+
const { c0, c1 } = BaseCaseMultiply(f[2 * i + 0], f[2 * i + 1], g[2 * i + 0], g[2 * i + 1], z3);
|
|
836
|
+
f[2 * i + 0] = c0;
|
|
837
|
+
f[2 * i + 1] = c1;
|
|
1119
838
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
);
|
|
839
|
+
return f;
|
|
840
|
+
}
|
|
841
|
+
function SampleNTT(xof_) {
|
|
842
|
+
const xof = xof_;
|
|
843
|
+
const r = new Uint16Array(N);
|
|
844
|
+
for (let j = 0; j < N; ) {
|
|
845
|
+
const b = xof();
|
|
846
|
+
if (b.length % 3)
|
|
847
|
+
throw new Error("SampleNTT: unaligned block");
|
|
848
|
+
for (let i = 0; j < N && i + 3 <= b.length; i += 3) {
|
|
849
|
+
const d1 = (b[i + 0] >> 0 | b[i + 1] << 8) & 4095;
|
|
850
|
+
const d2 = (b[i + 1] >> 4 | b[i + 2] << 4) & 4095;
|
|
851
|
+
if (d1 < Q)
|
|
852
|
+
r[j++] = d1;
|
|
853
|
+
if (j < N && d2 < Q)
|
|
854
|
+
r[j++] = d2;
|
|
1129
855
|
}
|
|
1130
856
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
)
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
errors.push(
|
|
1152
|
-
issue(
|
|
1153
|
-
"ENC_SLOT_INVALID_SHAPE",
|
|
1154
|
-
[...slotPath, "kem_ct"],
|
|
1155
|
-
`slot for kem='${kem}' is missing required 'kem_ct'`
|
|
1156
|
-
)
|
|
1157
|
-
);
|
|
1158
|
-
} else {
|
|
1159
|
-
const reassembled = bytesChunkArrayConcat(slot.kem_ct).length;
|
|
1160
|
-
if (reassembled !== descriptor.fieldLength) {
|
|
1161
|
-
errors.push(
|
|
1162
|
-
issue(
|
|
1163
|
-
KEM_FIELD_LENGTH_CODE.kem_ct,
|
|
1164
|
-
[...slotPath, "kem_ct"],
|
|
1165
|
-
`slot.kem_ct reassembles to ${reassembled} bytes != ${descriptor.fieldLength} for ${kem}`
|
|
1166
|
-
)
|
|
1167
|
-
);
|
|
857
|
+
return r;
|
|
858
|
+
}
|
|
859
|
+
var sampleCBDBytes = (buf, eta) => {
|
|
860
|
+
const r = new Uint16Array(N);
|
|
861
|
+
const b32 = u32(buf);
|
|
862
|
+
swap32IfBE(b32);
|
|
863
|
+
let len = 0;
|
|
864
|
+
for (let i = 0, p = 0, bb = 0, t0 = 0; i < b32.length; i++) {
|
|
865
|
+
let b = b32[i];
|
|
866
|
+
for (let j = 0; j < 32; j++) {
|
|
867
|
+
bb += b & 1;
|
|
868
|
+
b >>= 1;
|
|
869
|
+
len += 1;
|
|
870
|
+
if (len === eta) {
|
|
871
|
+
t0 = bb;
|
|
872
|
+
bb = 0;
|
|
873
|
+
} else if (len === 2 * eta) {
|
|
874
|
+
r[p++] = crystals.mod(t0 - bb);
|
|
875
|
+
bb = 0;
|
|
876
|
+
len = 0;
|
|
1168
877
|
}
|
|
1169
878
|
}
|
|
1170
879
|
}
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
} else if (slot.wrap.length !== descriptor.wrapLength) {
|
|
1180
|
-
errors.push(
|
|
1181
|
-
issue(
|
|
1182
|
-
"WRAP_LENGTH_MISMATCH",
|
|
1183
|
-
[...slotPath, "wrap"],
|
|
1184
|
-
`slot.wrap length ${slot.wrap.length} != ${descriptor.wrapLength}`
|
|
1185
|
-
)
|
|
1186
|
-
);
|
|
1187
|
-
}
|
|
880
|
+
swap32IfBE(b32);
|
|
881
|
+
if (len)
|
|
882
|
+
throw new Error(`sampleCBD: leftover bits: ${len}`);
|
|
883
|
+
return r;
|
|
884
|
+
};
|
|
885
|
+
function sampleCBD(PRF_, seed, nonce, eta) {
|
|
886
|
+
const PRF = PRF_;
|
|
887
|
+
return sampleCBDBytes(PRF(eta * N / 4, seed, nonce), eta);
|
|
1188
888
|
}
|
|
1189
|
-
|
|
1190
|
-
const
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
889
|
+
var genKPKE = (opts_) => {
|
|
890
|
+
const opts2 = opts_;
|
|
891
|
+
const { K, PRF, XOF, HASH512, ETA1, ETA2, du, dv } = opts2;
|
|
892
|
+
const poly1 = polyCoder(1);
|
|
893
|
+
const polyV = polyCoder(dv);
|
|
894
|
+
const polyU = polyCoder(du);
|
|
895
|
+
const publicCoder = splitCoder("publicKey", vecCoder(polyCoder(12), K), 32);
|
|
896
|
+
const secretCoder = vecCoder(polyCoder(12), K);
|
|
897
|
+
const cipherCoder = splitCoder("ciphertext", vecCoder(polyU, K), polyV);
|
|
898
|
+
const seedCoder = splitCoder("seed", 32, 32);
|
|
899
|
+
return {
|
|
900
|
+
secretCoder,
|
|
901
|
+
lengths: {
|
|
902
|
+
secretKey: secretCoder.bytesLen,
|
|
903
|
+
publicKey: publicCoder.bytesLen,
|
|
904
|
+
cipherText: cipherCoder.bytesLen
|
|
905
|
+
},
|
|
906
|
+
keygen: (seed) => {
|
|
907
|
+
abytesDoc(seed, 32, "seed");
|
|
908
|
+
const seedDst = new Uint8Array(33);
|
|
909
|
+
seedDst.set(seed);
|
|
910
|
+
seedDst[32] = K;
|
|
911
|
+
const seedHash = HASH512(seedDst);
|
|
912
|
+
const [rho, sigma] = seedCoder.decode(seedHash);
|
|
913
|
+
const sHat = [];
|
|
914
|
+
const tHat = [];
|
|
915
|
+
for (let i = 0; i < K; i++)
|
|
916
|
+
sHat.push(crystals.NTT.encode(sampleCBD(PRF, sigma, i, ETA1)));
|
|
917
|
+
const x = XOF(rho);
|
|
918
|
+
for (let i = 0; i < K; i++) {
|
|
919
|
+
const e = crystals.NTT.encode(sampleCBD(PRF, sigma, K + i, ETA1));
|
|
920
|
+
for (let j = 0; j < K; j++) {
|
|
921
|
+
const aji = SampleNTT(x.get(j, i));
|
|
922
|
+
polyAdd(e, MultiplyNTTs(aji, sHat[j]));
|
|
923
|
+
}
|
|
924
|
+
tHat.push(e);
|
|
925
|
+
}
|
|
926
|
+
x.clean();
|
|
927
|
+
const res = {
|
|
928
|
+
publicKey: publicCoder.encode([tHat, rho]),
|
|
929
|
+
secretKey: secretCoder.encode(sHat)
|
|
930
|
+
};
|
|
931
|
+
cleanBytes(rho, sigma, sHat, tHat, seedDst, seedHash);
|
|
932
|
+
return res;
|
|
933
|
+
},
|
|
934
|
+
encrypt: (publicKey, msg, seed) => {
|
|
935
|
+
const [tHat, rho] = publicCoder.decode(publicKey);
|
|
936
|
+
const rHat = [];
|
|
937
|
+
for (let i = 0; i < K; i++)
|
|
938
|
+
rHat.push(crystals.NTT.encode(sampleCBD(PRF, seed, i, ETA1)));
|
|
939
|
+
const x = XOF(rho);
|
|
940
|
+
const tmp2 = new Uint16Array(N);
|
|
941
|
+
const u = [];
|
|
942
|
+
for (let i = 0; i < K; i++) {
|
|
943
|
+
const e1 = sampleCBD(PRF, seed, K + i, ETA2);
|
|
944
|
+
const tmp = new Uint16Array(N);
|
|
945
|
+
for (let j = 0; j < K; j++) {
|
|
946
|
+
const aij = SampleNTT(x.get(i, j));
|
|
947
|
+
polyAdd(tmp, MultiplyNTTs(aij, rHat[j]));
|
|
948
|
+
}
|
|
949
|
+
polyAdd(e1, crystals.NTT.decode(tmp));
|
|
950
|
+
u.push(e1);
|
|
951
|
+
polyAdd(tmp2, MultiplyNTTs(tHat[i], rHat[i]));
|
|
952
|
+
cleanBytes(tmp);
|
|
953
|
+
}
|
|
954
|
+
x.clean();
|
|
955
|
+
const e2 = sampleCBD(PRF, seed, 2 * K, ETA2);
|
|
956
|
+
polyAdd(e2, crystals.NTT.decode(tmp2));
|
|
957
|
+
const v = poly1.decode(msg);
|
|
958
|
+
polyAdd(v, e2);
|
|
959
|
+
cleanBytes(tHat, rHat, tmp2, e2);
|
|
960
|
+
return cipherCoder.encode([u, v]);
|
|
961
|
+
},
|
|
962
|
+
decrypt: (cipherText, privateKey) => {
|
|
963
|
+
const [u, v] = cipherCoder.decode(cipherText);
|
|
964
|
+
const sk = secretCoder.decode(privateKey);
|
|
965
|
+
const tmp = new Uint16Array(N);
|
|
966
|
+
for (let i = 0; i < K; i++)
|
|
967
|
+
polyAdd(tmp, MultiplyNTTs(sk[i], crystals.NTT.encode(u[i])));
|
|
968
|
+
polySub(v, crystals.NTT.decode(tmp));
|
|
969
|
+
cleanBytes(tmp, sk, u);
|
|
970
|
+
return poly1.encode(v);
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
};
|
|
974
|
+
function createKyber(opts2) {
|
|
975
|
+
const rawOpts = opts2;
|
|
976
|
+
const KPKE = genKPKE(rawOpts);
|
|
977
|
+
const { HASH256, HASH512, KDF } = rawOpts;
|
|
978
|
+
const { secretCoder: KPKESecretCoder, lengths } = KPKE;
|
|
979
|
+
const secretCoder = splitCoder("secretKey", lengths.secretKey, lengths.publicKey, 32, 32);
|
|
980
|
+
const msgLen = 32;
|
|
981
|
+
const seedLen = 64;
|
|
982
|
+
const kemLengths = Object.freeze({
|
|
983
|
+
...lengths,
|
|
984
|
+
seed: 64,
|
|
985
|
+
msg: msgLen,
|
|
986
|
+
msgRand: msgLen,
|
|
987
|
+
secretKey: secretCoder.bytesLen
|
|
988
|
+
});
|
|
989
|
+
return Object.freeze({
|
|
990
|
+
info: Object.freeze({ type: "ml-kem" }),
|
|
991
|
+
lengths: kemLengths,
|
|
992
|
+
keygen: (seed = randomBytes(seedLen)) => {
|
|
993
|
+
abytesDoc(seed, seedLen, "seed");
|
|
994
|
+
const { publicKey, secretKey: sk } = KPKE.keygen(seed.subarray(0, 32));
|
|
995
|
+
const publicKeyHash = HASH256(publicKey);
|
|
996
|
+
const secretKey = secretCoder.encode([sk, publicKey, publicKeyHash, seed.subarray(32)]);
|
|
997
|
+
cleanBytes(sk, publicKeyHash);
|
|
998
|
+
return {
|
|
999
|
+
publicKey,
|
|
1000
|
+
secretKey
|
|
1001
|
+
};
|
|
1002
|
+
},
|
|
1003
|
+
getPublicKey: (secretKey) => {
|
|
1004
|
+
const [_sk, publicKey, _publicKeyHash, _z] = secretCoder.decode(secretKey);
|
|
1005
|
+
return Uint8Array.from(publicKey);
|
|
1006
|
+
},
|
|
1007
|
+
encapsulate: (publicKey, msg = randomBytes(msgLen)) => {
|
|
1008
|
+
abytesDoc(publicKey, lengths.publicKey, "publicKey");
|
|
1009
|
+
abytesDoc(msg, msgLen, "message");
|
|
1010
|
+
const eke = publicKey.subarray(0, 384 * opts2.K);
|
|
1011
|
+
const ek = KPKESecretCoder.encode(KPKESecretCoder.decode(copyBytes(eke)));
|
|
1012
|
+
if (!equalBytes(ek, eke)) {
|
|
1013
|
+
cleanBytes(ek);
|
|
1014
|
+
throw new Error("ML-KEM.encapsulate: wrong publicKey modulus");
|
|
1015
|
+
}
|
|
1016
|
+
cleanBytes(ek);
|
|
1017
|
+
const kr = HASH512.create().update(msg).update(HASH256(publicKey)).digest();
|
|
1018
|
+
const cipherText = KPKE.encrypt(publicKey, msg, kr.subarray(32, 64));
|
|
1019
|
+
cleanBytes(kr.subarray(32));
|
|
1020
|
+
return {
|
|
1021
|
+
cipherText,
|
|
1022
|
+
sharedSecret: kr.subarray(0, 32)
|
|
1023
|
+
};
|
|
1024
|
+
},
|
|
1025
|
+
decapsulate: (cipherText, secretKey) => {
|
|
1026
|
+
abytesDoc(secretKey, secretCoder.bytesLen, "secretKey");
|
|
1027
|
+
abytesDoc(cipherText, lengths.cipherText, "cipherText");
|
|
1028
|
+
const k768 = secretCoder.bytesLen - 96;
|
|
1029
|
+
const start = k768 + 32;
|
|
1030
|
+
const test = HASH256(secretKey.subarray(k768 / 2, start));
|
|
1031
|
+
if (!equalBytes(test, secretKey.subarray(start, start + 32)))
|
|
1032
|
+
throw new Error("invalid secretKey: hash check failed");
|
|
1033
|
+
const [sk, publicKey, publicKeyHash, z3] = secretCoder.decode(secretKey);
|
|
1034
|
+
const msg = KPKE.decrypt(cipherText, sk);
|
|
1035
|
+
const kr = HASH512.create().update(msg).update(publicKeyHash).digest();
|
|
1036
|
+
const Khat = kr.subarray(0, 32);
|
|
1037
|
+
const cipherText2 = KPKE.encrypt(publicKey, msg, kr.subarray(32, 64));
|
|
1038
|
+
const isValid = equalBytes(cipherText, cipherText2);
|
|
1039
|
+
const Kbar = KDF.create({ dkLen: 32 }).update(z3).update(cipherText).digest();
|
|
1040
|
+
cleanBytes(msg, cipherText2, !isValid ? Khat : Kbar);
|
|
1041
|
+
return isValid ? Khat : Kbar;
|
|
1198
1042
|
}
|
|
1199
|
-
return keys;
|
|
1200
1043
|
});
|
|
1201
1044
|
}
|
|
1202
|
-
function
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
if (
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
)
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
if (entry.cose_key !== void 0) {
|
|
1237
|
-
const keyIssue = inspectCoseKey(entry.cose_key, idx);
|
|
1238
|
-
if (keyIssue !== null) {
|
|
1239
|
-
errors.push(keyIssue);
|
|
1240
|
-
return;
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
const merged = bytesChunkArrayConcat(entry.cose_sign1);
|
|
1244
|
-
let cose;
|
|
1245
|
-
try {
|
|
1246
|
-
cose = decodeCoseSign1(merged);
|
|
1247
|
-
} catch (cause) {
|
|
1248
|
-
errors.push(
|
|
1249
|
-
issue(
|
|
1250
|
-
"MALFORMED_SIG_COSE_SIGN1",
|
|
1251
|
-
["sigs", idx],
|
|
1252
|
-
cause instanceof CoseVerifyError || cause instanceof Error ? cause.message : String(cause)
|
|
1253
|
-
)
|
|
1254
|
-
);
|
|
1255
|
-
return;
|
|
1256
|
-
}
|
|
1257
|
-
if (cose.payload !== null) {
|
|
1258
|
-
errors.push(
|
|
1259
|
-
issue(
|
|
1260
|
-
"MALFORMED_SIG_COSE_SIGN1",
|
|
1261
|
-
["sigs", idx],
|
|
1262
|
-
"COSE_Sign1 payload must be null (detached); attached form forbidden"
|
|
1263
|
-
)
|
|
1264
|
-
);
|
|
1265
|
-
return;
|
|
1266
|
-
}
|
|
1267
|
-
const alg = cose.protectedHeader.get(1);
|
|
1268
|
-
if (typeof alg !== "number" || !KNOWN_SIG_ALG_IDS.has(alg)) {
|
|
1269
|
-
info.push(
|
|
1270
|
-
issue(
|
|
1271
|
-
"SIGNATURE_UNSUPPORTED",
|
|
1272
|
-
["sigs", idx],
|
|
1273
|
-
`COSE_Sign1 protected alg ${String(alg)} not in {-8, -19}`
|
|
1274
|
-
)
|
|
1275
|
-
);
|
|
1276
|
-
}
|
|
1277
|
-
const protectedKid = cose.protectedHeader.get(4);
|
|
1278
|
-
if (protectedKid instanceof Uint8Array && protectedKid.length === 32 && entry.cose_key !== void 0) {
|
|
1279
|
-
errors.push(
|
|
1280
|
-
issue(
|
|
1281
|
-
"SIG_ENTRY_KID_COSE_KEY_CONFLICT",
|
|
1282
|
-
["sigs", idx],
|
|
1283
|
-
"sigs[i] carries both a 32-byte protected `kid` (path 1) and an inline `cose_key` (path 2); paths are mutually exclusive"
|
|
1284
|
-
)
|
|
1285
|
-
);
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1288
|
-
function inspectCoseKey(keyChunks, i) {
|
|
1289
|
-
let decoded;
|
|
1290
|
-
try {
|
|
1291
|
-
decoded = decodeCanonicalCbor(bytesChunkArrayConcat(keyChunks));
|
|
1292
|
-
} catch (cause) {
|
|
1293
|
-
return issue(
|
|
1294
|
-
"MALFORMED_SIG_COSE_SIGN1",
|
|
1295
|
-
["sigs", i, "cose_key"],
|
|
1296
|
-
`sigs[${i}].cose_key failed to decode as cbor<COSE_Key>: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
1297
|
-
);
|
|
1045
|
+
function shakePRF(dkLen, key, nonce) {
|
|
1046
|
+
return shake256.create({ dkLen }).update(key).update(new Uint8Array([nonce])).digest();
|
|
1047
|
+
}
|
|
1048
|
+
var opts = /* @__PURE__ */ (() => ({
|
|
1049
|
+
HASH256: sha3_256,
|
|
1050
|
+
HASH512: sha3_512,
|
|
1051
|
+
KDF: shake256,
|
|
1052
|
+
XOF: XOF128,
|
|
1053
|
+
PRF: shakePRF
|
|
1054
|
+
}))();
|
|
1055
|
+
var mk = (params) => createKyber({
|
|
1056
|
+
...opts,
|
|
1057
|
+
...params
|
|
1058
|
+
});
|
|
1059
|
+
var ml_kem768 = /* @__PURE__ */ (() => mk(PARAMS[768]))();
|
|
1060
|
+
|
|
1061
|
+
// ../../node_modules/.pnpm/@noble+post-quantum@0.6.1/node_modules/@noble/post-quantum/hybrid.js
|
|
1062
|
+
function ecKeygen(curve, allowZeroKey = false) {
|
|
1063
|
+
const lengths = curve.lengths;
|
|
1064
|
+
let keygen = curve.keygen;
|
|
1065
|
+
if (allowZeroKey) {
|
|
1066
|
+
if (!("getSharedSecret" in curve && "sign" in curve && "verify" in curve))
|
|
1067
|
+
throw new Error("allowZeroKey requires a Weierstrass curve");
|
|
1068
|
+
const wCurve = curve;
|
|
1069
|
+
const Fn = wCurve.Point.Fn;
|
|
1070
|
+
keygen = (seed = randomBytes(lengths.seed)) => {
|
|
1071
|
+
abytes(seed, lengths.seed, "seed");
|
|
1072
|
+
const seedScalar = Fn.isLE ? bytesToNumberLE(seed) : bytesToNumberBE(seed);
|
|
1073
|
+
const secretKey = Fn.toBytes(Fn.create(seedScalar));
|
|
1074
|
+
return {
|
|
1075
|
+
secretKey,
|
|
1076
|
+
publicKey: curve.getPublicKey(secretKey)
|
|
1077
|
+
};
|
|
1078
|
+
};
|
|
1298
1079
|
}
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
}
|
|
1304
|
-
return void 0;
|
|
1080
|
+
return {
|
|
1081
|
+
lengths: { secretKey: lengths.secretKey, publicKey: lengths.publicKey, seed: lengths.seed },
|
|
1082
|
+
keygen: (seed) => keygen(seed),
|
|
1083
|
+
getPublicKey: (secretKey) => curve.getPublicKey(secretKey)
|
|
1305
1084
|
};
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1085
|
+
}
|
|
1086
|
+
function ecdhKem(curve, allowZeroKey = false) {
|
|
1087
|
+
const kg = ecKeygen(curve, allowZeroKey);
|
|
1088
|
+
if (!curve.getSharedSecret)
|
|
1089
|
+
throw new Error("wrong curve");
|
|
1090
|
+
return {
|
|
1091
|
+
lengths: { ...kg.lengths, msg: kg.lengths.seed, cipherText: kg.lengths.publicKey },
|
|
1092
|
+
keygen: kg.keygen,
|
|
1093
|
+
getPublicKey: kg.getPublicKey,
|
|
1094
|
+
encapsulate(publicKey, rand = randomBytes(curve.lengths.seed)) {
|
|
1095
|
+
const seed = copyBytes(rand);
|
|
1096
|
+
let ek = void 0;
|
|
1097
|
+
try {
|
|
1098
|
+
ek = this.keygen(seed).secretKey;
|
|
1099
|
+
const sharedSecret = this.decapsulate(publicKey, ek);
|
|
1100
|
+
const cipherText = curve.getPublicKey(ek);
|
|
1101
|
+
return { sharedSecret, cipherText };
|
|
1102
|
+
} finally {
|
|
1103
|
+
cleanBytes(seed);
|
|
1104
|
+
if (ek)
|
|
1105
|
+
cleanBytes(ek);
|
|
1106
|
+
}
|
|
1107
|
+
},
|
|
1108
|
+
decapsulate(cipherText, secretKey) {
|
|
1109
|
+
const res = curve.getSharedSecret(secretKey, cipherText);
|
|
1110
|
+
return curve.lengths.publicKeyHasPrefix ? res.subarray(1) : res;
|
|
1310
1111
|
}
|
|
1311
|
-
return false;
|
|
1312
1112
|
};
|
|
1313
|
-
if (hasLabel(-4)) {
|
|
1314
|
-
return issue(
|
|
1315
|
-
"SIG_PRIVATE_KEY_LEAKED",
|
|
1316
|
-
["sigs", i, "cose_key"],
|
|
1317
|
-
"cose_key carries COSE_Key private-key material (label -4, the OKP/EC2 private scalar d); publishing a private key on the permanent ledger is forbidden"
|
|
1318
|
-
);
|
|
1319
|
-
}
|
|
1320
|
-
const kty = getLabel(1);
|
|
1321
|
-
if (kty !== 1) {
|
|
1322
|
-
return issue(
|
|
1323
|
-
"MALFORMED_SIG_COSE_SIGN1",
|
|
1324
|
-
["sigs", i, "cose_key"],
|
|
1325
|
-
`sigs[${i}].cose_key COSE_Key kty (label 1) must be 1 (OKP); got ${String(kty)}`
|
|
1326
|
-
);
|
|
1327
|
-
}
|
|
1328
|
-
const crv = getLabel(-1);
|
|
1329
|
-
if (crv !== 6) {
|
|
1330
|
-
return issue(
|
|
1331
|
-
"MALFORMED_SIG_COSE_SIGN1",
|
|
1332
|
-
["sigs", i, "cose_key"],
|
|
1333
|
-
`sigs[${i}].cose_key COSE_Key crv (label -1) must be 6 (Ed25519); got ${String(crv)}`
|
|
1334
|
-
);
|
|
1335
|
-
}
|
|
1336
|
-
if (!hasLabel(-2)) {
|
|
1337
|
-
return issue(
|
|
1338
|
-
"MALFORMED_SIG_COSE_SIGN1",
|
|
1339
|
-
["sigs", i, "cose_key"],
|
|
1340
|
-
`sigs[${i}].cose_key COSE_Key missing label -2 (Ed25519 public-key bytes)`
|
|
1341
|
-
);
|
|
1342
|
-
}
|
|
1343
|
-
const x = getLabel(-2);
|
|
1344
|
-
if (!(x instanceof Uint8Array) || x.length !== 32) {
|
|
1345
|
-
const got = x instanceof Uint8Array ? `${x.length}-byte bstr` : typeof x;
|
|
1346
|
-
return issue(
|
|
1347
|
-
"MALFORMED_SIG_COSE_SIGN1",
|
|
1348
|
-
["sigs", i, "cose_key"],
|
|
1349
|
-
`sigs[${i}].cose_key COSE_Key label -2 must be a 32-byte byte string (Ed25519 public key); got ${got}`
|
|
1350
|
-
);
|
|
1351
|
-
}
|
|
1352
|
-
return null;
|
|
1353
1113
|
}
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1114
|
+
function splitLengths(lst, name) {
|
|
1115
|
+
return splitCoder(name, ...lst.map((i) => {
|
|
1116
|
+
if (typeof i.lengths[name] !== "number")
|
|
1117
|
+
throw new Error("wrong length: " + name);
|
|
1118
|
+
return i.lengths[name];
|
|
1119
|
+
}));
|
|
1120
|
+
}
|
|
1121
|
+
function expandSeedXof(xof) {
|
|
1122
|
+
return ((seed, seedLen) => xof(seed, { dkLen: seedLen }));
|
|
1123
|
+
}
|
|
1124
|
+
function combineKeys(realSeedLen, expandSeed_, ...ck_) {
|
|
1125
|
+
const expandSeed = expandSeed_;
|
|
1126
|
+
const ck = ck_;
|
|
1127
|
+
const seedCoder = splitLengths(ck, "seed");
|
|
1128
|
+
const pkCoder = splitLengths(ck, "publicKey");
|
|
1129
|
+
anumber(realSeedLen);
|
|
1130
|
+
function expandDecapsulationKey(seed) {
|
|
1131
|
+
abytes(seed, realSeedLen);
|
|
1132
|
+
const expandedRaw = expandSeed(seed, seedCoder.bytesLen);
|
|
1133
|
+
const expandedSeed = expandedRaw.buffer === seed.buffer ? copyBytes(expandedRaw) : expandedRaw;
|
|
1134
|
+
const expanded = [];
|
|
1135
|
+
const keySecret = [];
|
|
1136
|
+
const secretKey = [];
|
|
1137
|
+
const publicKey = [];
|
|
1138
|
+
let ok = false;
|
|
1364
1139
|
try {
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1140
|
+
for (const part of seedCoder.decode(expandedSeed))
|
|
1141
|
+
expanded.push(copyBytes(part));
|
|
1142
|
+
for (let i = 0; i < ck.length; i++) {
|
|
1143
|
+
const keys = ck[i].keygen(expanded[i]);
|
|
1144
|
+
keySecret.push(keys.secretKey);
|
|
1145
|
+
secretKey.push(copyBytes(keys.secretKey));
|
|
1146
|
+
publicKey.push(keys.publicKey);
|
|
1147
|
+
}
|
|
1148
|
+
ok = true;
|
|
1149
|
+
return { secretKey, publicKey };
|
|
1150
|
+
} finally {
|
|
1151
|
+
cleanBytes(expandedSeed, expanded, keySecret);
|
|
1152
|
+
if (!ok)
|
|
1153
|
+
cleanBytes(secretKey);
|
|
1154
|
+
}
|
|
1370
1155
|
}
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1156
|
+
return {
|
|
1157
|
+
info: { lengths: { seed: realSeedLen, publicKey: pkCoder.bytesLen, secretKey: realSeedLen } },
|
|
1158
|
+
getPublicKey(secretKey) {
|
|
1159
|
+
return this.keygen(secretKey).publicKey;
|
|
1160
|
+
},
|
|
1161
|
+
keygen(seed = randomBytes(realSeedLen)) {
|
|
1162
|
+
const { publicKey: pk, secretKey } = expandDecapsulationKey(seed);
|
|
1163
|
+
try {
|
|
1164
|
+
const publicKey = pkCoder.encode(pk);
|
|
1165
|
+
return { secretKey: seed, publicKey };
|
|
1166
|
+
} finally {
|
|
1167
|
+
cleanBytes(pk);
|
|
1168
|
+
cleanBytes(secretKey);
|
|
1169
|
+
}
|
|
1170
|
+
},
|
|
1171
|
+
expandDecapsulationKey,
|
|
1172
|
+
realSeedLen
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
function combineKEMS(realSeedLen, realMsgLen, expandSeed, combiner, ...kems) {
|
|
1176
|
+
const rawCombiner = combiner;
|
|
1177
|
+
const rawKems = kems;
|
|
1178
|
+
const keys = combineKeys(realSeedLen, expandSeed, ...rawKems);
|
|
1179
|
+
const ctCoder = splitLengths(rawKems, "cipherText");
|
|
1180
|
+
const pkCoder = splitLengths(rawKems, "publicKey");
|
|
1181
|
+
const msgCoder = splitLengths(rawKems, "msg");
|
|
1182
|
+
anumber(realMsgLen);
|
|
1183
|
+
const lengths = Object.freeze({
|
|
1184
|
+
...keys.info.lengths,
|
|
1185
|
+
msg: realMsgLen,
|
|
1186
|
+
msgRand: msgCoder.bytesLen,
|
|
1187
|
+
cipherText: ctCoder.bytesLen
|
|
1188
|
+
});
|
|
1189
|
+
return Object.freeze({
|
|
1190
|
+
lengths,
|
|
1191
|
+
getPublicKey: keys.getPublicKey,
|
|
1192
|
+
keygen: keys.keygen,
|
|
1193
|
+
encapsulate(pk, randomness = randomBytes(msgCoder.bytesLen)) {
|
|
1194
|
+
const pks = pkCoder.decode(pk);
|
|
1195
|
+
const rand = msgCoder.decode(randomness);
|
|
1196
|
+
const sharedSecret = [];
|
|
1197
|
+
const cipherText = [];
|
|
1198
|
+
try {
|
|
1199
|
+
for (let i = 0; i < rawKems.length; i++) {
|
|
1200
|
+
const enc = rawKems[i].encapsulate(pks[i], rand[i]);
|
|
1201
|
+
sharedSecret.push(enc.sharedSecret);
|
|
1202
|
+
cipherText.push(enc.cipherText);
|
|
1203
|
+
}
|
|
1204
|
+
return {
|
|
1205
|
+
// Detach the combiner result before cleanup: a caller-provided combiner may alias one of
|
|
1206
|
+
// the child sharedSecret buffers, and those child buffers are zeroized immediately below.
|
|
1207
|
+
sharedSecret: copyBytes(rawCombiner(pks, cipherText, sharedSecret)),
|
|
1208
|
+
cipherText: ctCoder.encode(cipherText)
|
|
1209
|
+
};
|
|
1210
|
+
} finally {
|
|
1211
|
+
cleanBytes(sharedSecret, cipherText);
|
|
1212
|
+
}
|
|
1213
|
+
},
|
|
1214
|
+
decapsulate(ct, seed) {
|
|
1215
|
+
const cts = ctCoder.decode(ct);
|
|
1216
|
+
const { publicKey, secretKey } = keys.expandDecapsulationKey(seed);
|
|
1217
|
+
const sharedSecret = rawKems.map((i, j) => i.decapsulate(cts[j], secretKey[j]));
|
|
1218
|
+
try {
|
|
1219
|
+
return copyBytes(rawCombiner(publicKey, cts, sharedSecret));
|
|
1220
|
+
} finally {
|
|
1221
|
+
cleanBytes(secretKey, sharedSecret);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
var x25519kem = /* @__PURE__ */ ecdhKem(x25519);
|
|
1227
|
+
var ml_kem768_x25519 = /* @__PURE__ */ (() => combineKEMS(
|
|
1228
|
+
32,
|
|
1229
|
+
32,
|
|
1230
|
+
expandSeedXof(shake256),
|
|
1231
|
+
// Awesome label, so much escaping hell in a single line.
|
|
1232
|
+
(pk, ct, ss) => sha3_256(concatBytes$1(ss[0], ss[1], ct[1], pk[1], asciiToBytes("\\.//^\\"))),
|
|
1233
|
+
ml_kem768,
|
|
1234
|
+
x25519kem
|
|
1235
|
+
))();
|
|
1236
|
+
var XWing = /* @__PURE__ */ (() => ml_kem768_x25519)();
|
|
1237
|
+
var AeadVerificationError = class extends Error {
|
|
1238
|
+
code = "aead_verification_failed";
|
|
1239
|
+
constructor(message, options) {
|
|
1240
|
+
super(message, options);
|
|
1241
|
+
this.name = "AeadVerificationError";
|
|
1242
|
+
}
|
|
1243
|
+
};
|
|
1244
|
+
function chacha20Poly1305Decrypt(opts2) {
|
|
1374
1245
|
try {
|
|
1375
|
-
|
|
1376
|
-
} catch {
|
|
1377
|
-
|
|
1246
|
+
return chacha20poly1305(opts2.key, opts2.nonce, opts2.aad).decrypt(opts2.ciphertext);
|
|
1247
|
+
} catch (cause) {
|
|
1248
|
+
throw new AeadVerificationError("chacha20-poly1305 decrypt failed", { cause });
|
|
1378
1249
|
}
|
|
1379
|
-
if (bytes.length < 4) return false;
|
|
1380
|
-
const versionParse = readVarint(bytes, 0);
|
|
1381
|
-
if (versionParse === null || versionParse.value !== 1) return false;
|
|
1382
|
-
const codecParse = readVarint(bytes, versionParse.next);
|
|
1383
|
-
if (codecParse === null) return false;
|
|
1384
|
-
if (!ACCEPTED_MULTICODECS.has(codecParse.value)) return false;
|
|
1385
|
-
const mhParse = readVarint(bytes, codecParse.next);
|
|
1386
|
-
if (mhParse === null) return false;
|
|
1387
|
-
const lenParse = readVarint(bytes, mhParse.next);
|
|
1388
|
-
if (lenParse === null) return false;
|
|
1389
|
-
const digestLen = lenParse.value;
|
|
1390
|
-
const expectedLen = ACCEPTED_MULTIHASHES.get(mhParse.value);
|
|
1391
|
-
if (expectedLen === void 0 || digestLen !== expectedLen) return false;
|
|
1392
|
-
if (lenParse.next + digestLen !== bytes.length) return false;
|
|
1393
|
-
return true;
|
|
1394
1250
|
}
|
|
1395
|
-
function
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
const b = bytes[i];
|
|
1401
|
-
value |= (b & 127) << shift;
|
|
1402
|
-
i++;
|
|
1403
|
-
if ((b & 128) === 0) return { value, next: i };
|
|
1404
|
-
shift += 7;
|
|
1405
|
-
if (shift > 28) return null;
|
|
1251
|
+
function xchacha20Poly1305Decrypt(opts2) {
|
|
1252
|
+
try {
|
|
1253
|
+
return xchacha20poly1305(opts2.key, opts2.nonce, opts2.aad).decrypt(opts2.ciphertext);
|
|
1254
|
+
} catch (cause) {
|
|
1255
|
+
throw new AeadVerificationError("xchacha20-poly1305 decrypt failed", { cause });
|
|
1406
1256
|
}
|
|
1407
|
-
return null;
|
|
1408
1257
|
}
|
|
1409
|
-
function
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
case "z":
|
|
1420
|
-
return decodeBase58btc(body);
|
|
1421
|
-
default:
|
|
1422
|
-
throw new Error(`unsupported multibase prefix ${prefix}`);
|
|
1258
|
+
function hkdfSha256(opts2) {
|
|
1259
|
+
return hkdf(sha256, opts2.ikm, opts2.salt, opts2.info, opts2.length);
|
|
1260
|
+
}
|
|
1261
|
+
var MLKEM768X25519_ENC_LENGTH = 1120;
|
|
1262
|
+
var MLKEM768X25519_SEED_LENGTH = 32;
|
|
1263
|
+
function mlkem768x25519Keygen(seed) {
|
|
1264
|
+
if (seed.length !== MLKEM768X25519_SEED_LENGTH) {
|
|
1265
|
+
throw new Error(
|
|
1266
|
+
`mlkem768x25519 seed must be ${MLKEM768X25519_SEED_LENGTH} bytes, got ${seed.length}`
|
|
1267
|
+
);
|
|
1423
1268
|
}
|
|
1269
|
+
const { secretKey, publicKey } = XWing.keygen(seed);
|
|
1270
|
+
return { secretSeed: secretKey, publicKey };
|
|
1424
1271
|
}
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
const alphabet = s === s.toLowerCase() ? BASE16_LOWER : BASE16_UPPER;
|
|
1431
|
-
for (let i = 0; i < out.length; i++) {
|
|
1432
|
-
const hi = alphabet.indexOf(s[i * 2]);
|
|
1433
|
-
const lo = alphabet.indexOf(s[i * 2 + 1]);
|
|
1434
|
-
if (hi < 0 || lo < 0) throw new Error(`base16: non-hex char at ${i * 2}`);
|
|
1435
|
-
out[i] = hi << 4 | lo;
|
|
1272
|
+
function mlkem768x25519Decapsulate(opts2) {
|
|
1273
|
+
if (opts2.secretSeed.length !== MLKEM768X25519_SEED_LENGTH) {
|
|
1274
|
+
throw new Error(
|
|
1275
|
+
`mlkem768x25519 secret seed must be ${MLKEM768X25519_SEED_LENGTH} bytes, got ${opts2.secretSeed.length}`
|
|
1276
|
+
);
|
|
1436
1277
|
}
|
|
1437
|
-
|
|
1278
|
+
if (opts2.enc.length !== MLKEM768X25519_ENC_LENGTH) {
|
|
1279
|
+
throw new Error(
|
|
1280
|
+
`mlkem768x25519 enc must be ${MLKEM768X25519_ENC_LENGTH} bytes, got ${opts2.enc.length}`
|
|
1281
|
+
);
|
|
1282
|
+
}
|
|
1283
|
+
return XWing.decapsulate(opts2.enc, opts2.secretSeed);
|
|
1438
1284
|
}
|
|
1439
|
-
var
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
const out = [];
|
|
1445
|
-
let buf = 0;
|
|
1446
|
-
let bits = 0;
|
|
1447
|
-
for (const ch of trimmed) {
|
|
1448
|
-
const idx = alphabet.indexOf(ch);
|
|
1449
|
-
if (idx < 0) throw new Error(`base32: invalid char '${ch}'`);
|
|
1450
|
-
buf = buf << 5 | idx;
|
|
1451
|
-
bits += 5;
|
|
1452
|
-
if (bits >= 8) {
|
|
1453
|
-
bits -= 8;
|
|
1454
|
-
out.push(buf >> bits & 255);
|
|
1455
|
-
}
|
|
1285
|
+
var X25519LowOrderPointError = class extends Error {
|
|
1286
|
+
code = "X25519_LOW_ORDER_POINT";
|
|
1287
|
+
constructor(options) {
|
|
1288
|
+
super("x25519 ECDH rejected: peer public key is a small-order point", options);
|
|
1289
|
+
this.name = "X25519LowOrderPointError";
|
|
1456
1290
|
}
|
|
1457
|
-
|
|
1291
|
+
};
|
|
1292
|
+
var NOBLE_LOW_ORDER_MESSAGE = "invalid private or public key received";
|
|
1293
|
+
function x25519PublicKey(opts2) {
|
|
1294
|
+
return x25519.getPublicKey(opts2.secretKey);
|
|
1458
1295
|
}
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
const b256 = new Uint8Array(size);
|
|
1466
|
-
let length = 0;
|
|
1467
|
-
for (let i = zeros; i < s.length; i++) {
|
|
1468
|
-
const ch = s[i];
|
|
1469
|
-
const carryIdx = BASE58_ALPHABET.indexOf(ch);
|
|
1470
|
-
if (carryIdx < 0) throw new Error(`base58: invalid char '${ch}'`);
|
|
1471
|
-
let carry = carryIdx;
|
|
1472
|
-
let k = 0;
|
|
1473
|
-
for (let j2 = size - 1; (carry !== 0 || k < length) && j2 >= 0; j2--, k++) {
|
|
1474
|
-
carry += 58 * b256[j2];
|
|
1475
|
-
b256[j2] = carry % 256;
|
|
1476
|
-
carry = Math.floor(carry / 256);
|
|
1296
|
+
function x25519Ecdh(opts2) {
|
|
1297
|
+
try {
|
|
1298
|
+
return x25519.getSharedSecret(opts2.secretKey, opts2.theirPublicKey);
|
|
1299
|
+
} catch (e) {
|
|
1300
|
+
if (e instanceof Error && e.message === NOBLE_LOW_ORDER_MESSAGE) {
|
|
1301
|
+
throw new X25519LowOrderPointError({ cause: e });
|
|
1477
1302
|
}
|
|
1478
|
-
|
|
1479
|
-
}
|
|
1480
|
-
let it = size - length;
|
|
1481
|
-
while (it < size && b256[it] === 0) it++;
|
|
1482
|
-
const out = new Uint8Array(zeros + (size - it));
|
|
1483
|
-
let j = zeros;
|
|
1484
|
-
while (it < size) {
|
|
1485
|
-
out[j++] = b256[it++];
|
|
1303
|
+
throw e;
|
|
1486
1304
|
}
|
|
1487
|
-
return out;
|
|
1488
1305
|
}
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
);
|
|
1496
|
-
return invalid;
|
|
1306
|
+
var EciesSealedPoeError = class extends Error {
|
|
1307
|
+
code;
|
|
1308
|
+
constructor(code, message, options) {
|
|
1309
|
+
super(message, options);
|
|
1310
|
+
this.name = "EciesSealedPoeError";
|
|
1311
|
+
this.code = code;
|
|
1497
1312
|
}
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
reason = `'${critName}' is a base key and MUST NOT appear in crit[]`;
|
|
1504
|
-
} else if (!isExtensionKey(critName)) {
|
|
1505
|
-
reason = `'${critName}' does not match the extension-key regex (^x-.+ or ^[a-z]+-.+)`;
|
|
1506
|
-
} else if (!decodedTopKeys.has(critName)) {
|
|
1507
|
-
reason = `'${critName}' is named in crit but absent from the record map`;
|
|
1508
|
-
} else if (seen.has(critName)) {
|
|
1509
|
-
reason = `'${critName}' appears more than once in crit[]`;
|
|
1510
|
-
}
|
|
1511
|
-
seen.add(critName);
|
|
1512
|
-
if (reason !== null) {
|
|
1513
|
-
invalid.add(i);
|
|
1514
|
-
errors.push(issue("CRIT_SHAPE_INVALID", ["crit", i], reason));
|
|
1515
|
-
}
|
|
1313
|
+
};
|
|
1314
|
+
var CHUNK_MAX_BYTES = 64;
|
|
1315
|
+
function chunkKemCt(value) {
|
|
1316
|
+
if (value.length === 0) {
|
|
1317
|
+
throw new Error("chunkKemCt: refusing to chunk an empty byte string");
|
|
1516
1318
|
}
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
if (decoded === null || typeof decoded !== "object") return /* @__PURE__ */ new Set();
|
|
1521
|
-
if (decoded instanceof Map) {
|
|
1522
|
-
const out = /* @__PURE__ */ new Set();
|
|
1523
|
-
for (const k of decoded.keys()) {
|
|
1524
|
-
if (typeof k === "string") out.add(k);
|
|
1525
|
-
}
|
|
1526
|
-
return out;
|
|
1319
|
+
const chunks = [];
|
|
1320
|
+
for (let i = 0; i < value.length; i += CHUNK_MAX_BYTES) {
|
|
1321
|
+
chunks.push(value.subarray(i, Math.min(i + CHUNK_MAX_BYTES, value.length)));
|
|
1527
1322
|
}
|
|
1528
|
-
return
|
|
1529
|
-
}
|
|
1530
|
-
function issue(code, path, message) {
|
|
1531
|
-
return { code, path, message, severity: SEVERITY[code] };
|
|
1323
|
+
return chunks;
|
|
1532
1324
|
}
|
|
1533
|
-
function
|
|
1534
|
-
|
|
1325
|
+
function joinKemCt(chunks) {
|
|
1326
|
+
let total = 0;
|
|
1327
|
+
for (const c of chunks) total += c.length;
|
|
1328
|
+
const out = new Uint8Array(total);
|
|
1329
|
+
let offset = 0;
|
|
1330
|
+
for (const c of chunks) {
|
|
1331
|
+
out.set(c, offset);
|
|
1332
|
+
offset += c.length;
|
|
1333
|
+
}
|
|
1334
|
+
return out;
|
|
1535
1335
|
}
|
|
1536
|
-
function
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
if (cur === null || cur === void 0) return void 0;
|
|
1540
|
-
if (cur instanceof Map) {
|
|
1541
|
-
cur = cur.get(seg);
|
|
1542
|
-
continue;
|
|
1543
|
-
}
|
|
1544
|
-
if (typeof cur !== "object") return void 0;
|
|
1545
|
-
cur = cur[seg];
|
|
1336
|
+
function canonicalizeSlots(slots, kem) {
|
|
1337
|
+
if (kem === "x25519") {
|
|
1338
|
+
return slots.map((s) => ({ epk: s.epk, wrap: s.wrap }));
|
|
1546
1339
|
}
|
|
1547
|
-
return
|
|
1340
|
+
return slots.map((s) => ({
|
|
1341
|
+
kem_ct: chunkKemCt(joinKemCt(s.kem_ct)),
|
|
1342
|
+
wrap: s.wrap
|
|
1343
|
+
}));
|
|
1548
1344
|
}
|
|
1549
|
-
|
|
1550
|
-
return
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
memorySize: opts2.memSizeKB,
|
|
1556
|
-
hashLength: opts2.outBytes,
|
|
1557
|
-
outputType: "binary"
|
|
1345
|
+
function encodeCanonicalCbor3(value) {
|
|
1346
|
+
return encode(value, {
|
|
1347
|
+
cde: true,
|
|
1348
|
+
collapseBigInts: true,
|
|
1349
|
+
rejectDuplicateKeys: true,
|
|
1350
|
+
sortKeys: sortCoreDeterministic
|
|
1558
1351
|
});
|
|
1559
1352
|
}
|
|
1560
|
-
var
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1353
|
+
var CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX = new TextEncoder().encode(
|
|
1354
|
+
"cardano-poe-slots-transcript-v1"
|
|
1355
|
+
);
|
|
1356
|
+
var CARDANO_POE_HKDF_INFO_PAYLOAD = new TextEncoder().encode(
|
|
1357
|
+
"cardano-poe-payload-v1"
|
|
1358
|
+
);
|
|
1359
|
+
var CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE = new TextEncoder().encode(
|
|
1360
|
+
"cardano-poe-payload-passphrase-v1"
|
|
1361
|
+
);
|
|
1362
|
+
var CARDANO_POE_XWING_KEK_SALT_PREFIX = new TextEncoder().encode(
|
|
1363
|
+
"cardano-poe-xwing-kek-salt-v1"
|
|
1364
|
+
);
|
|
1365
|
+
if (CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length !== 31) {
|
|
1366
|
+
throw new Error(
|
|
1367
|
+
"CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX byte-length invariant violated (expected 31)"
|
|
1368
|
+
);
|
|
1573
1369
|
}
|
|
1574
|
-
|
|
1575
|
-
|
|
1370
|
+
if (CARDANO_POE_HKDF_INFO_PAYLOAD.length !== 22) {
|
|
1371
|
+
throw new Error("CARDANO_POE_HKDF_INFO_PAYLOAD byte-length invariant violated (expected 22)");
|
|
1576
1372
|
}
|
|
1577
|
-
|
|
1578
|
-
|
|
1373
|
+
if (CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE.length !== 33) {
|
|
1374
|
+
throw new Error(
|
|
1375
|
+
"CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE byte-length invariant violated (expected 33)"
|
|
1376
|
+
);
|
|
1579
1377
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1378
|
+
if (CARDANO_POE_XWING_KEK_SALT_PREFIX.length !== 29) {
|
|
1379
|
+
throw new Error("CARDANO_POE_XWING_KEK_SALT_PREFIX byte-length invariant violated (expected 29)");
|
|
1380
|
+
}
|
|
1381
|
+
var CARDANO_POE_PW_NORM_PROFILE = "cardano-poe-pw-norm-v1";
|
|
1382
|
+
var MAX_SLOTS = 1024;
|
|
1383
|
+
var MAX_DECODED_ENVELOPE_BYTES = 65536;
|
|
1384
|
+
var MAX_SEALED_PLAINTEXT = 274877906880;
|
|
1385
|
+
var MAX_SEALED_CIPHERTEXT = MAX_SEALED_PLAINTEXT + 16;
|
|
1386
|
+
function assertCiphertextWithinBound(ciphertextLength) {
|
|
1387
|
+
if (ciphertextLength >= MAX_SEALED_CIPHERTEXT) {
|
|
1388
|
+
throw new SealedPayloadTooLargeError(
|
|
1389
|
+
`ciphertext length ${ciphertextLength} is at or above the maximum sealed ciphertext size ${MAX_SEALED_CIPHERTEXT}`
|
|
1390
|
+
);
|
|
1391
|
+
}
|
|
1582
1392
|
}
|
|
1583
|
-
var
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
if (leaves.length === 0) {
|
|
1588
|
-
throw new Error(`${fnName}: empty leaf list (n == 0 is forbidden by RFC 9162 \xA72.1.1)`);
|
|
1393
|
+
var SealedPayloadTooLargeError = class extends Error {
|
|
1394
|
+
constructor(message) {
|
|
1395
|
+
super(message);
|
|
1396
|
+
this.name = "SealedPayloadTooLargeError";
|
|
1589
1397
|
}
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1398
|
+
};
|
|
1399
|
+
function computeSlotsHash(args) {
|
|
1400
|
+
const transcript = {
|
|
1401
|
+
scheme: 1,
|
|
1402
|
+
path: "slots",
|
|
1403
|
+
aead: "xchacha20-poly1305",
|
|
1404
|
+
kem: args.kem,
|
|
1405
|
+
nonce: args.nonce,
|
|
1406
|
+
slots: canonicalizeSlots(args.slots, args.kem)
|
|
1407
|
+
};
|
|
1408
|
+
const encoded = encodeCanonicalCbor3(transcript);
|
|
1409
|
+
const message = new Uint8Array(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length + encoded.length);
|
|
1410
|
+
message.set(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX, 0);
|
|
1411
|
+
message.set(encoded, CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length);
|
|
1412
|
+
return sha256(message);
|
|
1413
|
+
}
|
|
1414
|
+
function adContentSlots(args) {
|
|
1415
|
+
const ad = {
|
|
1416
|
+
scheme: 1,
|
|
1417
|
+
path: "slots",
|
|
1418
|
+
aead: "xchacha20-poly1305",
|
|
1419
|
+
kem: args.kem,
|
|
1420
|
+
nonce: args.nonce,
|
|
1421
|
+
slots_hash: args.slotsHash,
|
|
1422
|
+
slots_mac: args.slotsMac
|
|
1423
|
+
};
|
|
1424
|
+
return encodeCanonicalCbor3(ad);
|
|
1425
|
+
}
|
|
1426
|
+
function adContentPassphrase(args) {
|
|
1427
|
+
const ad = {
|
|
1428
|
+
scheme: 1,
|
|
1429
|
+
path: "passphrase",
|
|
1430
|
+
aead: "xchacha20-poly1305",
|
|
1431
|
+
nonce: args.nonce,
|
|
1432
|
+
passphrase: {
|
|
1433
|
+
alg: args.passphrase.alg,
|
|
1434
|
+
salt: args.passphrase.salt,
|
|
1435
|
+
params: {
|
|
1436
|
+
m: args.passphrase.params.m,
|
|
1437
|
+
t: args.passphrase.params.t,
|
|
1438
|
+
p: args.passphrase.params.p
|
|
1439
|
+
},
|
|
1440
|
+
normalization: CARDANO_POE_PW_NORM_PROFILE
|
|
1596
1441
|
}
|
|
1597
|
-
}
|
|
1442
|
+
};
|
|
1443
|
+
return encodeCanonicalCbor3(ad);
|
|
1598
1444
|
}
|
|
1599
|
-
function
|
|
1600
|
-
|
|
1601
|
-
|
|
1445
|
+
function slotsPayloadKey(args) {
|
|
1446
|
+
return hkdfSha256({
|
|
1447
|
+
ikm: args.cek,
|
|
1448
|
+
salt: args.nonce,
|
|
1449
|
+
info: CARDANO_POE_HKDF_INFO_PAYLOAD,
|
|
1450
|
+
length: 32
|
|
1451
|
+
});
|
|
1602
1452
|
}
|
|
1603
|
-
function
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1453
|
+
function passphrasePayloadKey(args) {
|
|
1454
|
+
return hkdfSha256({
|
|
1455
|
+
ikm: args.cek,
|
|
1456
|
+
salt: args.nonce,
|
|
1457
|
+
info: CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE,
|
|
1458
|
+
length: 32
|
|
1459
|
+
});
|
|
1607
1460
|
}
|
|
1608
|
-
function
|
|
1609
|
-
const
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1461
|
+
function xwingKekSalt(args) {
|
|
1462
|
+
const message = new Uint8Array(
|
|
1463
|
+
CARDANO_POE_XWING_KEK_SALT_PREFIX.length + args.kemCt.length + args.pubR.length
|
|
1464
|
+
);
|
|
1465
|
+
let offset = 0;
|
|
1466
|
+
message.set(CARDANO_POE_XWING_KEK_SALT_PREFIX, offset);
|
|
1467
|
+
offset += CARDANO_POE_XWING_KEK_SALT_PREFIX.length;
|
|
1468
|
+
message.set(args.kemCt, offset);
|
|
1469
|
+
offset += args.kemCt.length;
|
|
1470
|
+
message.set(args.pubR, offset);
|
|
1471
|
+
return sha256(message);
|
|
1613
1472
|
}
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1473
|
+
var CARDANO_POE_HKDF_INFO_KEK = new TextEncoder().encode("cardano-poe-kek-v1");
|
|
1474
|
+
var CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 = new TextEncoder().encode(
|
|
1475
|
+
"cardano-poe-kek-mlkem768x25519-v1"
|
|
1476
|
+
);
|
|
1477
|
+
var CARDANO_POE_HKDF_INFO_SLOTS_MAC = new TextEncoder().encode(
|
|
1478
|
+
"cardano-poe-slots-mac-v1"
|
|
1479
|
+
);
|
|
1480
|
+
var ZERO_NONCE_12 = new Uint8Array(12);
|
|
1481
|
+
if (CARDANO_POE_HKDF_INFO_KEK.length !== 18) {
|
|
1482
|
+
throw new Error("CARDANO_POE_HKDF_INFO_KEK byte-length invariant violated (expected 18)");
|
|
1620
1483
|
}
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
}
|
|
1626
|
-
const k = largestPow2Lt(n);
|
|
1627
|
-
const left = mthRecursive(leaves, start, start + k);
|
|
1628
|
-
const right = mthRecursive(leaves, start + k, end);
|
|
1629
|
-
return hashNode(left, right);
|
|
1484
|
+
if (CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519.length !== 33) {
|
|
1485
|
+
throw new Error(
|
|
1486
|
+
"CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 byte-length invariant violated (expected 33)"
|
|
1487
|
+
);
|
|
1630
1488
|
}
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1489
|
+
if (CARDANO_POE_HKDF_INFO_SLOTS_MAC.length !== 24) {
|
|
1490
|
+
throw new Error("CARDANO_POE_HKDF_INFO_SLOTS_MAC byte-length invariant violated (expected 24)");
|
|
1491
|
+
}
|
|
1492
|
+
if (ZERO_NONCE_12.length !== 12) {
|
|
1493
|
+
throw new Error("ZERO_NONCE_12 byte-length invariant violated (expected 12)");
|
|
1494
|
+
}
|
|
1495
|
+
function compareCt2(a, b) {
|
|
1496
|
+
if (a.length !== b.length) return false;
|
|
1636
1497
|
let diff = 0;
|
|
1637
|
-
for (let i = 0; i < a.length; i++)
|
|
1638
|
-
diff |= a[i] ^ b[i];
|
|
1498
|
+
for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
|
|
1639
1499
|
return diff === 0;
|
|
1640
1500
|
}
|
|
1641
|
-
function
|
|
1642
|
-
return
|
|
1501
|
+
function selectBundleSecrets(envelope, bundle) {
|
|
1502
|
+
return envelope.kem === "x25519" ? bundle.x25519PrivateKeys : bundle.mlkem768x25519SecretSeeds;
|
|
1643
1503
|
}
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
res.set(b, pos);
|
|
1657
|
-
if (typeof c !== "number")
|
|
1658
|
-
b.fill(0);
|
|
1659
|
-
pos += l;
|
|
1660
|
-
}
|
|
1661
|
-
return res;
|
|
1662
|
-
},
|
|
1663
|
-
decode: (buf) => {
|
|
1664
|
-
abytes(buf, bytesLen, label);
|
|
1665
|
-
const res = [];
|
|
1666
|
-
for (const c of lengths) {
|
|
1667
|
-
const l = getLength(c);
|
|
1668
|
-
const b = buf.subarray(0, l);
|
|
1669
|
-
res.push(typeof c === "number" ? b : c.decode(b));
|
|
1670
|
-
buf = buf.subarray(l);
|
|
1671
|
-
}
|
|
1672
|
-
return res;
|
|
1673
|
-
}
|
|
1674
|
-
};
|
|
1504
|
+
var ZERO_NONCE_122 = new Uint8Array(12);
|
|
1505
|
+
var EMPTY_SALT2 = new Uint8Array(0);
|
|
1506
|
+
var X25519_SECRET_KEY_LENGTH2 = 32;
|
|
1507
|
+
var X25519_PUBLIC_KEY_LENGTH2 = 32;
|
|
1508
|
+
var NONCE_LENGTH2 = 24;
|
|
1509
|
+
var WRAP_LENGTH2 = 48;
|
|
1510
|
+
var SLOTS_MAC_LENGTH2 = 32;
|
|
1511
|
+
function concat2(a, b) {
|
|
1512
|
+
const out = new Uint8Array(a.length + b.length);
|
|
1513
|
+
out.set(a, 0);
|
|
1514
|
+
out.set(b, a.length);
|
|
1515
|
+
return out;
|
|
1675
1516
|
}
|
|
1676
|
-
function
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1517
|
+
function bytesKey(bytes) {
|
|
1518
|
+
let s = "";
|
|
1519
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
1520
|
+
s += String.fromCharCode(bytes[i]);
|
|
1521
|
+
}
|
|
1522
|
+
return s;
|
|
1523
|
+
}
|
|
1524
|
+
function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
1525
|
+
if (envelope.scheme !== 1) {
|
|
1526
|
+
throw new EciesSealedPoeError(
|
|
1527
|
+
"UNSUPPORTED_ENC_VERSION",
|
|
1528
|
+
`envelope.scheme=${String(envelope.scheme)} unsupported (expected 1)`
|
|
1529
|
+
);
|
|
1530
|
+
}
|
|
1531
|
+
if (envelope.aead !== "xchacha20-poly1305") {
|
|
1532
|
+
throw new EciesSealedPoeError(
|
|
1533
|
+
"UNSUPPORTED_AEAD_ALG",
|
|
1534
|
+
`envelope.aead=${String(envelope.aead)} unsupported (expected 'xchacha20-poly1305')`
|
|
1535
|
+
);
|
|
1536
|
+
}
|
|
1537
|
+
if (envelope.kem !== "x25519" && envelope.kem !== "mlkem768x25519") {
|
|
1538
|
+
throw new EciesSealedPoeError(
|
|
1539
|
+
"UNSUPPORTED_KEM_ALG",
|
|
1540
|
+
`envelope.kem=${String(envelope.kem)} unsupported (expected 'x25519' or 'mlkem768x25519')`
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
const n = envelope.slots.length;
|
|
1544
|
+
if (n < 1) {
|
|
1545
|
+
throw new EciesSealedPoeError("ENC_SLOTS_EMPTY", `envelope.slots.length=${n} must be >= 1`);
|
|
1546
|
+
}
|
|
1547
|
+
if (n > MAX_SLOTS) {
|
|
1548
|
+
throw new EciesSealedPoeError(
|
|
1549
|
+
"ENC_SLOTS_TOO_MANY",
|
|
1550
|
+
`envelope.slots.length=${n} exceeds MAX_SLOTS=${MAX_SLOTS}`
|
|
1551
|
+
);
|
|
1552
|
+
}
|
|
1553
|
+
if (envelope.nonce.length !== NONCE_LENGTH2) {
|
|
1554
|
+
throw new EciesSealedPoeError(
|
|
1555
|
+
"NONCE_LENGTH_MISMATCH",
|
|
1556
|
+
`envelope.nonce MUST be exactly ${NONCE_LENGTH2} bytes, got ${envelope.nonce.length}`
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
if (envelope.slots_mac.length !== SLOTS_MAC_LENGTH2) {
|
|
1560
|
+
throw new EciesSealedPoeError(
|
|
1561
|
+
"ENC_SLOTS_MAC_INVALID_LENGTH",
|
|
1562
|
+
`envelope.slots_mac MUST be exactly ${SLOTS_MAC_LENGTH2} bytes, got ${envelope.slots_mac.length}`
|
|
1563
|
+
);
|
|
1564
|
+
}
|
|
1565
|
+
const seenKemMaterial = /* @__PURE__ */ new Set();
|
|
1566
|
+
if (envelope.kem === "x25519") {
|
|
1567
|
+
for (let i = 0; i < n; i++) {
|
|
1568
|
+
const slot = envelope.slots[i];
|
|
1569
|
+
if (slot.epk.length !== X25519_PUBLIC_KEY_LENGTH2) {
|
|
1570
|
+
throw new EciesSealedPoeError(
|
|
1571
|
+
"KEM_EPK_LENGTH_MISMATCH",
|
|
1572
|
+
`envelope.slots[${i}].epk MUST be exactly ${X25519_PUBLIC_KEY_LENGTH2} bytes, got ${slot.epk.length}`
|
|
1573
|
+
);
|
|
1690
1574
|
}
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1575
|
+
if (slot.wrap.length !== WRAP_LENGTH2) {
|
|
1576
|
+
throw new EciesSealedPoeError(
|
|
1577
|
+
"WRAP_LENGTH_MISMATCH",
|
|
1578
|
+
`envelope.slots[${i}].wrap MUST be exactly ${WRAP_LENGTH2} bytes, got ${slot.wrap.length}`
|
|
1579
|
+
);
|
|
1580
|
+
}
|
|
1581
|
+
const key = bytesKey(slot.epk);
|
|
1582
|
+
if (seenKemMaterial.has(key)) {
|
|
1583
|
+
throw new EciesSealedPoeError(
|
|
1584
|
+
"ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
|
|
1585
|
+
`envelope.slots[${i}].epk duplicates an earlier slot \u2014 per-slot KEK uniqueness is violated`
|
|
1586
|
+
);
|
|
1587
|
+
}
|
|
1588
|
+
seenKemMaterial.add(key);
|
|
1589
|
+
}
|
|
1590
|
+
} else {
|
|
1591
|
+
for (let i = 0; i < n; i++) {
|
|
1592
|
+
const slot = envelope.slots[i];
|
|
1593
|
+
const enc = joinKemCt(slot.kem_ct);
|
|
1594
|
+
if (enc.length !== MLKEM768X25519_ENC_LENGTH) {
|
|
1595
|
+
throw new EciesSealedPoeError(
|
|
1596
|
+
"KEM_CT_LENGTH_MISMATCH",
|
|
1597
|
+
`envelope.slots[${i}].kem_ct MUST reassemble to exactly ${MLKEM768X25519_ENC_LENGTH} bytes, got ${enc.length}`
|
|
1598
|
+
);
|
|
1599
|
+
}
|
|
1600
|
+
if (slot.wrap.length !== WRAP_LENGTH2) {
|
|
1601
|
+
throw new EciesSealedPoeError(
|
|
1602
|
+
"WRAP_LENGTH_MISMATCH",
|
|
1603
|
+
`envelope.slots[${i}].wrap MUST be exactly ${WRAP_LENGTH2} bytes, got ${slot.wrap.length}`
|
|
1604
|
+
);
|
|
1605
|
+
}
|
|
1606
|
+
const key = bytesKey(enc);
|
|
1607
|
+
if (seenKemMaterial.has(key)) {
|
|
1608
|
+
throw new EciesSealedPoeError(
|
|
1609
|
+
"ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
|
|
1610
|
+
`envelope.slots[${i}].kem_ct duplicates an earlier slot \u2014 per-slot KEK uniqueness is violated`
|
|
1611
|
+
);
|
|
1612
|
+
}
|
|
1613
|
+
seenKemMaterial.add(key);
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
const perSlotBytes = envelope.kem === "x25519" ? X25519_PUBLIC_KEY_LENGTH2 + WRAP_LENGTH2 : MLKEM768X25519_ENC_LENGTH + WRAP_LENGTH2;
|
|
1617
|
+
const decodedEnvelopeBytes = NONCE_LENGTH2 + SLOTS_MAC_LENGTH2 + n * perSlotBytes;
|
|
1618
|
+
if (decodedEnvelopeBytes > MAX_DECODED_ENVELOPE_BYTES) {
|
|
1619
|
+
throw new EciesSealedPoeError(
|
|
1620
|
+
"ENC_ENVELOPE_TOO_LARGE",
|
|
1621
|
+
`decoded envelope size ${decodedEnvelopeBytes} exceeds MAX_DECODED_ENVELOPE_BYTES=${MAX_DECODED_ENVELOPE_BYTES}`
|
|
1622
|
+
);
|
|
1623
|
+
}
|
|
1624
|
+
if (multiPrivKeys !== void 0) {
|
|
1625
|
+
for (let i = 0; i < multiPrivKeys.length; i++) {
|
|
1626
|
+
if (multiPrivKeys[i].length !== X25519_SECRET_KEY_LENGTH2) {
|
|
1627
|
+
throw new EciesSealedPoeError(
|
|
1628
|
+
"INVALID_RECIPIENT_KEY",
|
|
1629
|
+
`recipientSecretKeys[${i}] MUST be exactly ${X25519_SECRET_KEY_LENGTH2} bytes, got ${multiPrivKeys[i].length}`
|
|
1630
|
+
);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
} else if (singlePrivKey !== void 0) {
|
|
1634
|
+
if (singlePrivKey.length !== X25519_SECRET_KEY_LENGTH2) {
|
|
1635
|
+
throw new EciesSealedPoeError(
|
|
1636
|
+
"INVALID_RECIPIENT_KEY",
|
|
1637
|
+
`recipientSecretKey MUST be exactly ${X25519_SECRET_KEY_LENGTH2} bytes, got ${singlePrivKey.length}`
|
|
1638
|
+
);
|
|
1699
1639
|
}
|
|
1700
|
-
};
|
|
1701
|
-
}
|
|
1702
|
-
function cleanBytes(...list) {
|
|
1703
|
-
for (const t of list) {
|
|
1704
|
-
if (Array.isArray(t))
|
|
1705
|
-
for (const b of t)
|
|
1706
|
-
b.fill(0);
|
|
1707
|
-
else
|
|
1708
|
-
t.fill(0);
|
|
1709
1640
|
}
|
|
1710
1641
|
}
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1642
|
+
var ZERO_IKM_32 = new Uint8Array(32);
|
|
1643
|
+
function tryX25519Slot(args) {
|
|
1644
|
+
const salt = concat2(args.slot.epk, args.pubRLocal);
|
|
1645
|
+
let shared;
|
|
1646
|
+
try {
|
|
1647
|
+
shared = x25519Ecdh({
|
|
1648
|
+
secretKey: args.recipientSecretKey,
|
|
1649
|
+
theirPublicKey: args.slot.epk
|
|
1650
|
+
});
|
|
1651
|
+
} catch (e) {
|
|
1652
|
+
if (!(e instanceof X25519LowOrderPointError)) throw e;
|
|
1653
|
+
hkdfSha256({ ikm: ZERO_IKM_32, salt, info: CARDANO_POE_HKDF_INFO_KEK, length: 32 });
|
|
1654
|
+
return null;
|
|
1655
|
+
}
|
|
1656
|
+
const kek = hkdfSha256({ ikm: shared, salt, info: CARDANO_POE_HKDF_INFO_KEK, length: 32 });
|
|
1657
|
+
try {
|
|
1658
|
+
return chacha20Poly1305Decrypt({
|
|
1659
|
+
key: kek,
|
|
1660
|
+
nonce: ZERO_NONCE_122,
|
|
1661
|
+
aad: CARDANO_POE_HKDF_INFO_KEK,
|
|
1662
|
+
ciphertext: args.slot.wrap
|
|
1663
|
+
});
|
|
1664
|
+
} catch (e) {
|
|
1665
|
+
if (!(e instanceof AeadVerificationError)) throw e;
|
|
1666
|
+
return null;
|
|
1667
|
+
}
|
|
1715
1668
|
}
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
const
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
return (
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
return out;
|
|
1669
|
+
function tryMlkem768X25519Slot(args) {
|
|
1670
|
+
const enc = joinKemCt(args.slot.kem_ct);
|
|
1671
|
+
const ss = mlkem768x25519Decapsulate({ secretSeed: args.recipientSecretKey, enc });
|
|
1672
|
+
const kek = hkdfSha256({
|
|
1673
|
+
ikm: ss,
|
|
1674
|
+
salt: xwingKekSalt({ kemCt: enc, pubR: args.pubR }),
|
|
1675
|
+
info: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
|
|
1676
|
+
length: 32
|
|
1677
|
+
});
|
|
1678
|
+
try {
|
|
1679
|
+
return chacha20Poly1305Decrypt({
|
|
1680
|
+
key: kek,
|
|
1681
|
+
nonce: ZERO_NONCE_122,
|
|
1682
|
+
aad: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
|
|
1683
|
+
ciphertext: args.slot.wrap
|
|
1684
|
+
});
|
|
1685
|
+
} catch (e) {
|
|
1686
|
+
if (!(e instanceof AeadVerificationError)) throw e;
|
|
1687
|
+
return null;
|
|
1736
1688
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1689
|
+
}
|
|
1690
|
+
function tryRecipientUnwrapWithIdx(envelope, recipientSecretKey, constantTimeN, slotsAttemptedOut) {
|
|
1691
|
+
const n = envelope.slots.length;
|
|
1692
|
+
let cek = null;
|
|
1693
|
+
let matchedSlotIdx = -1;
|
|
1694
|
+
let cekConflict = false;
|
|
1695
|
+
const recordMatch = (candidate, i) => {
|
|
1696
|
+
if (candidate === null) return;
|
|
1697
|
+
if (cek === null) {
|
|
1698
|
+
cek = candidate;
|
|
1699
|
+
matchedSlotIdx = i;
|
|
1700
|
+
} else if (!compareCt2(candidate, cek)) {
|
|
1701
|
+
cekConflict = true;
|
|
1744
1702
|
}
|
|
1745
1703
|
};
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
const dit = FFTCore(field, { dit: true, ...nttOpts });
|
|
1755
|
-
const NTT = {
|
|
1756
|
-
encode: (r) => {
|
|
1757
|
-
return dif(r);
|
|
1758
|
-
},
|
|
1759
|
-
decode: (r) => {
|
|
1760
|
-
dit(r);
|
|
1761
|
-
for (let i = 0; i < r.length; i++)
|
|
1762
|
-
r[i] = mod(F2 * r[i]);
|
|
1763
|
-
return r;
|
|
1704
|
+
if (envelope.kem === "x25519") {
|
|
1705
|
+
const pubRLocal = x25519PublicKey({ secretKey: recipientSecretKey });
|
|
1706
|
+
for (let i = 0; i < n; i++) {
|
|
1707
|
+
if (slotsAttemptedOut !== void 0) {
|
|
1708
|
+
slotsAttemptedOut.count = i + 1;
|
|
1709
|
+
}
|
|
1710
|
+
recordMatch(tryX25519Slot({ slot: envelope.slots[i], recipientSecretKey, pubRLocal }), i);
|
|
1711
|
+
if (cek !== null && !constantTimeN) break;
|
|
1764
1712
|
}
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
bytesLen,
|
|
1771
|
-
encode: (poly_) => {
|
|
1772
|
-
const poly = poly_;
|
|
1773
|
-
const r = new Uint8Array(bytesLen);
|
|
1774
|
-
for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < poly.length; i++) {
|
|
1775
|
-
buf |= (c.encode(poly[i]) & mask) << bufLen;
|
|
1776
|
-
bufLen += d;
|
|
1777
|
-
for (; bufLen >= 8; bufLen -= 8, buf >>= 8)
|
|
1778
|
-
r[pos++] = buf & getMask(bufLen);
|
|
1779
|
-
}
|
|
1780
|
-
return r;
|
|
1781
|
-
},
|
|
1782
|
-
decode: (bytes) => {
|
|
1783
|
-
const r = newPoly(N2);
|
|
1784
|
-
for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < bytes.length; i++) {
|
|
1785
|
-
buf |= bytes[i] << bufLen;
|
|
1786
|
-
bufLen += 8;
|
|
1787
|
-
for (; bufLen >= d; bufLen -= d, buf >>= d)
|
|
1788
|
-
r[pos++] = c.decode(buf & mask);
|
|
1789
|
-
}
|
|
1790
|
-
return r;
|
|
1713
|
+
} else {
|
|
1714
|
+
const pubR = mlkem768x25519Keygen(recipientSecretKey).publicKey;
|
|
1715
|
+
for (let i = 0; i < n; i++) {
|
|
1716
|
+
if (slotsAttemptedOut !== void 0) {
|
|
1717
|
+
slotsAttemptedOut.count = i + 1;
|
|
1791
1718
|
}
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
return {
|
|
1795
|
-
mod,
|
|
1796
|
-
smod,
|
|
1797
|
-
nttZetas,
|
|
1798
|
-
NTT: {
|
|
1799
|
-
encode: (r) => NTT.encode(r),
|
|
1800
|
-
decode: (r) => NTT.decode(r)
|
|
1801
|
-
},
|
|
1802
|
-
bitsCoder
|
|
1803
|
-
};
|
|
1804
|
-
};
|
|
1805
|
-
var createXofShake = (shake) => (seed, blockLen) => {
|
|
1806
|
-
if (!blockLen)
|
|
1807
|
-
blockLen = shake.blockLen;
|
|
1808
|
-
const _seed = new Uint8Array(seed.length + 2);
|
|
1809
|
-
_seed.set(seed);
|
|
1810
|
-
const seedLen = seed.length;
|
|
1811
|
-
const buf = new Uint8Array(blockLen);
|
|
1812
|
-
let h = shake.create({});
|
|
1813
|
-
let calls = 0;
|
|
1814
|
-
let xofs = 0;
|
|
1815
|
-
return {
|
|
1816
|
-
stats: () => ({ calls, xofs }),
|
|
1817
|
-
get: (x, y) => {
|
|
1818
|
-
_seed[seedLen + 0] = x;
|
|
1819
|
-
_seed[seedLen + 1] = y;
|
|
1820
|
-
h.destroy();
|
|
1821
|
-
h = shake.create({}).update(_seed);
|
|
1822
|
-
calls++;
|
|
1823
|
-
return () => {
|
|
1824
|
-
xofs++;
|
|
1825
|
-
return h.xofInto(buf);
|
|
1826
|
-
};
|
|
1827
|
-
},
|
|
1828
|
-
clean: () => {
|
|
1829
|
-
h.destroy();
|
|
1830
|
-
cleanBytes(buf, _seed);
|
|
1719
|
+
recordMatch(tryMlkem768X25519Slot({ slot: envelope.slots[i], recipientSecretKey, pubR }), i);
|
|
1720
|
+
if (cek !== null && !constantTimeN) break;
|
|
1831
1721
|
}
|
|
1832
|
-
}
|
|
1833
|
-
};
|
|
1834
|
-
var XOF128 = /* @__PURE__ */ createXofShake(shake128);
|
|
1835
|
-
|
|
1836
|
-
// ../../node_modules/.pnpm/@noble+post-quantum@0.6.1/node_modules/@noble/post-quantum/ml-kem.js
|
|
1837
|
-
var N = 256;
|
|
1838
|
-
var Q = 3329;
|
|
1839
|
-
var F = 3303;
|
|
1840
|
-
var ROOT_OF_UNITY = 17;
|
|
1841
|
-
var crystals = /* @__PURE__ */ genCrystals({
|
|
1842
|
-
N,
|
|
1843
|
-
Q,
|
|
1844
|
-
F,
|
|
1845
|
-
ROOT_OF_UNITY,
|
|
1846
|
-
newPoly: (n) => new Uint16Array(n),
|
|
1847
|
-
brvBits: 7});
|
|
1848
|
-
var PARAMS = /* @__PURE__ */ (() => Object.freeze({
|
|
1849
|
-
512: Object.freeze({ N, Q, K: 2, ETA1: 3, ETA2: 2, du: 10, dv: 4, RBGstrength: 128 }),
|
|
1850
|
-
768: Object.freeze({ N, Q, K: 3, ETA1: 2, ETA2: 2, du: 10, dv: 4, RBGstrength: 192 }),
|
|
1851
|
-
1024: Object.freeze({ N, Q, K: 4, ETA1: 2, ETA2: 2, du: 11, dv: 5, RBGstrength: 256 })
|
|
1852
|
-
}))();
|
|
1853
|
-
var compress = (d) => {
|
|
1854
|
-
if (d >= 12)
|
|
1855
|
-
return { encode: (i) => i, decode: (i) => i >= Q ? i - Q : i };
|
|
1856
|
-
const a = 2 ** (d - 1);
|
|
1857
|
-
return {
|
|
1858
|
-
// This only matches standalone Compress_d after bitsCoder masks the result into Z_(2^d).
|
|
1859
|
-
encode: (i) => ((i << d) + Q / 2) / Q,
|
|
1860
|
-
// const decompress = (i: number) => round((Q / 2 ** d) * i);
|
|
1861
|
-
decode: (i) => i * Q + a >>> d
|
|
1862
|
-
};
|
|
1863
|
-
};
|
|
1864
|
-
var byteCoder = (d) => crystals.bitsCoder(d, { encode: (i) => i, decode: (i) => i >= Q ? i - Q : i } );
|
|
1865
|
-
var polyCoder = (d) => d === 12 ? byteCoder(12) : crystals.bitsCoder(d, compress(d));
|
|
1866
|
-
function polyAdd(a_, b_) {
|
|
1867
|
-
const a = a_;
|
|
1868
|
-
const b = b_;
|
|
1869
|
-
for (let i = 0; i < N; i++)
|
|
1870
|
-
a[i] = crystals.mod(a[i] + b[i]);
|
|
1871
|
-
}
|
|
1872
|
-
function polySub(a_, b_) {
|
|
1873
|
-
const a = a_;
|
|
1874
|
-
const b = b_;
|
|
1875
|
-
for (let i = 0; i < N; i++)
|
|
1876
|
-
a[i] = crystals.mod(a[i] - b[i]);
|
|
1722
|
+
}
|
|
1723
|
+
return cek === null ? null : { cek, slotIdx: matchedSlotIdx, cekConflict };
|
|
1877
1724
|
}
|
|
1878
|
-
function
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1725
|
+
function slotsHashBytes(envelope) {
|
|
1726
|
+
return computeSlotsHash({
|
|
1727
|
+
kem: envelope.kem,
|
|
1728
|
+
nonce: envelope.nonce,
|
|
1729
|
+
slots: envelope.slots
|
|
1730
|
+
});
|
|
1882
1731
|
}
|
|
1883
|
-
function
|
|
1884
|
-
const
|
|
1885
|
-
const
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1732
|
+
function eciesSealedPoeUnwrap(args) {
|
|
1733
|
+
const { envelope, ciphertext } = args;
|
|
1734
|
+
const constantTimeN = args.constantTimeN ?? true;
|
|
1735
|
+
const hasSingle = "recipientSecretKey" in args;
|
|
1736
|
+
const hasBundle = "recipientKeyBundle" in args;
|
|
1737
|
+
const multiPrivKeys = hasBundle ? selectBundleSecrets(envelope, args.recipientKeyBundle) : "recipientSecretKeys" in args ? args.recipientSecretKeys : void 0;
|
|
1738
|
+
const hasMulti = multiPrivKeys !== void 0;
|
|
1739
|
+
if (hasSingle === hasMulti) {
|
|
1740
|
+
throw new EciesSealedPoeError(
|
|
1741
|
+
"INVALID_RECIPIENT_KEY",
|
|
1742
|
+
"exactly one of recipientSecretKey / recipientSecretKeys / recipientKeyBundle MUST be supplied"
|
|
1743
|
+
);
|
|
1893
1744
|
}
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
const xof = xof_;
|
|
1898
|
-
const r = new Uint16Array(N);
|
|
1899
|
-
for (let j = 0; j < N; ) {
|
|
1900
|
-
const b = xof();
|
|
1901
|
-
if (b.length % 3)
|
|
1902
|
-
throw new Error("SampleNTT: unaligned block");
|
|
1903
|
-
for (let i = 0; j < N && i + 3 <= b.length; i += 3) {
|
|
1904
|
-
const d1 = (b[i + 0] >> 0 | b[i + 1] << 8) & 4095;
|
|
1905
|
-
const d2 = (b[i + 1] >> 4 | b[i + 2] << 4) & 4095;
|
|
1906
|
-
if (d1 < Q)
|
|
1907
|
-
r[j++] = d1;
|
|
1908
|
-
if (j < N && d2 < Q)
|
|
1909
|
-
r[j++] = d2;
|
|
1745
|
+
if (hasMulti && multiPrivKeys.length === 0) {
|
|
1746
|
+
if (hasBundle) {
|
|
1747
|
+
return { matched: false, reason: "WRONG_RECIPIENT_KEY" };
|
|
1910
1748
|
}
|
|
1749
|
+
throw new EciesSealedPoeError(
|
|
1750
|
+
"INVALID_RECIPIENT_KEY",
|
|
1751
|
+
"recipientSecretKeys MUST be a non-empty array, got length=0"
|
|
1752
|
+
);
|
|
1911
1753
|
}
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1754
|
+
if (hasMulti) {
|
|
1755
|
+
assertEnvelopeStructure(envelope, multiPrivKeys, void 0);
|
|
1756
|
+
} else {
|
|
1757
|
+
assertEnvelopeStructure(envelope, void 0, args.recipientSecretKey);
|
|
1758
|
+
}
|
|
1759
|
+
assertCiphertextWithinBound(ciphertext.length);
|
|
1760
|
+
const slotsHash = slotsHashBytes(envelope);
|
|
1761
|
+
let matchedCek = null;
|
|
1762
|
+
let anyCandidateRecovered = false;
|
|
1763
|
+
if (hasSingle) {
|
|
1764
|
+
const recipientSecretKey = args.recipientSecretKey;
|
|
1765
|
+
const candidate = tryRecipientUnwrapWithIdx(
|
|
1766
|
+
envelope,
|
|
1767
|
+
recipientSecretKey,
|
|
1768
|
+
constantTimeN,
|
|
1769
|
+
args._slotsAttemptedOut
|
|
1770
|
+
);
|
|
1771
|
+
if (candidate === null) {
|
|
1772
|
+
return { matched: false, reason: "WRONG_RECIPIENT_KEY" };
|
|
1773
|
+
}
|
|
1774
|
+
if (candidate.cekConflict) {
|
|
1775
|
+
return { matched: false, reason: "TAMPERED_HEADER" };
|
|
1776
|
+
}
|
|
1777
|
+
const hmacKey = hkdfSha256({
|
|
1778
|
+
ikm: candidate.cek,
|
|
1779
|
+
salt: EMPTY_SALT2,
|
|
1780
|
+
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1781
|
+
length: 32
|
|
1782
|
+
});
|
|
1783
|
+
const slotsMacCalc = hmac(sha256, hmacKey, slotsHash);
|
|
1784
|
+
if (!compareCt2(slotsMacCalc, envelope.slots_mac)) {
|
|
1785
|
+
return { matched: false, reason: "TAMPERED_HEADER" };
|
|
1786
|
+
}
|
|
1787
|
+
matchedCek = candidate.cek;
|
|
1788
|
+
} else {
|
|
1789
|
+
const recipientSecretKeys = multiPrivKeys;
|
|
1790
|
+
let cekConflict = false;
|
|
1791
|
+
for (let k = 0; k < recipientSecretKeys.length; k++) {
|
|
1792
|
+
if (args._privsAttemptedOut !== void 0) {
|
|
1793
|
+
args._privsAttemptedOut.count = k + 1;
|
|
1794
|
+
}
|
|
1795
|
+
if (args._slotsAttemptedOut !== void 0) {
|
|
1796
|
+
args._slotsAttemptedOut.count = 0;
|
|
1797
|
+
}
|
|
1798
|
+
const candidate = tryRecipientUnwrapWithIdx(
|
|
1799
|
+
envelope,
|
|
1800
|
+
recipientSecretKeys[k],
|
|
1801
|
+
constantTimeN,
|
|
1802
|
+
args._slotsAttemptedOut
|
|
1803
|
+
);
|
|
1804
|
+
if (args._slotsAttemptedOut?.perPrivCounts !== void 0) {
|
|
1805
|
+
args._slotsAttemptedOut.perPrivCounts.push(args._slotsAttemptedOut.count);
|
|
1806
|
+
}
|
|
1807
|
+
if (candidate === null) continue;
|
|
1808
|
+
if (candidate.cekConflict) cekConflict = true;
|
|
1809
|
+
const cek = candidate.cek;
|
|
1810
|
+
const hmacKey = hkdfSha256({
|
|
1811
|
+
ikm: cek,
|
|
1812
|
+
salt: EMPTY_SALT2,
|
|
1813
|
+
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1814
|
+
length: 32
|
|
1815
|
+
});
|
|
1816
|
+
const slotsMacCalc = hmac(sha256, hmacKey, slotsHash);
|
|
1817
|
+
if (compareCt2(slotsMacCalc, envelope.slots_mac)) {
|
|
1818
|
+
matchedCek = cek;
|
|
1819
|
+
break;
|
|
1932
1820
|
}
|
|
1821
|
+
anyCandidateRecovered = true;
|
|
1822
|
+
}
|
|
1823
|
+
if (matchedCek !== null && cekConflict) {
|
|
1824
|
+
return { matched: false, reason: "TAMPERED_HEADER" };
|
|
1825
|
+
}
|
|
1826
|
+
if (matchedCek === null) {
|
|
1827
|
+
return {
|
|
1828
|
+
matched: false,
|
|
1829
|
+
reason: anyCandidateRecovered ? "TAMPERED_HEADER" : "WRONG_RECIPIENT_KEY"
|
|
1830
|
+
};
|
|
1933
1831
|
}
|
|
1934
1832
|
}
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1833
|
+
const payloadKey = slotsPayloadKey({ cek: matchedCek, nonce: envelope.nonce });
|
|
1834
|
+
const adContent = adContentSlots({
|
|
1835
|
+
kem: envelope.kem,
|
|
1836
|
+
nonce: envelope.nonce,
|
|
1837
|
+
slotsHash,
|
|
1838
|
+
slotsMac: envelope.slots_mac
|
|
1839
|
+
});
|
|
1840
|
+
try {
|
|
1841
|
+
const plaintext = xchacha20Poly1305Decrypt({
|
|
1842
|
+
key: payloadKey,
|
|
1843
|
+
nonce: envelope.nonce,
|
|
1844
|
+
aad: adContent,
|
|
1845
|
+
ciphertext
|
|
1846
|
+
});
|
|
1847
|
+
return { matched: true, plaintext };
|
|
1848
|
+
} catch (e) {
|
|
1849
|
+
if (!(e instanceof AeadVerificationError)) throw e;
|
|
1850
|
+
return { matched: false, reason: "TAMPERED_CIPHERTEXT" };
|
|
1851
|
+
}
|
|
1943
1852
|
}
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
const
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
return {
|
|
1955
|
-
secretCoder,
|
|
1956
|
-
lengths: {
|
|
1957
|
-
secretKey: secretCoder.bytesLen,
|
|
1958
|
-
publicKey: publicCoder.bytesLen,
|
|
1959
|
-
cipherText: cipherCoder.bytesLen
|
|
1960
|
-
},
|
|
1961
|
-
keygen: (seed) => {
|
|
1962
|
-
abytesDoc(seed, 32, "seed");
|
|
1963
|
-
const seedDst = new Uint8Array(33);
|
|
1964
|
-
seedDst.set(seed);
|
|
1965
|
-
seedDst[32] = K;
|
|
1966
|
-
const seedHash = HASH512(seedDst);
|
|
1967
|
-
const [rho, sigma] = seedCoder.decode(seedHash);
|
|
1968
|
-
const sHat = [];
|
|
1969
|
-
const tHat = [];
|
|
1970
|
-
for (let i = 0; i < K; i++)
|
|
1971
|
-
sHat.push(crystals.NTT.encode(sampleCBD(PRF, sigma, i, ETA1)));
|
|
1972
|
-
const x = XOF(rho);
|
|
1973
|
-
for (let i = 0; i < K; i++) {
|
|
1974
|
-
const e = crystals.NTT.encode(sampleCBD(PRF, sigma, K + i, ETA1));
|
|
1975
|
-
for (let j = 0; j < K; j++) {
|
|
1976
|
-
const aji = SampleNTT(x.get(j, i));
|
|
1977
|
-
polyAdd(e, MultiplyNTTs(aji, sHat[j]));
|
|
1978
|
-
}
|
|
1979
|
-
tHat.push(e);
|
|
1980
|
-
}
|
|
1981
|
-
x.clean();
|
|
1982
|
-
const res = {
|
|
1983
|
-
publicKey: publicCoder.encode([tHat, rho]),
|
|
1984
|
-
secretKey: secretCoder.encode(sHat)
|
|
1985
|
-
};
|
|
1986
|
-
cleanBytes(rho, sigma, sHat, tHat, seedDst, seedHash);
|
|
1987
|
-
return res;
|
|
1988
|
-
},
|
|
1989
|
-
encrypt: (publicKey, msg, seed) => {
|
|
1990
|
-
const [tHat, rho] = publicCoder.decode(publicKey);
|
|
1991
|
-
const rHat = [];
|
|
1992
|
-
for (let i = 0; i < K; i++)
|
|
1993
|
-
rHat.push(crystals.NTT.encode(sampleCBD(PRF, seed, i, ETA1)));
|
|
1994
|
-
const x = XOF(rho);
|
|
1995
|
-
const tmp2 = new Uint16Array(N);
|
|
1996
|
-
const u = [];
|
|
1997
|
-
for (let i = 0; i < K; i++) {
|
|
1998
|
-
const e1 = sampleCBD(PRF, seed, K + i, ETA2);
|
|
1999
|
-
const tmp = new Uint16Array(N);
|
|
2000
|
-
for (let j = 0; j < K; j++) {
|
|
2001
|
-
const aij = SampleNTT(x.get(i, j));
|
|
2002
|
-
polyAdd(tmp, MultiplyNTTs(aij, rHat[j]));
|
|
2003
|
-
}
|
|
2004
|
-
polyAdd(e1, crystals.NTT.decode(tmp));
|
|
2005
|
-
u.push(e1);
|
|
2006
|
-
polyAdd(tmp2, MultiplyNTTs(tHat[i], rHat[i]));
|
|
2007
|
-
cleanBytes(tmp);
|
|
2008
|
-
}
|
|
2009
|
-
x.clean();
|
|
2010
|
-
const e2 = sampleCBD(PRF, seed, 2 * K, ETA2);
|
|
2011
|
-
polyAdd(e2, crystals.NTT.decode(tmp2));
|
|
2012
|
-
const v = poly1.decode(msg);
|
|
2013
|
-
polyAdd(v, e2);
|
|
2014
|
-
cleanBytes(tHat, rHat, tmp2, e2);
|
|
2015
|
-
return cipherCoder.encode([u, v]);
|
|
2016
|
-
},
|
|
2017
|
-
decrypt: (cipherText, privateKey) => {
|
|
2018
|
-
const [u, v] = cipherCoder.decode(cipherText);
|
|
2019
|
-
const sk = secretCoder.decode(privateKey);
|
|
2020
|
-
const tmp = new Uint16Array(N);
|
|
2021
|
-
for (let i = 0; i < K; i++)
|
|
2022
|
-
polyAdd(tmp, MultiplyNTTs(sk[i], crystals.NTT.encode(u[i])));
|
|
2023
|
-
polySub(v, crystals.NTT.decode(tmp));
|
|
2024
|
-
cleanBytes(tmp, sk, u);
|
|
2025
|
-
return poly1.encode(v);
|
|
1853
|
+
function sealedEnvelopeFromParsed(enc) {
|
|
1854
|
+
if (enc.scheme !== 1 || enc.aead !== "xchacha20-poly1305") return null;
|
|
1855
|
+
if (enc.nonce === void 0 || enc.slots_mac === void 0) return null;
|
|
1856
|
+
const slots = enc.slots;
|
|
1857
|
+
if (slots === void 0 || slots.length < 1) return null;
|
|
1858
|
+
if (enc.kem === "x25519") {
|
|
1859
|
+
const x25519Slots = [];
|
|
1860
|
+
for (const s of slots) {
|
|
1861
|
+
if (s.epk === void 0 || s.wrap === void 0) return null;
|
|
1862
|
+
x25519Slots.push({ epk: s.epk, wrap: s.wrap });
|
|
2026
1863
|
}
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
msgRand: msgLen,
|
|
2042
|
-
secretKey: secretCoder.bytesLen
|
|
2043
|
-
});
|
|
2044
|
-
return Object.freeze({
|
|
2045
|
-
info: Object.freeze({ type: "ml-kem" }),
|
|
2046
|
-
lengths: kemLengths,
|
|
2047
|
-
keygen: (seed = randomBytes(seedLen)) => {
|
|
2048
|
-
abytesDoc(seed, seedLen, "seed");
|
|
2049
|
-
const { publicKey, secretKey: sk } = KPKE.keygen(seed.subarray(0, 32));
|
|
2050
|
-
const publicKeyHash = HASH256(publicKey);
|
|
2051
|
-
const secretKey = secretCoder.encode([sk, publicKey, publicKeyHash, seed.subarray(32)]);
|
|
2052
|
-
cleanBytes(sk, publicKeyHash);
|
|
2053
|
-
return {
|
|
2054
|
-
publicKey,
|
|
2055
|
-
secretKey
|
|
2056
|
-
};
|
|
2057
|
-
},
|
|
2058
|
-
getPublicKey: (secretKey) => {
|
|
2059
|
-
const [_sk, publicKey, _publicKeyHash, _z] = secretCoder.decode(secretKey);
|
|
2060
|
-
return Uint8Array.from(publicKey);
|
|
2061
|
-
},
|
|
2062
|
-
encapsulate: (publicKey, msg = randomBytes(msgLen)) => {
|
|
2063
|
-
abytesDoc(publicKey, lengths.publicKey, "publicKey");
|
|
2064
|
-
abytesDoc(msg, msgLen, "message");
|
|
2065
|
-
const eke = publicKey.subarray(0, 384 * opts2.K);
|
|
2066
|
-
const ek = KPKESecretCoder.encode(KPKESecretCoder.decode(copyBytes(eke)));
|
|
2067
|
-
if (!equalBytes(ek, eke)) {
|
|
2068
|
-
cleanBytes(ek);
|
|
2069
|
-
throw new Error("ML-KEM.encapsulate: wrong publicKey modulus");
|
|
2070
|
-
}
|
|
2071
|
-
cleanBytes(ek);
|
|
2072
|
-
const kr = HASH512.create().update(msg).update(HASH256(publicKey)).digest();
|
|
2073
|
-
const cipherText = KPKE.encrypt(publicKey, msg, kr.subarray(32, 64));
|
|
2074
|
-
cleanBytes(kr.subarray(32));
|
|
2075
|
-
return {
|
|
2076
|
-
cipherText,
|
|
2077
|
-
sharedSecret: kr.subarray(0, 32)
|
|
2078
|
-
};
|
|
2079
|
-
},
|
|
2080
|
-
decapsulate: (cipherText, secretKey) => {
|
|
2081
|
-
abytesDoc(secretKey, secretCoder.bytesLen, "secretKey");
|
|
2082
|
-
abytesDoc(cipherText, lengths.cipherText, "cipherText");
|
|
2083
|
-
const k768 = secretCoder.bytesLen - 96;
|
|
2084
|
-
const start = k768 + 32;
|
|
2085
|
-
const test = HASH256(secretKey.subarray(k768 / 2, start));
|
|
2086
|
-
if (!equalBytes(test, secretKey.subarray(start, start + 32)))
|
|
2087
|
-
throw new Error("invalid secretKey: hash check failed");
|
|
2088
|
-
const [sk, publicKey, publicKeyHash, z3] = secretCoder.decode(secretKey);
|
|
2089
|
-
const msg = KPKE.decrypt(cipherText, sk);
|
|
2090
|
-
const kr = HASH512.create().update(msg).update(publicKeyHash).digest();
|
|
2091
|
-
const Khat = kr.subarray(0, 32);
|
|
2092
|
-
const cipherText2 = KPKE.encrypt(publicKey, msg, kr.subarray(32, 64));
|
|
2093
|
-
const isValid = equalBytes(cipherText, cipherText2);
|
|
2094
|
-
const Kbar = KDF.create({ dkLen: 32 }).update(z3).update(cipherText).digest();
|
|
2095
|
-
cleanBytes(msg, cipherText2, !isValid ? Khat : Kbar);
|
|
2096
|
-
return isValid ? Khat : Kbar;
|
|
1864
|
+
return {
|
|
1865
|
+
scheme: 1,
|
|
1866
|
+
aead: "xchacha20-poly1305",
|
|
1867
|
+
kem: "x25519",
|
|
1868
|
+
nonce: enc.nonce,
|
|
1869
|
+
slots: x25519Slots,
|
|
1870
|
+
slots_mac: enc.slots_mac
|
|
1871
|
+
};
|
|
1872
|
+
}
|
|
1873
|
+
if (enc.kem === "mlkem768x25519") {
|
|
1874
|
+
const hybridSlots = [];
|
|
1875
|
+
for (const s of slots) {
|
|
1876
|
+
if (s.kem_ct === void 0 || s.wrap === void 0) return null;
|
|
1877
|
+
hybridSlots.push({ kem_ct: s.kem_ct, wrap: s.wrap });
|
|
2097
1878
|
}
|
|
2098
|
-
|
|
1879
|
+
return {
|
|
1880
|
+
scheme: 1,
|
|
1881
|
+
aead: "xchacha20-poly1305",
|
|
1882
|
+
kem: "mlkem768x25519",
|
|
1883
|
+
nonce: enc.nonce,
|
|
1884
|
+
slots: hybridSlots,
|
|
1885
|
+
slots_mac: enc.slots_mac
|
|
1886
|
+
};
|
|
1887
|
+
}
|
|
1888
|
+
return null;
|
|
2099
1889
|
}
|
|
2100
|
-
|
|
2101
|
-
|
|
1890
|
+
|
|
1891
|
+
// ../poe-standard/src/chunked.ts
|
|
1892
|
+
var UTF8_ENCODER2 = new TextEncoder();
|
|
1893
|
+
function bytesChunkArrayConcat(chunks) {
|
|
1894
|
+
let total = 0;
|
|
1895
|
+
for (const c of chunks) total += c.length;
|
|
1896
|
+
const out = new Uint8Array(total);
|
|
1897
|
+
let offset = 0;
|
|
1898
|
+
for (const c of chunks) {
|
|
1899
|
+
out.set(c, offset);
|
|
1900
|
+
offset += c.length;
|
|
1901
|
+
}
|
|
1902
|
+
return out;
|
|
2102
1903
|
}
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
1904
|
+
function reconstructChunkedUri(chunks) {
|
|
1905
|
+
const merged = bytesChunkArrayConcat(chunks.map((c) => UTF8_ENCODER2.encode(c)));
|
|
1906
|
+
try {
|
|
1907
|
+
const uri = new TextDecoder("utf-8", { fatal: true }).decode(merged);
|
|
1908
|
+
return { ok: true, uri };
|
|
1909
|
+
} catch (cause) {
|
|
1910
|
+
return {
|
|
1911
|
+
ok: false,
|
|
1912
|
+
code: "INVALID_URI",
|
|
1913
|
+
reason: cause instanceof Error ? cause.message : String(cause)
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
var SEVERITY = Object.freeze({
|
|
1918
|
+
// --- Part A ---
|
|
1919
|
+
MALFORMED_CBOR: "error",
|
|
1920
|
+
SCHEMA_TYPE_MISMATCH: "error",
|
|
1921
|
+
SCHEMA_MISSING_REQUIRED: "error",
|
|
1922
|
+
SCHEMA_UNKNOWN_FIELD: "error",
|
|
1923
|
+
SCHEMA_INVALID_LITERAL: "error",
|
|
1924
|
+
SCHEMA_EMPTY_RECORD: "error",
|
|
1925
|
+
HASH_DIGEST_LENGTH_MISMATCH: "error",
|
|
1926
|
+
UNSUPPORTED_HASH_ALG: "error",
|
|
1927
|
+
UNSUPPORTED_MERKLE_COMMIT_ALG: "error",
|
|
1928
|
+
INVALID_URI: "error",
|
|
1929
|
+
CHUNK_TOO_LARGE: "error",
|
|
1930
|
+
UNAUTHENTICATED_CIPHER_FORBIDDEN: "error",
|
|
1931
|
+
UNSUPPORTED_AEAD_ALG: "error",
|
|
1932
|
+
NONCE_LENGTH_MISMATCH: "error",
|
|
1933
|
+
UNSUPPORTED_ENVELOPE_SCHEME: "error",
|
|
1934
|
+
ENC_SLOTS_EMPTY: "error",
|
|
1935
|
+
ENC_SLOT_INVALID_SHAPE: "error",
|
|
1936
|
+
ENC_SLOTS_DUPLICATE_KEM_MATERIAL: "error",
|
|
1937
|
+
ENC_SLOTS_TOO_MANY: "error",
|
|
1938
|
+
ENC_ENVELOPE_TOO_LARGE: "error",
|
|
1939
|
+
UNSUPPORTED_KEM_ALG: "error",
|
|
1940
|
+
ENC_KEM_REQUIRED: "error",
|
|
1941
|
+
KEM_EPK_LENGTH_MISMATCH: "error",
|
|
1942
|
+
KEM_CT_LENGTH_MISMATCH: "error",
|
|
1943
|
+
WRAP_LENGTH_MISMATCH: "error",
|
|
1944
|
+
ENC_SLOTS_MAC_INVALID_LENGTH: "error",
|
|
1945
|
+
ENC_SLOTS_MAC_REQUIRED: "error",
|
|
1946
|
+
ENC_SLOTS_REQUIRED: "error",
|
|
1947
|
+
ENC_EXCLUSIVITY_VIOLATION: "error",
|
|
1948
|
+
ENC_NO_KEY_PATH: "error",
|
|
1949
|
+
ENC_REQUIRES_CONTENT_HASH: "error",
|
|
1950
|
+
ENC_PASSPHRASE_ALG_UNSUPPORTED: "error",
|
|
1951
|
+
ENC_PASSPHRASE_SALT_TOO_SHORT: "error",
|
|
1952
|
+
ENC_PASSPHRASE_SALT_TOO_LONG: "error",
|
|
1953
|
+
ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW: "error",
|
|
1954
|
+
ENC_PASSPHRASE_PARAMS_EXCEED_POLICY: "error",
|
|
1955
|
+
MALFORMED_SIG_COSE_SIGN1: "error",
|
|
1956
|
+
SIGNATURE_UNSUPPORTED: "info",
|
|
1957
|
+
SIG_ENTRY_INVALID_SHAPE: "error",
|
|
1958
|
+
SIG_ENTRY_KID_COSE_KEY_CONFLICT: "error",
|
|
1959
|
+
SIG_PRIVATE_KEY_LEAKED: "error",
|
|
1960
|
+
SUPERSEDES_TX_INVALID_LENGTH: "error",
|
|
1961
|
+
EXTENSION_UNSUPPORTED_CRITICAL: "error",
|
|
1962
|
+
CRIT_SHAPE_INVALID: "error",
|
|
1963
|
+
// --- Part B ---
|
|
1964
|
+
METADATA_NOT_FOUND: "error",
|
|
1965
|
+
INSUFFICIENT_CONFIRMATIONS: "info",
|
|
1966
|
+
SIGNATURE_INVALID: "error",
|
|
1967
|
+
SIGNER_KEY_UNRESOLVED: "error",
|
|
1968
|
+
WALLET_ADDRESS_MISMATCH: "error",
|
|
1969
|
+
URI_TARGET_FORBIDDEN: "error",
|
|
1970
|
+
URI_INTEGRITY_MISMATCH: "error",
|
|
1971
|
+
URI_FETCH_FAILED: "warning",
|
|
1972
|
+
CONTENT_UNAVAILABLE: "error",
|
|
1973
|
+
CIPHERTEXT_UNAVAILABLE: "error",
|
|
1974
|
+
PROVIDER_UNAVAILABLE: "error",
|
|
1975
|
+
SERVICE_INDEPENDENCE_VIOLATION: "error",
|
|
1976
|
+
WRONG_DECRYPTION_INPUT_SHAPE: "error",
|
|
1977
|
+
WRONG_RECIPIENT_KEY: "error",
|
|
1978
|
+
TAMPERED_HEADER: "error",
|
|
1979
|
+
TAMPERED_CIPHERTEXT: "error",
|
|
1980
|
+
KDF_DERIVATION_FAILED: "error",
|
|
1981
|
+
SCHEMA_MERKLE_LEAF_COUNT_MISMATCH: "error",
|
|
1982
|
+
SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED: "error",
|
|
1983
|
+
SCHEMA_MERKLE_LEAVES_MALFORMED: "error",
|
|
1984
|
+
MERKLE_ROOT_MISMATCH: "error",
|
|
1985
|
+
MERKLE_LEAVES_UNAVAILABLE: "warning",
|
|
1986
|
+
MERKLE_LEAVES_INFORMATIVE_FORM: "info",
|
|
1987
|
+
// Dual-severity — default reading is `info`; the verifier promotes to
|
|
1988
|
+
// `error` for merkle-only records (no `items[]` content claim was
|
|
1989
|
+
// validated in the same record).
|
|
1990
|
+
MERKLE_UNSUPPORTED: "info",
|
|
1991
|
+
// Dual-severity — default reading is `info` (render mode); strict
|
|
1992
|
+
// end-to-end verifiers promote to `error`.
|
|
1993
|
+
OUT_OF_PROFILE_SKIPPED: "info"
|
|
2113
1994
|
});
|
|
2114
|
-
var ml_kem768 = /* @__PURE__ */ (() => mk(PARAMS[768]))();
|
|
2115
1995
|
|
|
2116
|
-
//
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
1996
|
+
// ../poe-standard/src/validator.ts
|
|
1997
|
+
var HASH_ALG_LENGTHS = {
|
|
1998
|
+
"sha2-256": 32,
|
|
1999
|
+
"blake2b-256": 32
|
|
2000
|
+
};
|
|
2001
|
+
var MERKLE_COMMIT_ALG_LENGTHS = {
|
|
2002
|
+
"rfc9162-sha256": 32
|
|
2003
|
+
};
|
|
2004
|
+
var AEAD_NONCE_LENGTHS = {
|
|
2005
|
+
"xchacha20-poly1305": 24
|
|
2006
|
+
};
|
|
2007
|
+
var UNAUTHENTICATED_CIPHER_RE = /(?:^|[-_])(?:cbc|ctr|ecb|cfb|ofb)(?:[-_]|\n?$)|^(?:rc4|des|3des)(?:[-_]|\n?$)/i;
|
|
2008
|
+
var KEM_SLOT_DESCRIPTORS = {
|
|
2009
|
+
x25519: { field: "epk", fieldLength: 32, wrapLength: 48 },
|
|
2010
|
+
mlkem768x25519: { field: "kem_ct", fieldLength: 1120, wrapLength: 48 }
|
|
2011
|
+
};
|
|
2012
|
+
var KEM_FIELD_LENGTH_CODE = {
|
|
2013
|
+
epk: "KEM_EPK_LENGTH_MISMATCH",
|
|
2014
|
+
kem_ct: "KEM_CT_LENGTH_MISMATCH"
|
|
2015
|
+
};
|
|
2016
|
+
var NONCE_LENGTH = 24;
|
|
2017
|
+
var SLOTS_MAC_LENGTH = 32;
|
|
2018
|
+
var PASSPHRASE_KDF_ALGS = /* @__PURE__ */ new Set(["argon2id"]);
|
|
2019
|
+
var KNOWN_SIG_ALG_IDS = /* @__PURE__ */ new Set([-8, -19]);
|
|
2020
|
+
function validatePoeRecord(bytes) {
|
|
2021
|
+
let decoded;
|
|
2022
|
+
try {
|
|
2023
|
+
decoded = decodeCanonicalCbor(bytes);
|
|
2024
|
+
} catch (cause) {
|
|
2025
|
+
return {
|
|
2026
|
+
ok: false,
|
|
2027
|
+
issues: [
|
|
2028
|
+
{
|
|
2029
|
+
code: "MALFORMED_CBOR",
|
|
2030
|
+
path: [],
|
|
2031
|
+
message: cause instanceof Error ? cause.message : String(cause),
|
|
2032
|
+
severity: "error"
|
|
2033
|
+
}
|
|
2034
|
+
]
|
|
2133
2035
|
};
|
|
2134
2036
|
}
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
}
|
|
2037
|
+
const parse = PoeRecordSchema.safeParse(decoded);
|
|
2038
|
+
if (!parse.success) {
|
|
2039
|
+
const issues = parse.error.issues.map((issue2) => mapZodIssue(issue2, decoded)).sort(compareIssuePath);
|
|
2040
|
+
return { ok: false, issues };
|
|
2041
|
+
}
|
|
2042
|
+
const record = parse.data;
|
|
2043
|
+
const errors = [];
|
|
2044
|
+
const warnings = [];
|
|
2045
|
+
const info = [];
|
|
2046
|
+
const itemsLen = Array.isArray(record.items) ? record.items.length : 0;
|
|
2047
|
+
const merkleLen = Array.isArray(record.merkle) ? record.merkle.length : 0;
|
|
2048
|
+
if (itemsLen === 0 && merkleLen === 0) {
|
|
2049
|
+
errors.push(
|
|
2050
|
+
issue(
|
|
2051
|
+
"SCHEMA_EMPTY_RECORD",
|
|
2052
|
+
[],
|
|
2053
|
+
"record must carry at least one of items[] or merkle[] non-empty"
|
|
2054
|
+
)
|
|
2055
|
+
);
|
|
2056
|
+
}
|
|
2057
|
+
const decodedTopKeys = topLevelKeysOf(decoded);
|
|
2058
|
+
const critShapeInvalidIndices = checkCritShape(record, decodedTopKeys, errors);
|
|
2059
|
+
for (const k of decodedTopKeys) {
|
|
2060
|
+
if (TOP_LEVEL_BASE_KEYS.has(k)) continue;
|
|
2061
|
+
if (isExtensionKey(k)) continue;
|
|
2062
|
+
errors.push(issue("SCHEMA_UNKNOWN_FIELD", [k], `unknown top-level field: ${k}`));
|
|
2063
|
+
}
|
|
2064
|
+
if (Array.isArray(record.crit)) {
|
|
2065
|
+
for (let i = 0; i < record.crit.length; i++) {
|
|
2066
|
+
if (critShapeInvalidIndices.has(i)) continue;
|
|
2067
|
+
const critName = record.crit[i];
|
|
2068
|
+
errors.push(
|
|
2069
|
+
issue(
|
|
2070
|
+
"EXTENSION_UNSUPPORTED_CRITICAL",
|
|
2071
|
+
["crit", i],
|
|
2072
|
+
`crit lists extension '${critName}' that this validator does not implement`
|
|
2073
|
+
)
|
|
2074
|
+
);
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
for (let i = 0; i < (record.items ?? []).length; i++) {
|
|
2078
|
+
const item = record.items[i];
|
|
2079
|
+
checkItemHashes(item, i, errors);
|
|
2080
|
+
if (item.uris) checkItemUris(item.uris, ["items", i, "uris"], errors);
|
|
2081
|
+
if (item.enc !== void 0) checkItemEnc(item, i, errors);
|
|
2082
|
+
}
|
|
2083
|
+
for (let i = 0; i < (record.merkle ?? []).length; i++) {
|
|
2084
|
+
const commit = record.merkle[i];
|
|
2085
|
+
checkMerkleCommit(commit, i, errors);
|
|
2086
|
+
}
|
|
2087
|
+
if (record.sigs) {
|
|
2088
|
+
for (let i = 0; i < record.sigs.length; i++) {
|
|
2089
|
+
checkSigEntry(record.sigs[i], i, errors, info);
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
if (errors.length > 0) {
|
|
2093
|
+
return { ok: false, issues: errors.sort(compareIssuePath) };
|
|
2094
|
+
}
|
|
2095
|
+
const result = {
|
|
2096
|
+
ok: true,
|
|
2097
|
+
record
|
|
2098
|
+
};
|
|
2099
|
+
if (warnings.length > 0) result.warnings = warnings.sort(compareIssuePath);
|
|
2100
|
+
if (info.length > 0) result.info = info.sort(compareIssuePath);
|
|
2101
|
+
return result;
|
|
2102
|
+
}
|
|
2103
|
+
function mapZodIssue(zissue, decoded) {
|
|
2104
|
+
const path = zissue.path;
|
|
2105
|
+
const explicit = zissue.params?.code;
|
|
2106
|
+
if (explicit !== void 0) {
|
|
2107
|
+
return issue(explicit, path, zissue.message);
|
|
2108
|
+
}
|
|
2109
|
+
const inSigsEntry = path.length >= 2 && path[0] === "sigs" && typeof path[1] === "number";
|
|
2110
|
+
const isInSlotEntry = (() => {
|
|
2111
|
+
if (path.length >= 5 && path[0] === "items" && typeof path[1] === "number" && path[2] === "enc" && path[3] === "slots" && typeof path[4] === "number") {
|
|
2112
|
+
return true;
|
|
2113
|
+
}
|
|
2114
|
+
if (path.length >= 2 && path[0] === "slots" && typeof path[1] === "number") {
|
|
2115
|
+
return true;
|
|
2116
|
+
}
|
|
2117
|
+
return false;
|
|
2118
|
+
})();
|
|
2119
|
+
const valueAtIssue = valueAtPath(decoded, path);
|
|
2120
|
+
const isMissing = valueAtIssue === void 0;
|
|
2121
|
+
switch (zissue.code) {
|
|
2122
|
+
case "invalid_type":
|
|
2123
|
+
if (isInSlotEntry) return issue("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
|
|
2124
|
+
if (isMissing) {
|
|
2125
|
+
if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2126
|
+
return issue("SCHEMA_MISSING_REQUIRED", path, zissue.message);
|
|
2127
|
+
}
|
|
2128
|
+
if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2129
|
+
return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
|
|
2130
|
+
case "invalid_value":
|
|
2131
|
+
if (path.length === 1 && path[0] === "v") {
|
|
2132
|
+
return issue(
|
|
2133
|
+
isMissing ? "SCHEMA_MISSING_REQUIRED" : "SCHEMA_INVALID_LITERAL",
|
|
2134
|
+
path,
|
|
2135
|
+
zissue.message
|
|
2136
|
+
);
|
|
2137
|
+
}
|
|
2138
|
+
return issue("SCHEMA_INVALID_LITERAL", path, zissue.message);
|
|
2139
|
+
case "unrecognized_keys":
|
|
2140
|
+
if (isInSlotEntry) return issue("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
|
|
2141
|
+
if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2142
|
+
return issue("SCHEMA_UNKNOWN_FIELD", path, zissue.message);
|
|
2143
|
+
case "invalid_format":
|
|
2144
|
+
case "too_big":
|
|
2145
|
+
case "too_small":
|
|
2146
|
+
if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2147
|
+
return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
|
|
2148
|
+
case "invalid_union":
|
|
2149
|
+
case "invalid_key":
|
|
2150
|
+
case "invalid_element":
|
|
2151
|
+
case "custom":
|
|
2152
|
+
default:
|
|
2153
|
+
if (isInSlotEntry) return issue("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
|
|
2154
|
+
if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2155
|
+
return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
|
|
2156
|
+
}
|
|
2140
2157
|
}
|
|
2141
|
-
function
|
|
2142
|
-
const
|
|
2143
|
-
if (
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
if (ek)
|
|
2160
|
-
cleanBytes(ek);
|
|
2161
|
-
}
|
|
2162
|
-
},
|
|
2163
|
-
decapsulate(cipherText, secretKey) {
|
|
2164
|
-
const res = curve.getSharedSecret(secretKey, cipherText);
|
|
2165
|
-
return curve.lengths.publicKeyHasPrefix ? res.subarray(1) : res;
|
|
2158
|
+
function checkItemHashes(item, idx, errors) {
|
|
2159
|
+
const entries = Object.entries(item.hashes);
|
|
2160
|
+
if (entries.length === 0) {
|
|
2161
|
+
errors.push(
|
|
2162
|
+
issue(
|
|
2163
|
+
"SCHEMA_TYPE_MISMATCH",
|
|
2164
|
+
["items", idx, "hashes"],
|
|
2165
|
+
"hashes must be a non-empty CBOR map of <alg-id> -> <digest>"
|
|
2166
|
+
)
|
|
2167
|
+
);
|
|
2168
|
+
return;
|
|
2169
|
+
}
|
|
2170
|
+
for (const [alg, digest] of entries) {
|
|
2171
|
+
if (!(alg in HASH_ALG_LENGTHS)) {
|
|
2172
|
+
errors.push(
|
|
2173
|
+
issue("UNSUPPORTED_HASH_ALG", ["items", idx, "hashes", alg], `unknown hash alg: ${alg}`)
|
|
2174
|
+
);
|
|
2175
|
+
continue;
|
|
2166
2176
|
}
|
|
2167
|
-
|
|
2177
|
+
const expected = HASH_ALG_LENGTHS[alg];
|
|
2178
|
+
if (digest.length !== expected) {
|
|
2179
|
+
errors.push(
|
|
2180
|
+
issue(
|
|
2181
|
+
"HASH_DIGEST_LENGTH_MISMATCH",
|
|
2182
|
+
["items", idx, "hashes", alg],
|
|
2183
|
+
`hashes['${alg}'] digest length ${digest.length} != ${expected}`
|
|
2184
|
+
)
|
|
2185
|
+
);
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2168
2188
|
}
|
|
2169
|
-
function
|
|
2170
|
-
|
|
2171
|
-
if (typeof i.lengths[name] !== "number")
|
|
2172
|
-
throw new Error("wrong length: " + name);
|
|
2173
|
-
return i.lengths[name];
|
|
2174
|
-
}));
|
|
2189
|
+
function checkItemUris(uris, basePath, errors) {
|
|
2190
|
+
uris.forEach((chunks, ui) => validateOneUri(chunks, [...basePath, ui], errors));
|
|
2175
2191
|
}
|
|
2176
|
-
function
|
|
2177
|
-
|
|
2192
|
+
function validateOneUri(chunks, path, errors) {
|
|
2193
|
+
const reconstructed = reconstructChunkedUri(chunks);
|
|
2194
|
+
if (!reconstructed.ok) {
|
|
2195
|
+
errors.push(issue(reconstructed.code, path, reconstructed.reason));
|
|
2196
|
+
return;
|
|
2197
|
+
}
|
|
2198
|
+
const uri = reconstructed.uri;
|
|
2199
|
+
if (uri.includes("#")) {
|
|
2200
|
+
errors.push(
|
|
2201
|
+
issue("INVALID_URI", path, "URI contains a fragment identifier ('#'), which is forbidden")
|
|
2202
|
+
);
|
|
2203
|
+
return;
|
|
2204
|
+
}
|
|
2205
|
+
const sepIdx = uri.indexOf("://");
|
|
2206
|
+
if (sepIdx <= 0 || !/^[a-z][a-z0-9+.-]*$/i.test(uri.slice(0, sepIdx))) {
|
|
2207
|
+
errors.push(
|
|
2208
|
+
issue("INVALID_URI", path, "URI is not absolute (missing scheme://hierarchical-part)")
|
|
2209
|
+
);
|
|
2210
|
+
return;
|
|
2211
|
+
}
|
|
2212
|
+
const scheme = uri.slice(0, sepIdx).toLowerCase();
|
|
2213
|
+
const rest = uri.slice(sepIdx + "://".length);
|
|
2214
|
+
if (scheme === "ar") {
|
|
2215
|
+
if (!/^ar:\/\/[A-Za-z0-9_-]{43}$/.test("ar://" + rest)) {
|
|
2216
|
+
errors.push(
|
|
2217
|
+
issue(
|
|
2218
|
+
"INVALID_URI",
|
|
2219
|
+
path,
|
|
2220
|
+
"ar:// URI does not match `^ar://[A-Za-z0-9_-]{43}$` (43-char base64url txid, no path/query/fragment)"
|
|
2221
|
+
)
|
|
2222
|
+
);
|
|
2223
|
+
}
|
|
2224
|
+
return;
|
|
2225
|
+
}
|
|
2226
|
+
if (scheme === "ipfs") {
|
|
2227
|
+
const slashIdx = rest.indexOf("/");
|
|
2228
|
+
const cid = slashIdx === -1 ? rest : rest.slice(0, slashIdx);
|
|
2229
|
+
if (!validateCidProfile(cid)) {
|
|
2230
|
+
errors.push(
|
|
2231
|
+
issue("INVALID_URI", path, "ipfs:// URI is not a valid CID under the Label 309 profile")
|
|
2232
|
+
);
|
|
2233
|
+
}
|
|
2234
|
+
return;
|
|
2235
|
+
}
|
|
2236
|
+
errors.push(
|
|
2237
|
+
issue("INVALID_URI", path, "unsupported URI scheme; v1 PoE URI set is {ar://, ipfs://}")
|
|
2238
|
+
);
|
|
2178
2239
|
}
|
|
2179
|
-
function
|
|
2180
|
-
const
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2240
|
+
function checkItemEnc(item, idx, errors) {
|
|
2241
|
+
const hasContentHash = Object.keys(item.hashes).some((alg) => alg in HASH_ALG_LENGTHS);
|
|
2242
|
+
if (!hasContentHash) {
|
|
2243
|
+
errors.push(
|
|
2244
|
+
issue(
|
|
2245
|
+
"ENC_REQUIRES_CONTENT_HASH",
|
|
2246
|
+
["items", idx, "enc"],
|
|
2247
|
+
"item carries `enc` but `hashes` has no content-hash entry (sha2-256 or blake2b-256)"
|
|
2248
|
+
)
|
|
2249
|
+
);
|
|
2250
|
+
return;
|
|
2251
|
+
}
|
|
2252
|
+
const encParse = EncryptionEnvelopeSchema.safeParse(item.enc);
|
|
2253
|
+
if (!encParse.success) {
|
|
2254
|
+
for (const zissue of encParse.error.issues) {
|
|
2255
|
+
const mapped = mapZodIssue(zissue, item.enc);
|
|
2256
|
+
errors.push({
|
|
2257
|
+
...mapped,
|
|
2258
|
+
path: ["items", idx, "enc", ...mapped.path]
|
|
2259
|
+
});
|
|
2260
|
+
}
|
|
2261
|
+
return;
|
|
2262
|
+
}
|
|
2263
|
+
const enc = encParse.data;
|
|
2264
|
+
const basePath = ["items", idx, "enc"];
|
|
2265
|
+
if (typeof enc.scheme !== "number" || !Number.isInteger(enc.scheme) || enc.scheme !== 1) {
|
|
2266
|
+
errors.push(
|
|
2267
|
+
issue(
|
|
2268
|
+
"UNSUPPORTED_ENVELOPE_SCHEME",
|
|
2269
|
+
[...basePath, "scheme"],
|
|
2270
|
+
`enc.scheme must be the unsigned integer 1; got ${String(enc.scheme)}`
|
|
2271
|
+
)
|
|
2272
|
+
);
|
|
2273
|
+
}
|
|
2274
|
+
if (UNAUTHENTICATED_CIPHER_RE.test(enc.aead)) {
|
|
2275
|
+
errors.push(
|
|
2276
|
+
issue(
|
|
2277
|
+
"UNAUTHENTICATED_CIPHER_FORBIDDEN",
|
|
2278
|
+
[...basePath, "aead"],
|
|
2279
|
+
`'${enc.aead}' is an unauthenticated cipher; Label 309 mandates an authenticated (AEAD) cipher`
|
|
2280
|
+
)
|
|
2281
|
+
);
|
|
2282
|
+
return;
|
|
2283
|
+
}
|
|
2284
|
+
if (!(enc.aead in AEAD_NONCE_LENGTHS)) {
|
|
2285
|
+
errors.push(
|
|
2286
|
+
issue("UNSUPPORTED_AEAD_ALG", [...basePath, "aead"], `unknown aead alg: ${enc.aead}`)
|
|
2287
|
+
);
|
|
2288
|
+
return;
|
|
2289
|
+
}
|
|
2290
|
+
const expectedNonceLen = AEAD_NONCE_LENGTHS[enc.aead];
|
|
2291
|
+
if (enc.nonce.length !== expectedNonceLen) {
|
|
2292
|
+
errors.push(
|
|
2293
|
+
issue(
|
|
2294
|
+
"NONCE_LENGTH_MISMATCH",
|
|
2295
|
+
[...basePath, "nonce"],
|
|
2296
|
+
`nonce length ${enc.nonce.length} != ${expectedNonceLen} for ${enc.aead}`
|
|
2297
|
+
)
|
|
2298
|
+
);
|
|
2299
|
+
}
|
|
2300
|
+
if (enc.kem !== void 0 && !(enc.kem in KEM_SLOT_DESCRIPTORS)) {
|
|
2301
|
+
errors.push(issue("UNSUPPORTED_KEM_ALG", [...basePath, "kem"], `unknown kem alg: ${enc.kem}`));
|
|
2302
|
+
}
|
|
2303
|
+
const hasSlots = enc.slots !== void 0;
|
|
2304
|
+
const hasSlotsMac = enc.slots_mac !== void 0;
|
|
2305
|
+
const hasPassphrase = enc.passphrase !== void 0;
|
|
2306
|
+
if (hasSlots && hasPassphrase) {
|
|
2307
|
+
errors.push(
|
|
2308
|
+
issue("ENC_EXCLUSIVITY_VIOLATION", basePath, "enc combines slots with passphrase; pick one")
|
|
2309
|
+
);
|
|
2310
|
+
}
|
|
2311
|
+
if (hasSlots && !hasSlotsMac) {
|
|
2312
|
+
errors.push(
|
|
2313
|
+
issue("ENC_SLOTS_MAC_REQUIRED", basePath, "enc.slots present but enc.slots_mac absent")
|
|
2314
|
+
);
|
|
2315
|
+
}
|
|
2316
|
+
if (hasSlotsMac && !hasSlots) {
|
|
2317
|
+
errors.push(
|
|
2318
|
+
issue("ENC_SLOTS_REQUIRED", basePath, "enc.slots_mac present but enc.slots absent")
|
|
2319
|
+
);
|
|
2320
|
+
}
|
|
2321
|
+
if (hasSlots && enc.kem === void 0) {
|
|
2322
|
+
errors.push(issue("ENC_KEM_REQUIRED", basePath, "enc.slots present but enc.kem absent"));
|
|
2323
|
+
}
|
|
2324
|
+
if (!hasSlots && !hasPassphrase) {
|
|
2325
|
+
errors.push(
|
|
2326
|
+
issue(
|
|
2327
|
+
"ENC_NO_KEY_PATH",
|
|
2328
|
+
basePath,
|
|
2329
|
+
"enc requires either slots or passphrase \u2014 no on-chain key path otherwise"
|
|
2330
|
+
)
|
|
2331
|
+
);
|
|
2332
|
+
}
|
|
2333
|
+
if (hasSlots) {
|
|
2334
|
+
const slotCount = enc.slots.length;
|
|
2335
|
+
if (slotCount < 1) {
|
|
2336
|
+
errors.push(
|
|
2337
|
+
issue("ENC_SLOTS_EMPTY", [...basePath, "slots"], `slots length ${slotCount} < 1`)
|
|
2338
|
+
);
|
|
2339
|
+
} else if (slotCount > MAX_SLOTS) {
|
|
2340
|
+
errors.push(
|
|
2341
|
+
issue(
|
|
2342
|
+
"ENC_SLOTS_TOO_MANY",
|
|
2343
|
+
[...basePath, "slots"],
|
|
2344
|
+
`slots length ${slotCount} exceeds MAX_SLOTS=${MAX_SLOTS}`
|
|
2345
|
+
)
|
|
2346
|
+
);
|
|
2347
|
+
} else {
|
|
2348
|
+
const descriptor = enc.kem !== void 0 ? KEM_SLOT_DESCRIPTORS[enc.kem] : void 0;
|
|
2349
|
+
if (descriptor !== void 0) {
|
|
2350
|
+
const rawSlotKeys = rawSlotKeySets(item.enc);
|
|
2351
|
+
const seenKemMaterial = /* @__PURE__ */ new Set();
|
|
2352
|
+
enc.slots.forEach((slot, si) => {
|
|
2353
|
+
const slotPath = [...basePath, "slots", si];
|
|
2354
|
+
checkSlotShape(
|
|
2355
|
+
slot,
|
|
2356
|
+
rawSlotKeys[si] ?? /* @__PURE__ */ new Set(),
|
|
2357
|
+
descriptor,
|
|
2358
|
+
enc.kem,
|
|
2359
|
+
slotPath,
|
|
2360
|
+
errors
|
|
2361
|
+
);
|
|
2362
|
+
const material = slotKemMaterial(slot, descriptor);
|
|
2363
|
+
if (material !== void 0) {
|
|
2364
|
+
const key = bytesToHex(material);
|
|
2365
|
+
if (seenKemMaterial.has(key)) {
|
|
2366
|
+
errors.push(
|
|
2367
|
+
issue(
|
|
2368
|
+
"ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
|
|
2369
|
+
[...slotPath, descriptor.field],
|
|
2370
|
+
`slot ${si} ${descriptor.field} duplicates an earlier slot \u2014 per-slot KEK uniqueness is violated`
|
|
2371
|
+
)
|
|
2372
|
+
);
|
|
2373
|
+
} else {
|
|
2374
|
+
seenKemMaterial.add(key);
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
});
|
|
2378
|
+
const perSlotBytes = descriptor.fieldLength + descriptor.wrapLength;
|
|
2379
|
+
const decodedEnvelopeBytes = NONCE_LENGTH + SLOTS_MAC_LENGTH + slotCount * perSlotBytes;
|
|
2380
|
+
if (decodedEnvelopeBytes > MAX_DECODED_ENVELOPE_BYTES) {
|
|
2381
|
+
errors.push(
|
|
2382
|
+
issue(
|
|
2383
|
+
"ENC_ENVELOPE_TOO_LARGE",
|
|
2384
|
+
[...basePath, "slots"],
|
|
2385
|
+
`decoded envelope size ${decodedEnvelopeBytes} exceeds MAX_DECODED_ENVELOPE_BYTES=${MAX_DECODED_ENVELOPE_BYTES}`
|
|
2386
|
+
)
|
|
2387
|
+
);
|
|
2388
|
+
}
|
|
2202
2389
|
}
|
|
2203
|
-
ok = true;
|
|
2204
|
-
return { secretKey, publicKey };
|
|
2205
|
-
} finally {
|
|
2206
|
-
cleanBytes(expandedSeed, expanded, keySecret);
|
|
2207
|
-
if (!ok)
|
|
2208
|
-
cleanBytes(secretKey);
|
|
2209
2390
|
}
|
|
2210
2391
|
}
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2392
|
+
if (hasPassphrase) {
|
|
2393
|
+
const pp = enc.passphrase;
|
|
2394
|
+
const ppPath = [...basePath, "passphrase"];
|
|
2395
|
+
if (!PASSPHRASE_KDF_ALGS.has(pp.alg)) {
|
|
2396
|
+
errors.push(
|
|
2397
|
+
issue(
|
|
2398
|
+
"ENC_PASSPHRASE_ALG_UNSUPPORTED",
|
|
2399
|
+
[...ppPath, "alg"],
|
|
2400
|
+
`unknown passphrase kdf alg: ${pp.alg}`
|
|
2401
|
+
)
|
|
2402
|
+
);
|
|
2403
|
+
return;
|
|
2404
|
+
}
|
|
2405
|
+
if (pp.alg === "argon2id") {
|
|
2406
|
+
const allowed = /* @__PURE__ */ new Set(["m", "t", "p"]);
|
|
2407
|
+
for (const k of Object.keys(pp.params)) {
|
|
2408
|
+
if (!allowed.has(k)) {
|
|
2409
|
+
errors.push(
|
|
2410
|
+
issue(
|
|
2411
|
+
"SCHEMA_UNKNOWN_FIELD",
|
|
2412
|
+
[...ppPath, "params", k],
|
|
2413
|
+
`unknown argon2id params field: ${k}`
|
|
2414
|
+
)
|
|
2415
|
+
);
|
|
2416
|
+
}
|
|
2224
2417
|
}
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
const msgCoder = splitLengths(rawKems, "msg");
|
|
2237
|
-
anumber(realMsgLen);
|
|
2238
|
-
const lengths = Object.freeze({
|
|
2239
|
-
...keys.info.lengths,
|
|
2240
|
-
msg: realMsgLen,
|
|
2241
|
-
msgRand: msgCoder.bytesLen,
|
|
2242
|
-
cipherText: ctCoder.bytesLen
|
|
2243
|
-
});
|
|
2244
|
-
return Object.freeze({
|
|
2245
|
-
lengths,
|
|
2246
|
-
getPublicKey: keys.getPublicKey,
|
|
2247
|
-
keygen: keys.keygen,
|
|
2248
|
-
encapsulate(pk, randomness = randomBytes(msgCoder.bytesLen)) {
|
|
2249
|
-
const pks = pkCoder.decode(pk);
|
|
2250
|
-
const rand = msgCoder.decode(randomness);
|
|
2251
|
-
const sharedSecret = [];
|
|
2252
|
-
const cipherText = [];
|
|
2253
|
-
try {
|
|
2254
|
-
for (let i = 0; i < rawKems.length; i++) {
|
|
2255
|
-
const enc = rawKems[i].encapsulate(pks[i], rand[i]);
|
|
2256
|
-
sharedSecret.push(enc.sharedSecret);
|
|
2257
|
-
cipherText.push(enc.cipherText);
|
|
2418
|
+
const p = pp.params;
|
|
2419
|
+
const argonInt = (val, name) => {
|
|
2420
|
+
if (typeof val !== "number" || !Number.isInteger(val)) {
|
|
2421
|
+
errors.push(
|
|
2422
|
+
issue(
|
|
2423
|
+
"SCHEMA_TYPE_MISMATCH",
|
|
2424
|
+
[...ppPath, "params", name],
|
|
2425
|
+
`argon2id params.${name} must be a CBOR unsigned integer`
|
|
2426
|
+
)
|
|
2427
|
+
);
|
|
2428
|
+
return null;
|
|
2258
2429
|
}
|
|
2259
|
-
return
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2430
|
+
return val;
|
|
2431
|
+
};
|
|
2432
|
+
const mVal = argonInt(p.m, "m");
|
|
2433
|
+
const tVal = argonInt(p.t, "t");
|
|
2434
|
+
const pVal = argonInt(p.p, "p");
|
|
2435
|
+
if (mVal !== null && mVal < 65536) {
|
|
2436
|
+
errors.push(
|
|
2437
|
+
issue(
|
|
2438
|
+
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
2439
|
+
[...ppPath, "params", "m"],
|
|
2440
|
+
"argon2id requires m >= 65536 KiB"
|
|
2441
|
+
)
|
|
2442
|
+
);
|
|
2267
2443
|
}
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2444
|
+
if (tVal !== null && tVal < 3) {
|
|
2445
|
+
errors.push(
|
|
2446
|
+
issue(
|
|
2447
|
+
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
2448
|
+
[...ppPath, "params", "t"],
|
|
2449
|
+
"argon2id requires t >= 3"
|
|
2450
|
+
)
|
|
2451
|
+
);
|
|
2452
|
+
}
|
|
2453
|
+
if (pVal !== null && pVal < 1) {
|
|
2454
|
+
errors.push(
|
|
2455
|
+
issue(
|
|
2456
|
+
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
2457
|
+
[...ppPath, "params", "p"],
|
|
2458
|
+
"argon2id requires p >= 1"
|
|
2459
|
+
)
|
|
2460
|
+
);
|
|
2277
2461
|
}
|
|
2278
2462
|
}
|
|
2279
|
-
}
|
|
2463
|
+
}
|
|
2280
2464
|
}
|
|
2281
|
-
var
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
)
|
|
2291
|
-
|
|
2292
|
-
var AeadVerificationError2 = class extends Error {
|
|
2293
|
-
code = "aead_verification_failed";
|
|
2294
|
-
constructor(message, options) {
|
|
2295
|
-
super(message, options);
|
|
2296
|
-
this.name = "AeadVerificationError";
|
|
2465
|
+
var SLOT_KEY_UNIVERSE = /* @__PURE__ */ new Set(["epk", "kem_ct", "wrap"]);
|
|
2466
|
+
function checkSlotShape(slot, rawKeys, descriptor, kem, slotPath, errors) {
|
|
2467
|
+
const foreignField = descriptor.field === "epk" ? "kem_ct" : "epk";
|
|
2468
|
+
if (rawKeys.has(foreignField)) {
|
|
2469
|
+
errors.push(
|
|
2470
|
+
issue(
|
|
2471
|
+
"ENC_SLOT_INVALID_SHAPE",
|
|
2472
|
+
[...slotPath, foreignField],
|
|
2473
|
+
`slot carries '${foreignField}' but kem='${kem}' expects '${descriptor.field}'`
|
|
2474
|
+
)
|
|
2475
|
+
);
|
|
2297
2476
|
}
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2477
|
+
for (const k of rawKeys) {
|
|
2478
|
+
if (!SLOT_KEY_UNIVERSE.has(k)) {
|
|
2479
|
+
errors.push(
|
|
2480
|
+
issue(
|
|
2481
|
+
"ENC_SLOT_INVALID_SHAPE",
|
|
2482
|
+
[...slotPath, k],
|
|
2483
|
+
`slot carries unexpected key '${k}'; a slot is a 2-key map {${descriptor.field}, wrap}`
|
|
2484
|
+
)
|
|
2485
|
+
);
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
if (descriptor.field === "epk") {
|
|
2489
|
+
if (slot.epk === void 0) {
|
|
2490
|
+
errors.push(
|
|
2491
|
+
issue(
|
|
2492
|
+
"ENC_SLOT_INVALID_SHAPE",
|
|
2493
|
+
[...slotPath, "epk"],
|
|
2494
|
+
`slot for kem='${kem}' is missing required 'epk'`
|
|
2495
|
+
)
|
|
2496
|
+
);
|
|
2497
|
+
} else if (slot.epk.length !== descriptor.fieldLength) {
|
|
2498
|
+
errors.push(
|
|
2499
|
+
issue(
|
|
2500
|
+
KEM_FIELD_LENGTH_CODE.epk,
|
|
2501
|
+
[...slotPath, "epk"],
|
|
2502
|
+
`slot.epk length ${slot.epk.length} != ${descriptor.fieldLength} for ${kem}`
|
|
2503
|
+
)
|
|
2504
|
+
);
|
|
2505
|
+
}
|
|
2506
|
+
} else {
|
|
2507
|
+
if (slot.kem_ct === void 0) {
|
|
2508
|
+
errors.push(
|
|
2509
|
+
issue(
|
|
2510
|
+
"ENC_SLOT_INVALID_SHAPE",
|
|
2511
|
+
[...slotPath, "kem_ct"],
|
|
2512
|
+
`slot for kem='${kem}' is missing required 'kem_ct'`
|
|
2513
|
+
)
|
|
2514
|
+
);
|
|
2515
|
+
} else {
|
|
2516
|
+
const reassembled = bytesChunkArrayConcat(slot.kem_ct).length;
|
|
2517
|
+
if (reassembled !== descriptor.fieldLength) {
|
|
2518
|
+
errors.push(
|
|
2519
|
+
issue(
|
|
2520
|
+
KEM_FIELD_LENGTH_CODE.kem_ct,
|
|
2521
|
+
[...slotPath, "kem_ct"],
|
|
2522
|
+
`slot.kem_ct reassembles to ${reassembled} bytes != ${descriptor.fieldLength} for ${kem}`
|
|
2523
|
+
)
|
|
2524
|
+
);
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
if (slot.wrap === void 0) {
|
|
2529
|
+
errors.push(
|
|
2530
|
+
issue(
|
|
2531
|
+
"ENC_SLOT_INVALID_SHAPE",
|
|
2532
|
+
[...slotPath, "wrap"],
|
|
2533
|
+
`slot for kem='${kem}' is missing required 'wrap'`
|
|
2534
|
+
)
|
|
2535
|
+
);
|
|
2536
|
+
} else if (slot.wrap.length !== descriptor.wrapLength) {
|
|
2537
|
+
errors.push(
|
|
2538
|
+
issue(
|
|
2539
|
+
"WRAP_LENGTH_MISMATCH",
|
|
2540
|
+
[...slotPath, "wrap"],
|
|
2541
|
+
`slot.wrap length ${slot.wrap.length} != ${descriptor.wrapLength}`
|
|
2542
|
+
)
|
|
2543
|
+
);
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
function slotKemMaterial(slot, descriptor) {
|
|
2547
|
+
if (descriptor.field === "epk") {
|
|
2548
|
+
return slot.epk;
|
|
2549
|
+
}
|
|
2550
|
+
if (slot.kem_ct === void 0) return void 0;
|
|
2551
|
+
return bytesChunkArrayConcat(slot.kem_ct);
|
|
2552
|
+
}
|
|
2553
|
+
function bytesToHex(bytes) {
|
|
2554
|
+
let hex = "";
|
|
2555
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2556
|
+
hex += bytes[i].toString(16).padStart(2, "0");
|
|
2304
2557
|
}
|
|
2558
|
+
return hex;
|
|
2305
2559
|
}
|
|
2306
|
-
function
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2560
|
+
function rawSlotKeySets(rawEnc) {
|
|
2561
|
+
const slots = mapLikeGet(rawEnc, "slots");
|
|
2562
|
+
if (!Array.isArray(slots)) return [];
|
|
2563
|
+
return slots.map((slot) => {
|
|
2564
|
+
const keys = /* @__PURE__ */ new Set();
|
|
2565
|
+
if (slot instanceof Map) {
|
|
2566
|
+
for (const k of slot.keys()) if (typeof k === "string") keys.add(k);
|
|
2567
|
+
} else if (typeof slot === "object" && slot !== null) {
|
|
2568
|
+
for (const k of Object.keys(slot)) keys.add(k);
|
|
2569
|
+
}
|
|
2570
|
+
return keys;
|
|
2571
|
+
});
|
|
2312
2572
|
}
|
|
2313
|
-
function
|
|
2314
|
-
|
|
2573
|
+
function mapLikeGet(value, key) {
|
|
2574
|
+
if (value instanceof Map) return value.get(key);
|
|
2575
|
+
if (typeof value === "object" && value !== null) {
|
|
2576
|
+
return value[key];
|
|
2577
|
+
}
|
|
2578
|
+
return void 0;
|
|
2315
2579
|
}
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2580
|
+
function checkMerkleCommit(commit, idx, errors) {
|
|
2581
|
+
const basePath = ["merkle", idx];
|
|
2582
|
+
if (!(commit.alg in MERKLE_COMMIT_ALG_LENGTHS)) {
|
|
2583
|
+
errors.push(
|
|
2584
|
+
issue(
|
|
2585
|
+
"UNSUPPORTED_MERKLE_COMMIT_ALG",
|
|
2586
|
+
[...basePath, "alg"],
|
|
2587
|
+
`unknown merkle commitment alg: ${commit.alg}`
|
|
2588
|
+
)
|
|
2322
2589
|
);
|
|
2590
|
+
return;
|
|
2323
2591
|
}
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2592
|
+
const expected = MERKLE_COMMIT_ALG_LENGTHS[commit.alg];
|
|
2593
|
+
if (commit.root.length !== expected) {
|
|
2594
|
+
errors.push(
|
|
2595
|
+
issue(
|
|
2596
|
+
"HASH_DIGEST_LENGTH_MISMATCH",
|
|
2597
|
+
[...basePath, "root"],
|
|
2598
|
+
`merkle entry root length ${commit.root.length} != ${expected} for ${commit.alg}`
|
|
2599
|
+
)
|
|
2327
2600
|
);
|
|
2328
2601
|
}
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
var X25519LowOrderPointError = class extends Error {
|
|
2332
|
-
code = "X25519_LOW_ORDER_POINT";
|
|
2333
|
-
constructor(options) {
|
|
2334
|
-
super("x25519 ECDH rejected: peer public key is a small-order point", options);
|
|
2335
|
-
this.name = "X25519LowOrderPointError";
|
|
2602
|
+
if (commit.uris) {
|
|
2603
|
+
checkItemUris(commit.uris, [...basePath, "uris"], errors);
|
|
2336
2604
|
}
|
|
2337
|
-
};
|
|
2338
|
-
var NOBLE_LOW_ORDER_MESSAGE = "invalid private or public key received";
|
|
2339
|
-
function x25519PublicKey(opts2) {
|
|
2340
|
-
return x25519.getPublicKey(opts2.secretKey);
|
|
2341
2605
|
}
|
|
2342
|
-
function
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2606
|
+
function checkSigEntry(entry, idx, errors, info) {
|
|
2607
|
+
if (entry.cose_key !== void 0) {
|
|
2608
|
+
const keyIssue = inspectCoseKey(entry.cose_key, idx);
|
|
2609
|
+
if (keyIssue !== null) {
|
|
2610
|
+
errors.push(keyIssue);
|
|
2611
|
+
return;
|
|
2348
2612
|
}
|
|
2349
|
-
throw e;
|
|
2350
2613
|
}
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2614
|
+
const merged = bytesChunkArrayConcat(entry.cose_sign1);
|
|
2615
|
+
let cose;
|
|
2616
|
+
try {
|
|
2617
|
+
cose = decodeCoseSign1(merged);
|
|
2618
|
+
} catch (cause) {
|
|
2619
|
+
errors.push(
|
|
2620
|
+
issue(
|
|
2621
|
+
"MALFORMED_SIG_COSE_SIGN1",
|
|
2622
|
+
["sigs", idx],
|
|
2623
|
+
cause instanceof CoseVerifyError || cause instanceof Error ? cause.message : String(cause)
|
|
2624
|
+
)
|
|
2625
|
+
);
|
|
2626
|
+
return;
|
|
2358
2627
|
}
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
var CHUNK_MAX_BYTES = 64;
|
|
2369
|
-
function chunkKemCt(value) {
|
|
2370
|
-
if (value.length === 0) {
|
|
2371
|
-
throw new Error("chunkKemCt: refusing to chunk an empty byte string");
|
|
2628
|
+
if (cose.payload !== null) {
|
|
2629
|
+
errors.push(
|
|
2630
|
+
issue(
|
|
2631
|
+
"MALFORMED_SIG_COSE_SIGN1",
|
|
2632
|
+
["sigs", idx],
|
|
2633
|
+
"COSE_Sign1 payload must be null (detached); attached form forbidden"
|
|
2634
|
+
)
|
|
2635
|
+
);
|
|
2636
|
+
return;
|
|
2372
2637
|
}
|
|
2373
|
-
const
|
|
2374
|
-
|
|
2375
|
-
|
|
2638
|
+
const alg = cose.protectedHeader.get(1);
|
|
2639
|
+
if (typeof alg !== "number" || !KNOWN_SIG_ALG_IDS.has(alg)) {
|
|
2640
|
+
info.push(
|
|
2641
|
+
issue(
|
|
2642
|
+
"SIGNATURE_UNSUPPORTED",
|
|
2643
|
+
["sigs", idx],
|
|
2644
|
+
`COSE_Sign1 protected alg ${String(alg)} not in {-8, -19}`
|
|
2645
|
+
)
|
|
2646
|
+
);
|
|
2376
2647
|
}
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
offset += c.length;
|
|
2648
|
+
const protectedKid = cose.protectedHeader.get(4);
|
|
2649
|
+
if (protectedKid instanceof Uint8Array && protectedKid.length === 32 && entry.cose_key !== void 0) {
|
|
2650
|
+
errors.push(
|
|
2651
|
+
issue(
|
|
2652
|
+
"SIG_ENTRY_KID_COSE_KEY_CONFLICT",
|
|
2653
|
+
["sigs", idx],
|
|
2654
|
+
"sigs[i] carries both a 32-byte protected `kid` (path 1) and an inline `cose_key` (path 2); paths are mutually exclusive"
|
|
2655
|
+
)
|
|
2656
|
+
);
|
|
2387
2657
|
}
|
|
2388
|
-
return out;
|
|
2389
|
-
}
|
|
2390
|
-
function slotsToMacCbor(slots, kem) {
|
|
2391
|
-
let value;
|
|
2392
|
-
if (kem === "x25519") {
|
|
2393
|
-
value = slots.map((s) => ({ epk: s.epk, wrap: s.wrap }));
|
|
2394
|
-
} else {
|
|
2395
|
-
value = slots.map((s) => ({
|
|
2396
|
-
// Canonicalize the chunk boundaries before the MAC commits to them:
|
|
2397
|
-
// reassemble the logical ciphertext and re-split into canonical ≤ 64-byte
|
|
2398
|
-
// chunks. The on-wire `kem_ct` array is a transport detail (the Cardano
|
|
2399
|
-
// ledger's 64-byte metadatum cap), and a hostile or non-canonical chunking
|
|
2400
|
-
// ([1, 63, …] instead of [64, …]) reassembles to the SAME bytes — so the
|
|
2401
|
-
// MAC must be invariant to it. Committing to the verbatim wire chunks would
|
|
2402
|
-
// let an attacker re-chunk an honest envelope and break the slots_mac match
|
|
2403
|
-
// for an honest recipient. Honest (already-64B-chunked) records are
|
|
2404
|
-
// unchanged; a real byte flip still changes the reassembled bytes and is
|
|
2405
|
-
// still rejected.
|
|
2406
|
-
kem_ct: chunkKemCt(joinKemCt(s.kem_ct)),
|
|
2407
|
-
wrap: s.wrap
|
|
2408
|
-
}));
|
|
2409
|
-
}
|
|
2410
|
-
return encodeCanonicalCbor3(value);
|
|
2411
|
-
}
|
|
2412
|
-
var CARDANO_POE_HKDF_INFO_KEK = new TextEncoder().encode("cardano-poe-kek-v1");
|
|
2413
|
-
var CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 = new TextEncoder().encode(
|
|
2414
|
-
"cardano-poe-kek-mlkem768x25519-v1"
|
|
2415
|
-
);
|
|
2416
|
-
var CARDANO_POE_HKDF_INFO_SLOTS_MAC = new TextEncoder().encode(
|
|
2417
|
-
"cardano-poe-slots-mac-v1"
|
|
2418
|
-
);
|
|
2419
|
-
var ZERO_NONCE_12 = new Uint8Array(12);
|
|
2420
|
-
if (CARDANO_POE_HKDF_INFO_KEK.length !== 18) {
|
|
2421
|
-
throw new Error("CARDANO_POE_HKDF_INFO_KEK byte-length invariant violated (expected 18)");
|
|
2422
|
-
}
|
|
2423
|
-
if (CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519.length !== 33) {
|
|
2424
|
-
throw new Error(
|
|
2425
|
-
"CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 byte-length invariant violated (expected 33)"
|
|
2426
|
-
);
|
|
2427
|
-
}
|
|
2428
|
-
if (CARDANO_POE_HKDF_INFO_SLOTS_MAC.length !== 24) {
|
|
2429
|
-
throw new Error("CARDANO_POE_HKDF_INFO_SLOTS_MAC byte-length invariant violated (expected 24)");
|
|
2430
|
-
}
|
|
2431
|
-
if (ZERO_NONCE_12.length !== 12) {
|
|
2432
|
-
throw new Error("ZERO_NONCE_12 byte-length invariant violated (expected 12)");
|
|
2433
|
-
}
|
|
2434
|
-
function compareCt2(a, b) {
|
|
2435
|
-
if (a.length !== b.length) return false;
|
|
2436
|
-
let diff = 0;
|
|
2437
|
-
for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
|
|
2438
|
-
return diff === 0;
|
|
2439
|
-
}
|
|
2440
|
-
function selectBundleSecrets(envelope, bundle) {
|
|
2441
|
-
return envelope.kem === "x25519" ? bundle.x25519PrivateKeys : bundle.mlkem768x25519SecretSeeds;
|
|
2442
|
-
}
|
|
2443
|
-
var ZERO_NONCE_122 = new Uint8Array(12);
|
|
2444
|
-
var EMPTY_SALT2 = new Uint8Array(0);
|
|
2445
|
-
var X25519_SECRET_KEY_LENGTH2 = 32;
|
|
2446
|
-
var X25519_PUBLIC_KEY_LENGTH2 = 32;
|
|
2447
|
-
var NONCE_LENGTH2 = 24;
|
|
2448
|
-
var WRAP_LENGTH2 = 48;
|
|
2449
|
-
var SLOTS_MAC_LENGTH2 = 32;
|
|
2450
|
-
function concat2(a, b) {
|
|
2451
|
-
const out = new Uint8Array(a.length + b.length);
|
|
2452
|
-
out.set(a, 0);
|
|
2453
|
-
out.set(b, a.length);
|
|
2454
|
-
return out;
|
|
2455
2658
|
}
|
|
2456
|
-
function
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2659
|
+
function inspectCoseKey(keyChunks, i) {
|
|
2660
|
+
let decoded;
|
|
2661
|
+
try {
|
|
2662
|
+
decoded = decodeCanonicalCbor(bytesChunkArrayConcat(keyChunks));
|
|
2663
|
+
} catch (cause) {
|
|
2664
|
+
return issue(
|
|
2665
|
+
"MALFORMED_SIG_COSE_SIGN1",
|
|
2666
|
+
["sigs", i, "cose_key"],
|
|
2667
|
+
`sigs[${i}].cose_key failed to decode as cbor<COSE_Key>: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
2461
2668
|
);
|
|
2462
2669
|
}
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2670
|
+
const getLabel = (label) => {
|
|
2671
|
+
if (decoded instanceof Map) return decoded.get(label);
|
|
2672
|
+
if (typeof decoded === "object" && decoded !== null) {
|
|
2673
|
+
return decoded[String(label)];
|
|
2674
|
+
}
|
|
2675
|
+
return void 0;
|
|
2676
|
+
};
|
|
2677
|
+
const hasLabel = (label) => {
|
|
2678
|
+
if (decoded instanceof Map) return decoded.has(label);
|
|
2679
|
+
if (typeof decoded === "object" && decoded !== null) {
|
|
2680
|
+
return Object.prototype.hasOwnProperty.call(decoded, String(label));
|
|
2681
|
+
}
|
|
2682
|
+
return false;
|
|
2683
|
+
};
|
|
2684
|
+
if (hasLabel(-4)) {
|
|
2685
|
+
return issue(
|
|
2686
|
+
"SIG_PRIVATE_KEY_LEAKED",
|
|
2687
|
+
["sigs", i, "cose_key"],
|
|
2688
|
+
"cose_key carries COSE_Key private-key material (label -4, the OKP/EC2 private scalar d); publishing a private key on the permanent ledger is forbidden"
|
|
2467
2689
|
);
|
|
2468
2690
|
}
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2691
|
+
const kty = getLabel(1);
|
|
2692
|
+
if (kty !== 1) {
|
|
2693
|
+
return issue(
|
|
2694
|
+
"MALFORMED_SIG_COSE_SIGN1",
|
|
2695
|
+
["sigs", i, "cose_key"],
|
|
2696
|
+
`sigs[${i}].cose_key COSE_Key kty (label 1) must be 1 (OKP); got ${String(kty)}`
|
|
2473
2697
|
);
|
|
2474
2698
|
}
|
|
2475
|
-
const
|
|
2476
|
-
if (
|
|
2477
|
-
|
|
2699
|
+
const crv = getLabel(-1);
|
|
2700
|
+
if (crv !== 6) {
|
|
2701
|
+
return issue(
|
|
2702
|
+
"MALFORMED_SIG_COSE_SIGN1",
|
|
2703
|
+
["sigs", i, "cose_key"],
|
|
2704
|
+
`sigs[${i}].cose_key COSE_Key crv (label -1) must be 6 (Ed25519); got ${String(crv)}`
|
|
2705
|
+
);
|
|
2478
2706
|
}
|
|
2479
|
-
if (
|
|
2480
|
-
|
|
2481
|
-
"
|
|
2482
|
-
|
|
2707
|
+
if (!hasLabel(-2)) {
|
|
2708
|
+
return issue(
|
|
2709
|
+
"MALFORMED_SIG_COSE_SIGN1",
|
|
2710
|
+
["sigs", i, "cose_key"],
|
|
2711
|
+
`sigs[${i}].cose_key COSE_Key missing label -2 (Ed25519 public-key bytes)`
|
|
2483
2712
|
);
|
|
2484
2713
|
}
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2714
|
+
const x = getLabel(-2);
|
|
2715
|
+
if (!(x instanceof Uint8Array) || x.length !== 32) {
|
|
2716
|
+
const got = x instanceof Uint8Array ? `${x.length}-byte bstr` : typeof x;
|
|
2717
|
+
return issue(
|
|
2718
|
+
"MALFORMED_SIG_COSE_SIGN1",
|
|
2719
|
+
["sigs", i, "cose_key"],
|
|
2720
|
+
`sigs[${i}].cose_key COSE_Key label -2 must be a 32-byte byte string (Ed25519 public key); got ${got}`
|
|
2489
2721
|
);
|
|
2490
2722
|
}
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
} else {
|
|
2508
|
-
for (let i = 0; i < n; i++) {
|
|
2509
|
-
const slot = envelope.slots[i];
|
|
2510
|
-
const enc = joinKemCt(slot.kem_ct);
|
|
2511
|
-
if (enc.length !== MLKEM768X25519_ENC_LENGTH) {
|
|
2512
|
-
throw new EciesSealedPoeError(
|
|
2513
|
-
"KEM_CT_LENGTH_MISMATCH",
|
|
2514
|
-
`envelope.slots[${i}].kem_ct MUST reassemble to exactly ${MLKEM768X25519_ENC_LENGTH} bytes, got ${enc.length}`
|
|
2515
|
-
);
|
|
2516
|
-
}
|
|
2517
|
-
if (slot.wrap.length !== WRAP_LENGTH2) {
|
|
2518
|
-
throw new EciesSealedPoeError(
|
|
2519
|
-
"WRAP_LENGTH_MISMATCH",
|
|
2520
|
-
`envelope.slots[${i}].wrap MUST be exactly ${WRAP_LENGTH2} bytes, got ${slot.wrap.length}`
|
|
2521
|
-
);
|
|
2522
|
-
}
|
|
2723
|
+
return null;
|
|
2724
|
+
}
|
|
2725
|
+
var ACCEPTED_CIDV1_MULTIBASE = /* @__PURE__ */ new Set(["b", "B", "f", "F", "z"]);
|
|
2726
|
+
var ACCEPTED_MULTICODECS = /* @__PURE__ */ new Set([85, 112, 113]);
|
|
2727
|
+
var ACCEPTED_MULTIHASHES = /* @__PURE__ */ new Map([
|
|
2728
|
+
[18, 32],
|
|
2729
|
+
[45600, 32]
|
|
2730
|
+
]);
|
|
2731
|
+
function validateCidProfile(cid) {
|
|
2732
|
+
if (cid.length === 0) return false;
|
|
2733
|
+
if (cid.startsWith("Qm")) {
|
|
2734
|
+
let decoded;
|
|
2735
|
+
try {
|
|
2736
|
+
decoded = decodeBase58btc(cid);
|
|
2737
|
+
} catch {
|
|
2738
|
+
return false;
|
|
2523
2739
|
}
|
|
2740
|
+
return decoded.length === 34 && decoded[0] === 18 && decoded[1] === 32;
|
|
2524
2741
|
}
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2742
|
+
const mbPrefix = cid[0];
|
|
2743
|
+
if (!ACCEPTED_CIDV1_MULTIBASE.has(mbPrefix)) return false;
|
|
2744
|
+
let bytes;
|
|
2745
|
+
try {
|
|
2746
|
+
bytes = decodeMultibase(mbPrefix, cid.slice(1));
|
|
2747
|
+
} catch {
|
|
2748
|
+
return false;
|
|
2749
|
+
}
|
|
2750
|
+
if (bytes.length < 4) return false;
|
|
2751
|
+
const versionParse = readVarint(bytes, 0);
|
|
2752
|
+
if (versionParse === null || versionParse.value !== 1) return false;
|
|
2753
|
+
const codecParse = readVarint(bytes, versionParse.next);
|
|
2754
|
+
if (codecParse === null) return false;
|
|
2755
|
+
if (!ACCEPTED_MULTICODECS.has(codecParse.value)) return false;
|
|
2756
|
+
const mhParse = readVarint(bytes, codecParse.next);
|
|
2757
|
+
if (mhParse === null) return false;
|
|
2758
|
+
const lenParse = readVarint(bytes, mhParse.next);
|
|
2759
|
+
if (lenParse === null) return false;
|
|
2760
|
+
const digestLen = lenParse.value;
|
|
2761
|
+
const expectedLen = ACCEPTED_MULTIHASHES.get(mhParse.value);
|
|
2762
|
+
if (expectedLen === void 0 || digestLen !== expectedLen) return false;
|
|
2763
|
+
if (lenParse.next + digestLen !== bytes.length) return false;
|
|
2764
|
+
return true;
|
|
2765
|
+
}
|
|
2766
|
+
function readVarint(bytes, start) {
|
|
2767
|
+
let value = 0;
|
|
2768
|
+
let shift = 0;
|
|
2769
|
+
let i = start;
|
|
2770
|
+
while (i < bytes.length) {
|
|
2771
|
+
const b = bytes[i];
|
|
2772
|
+
value |= (b & 127) << shift;
|
|
2773
|
+
i++;
|
|
2774
|
+
if ((b & 128) === 0) return { value, next: i };
|
|
2775
|
+
shift += 7;
|
|
2776
|
+
if (shift > 28) return null;
|
|
2777
|
+
}
|
|
2778
|
+
return null;
|
|
2779
|
+
}
|
|
2780
|
+
function decodeMultibase(prefix, body) {
|
|
2781
|
+
switch (prefix) {
|
|
2782
|
+
case "b":
|
|
2783
|
+
return decodeBase32(body.toLowerCase(), "rfc4648-lower");
|
|
2784
|
+
case "B":
|
|
2785
|
+
return decodeBase32(body.toUpperCase(), "rfc4648-upper");
|
|
2786
|
+
case "f":
|
|
2787
|
+
return decodeBase16(body.toLowerCase());
|
|
2788
|
+
case "F":
|
|
2789
|
+
return decodeBase16(body.toUpperCase());
|
|
2790
|
+
case "z":
|
|
2791
|
+
return decodeBase58btc(body);
|
|
2792
|
+
default:
|
|
2793
|
+
throw new Error(`unsupported multibase prefix ${prefix}`);
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
var BASE16_LOWER = "0123456789abcdef";
|
|
2797
|
+
var BASE16_UPPER = "0123456789ABCDEF";
|
|
2798
|
+
function decodeBase16(s) {
|
|
2799
|
+
if (s.length % 2 !== 0) throw new Error("base16: odd-length");
|
|
2800
|
+
const out = new Uint8Array(s.length / 2);
|
|
2801
|
+
const alphabet = s === s.toLowerCase() ? BASE16_LOWER : BASE16_UPPER;
|
|
2802
|
+
for (let i = 0; i < out.length; i++) {
|
|
2803
|
+
const hi = alphabet.indexOf(s[i * 2]);
|
|
2804
|
+
const lo = alphabet.indexOf(s[i * 2 + 1]);
|
|
2805
|
+
if (hi < 0 || lo < 0) throw new Error(`base16: non-hex char at ${i * 2}`);
|
|
2806
|
+
out[i] = hi << 4 | lo;
|
|
2807
|
+
}
|
|
2808
|
+
return out;
|
|
2809
|
+
}
|
|
2810
|
+
var BASE32_RFC4648_LOWER = "abcdefghijklmnopqrstuvwxyz234567";
|
|
2811
|
+
var BASE32_RFC4648_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
2812
|
+
function decodeBase32(s, variant) {
|
|
2813
|
+
const alphabet = variant === "rfc4648-lower" ? BASE32_RFC4648_LOWER : BASE32_RFC4648_UPPER;
|
|
2814
|
+
const trimmed = s.replace(/=+$/, "");
|
|
2815
|
+
const out = [];
|
|
2816
|
+
let buf = 0;
|
|
2817
|
+
let bits = 0;
|
|
2818
|
+
for (const ch of trimmed) {
|
|
2819
|
+
const idx = alphabet.indexOf(ch);
|
|
2820
|
+
if (idx < 0) throw new Error(`base32: invalid char '${ch}'`);
|
|
2821
|
+
buf = buf << 5 | idx;
|
|
2822
|
+
bits += 5;
|
|
2823
|
+
if (bits >= 8) {
|
|
2824
|
+
bits -= 8;
|
|
2825
|
+
out.push(buf >> bits & 255);
|
|
2540
2826
|
}
|
|
2541
2827
|
}
|
|
2828
|
+
return Uint8Array.from(out);
|
|
2542
2829
|
}
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
});
|
|
2562
|
-
} catch (e) {
|
|
2563
|
-
if (!(e instanceof AeadVerificationError2) && !(e instanceof X25519LowOrderPointError)) {
|
|
2564
|
-
throw e;
|
|
2565
|
-
}
|
|
2566
|
-
return null;
|
|
2830
|
+
var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
2831
|
+
function decodeBase58btc(s) {
|
|
2832
|
+
if (s.length === 0) return new Uint8Array(0);
|
|
2833
|
+
let zeros = 0;
|
|
2834
|
+
while (zeros < s.length && s[zeros] === "1") zeros++;
|
|
2835
|
+
const size = Math.floor((s.length - zeros) * 733 / 1e3) + 1;
|
|
2836
|
+
const b256 = new Uint8Array(size);
|
|
2837
|
+
let length = 0;
|
|
2838
|
+
for (let i = zeros; i < s.length; i++) {
|
|
2839
|
+
const ch = s[i];
|
|
2840
|
+
const carryIdx = BASE58_ALPHABET.indexOf(ch);
|
|
2841
|
+
if (carryIdx < 0) throw new Error(`base58: invalid char '${ch}'`);
|
|
2842
|
+
let carry = carryIdx;
|
|
2843
|
+
let k = 0;
|
|
2844
|
+
for (let j2 = size - 1; (carry !== 0 || k < length) && j2 >= 0; j2--, k++) {
|
|
2845
|
+
carry += 58 * b256[j2];
|
|
2846
|
+
b256[j2] = carry % 256;
|
|
2847
|
+
carry = Math.floor(carry / 256);
|
|
2567
2848
|
}
|
|
2849
|
+
length = k;
|
|
2568
2850
|
}
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
ikm: shared,
|
|
2576
|
-
salt: concat2(args.slot.epk, args.pubRLocal),
|
|
2577
|
-
info: CARDANO_POE_HKDF_INFO_KEK,
|
|
2578
|
-
length: 32
|
|
2579
|
-
});
|
|
2580
|
-
} catch (e) {
|
|
2581
|
-
if (!(e instanceof X25519LowOrderPointError)) throw e;
|
|
2851
|
+
let it = size - length;
|
|
2852
|
+
while (it < size && b256[it] === 0) it++;
|
|
2853
|
+
const out = new Uint8Array(zeros + (size - it));
|
|
2854
|
+
let j = zeros;
|
|
2855
|
+
while (it < size) {
|
|
2856
|
+
out[j++] = b256[it++];
|
|
2582
2857
|
}
|
|
2583
|
-
return
|
|
2858
|
+
return out;
|
|
2584
2859
|
}
|
|
2585
|
-
function
|
|
2586
|
-
const
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
});
|
|
2594
|
-
if (!args.liveSlot) {
|
|
2595
|
-
return null;
|
|
2860
|
+
function checkCritShape(record, decodedTopKeys, errors) {
|
|
2861
|
+
const invalid = /* @__PURE__ */ new Set();
|
|
2862
|
+
if (!Array.isArray(record.crit)) return invalid;
|
|
2863
|
+
if (record.crit.length === 0) {
|
|
2864
|
+
errors.push(
|
|
2865
|
+
issue("SCHEMA_TYPE_MISMATCH", ["crit"], "crit[] must carry at least one entry when present")
|
|
2866
|
+
);
|
|
2867
|
+
return invalid;
|
|
2596
2868
|
}
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
})
|
|
2604
|
-
|
|
2605
|
-
if (!(
|
|
2606
|
-
|
|
2869
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2870
|
+
for (let i = 0; i < record.crit.length; i++) {
|
|
2871
|
+
const critName = record.crit[i];
|
|
2872
|
+
let reason = null;
|
|
2873
|
+
if (TOP_LEVEL_BASE_KEYS.has(critName)) {
|
|
2874
|
+
reason = `'${critName}' is a base key and MUST NOT appear in crit[]`;
|
|
2875
|
+
} else if (!isExtensionKey(critName)) {
|
|
2876
|
+
reason = `'${critName}' does not match the extension-key regex (^x-.+ or ^[a-z]+-.+)`;
|
|
2877
|
+
} else if (!decodedTopKeys.has(critName)) {
|
|
2878
|
+
reason = `'${critName}' is named in crit but absent from the record map`;
|
|
2879
|
+
} else if (seen.has(critName)) {
|
|
2880
|
+
reason = `'${critName}' appears more than once in crit[]`;
|
|
2881
|
+
}
|
|
2882
|
+
seen.add(critName);
|
|
2883
|
+
if (reason !== null) {
|
|
2884
|
+
invalid.add(i);
|
|
2885
|
+
errors.push(issue("CRIT_SHAPE_INVALID", ["crit", i], reason));
|
|
2886
|
+
}
|
|
2607
2887
|
}
|
|
2888
|
+
return invalid;
|
|
2608
2889
|
}
|
|
2609
|
-
function
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
for (let i = 0; i < n; i++) {
|
|
2616
|
-
if (slotsAttemptedOut !== void 0) {
|
|
2617
|
-
slotsAttemptedOut.count = i + 1;
|
|
2618
|
-
}
|
|
2619
|
-
const candidate = tryX25519Slot({
|
|
2620
|
-
slot: envelope.slots[i],
|
|
2621
|
-
recipientSecretKey,
|
|
2622
|
-
pubRLocal,
|
|
2623
|
-
liveSlot: cek === null
|
|
2624
|
-
});
|
|
2625
|
-
if (cek === null && candidate !== null) {
|
|
2626
|
-
cek = candidate;
|
|
2627
|
-
matchedSlotIdx = i;
|
|
2628
|
-
}
|
|
2629
|
-
if (cek !== null && !constantTimeN) break;
|
|
2630
|
-
}
|
|
2631
|
-
} else {
|
|
2632
|
-
for (let i = 0; i < n; i++) {
|
|
2633
|
-
if (slotsAttemptedOut !== void 0) {
|
|
2634
|
-
slotsAttemptedOut.count = i + 1;
|
|
2635
|
-
}
|
|
2636
|
-
const candidate = tryMlkem768X25519Slot({
|
|
2637
|
-
slot: envelope.slots[i],
|
|
2638
|
-
recipientSecretKey,
|
|
2639
|
-
liveSlot: cek === null
|
|
2640
|
-
});
|
|
2641
|
-
if (cek === null && candidate !== null) {
|
|
2642
|
-
cek = candidate;
|
|
2643
|
-
matchedSlotIdx = i;
|
|
2644
|
-
}
|
|
2645
|
-
if (cek !== null && !constantTimeN) break;
|
|
2890
|
+
function topLevelKeysOf(decoded) {
|
|
2891
|
+
if (decoded === null || typeof decoded !== "object") return /* @__PURE__ */ new Set();
|
|
2892
|
+
if (decoded instanceof Map) {
|
|
2893
|
+
const out = /* @__PURE__ */ new Set();
|
|
2894
|
+
for (const k of decoded.keys()) {
|
|
2895
|
+
if (typeof k === "string") out.add(k);
|
|
2646
2896
|
}
|
|
2897
|
+
return out;
|
|
2647
2898
|
}
|
|
2648
|
-
return
|
|
2899
|
+
return new Set(Object.keys(decoded));
|
|
2649
2900
|
}
|
|
2650
|
-
function
|
|
2651
|
-
return
|
|
2901
|
+
function issue(code, path, message) {
|
|
2902
|
+
return { code, path, message, severity: SEVERITY[code] };
|
|
2652
2903
|
}
|
|
2653
|
-
function
|
|
2654
|
-
return
|
|
2655
|
-
envelope.slots,
|
|
2656
|
-
envelope.kem
|
|
2657
|
-
);
|
|
2904
|
+
function compareIssuePath(a, b) {
|
|
2905
|
+
return a.path.join(".").localeCompare(b.path.join("."));
|
|
2658
2906
|
}
|
|
2659
|
-
function
|
|
2660
|
-
|
|
2661
|
-
const
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
if (hasSingle === hasMulti) {
|
|
2667
|
-
throw new EciesSealedPoeError(
|
|
2668
|
-
"INVALID_RECIPIENT_KEY",
|
|
2669
|
-
"exactly one of recipientSecretKey / recipientSecretKeys / recipientKeyBundle MUST be supplied"
|
|
2670
|
-
);
|
|
2671
|
-
}
|
|
2672
|
-
if (hasMulti && multiPrivKeys.length === 0) {
|
|
2673
|
-
if (hasBundle) {
|
|
2674
|
-
return { matched: false, reason: "WRONG_RECIPIENT_KEY" };
|
|
2907
|
+
function valueAtPath(root, path) {
|
|
2908
|
+
let cur = root;
|
|
2909
|
+
for (const seg of path) {
|
|
2910
|
+
if (cur === null || cur === void 0) return void 0;
|
|
2911
|
+
if (cur instanceof Map) {
|
|
2912
|
+
cur = cur.get(seg);
|
|
2913
|
+
continue;
|
|
2675
2914
|
}
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
"recipientSecretKeys MUST be a non-empty array, got length=0"
|
|
2679
|
-
);
|
|
2680
|
-
}
|
|
2681
|
-
if (hasMulti) {
|
|
2682
|
-
assertEnvelopeStructure(envelope, multiPrivKeys, void 0);
|
|
2683
|
-
} else {
|
|
2684
|
-
assertEnvelopeStructure(envelope, void 0, args.recipientSecretKey);
|
|
2915
|
+
if (typeof cur !== "object") return void 0;
|
|
2916
|
+
cur = cur[seg];
|
|
2685
2917
|
}
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
length: 32
|
|
2705
|
-
});
|
|
2706
|
-
const slotsMacCalc = hmac(sha256, hmacKey, slotsCbor);
|
|
2707
|
-
if (!compareCt2(slotsMacCalc, envelope.slots_mac)) {
|
|
2708
|
-
return { matched: false, reason: "TAMPERED_HEADER" };
|
|
2709
|
-
}
|
|
2710
|
-
matchedCek = cek;
|
|
2711
|
-
} else {
|
|
2712
|
-
const slotsCbor = slotsMacCborBytes(envelope);
|
|
2713
|
-
const recipientSecretKeys = multiPrivKeys;
|
|
2714
|
-
for (let k = 0; k < recipientSecretKeys.length; k++) {
|
|
2715
|
-
if (args._privsAttemptedOut !== void 0) {
|
|
2716
|
-
args._privsAttemptedOut.count = k + 1;
|
|
2717
|
-
}
|
|
2718
|
-
if (args._slotsAttemptedOut !== void 0) {
|
|
2719
|
-
args._slotsAttemptedOut.count = 0;
|
|
2720
|
-
}
|
|
2721
|
-
const cek = tryRecipientUnwrap(
|
|
2722
|
-
envelope,
|
|
2723
|
-
recipientSecretKeys[k],
|
|
2724
|
-
constantTimeN,
|
|
2725
|
-
args._slotsAttemptedOut
|
|
2726
|
-
);
|
|
2727
|
-
if (args._slotsAttemptedOut?.perPrivCounts !== void 0) {
|
|
2728
|
-
args._slotsAttemptedOut.perPrivCounts.push(args._slotsAttemptedOut.count);
|
|
2729
|
-
}
|
|
2730
|
-
if (cek === null) continue;
|
|
2731
|
-
const hmacKey = hkdfSha256({
|
|
2732
|
-
ikm: cek,
|
|
2733
|
-
salt: EMPTY_SALT2,
|
|
2734
|
-
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
2735
|
-
length: 32
|
|
2736
|
-
});
|
|
2737
|
-
const slotsMacCalc = hmac(sha256, hmacKey, slotsCbor);
|
|
2738
|
-
if (compareCt2(slotsMacCalc, envelope.slots_mac)) {
|
|
2739
|
-
matchedCek = cek;
|
|
2740
|
-
break;
|
|
2741
|
-
}
|
|
2742
|
-
anyCandidateRecovered = true;
|
|
2743
|
-
}
|
|
2744
|
-
if (matchedCek === null) {
|
|
2745
|
-
return {
|
|
2746
|
-
matched: false,
|
|
2747
|
-
reason: anyCandidateRecovered ? "TAMPERED_HEADER" : "WRONG_RECIPIENT_KEY"
|
|
2748
|
-
};
|
|
2749
|
-
}
|
|
2918
|
+
return cur;
|
|
2919
|
+
}
|
|
2920
|
+
async function argon2idV13(opts2) {
|
|
2921
|
+
return await argon2id({
|
|
2922
|
+
password: opts2.password,
|
|
2923
|
+
salt: opts2.salt,
|
|
2924
|
+
parallelism: opts2.parallelism,
|
|
2925
|
+
iterations: opts2.iterations,
|
|
2926
|
+
memorySize: opts2.memSizeKB,
|
|
2927
|
+
hashLength: opts2.outBytes,
|
|
2928
|
+
outputType: "binary"
|
|
2929
|
+
});
|
|
2930
|
+
}
|
|
2931
|
+
var AeadVerificationError2 = class extends Error {
|
|
2932
|
+
code = "aead_verification_failed";
|
|
2933
|
+
constructor(message, options) {
|
|
2934
|
+
super(message, options);
|
|
2935
|
+
this.name = "AeadVerificationError";
|
|
2750
2936
|
}
|
|
2751
|
-
|
|
2937
|
+
};
|
|
2938
|
+
function xchacha20Poly1305Decrypt2(opts2) {
|
|
2752
2939
|
try {
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
aad: adContent,
|
|
2757
|
-
ciphertext
|
|
2758
|
-
});
|
|
2759
|
-
return { matched: true, plaintext };
|
|
2760
|
-
} catch (e) {
|
|
2761
|
-
if (!(e instanceof AeadVerificationError2)) throw e;
|
|
2762
|
-
return { matched: false, reason: "TAMPERED_CIPHERTEXT" };
|
|
2940
|
+
return xchacha20poly1305(opts2.key, opts2.nonce, opts2.aad).decrypt(opts2.ciphertext);
|
|
2941
|
+
} catch (cause) {
|
|
2942
|
+
throw new AeadVerificationError2("xchacha20-poly1305 decrypt failed", { cause });
|
|
2763
2943
|
}
|
|
2764
2944
|
}
|
|
2765
|
-
function
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
nonce: enc.nonce,
|
|
2781
|
-
slots: x25519Slots,
|
|
2782
|
-
slots_mac: enc.slots_mac
|
|
2783
|
-
};
|
|
2945
|
+
function sha2564(input) {
|
|
2946
|
+
return sha256(input);
|
|
2947
|
+
}
|
|
2948
|
+
function blake2b256(input) {
|
|
2949
|
+
return blake2b(input, { dkLen: 32 });
|
|
2950
|
+
}
|
|
2951
|
+
function blake2b2242(input) {
|
|
2952
|
+
return blake2b(input, { dkLen: 28 });
|
|
2953
|
+
}
|
|
2954
|
+
var LEAF_PREFIX = 0;
|
|
2955
|
+
var NODE_PREFIX = 1;
|
|
2956
|
+
var DIGEST_LENGTH = 32;
|
|
2957
|
+
function validateLeaves(leaves, fnName) {
|
|
2958
|
+
if (leaves.length === 0) {
|
|
2959
|
+
throw new Error(`${fnName}: empty leaf list (n == 0 is forbidden by RFC 9162 \xA72.1.1)`);
|
|
2784
2960
|
}
|
|
2785
|
-
|
|
2786
|
-
const
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2961
|
+
for (let i = 0; i < leaves.length; i++) {
|
|
2962
|
+
const leaf = leaves[i];
|
|
2963
|
+
if (!(leaf instanceof Uint8Array) || leaf.length !== DIGEST_LENGTH) {
|
|
2964
|
+
throw new Error(
|
|
2965
|
+
`${fnName}: leaf[${i}] must be a Uint8Array(${DIGEST_LENGTH}); got length ${leaf instanceof Uint8Array ? leaf.length : "non-Uint8Array"}`
|
|
2966
|
+
);
|
|
2790
2967
|
}
|
|
2791
|
-
return {
|
|
2792
|
-
scheme: 1,
|
|
2793
|
-
aead: "xchacha20-poly1305",
|
|
2794
|
-
kem: "mlkem768x25519",
|
|
2795
|
-
nonce: enc.nonce,
|
|
2796
|
-
slots: hybridSlots,
|
|
2797
|
-
slots_mac: enc.slots_mac
|
|
2798
|
-
};
|
|
2799
2968
|
}
|
|
2800
|
-
|
|
2969
|
+
}
|
|
2970
|
+
function merkleSha2256Root(leaves) {
|
|
2971
|
+
validateLeaves(leaves, "merkleSha2256Root");
|
|
2972
|
+
return mthRecursive(leaves, 0, leaves.length);
|
|
2973
|
+
}
|
|
2974
|
+
function largestPow2Lt(n) {
|
|
2975
|
+
let k = 1;
|
|
2976
|
+
while (k * 2 < n) k *= 2;
|
|
2977
|
+
return k;
|
|
2978
|
+
}
|
|
2979
|
+
function hashLeaf(d) {
|
|
2980
|
+
const buf = new Uint8Array(1 + d.length);
|
|
2981
|
+
buf[0] = LEAF_PREFIX;
|
|
2982
|
+
buf.set(d, 1);
|
|
2983
|
+
return sha256(buf);
|
|
2984
|
+
}
|
|
2985
|
+
function hashNode(left, right) {
|
|
2986
|
+
const buf = new Uint8Array(1 + left.length + right.length);
|
|
2987
|
+
buf[0] = NODE_PREFIX;
|
|
2988
|
+
buf.set(left, 1);
|
|
2989
|
+
buf.set(right, 1 + left.length);
|
|
2990
|
+
return sha256(buf);
|
|
2991
|
+
}
|
|
2992
|
+
function mthRecursive(leaves, start, end) {
|
|
2993
|
+
const n = end - start;
|
|
2994
|
+
if (n === 1) {
|
|
2995
|
+
return hashLeaf(leaves[start]);
|
|
2996
|
+
}
|
|
2997
|
+
const k = largestPow2Lt(n);
|
|
2998
|
+
const left = mthRecursive(leaves, start, start + k);
|
|
2999
|
+
const right = mthRecursive(leaves, start + k, end);
|
|
3000
|
+
return hashNode(left, right);
|
|
2801
3001
|
}
|
|
2802
3002
|
|
|
2803
3003
|
// ../crypto-core/dist/util.js
|
|
@@ -3194,7 +3394,53 @@ async function fetchItemCiphertext(args) {
|
|
|
3194
3394
|
|
|
3195
3395
|
// src/verifier/decrypt.ts
|
|
3196
3396
|
var PASSPHRASE_KDF_ARGON2ID = "argon2id";
|
|
3197
|
-
var
|
|
3397
|
+
var MAX_PASSPHRASE_INPUT_BYTES = 4096;
|
|
3398
|
+
var UNICODE_WHITE_SPACE = /* @__PURE__ */ new Set([
|
|
3399
|
+
9,
|
|
3400
|
+
10,
|
|
3401
|
+
11,
|
|
3402
|
+
12,
|
|
3403
|
+
13,
|
|
3404
|
+
32,
|
|
3405
|
+
133,
|
|
3406
|
+
160,
|
|
3407
|
+
5760,
|
|
3408
|
+
8192,
|
|
3409
|
+
8193,
|
|
3410
|
+
8194,
|
|
3411
|
+
8195,
|
|
3412
|
+
8196,
|
|
3413
|
+
8197,
|
|
3414
|
+
8198,
|
|
3415
|
+
8199,
|
|
3416
|
+
8200,
|
|
3417
|
+
8201,
|
|
3418
|
+
8202,
|
|
3419
|
+
8232,
|
|
3420
|
+
8233,
|
|
3421
|
+
8239,
|
|
3422
|
+
8287,
|
|
3423
|
+
12288
|
|
3424
|
+
]);
|
|
3425
|
+
function normalizePassphrase(passphrase) {
|
|
3426
|
+
const nfkc = passphrase.normalize("NFKC");
|
|
3427
|
+
let collapsed = "";
|
|
3428
|
+
let inRun = false;
|
|
3429
|
+
for (const ch of nfkc) {
|
|
3430
|
+
if (UNICODE_WHITE_SPACE.has(ch.codePointAt(0))) {
|
|
3431
|
+
if (!inRun) {
|
|
3432
|
+
collapsed += " ";
|
|
3433
|
+
inRun = true;
|
|
3434
|
+
}
|
|
3435
|
+
} else {
|
|
3436
|
+
collapsed += ch;
|
|
3437
|
+
inRun = false;
|
|
3438
|
+
}
|
|
3439
|
+
}
|
|
3440
|
+
if (collapsed.startsWith(" ")) collapsed = collapsed.slice(1);
|
|
3441
|
+
if (collapsed.endsWith(" ")) collapsed = collapsed.slice(0, -1);
|
|
3442
|
+
return new TextEncoder().encode(collapsed);
|
|
3443
|
+
}
|
|
3198
3444
|
async function tryDecryptions(args) {
|
|
3199
3445
|
const { record, input } = args;
|
|
3200
3446
|
const items = record.items ?? [];
|
|
@@ -3313,7 +3559,7 @@ async function tryDecryptions(args) {
|
|
|
3313
3559
|
passphrase: req.passphrase
|
|
3314
3560
|
});
|
|
3315
3561
|
} catch (e) {
|
|
3316
|
-
if (e instanceof
|
|
3562
|
+
if (e instanceof AeadVerificationError2) {
|
|
3317
3563
|
failure = { verdict: "tampered-ciphertext", reason: "TAMPERED_CIPHERTEXT" };
|
|
3318
3564
|
} else if (e instanceof Error && e.message.startsWith("KDF_")) {
|
|
3319
3565
|
failure = { verdict: "kdf-failed", reason: e.message };
|
|
@@ -3343,8 +3589,13 @@ async function decryptPassphrase(args) {
|
|
|
3343
3589
|
if (enc.passphrase.alg !== PASSPHRASE_KDF_ARGON2ID) {
|
|
3344
3590
|
throw new Error(`KDF_DERIVATION_FAILED: unsupported passphrase alg ${enc.passphrase.alg}`);
|
|
3345
3591
|
}
|
|
3346
|
-
const
|
|
3347
|
-
|
|
3592
|
+
const rawPassphraseBytes = new TextEncoder().encode(passphrase).length;
|
|
3593
|
+
if (rawPassphraseBytes > MAX_PASSPHRASE_INPUT_BYTES) {
|
|
3594
|
+
throw new Error(
|
|
3595
|
+
`KDF_DERIVATION_FAILED: passphrase length ${rawPassphraseBytes} bytes exceeds the maximum ${MAX_PASSPHRASE_INPUT_BYTES} bytes`
|
|
3596
|
+
);
|
|
3597
|
+
}
|
|
3598
|
+
const password = normalizePassphrase(passphrase);
|
|
3348
3599
|
let cek;
|
|
3349
3600
|
try {
|
|
3350
3601
|
cek = await argon2idV13({
|
|
@@ -3362,10 +3613,20 @@ async function decryptPassphrase(args) {
|
|
|
3362
3613
|
if (enc.aead !== "xchacha20-poly1305") {
|
|
3363
3614
|
throw new Error(`KDF_DERIVATION_FAILED: unsupported aead ${enc.aead}`);
|
|
3364
3615
|
}
|
|
3365
|
-
|
|
3366
|
-
|
|
3616
|
+
assertCiphertextWithinBound(ciphertext.length);
|
|
3617
|
+
const payloadKey = passphrasePayloadKey({ cek, nonce: enc.nonce });
|
|
3618
|
+
const aad = adContentPassphrase({
|
|
3619
|
+
nonce: enc.nonce,
|
|
3620
|
+
passphrase: {
|
|
3621
|
+
alg: enc.passphrase.alg,
|
|
3622
|
+
salt: enc.passphrase.salt,
|
|
3623
|
+
params: enc.passphrase.params
|
|
3624
|
+
}
|
|
3625
|
+
});
|
|
3626
|
+
return xchacha20Poly1305Decrypt2({
|
|
3627
|
+
key: payloadKey,
|
|
3367
3628
|
nonce: enc.nonce,
|
|
3368
|
-
aad
|
|
3629
|
+
aad,
|
|
3369
3630
|
ciphertext
|
|
3370
3631
|
});
|
|
3371
3632
|
}
|
|
@@ -3374,7 +3635,7 @@ function recomputeHashes(item, plaintext) {
|
|
|
3374
3635
|
if (entries.length === 0) return false;
|
|
3375
3636
|
for (const [alg, digest] of entries) {
|
|
3376
3637
|
if (alg === "sha2-256") {
|
|
3377
|
-
if (!compareCt3(
|
|
3638
|
+
if (!compareCt3(sha2564(plaintext), digest)) return false;
|
|
3378
3639
|
} else if (alg === "blake2b-256") {
|
|
3379
3640
|
if (!compareCt3(blake2b256(plaintext), digest)) return false;
|
|
3380
3641
|
} else {
|
|
@@ -4141,7 +4402,7 @@ function hexToBytes(hex) {
|
|
|
4141
4402
|
}
|
|
4142
4403
|
|
|
4143
4404
|
// src/hex.ts
|
|
4144
|
-
function
|
|
4405
|
+
function bytesToHex2(bytes) {
|
|
4145
4406
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
4146
4407
|
}
|
|
4147
4408
|
|
|
@@ -4186,7 +4447,7 @@ async function verifyOneSig(index, entry, recordBodyCbor, input) {
|
|
|
4186
4447
|
index,
|
|
4187
4448
|
verdict: "unsupported",
|
|
4188
4449
|
signer_type: signerType,
|
|
4189
|
-
signer_pub:
|
|
4450
|
+
signer_pub: bytesToHex2(pub),
|
|
4190
4451
|
reason
|
|
4191
4452
|
};
|
|
4192
4453
|
}
|
|
@@ -4194,7 +4455,7 @@ async function verifyOneSig(index, entry, recordBodyCbor, input) {
|
|
|
4194
4455
|
index,
|
|
4195
4456
|
verdict: "invalid",
|
|
4196
4457
|
signer_type: signerType,
|
|
4197
|
-
signer_pub:
|
|
4458
|
+
signer_pub: bytesToHex2(pub),
|
|
4198
4459
|
reason
|
|
4199
4460
|
};
|
|
4200
4461
|
}
|
|
@@ -4205,7 +4466,7 @@ async function verifyOneSig(index, entry, recordBodyCbor, input) {
|
|
|
4205
4466
|
index,
|
|
4206
4467
|
verdict: "invalid",
|
|
4207
4468
|
signer_type: signerType,
|
|
4208
|
-
signer_pub:
|
|
4469
|
+
signer_pub: bytesToHex2(pub),
|
|
4209
4470
|
reason: "WALLET_ADDRESS_MISMATCH"
|
|
4210
4471
|
};
|
|
4211
4472
|
}
|
|
@@ -4214,7 +4475,7 @@ async function verifyOneSig(index, entry, recordBodyCbor, input) {
|
|
|
4214
4475
|
index,
|
|
4215
4476
|
verdict: "valid",
|
|
4216
4477
|
signer_type: signerType,
|
|
4217
|
-
signer_pub:
|
|
4478
|
+
signer_pub: bytesToHex2(pub)
|
|
4218
4479
|
};
|
|
4219
4480
|
}
|
|
4220
4481
|
function resolveSignerKey(cose, entry) {
|
|
@@ -4340,8 +4601,8 @@ function decodeTxWitnesses(witnessSetBytes, txBodyBytes) {
|
|
|
4340
4601
|
if (vkey instanceof Uint8Array && vkey.length === ED25519_PUBLIC_KEY_LENGTH3) {
|
|
4341
4602
|
out.push({
|
|
4342
4603
|
type: "vkey",
|
|
4343
|
-
vkey:
|
|
4344
|
-
key_hash:
|
|
4604
|
+
vkey: bytesToHex2(vkey),
|
|
4605
|
+
key_hash: bytesToHex2(blake2b2242(vkey)),
|
|
4345
4606
|
signature_valid: false
|
|
4346
4607
|
});
|
|
4347
4608
|
}
|
|
@@ -4355,8 +4616,8 @@ function decodeTxWitnesses(witnessSetBytes, txBodyBytes) {
|
|
|
4355
4616
|
}
|
|
4356
4617
|
out.push({
|
|
4357
4618
|
type: "vkey",
|
|
4358
|
-
vkey:
|
|
4359
|
-
key_hash:
|
|
4619
|
+
vkey: bytesToHex2(vkey),
|
|
4620
|
+
key_hash: bytesToHex2(blake2b2242(vkey)),
|
|
4360
4621
|
signature_valid: signatureValid
|
|
4361
4622
|
});
|
|
4362
4623
|
}
|
|
@@ -4389,7 +4650,7 @@ function decodeTxSummary(txBodyBytes, witnessSetBytes, network) {
|
|
|
4389
4650
|
lovelace: lovelace.toString()
|
|
4390
4651
|
});
|
|
4391
4652
|
}
|
|
4392
|
-
const requiredSigners = asArray(body.get(BODY_KEY_REQUIRED_SIGNERS)).filter((s) => s instanceof Uint8Array).map((s) =>
|
|
4653
|
+
const requiredSigners = asArray(body.get(BODY_KEY_REQUIRED_SIGNERS)).filter((s) => s instanceof Uint8Array).map((s) => bytesToHex2(s));
|
|
4393
4654
|
const summary = {
|
|
4394
4655
|
fee_lovelace: coinToString(body.get(BODY_KEY_FEE)),
|
|
4395
4656
|
input_count: inputs.length,
|