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