@cardanowall/crypto-core 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 +9 -9
- package/dist/canonical-DHeJLYDR.d.cts +7 -0
- package/dist/canonical-DHeJLYDR.d.ts +7 -0
- package/dist/cbor.cjs +1 -1
- package/dist/cbor.cjs.map +1 -1
- package/dist/cbor.d.cts +2 -6
- package/dist/cbor.d.ts +2 -6
- package/dist/cbor.js +1 -1
- package/dist/cbor.js.map +1 -1
- package/dist/cose.cjs +11 -11
- package/dist/cose.cjs.map +1 -1
- package/dist/cose.d.cts +7 -7
- package/dist/cose.d.ts +7 -7
- package/dist/cose.js +9 -9
- package/dist/cose.js.map +1 -1
- package/dist/index.cjs +291 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +270 -118
- package/dist/index.js.map +1 -1
- package/dist/kem.cjs.map +1 -1
- package/dist/kem.js.map +1 -1
- package/dist/merkle.cjs +1 -1
- package/dist/merkle.cjs.map +1 -1
- package/dist/merkle.js +1 -1
- package/dist/merkle.js.map +1 -1
- package/dist/sealed-poe.cjs +299 -118
- package/dist/sealed-poe.cjs.map +1 -1
- package/dist/sealed-poe.d.cts +55 -3
- package/dist/sealed-poe.d.ts +55 -3
- package/dist/sealed-poe.js +281 -118
- package/dist/sealed-poe.js.map +1 -1
- package/dist/seed-derive.cjs.map +1 -1
- package/dist/seed-derive.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -361,7 +361,7 @@ function decodeCanonicalCbor(bytes) {
|
|
|
361
361
|
...cdeDecodeOptions,
|
|
362
362
|
rejectStreaming: true,
|
|
363
363
|
rejectDuplicateKeys: true,
|
|
364
|
-
// A
|
|
364
|
+
// A Label 309 record carries integers, byte/text strings, arrays, maps and
|
|
365
365
|
// `null` — and nothing else. Without these rejections the major-type-7
|
|
366
366
|
// surface leaks into the decoder: a float16/32/64 that happens to hold an
|
|
367
367
|
// integral value (e.g. 1.0) silently decodes to the integer 1 and passes
|
|
@@ -421,7 +421,7 @@ function buildSigStructure(args) {
|
|
|
421
421
|
args.payload
|
|
422
422
|
]);
|
|
423
423
|
}
|
|
424
|
-
function
|
|
424
|
+
function buildLabel309SigStructure(args) {
|
|
425
425
|
const toSign = new Uint8Array(
|
|
426
426
|
CARDANO_POE_SIG_DOMAIN_PREFIX_BYTES.length + args.recordBodyCbor.length
|
|
427
427
|
);
|
|
@@ -512,21 +512,21 @@ var CoseSign1BuildError = class extends Error {
|
|
|
512
512
|
this.code = code;
|
|
513
513
|
}
|
|
514
514
|
};
|
|
515
|
-
function
|
|
515
|
+
function coseSign1Label309Build(args) {
|
|
516
516
|
if (args.signerSecretKey === void 0 && args.signer === void 0) {
|
|
517
517
|
throw new CoseSign1BuildError(
|
|
518
518
|
"SIGNER_NOT_PROVIDED",
|
|
519
|
-
"
|
|
519
|
+
"coseSign1Label309Build requires either signerSecretKey or signer"
|
|
520
520
|
);
|
|
521
521
|
}
|
|
522
522
|
if (args.signerSecretKey !== void 0 && args.signer !== void 0) {
|
|
523
523
|
throw new CoseSign1BuildError(
|
|
524
524
|
"SIGNER_AND_SEED_BOTH_PROVIDED",
|
|
525
|
-
"
|
|
525
|
+
"coseSign1Label309Build accepts signerSecretKey XOR signer (not both)"
|
|
526
526
|
);
|
|
527
527
|
}
|
|
528
528
|
const protectedBytes = args.protectedHeader.size === 0 ? EMPTY_BYTES : encodeCanonicalCbor(args.protectedHeader);
|
|
529
|
-
const sigStructureBytes =
|
|
529
|
+
const sigStructureBytes = buildLabel309SigStructure({
|
|
530
530
|
bodyProtectedBytes: protectedBytes,
|
|
531
531
|
recordBodyCbor: args.recordBodyCbor
|
|
532
532
|
});
|
|
@@ -549,7 +549,7 @@ function coseSign1Cip309Build(args) {
|
|
|
549
549
|
signature
|
|
550
550
|
});
|
|
551
551
|
}
|
|
552
|
-
function
|
|
552
|
+
function coseSign1Label309Verify(args) {
|
|
553
553
|
let decoded;
|
|
554
554
|
try {
|
|
555
555
|
decoded = decodeCoseSign1(args.message);
|
|
@@ -616,7 +616,7 @@ function coseSign1Cip309Verify(args) {
|
|
|
616
616
|
payload: hashedPayload
|
|
617
617
|
});
|
|
618
618
|
} else {
|
|
619
|
-
sigStructureBytes =
|
|
619
|
+
sigStructureBytes = buildLabel309SigStructure({
|
|
620
620
|
bodyProtectedBytes: decoded.protectedBytes,
|
|
621
621
|
recordBodyCbor: args.detachedRecordBodyCbor
|
|
622
622
|
});
|
|
@@ -775,27 +775,141 @@ function joinKemCt(chunks) {
|
|
|
775
775
|
}
|
|
776
776
|
return out;
|
|
777
777
|
}
|
|
778
|
-
function
|
|
779
|
-
let value;
|
|
778
|
+
function canonicalizeSlots(slots, kem) {
|
|
780
779
|
if (kem === "x25519") {
|
|
781
|
-
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
780
|
+
return slots.map((s) => ({ epk: s.epk, wrap: s.wrap }));
|
|
781
|
+
}
|
|
782
|
+
return slots.map((s) => ({
|
|
783
|
+
kem_ct: chunkKemCt(joinKemCt(s.kem_ct)),
|
|
784
|
+
wrap: s.wrap
|
|
785
|
+
}));
|
|
786
|
+
}
|
|
787
|
+
var CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX = new TextEncoder().encode(
|
|
788
|
+
"cardano-poe-slots-transcript-v1"
|
|
789
|
+
);
|
|
790
|
+
var CARDANO_POE_HKDF_INFO_PAYLOAD = new TextEncoder().encode(
|
|
791
|
+
"cardano-poe-payload-v1"
|
|
792
|
+
);
|
|
793
|
+
var CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE = new TextEncoder().encode(
|
|
794
|
+
"cardano-poe-payload-passphrase-v1"
|
|
795
|
+
);
|
|
796
|
+
var CARDANO_POE_XWING_KEK_SALT_PREFIX = new TextEncoder().encode(
|
|
797
|
+
"cardano-poe-xwing-kek-salt-v1"
|
|
798
|
+
);
|
|
799
|
+
if (CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length !== 31) {
|
|
800
|
+
throw new Error(
|
|
801
|
+
"CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX byte-length invariant violated (expected 31)"
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
if (CARDANO_POE_HKDF_INFO_PAYLOAD.length !== 22) {
|
|
805
|
+
throw new Error("CARDANO_POE_HKDF_INFO_PAYLOAD byte-length invariant violated (expected 22)");
|
|
806
|
+
}
|
|
807
|
+
if (CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE.length !== 33) {
|
|
808
|
+
throw new Error(
|
|
809
|
+
"CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE byte-length invariant violated (expected 33)"
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
if (CARDANO_POE_XWING_KEK_SALT_PREFIX.length !== 29) {
|
|
813
|
+
throw new Error("CARDANO_POE_XWING_KEK_SALT_PREFIX byte-length invariant violated (expected 29)");
|
|
814
|
+
}
|
|
815
|
+
var CARDANO_POE_PW_NORM_PROFILE = "cardano-poe-pw-norm-v1";
|
|
816
|
+
var MAX_SLOTS = 1024;
|
|
817
|
+
var MAX_DECODED_ENVELOPE_BYTES = 65536;
|
|
818
|
+
var MAX_SEALED_PLAINTEXT = 274877906880;
|
|
819
|
+
var MAX_SEALED_CIPHERTEXT = MAX_SEALED_PLAINTEXT + 16;
|
|
820
|
+
function assertPlaintextWithinBound(plaintextLength) {
|
|
821
|
+
if (plaintextLength >= MAX_SEALED_PLAINTEXT) {
|
|
822
|
+
throw new SealedPayloadTooLargeError(
|
|
823
|
+
`plaintext length ${plaintextLength} is at or above the maximum sealed payload size ${MAX_SEALED_PLAINTEXT}`
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
function assertCiphertextWithinBound(ciphertextLength) {
|
|
828
|
+
if (ciphertextLength >= MAX_SEALED_CIPHERTEXT) {
|
|
829
|
+
throw new SealedPayloadTooLargeError(
|
|
830
|
+
`ciphertext length ${ciphertextLength} is at or above the maximum sealed ciphertext size ${MAX_SEALED_CIPHERTEXT}`
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
var SealedPayloadTooLargeError = class extends Error {
|
|
835
|
+
constructor(message) {
|
|
836
|
+
super(message);
|
|
837
|
+
this.name = "SealedPayloadTooLargeError";
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
function computeSlotsHash(args) {
|
|
841
|
+
const transcript = {
|
|
842
|
+
scheme: 1,
|
|
843
|
+
path: "slots",
|
|
844
|
+
aead: "xchacha20-poly1305",
|
|
845
|
+
kem: args.kem,
|
|
846
|
+
nonce: args.nonce,
|
|
847
|
+
slots: canonicalizeSlots(args.slots, args.kem)
|
|
848
|
+
};
|
|
849
|
+
const encoded = encodeCanonicalCbor(transcript);
|
|
850
|
+
const message = new Uint8Array(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length + encoded.length);
|
|
851
|
+
message.set(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX, 0);
|
|
852
|
+
message.set(encoded, CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length);
|
|
853
|
+
return sha256$1(message);
|
|
854
|
+
}
|
|
855
|
+
function adContentSlots(args) {
|
|
856
|
+
const ad = {
|
|
857
|
+
scheme: 1,
|
|
858
|
+
path: "slots",
|
|
859
|
+
aead: "xchacha20-poly1305",
|
|
860
|
+
kem: args.kem,
|
|
861
|
+
nonce: args.nonce,
|
|
862
|
+
slots_hash: args.slotsHash,
|
|
863
|
+
slots_mac: args.slotsMac
|
|
864
|
+
};
|
|
865
|
+
return encodeCanonicalCbor(ad);
|
|
866
|
+
}
|
|
867
|
+
function adContentPassphrase(args) {
|
|
868
|
+
const ad = {
|
|
869
|
+
scheme: 1,
|
|
870
|
+
path: "passphrase",
|
|
871
|
+
aead: "xchacha20-poly1305",
|
|
872
|
+
nonce: args.nonce,
|
|
873
|
+
passphrase: {
|
|
874
|
+
alg: args.passphrase.alg,
|
|
875
|
+
salt: args.passphrase.salt,
|
|
876
|
+
params: {
|
|
877
|
+
m: args.passphrase.params.m,
|
|
878
|
+
t: args.passphrase.params.t,
|
|
879
|
+
p: args.passphrase.params.p
|
|
880
|
+
},
|
|
881
|
+
normalization: CARDANO_POE_PW_NORM_PROFILE
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
return encodeCanonicalCbor(ad);
|
|
885
|
+
}
|
|
886
|
+
function slotsPayloadKey(args) {
|
|
887
|
+
return hkdfSha256({
|
|
888
|
+
ikm: args.cek,
|
|
889
|
+
salt: args.nonce,
|
|
890
|
+
info: CARDANO_POE_HKDF_INFO_PAYLOAD,
|
|
891
|
+
length: 32
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
function passphrasePayloadKey(args) {
|
|
895
|
+
return hkdfSha256({
|
|
896
|
+
ikm: args.cek,
|
|
897
|
+
salt: args.nonce,
|
|
898
|
+
info: CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE,
|
|
899
|
+
length: 32
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
function xwingKekSalt(args) {
|
|
903
|
+
const message = new Uint8Array(
|
|
904
|
+
CARDANO_POE_XWING_KEK_SALT_PREFIX.length + args.kemCt.length + args.pubR.length
|
|
905
|
+
);
|
|
906
|
+
let offset = 0;
|
|
907
|
+
message.set(CARDANO_POE_XWING_KEK_SALT_PREFIX, offset);
|
|
908
|
+
offset += CARDANO_POE_XWING_KEK_SALT_PREFIX.length;
|
|
909
|
+
message.set(args.kemCt, offset);
|
|
910
|
+
offset += args.kemCt.length;
|
|
911
|
+
message.set(args.pubR, offset);
|
|
912
|
+
return sha256$1(message);
|
|
799
913
|
}
|
|
800
914
|
|
|
801
915
|
// src/sealed-poe/wrap.ts
|
|
@@ -889,7 +1003,7 @@ function wrapSlotMlkem768X25519(args) {
|
|
|
889
1003
|
}
|
|
890
1004
|
const kek = hkdfSha256({
|
|
891
1005
|
ikm: ss,
|
|
892
|
-
salt:
|
|
1006
|
+
salt: xwingKekSalt({ kemCt: enc, pubR: args.pubR }),
|
|
893
1007
|
info: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
|
|
894
1008
|
length: 32
|
|
895
1009
|
});
|
|
@@ -908,6 +1022,7 @@ function eciesSealedPoeWrap(args) {
|
|
|
908
1022
|
const { plaintext, recipientPublicKeys } = args;
|
|
909
1023
|
const kem = args.kem ?? "x25519";
|
|
910
1024
|
const n = recipientPublicKeys.length;
|
|
1025
|
+
assertPlaintextWithinBound(plaintext.length);
|
|
911
1026
|
if (n < 1) {
|
|
912
1027
|
throw new EciesSealedPoeError(
|
|
913
1028
|
"ENC_SLOTS_EMPTY",
|
|
@@ -977,6 +1092,7 @@ function eciesSealedPoeWrap(args) {
|
|
|
977
1092
|
);
|
|
978
1093
|
}
|
|
979
1094
|
let envelope;
|
|
1095
|
+
let slotsHash;
|
|
980
1096
|
if (kem === "x25519") {
|
|
981
1097
|
const slots = [];
|
|
982
1098
|
for (let i = 0; i < n; i++) {
|
|
@@ -992,14 +1108,14 @@ function eciesSealedPoeWrap(args) {
|
|
|
992
1108
|
if (args.skipShuffle !== true) {
|
|
993
1109
|
csprngShuffle(slots);
|
|
994
1110
|
}
|
|
995
|
-
|
|
1111
|
+
slotsHash = computeSlotsHash({ kem: "x25519", nonce, slots });
|
|
996
1112
|
envelope = {
|
|
997
1113
|
scheme: 1,
|
|
998
1114
|
aead: "xchacha20-poly1305",
|
|
999
1115
|
kem: "x25519",
|
|
1000
1116
|
nonce,
|
|
1001
1117
|
slots,
|
|
1002
|
-
slots_mac:
|
|
1118
|
+
slots_mac: computeSlotsMac(cek, slotsHash)
|
|
1003
1119
|
};
|
|
1004
1120
|
} else {
|
|
1005
1121
|
const slots = [];
|
|
@@ -1015,34 +1131,39 @@ function eciesSealedPoeWrap(args) {
|
|
|
1015
1131
|
if (args.skipShuffle !== true) {
|
|
1016
1132
|
csprngShuffle(slots);
|
|
1017
1133
|
}
|
|
1018
|
-
|
|
1134
|
+
slotsHash = computeSlotsHash({ kem: "mlkem768x25519", nonce, slots });
|
|
1019
1135
|
envelope = {
|
|
1020
1136
|
scheme: 1,
|
|
1021
1137
|
aead: "xchacha20-poly1305",
|
|
1022
1138
|
kem: "mlkem768x25519",
|
|
1023
1139
|
nonce,
|
|
1024
1140
|
slots,
|
|
1025
|
-
slots_mac:
|
|
1141
|
+
slots_mac: computeSlotsMac(cek, slotsHash)
|
|
1026
1142
|
};
|
|
1027
1143
|
}
|
|
1028
|
-
const
|
|
1144
|
+
const payloadKey = slotsPayloadKey({ cek, nonce });
|
|
1145
|
+
const adContent = adContentSlots({
|
|
1146
|
+
kem: envelope.kem,
|
|
1147
|
+
nonce,
|
|
1148
|
+
slotsHash,
|
|
1149
|
+
slotsMac: envelope.slots_mac
|
|
1150
|
+
});
|
|
1029
1151
|
const ciphertext = xchacha20Poly1305Encrypt({
|
|
1030
|
-
key:
|
|
1152
|
+
key: payloadKey,
|
|
1031
1153
|
nonce,
|
|
1032
1154
|
aad: adContent,
|
|
1033
1155
|
plaintext
|
|
1034
1156
|
});
|
|
1035
1157
|
return { envelope, ciphertext };
|
|
1036
1158
|
}
|
|
1037
|
-
function computeSlotsMac(cek,
|
|
1159
|
+
function computeSlotsMac(cek, slotsHash) {
|
|
1038
1160
|
const hmacKey = hkdfSha256({
|
|
1039
1161
|
ikm: cek,
|
|
1040
1162
|
salt: EMPTY_SALT2,
|
|
1041
1163
|
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1042
1164
|
length: 32
|
|
1043
1165
|
});
|
|
1044
|
-
const
|
|
1045
|
-
const slotsMac = hmac(sha256$1, hmacKey, slotsCbor);
|
|
1166
|
+
const slotsMac = hmac(sha256$1, hmacKey, slotsHash);
|
|
1046
1167
|
if (slotsMac.length !== SLOTS_MAC_LENGTH) {
|
|
1047
1168
|
throw new Error(`internal: slots_mac.length=${slotsMac.length}, expected ${SLOTS_MAC_LENGTH}`);
|
|
1048
1169
|
}
|
|
@@ -1064,6 +1185,13 @@ function concat2(a, b) {
|
|
|
1064
1185
|
out.set(b, a.length);
|
|
1065
1186
|
return out;
|
|
1066
1187
|
}
|
|
1188
|
+
function bytesKey(bytes) {
|
|
1189
|
+
let s = "";
|
|
1190
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
1191
|
+
s += String.fromCharCode(bytes[i]);
|
|
1192
|
+
}
|
|
1193
|
+
return s;
|
|
1194
|
+
}
|
|
1067
1195
|
function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
1068
1196
|
if (envelope.scheme !== 1) {
|
|
1069
1197
|
throw new EciesSealedPoeError(
|
|
@@ -1087,6 +1215,12 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1087
1215
|
if (n < 1) {
|
|
1088
1216
|
throw new EciesSealedPoeError("ENC_SLOTS_EMPTY", `envelope.slots.length=${n} must be >= 1`);
|
|
1089
1217
|
}
|
|
1218
|
+
if (n > MAX_SLOTS) {
|
|
1219
|
+
throw new EciesSealedPoeError(
|
|
1220
|
+
"ENC_SLOTS_TOO_MANY",
|
|
1221
|
+
`envelope.slots.length=${n} exceeds MAX_SLOTS=${MAX_SLOTS}`
|
|
1222
|
+
);
|
|
1223
|
+
}
|
|
1090
1224
|
if (envelope.nonce.length !== NONCE_LENGTH2) {
|
|
1091
1225
|
throw new EciesSealedPoeError(
|
|
1092
1226
|
"NONCE_LENGTH_MISMATCH",
|
|
@@ -1099,6 +1233,7 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1099
1233
|
`envelope.slots_mac MUST be exactly ${SLOTS_MAC_LENGTH2} bytes, got ${envelope.slots_mac.length}`
|
|
1100
1234
|
);
|
|
1101
1235
|
}
|
|
1236
|
+
const seenKemMaterial = /* @__PURE__ */ new Set();
|
|
1102
1237
|
if (envelope.kem === "x25519") {
|
|
1103
1238
|
for (let i = 0; i < n; i++) {
|
|
1104
1239
|
const slot = envelope.slots[i];
|
|
@@ -1114,6 +1249,14 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1114
1249
|
`envelope.slots[${i}].wrap MUST be exactly ${WRAP_LENGTH2} bytes, got ${slot.wrap.length}`
|
|
1115
1250
|
);
|
|
1116
1251
|
}
|
|
1252
|
+
const key = bytesKey(slot.epk);
|
|
1253
|
+
if (seenKemMaterial.has(key)) {
|
|
1254
|
+
throw new EciesSealedPoeError(
|
|
1255
|
+
"ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
|
|
1256
|
+
`envelope.slots[${i}].epk duplicates an earlier slot \u2014 per-slot KEK uniqueness is violated`
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
seenKemMaterial.add(key);
|
|
1117
1260
|
}
|
|
1118
1261
|
} else {
|
|
1119
1262
|
for (let i = 0; i < n; i++) {
|
|
@@ -1131,8 +1274,24 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1131
1274
|
`envelope.slots[${i}].wrap MUST be exactly ${WRAP_LENGTH2} bytes, got ${slot.wrap.length}`
|
|
1132
1275
|
);
|
|
1133
1276
|
}
|
|
1277
|
+
const key = bytesKey(enc);
|
|
1278
|
+
if (seenKemMaterial.has(key)) {
|
|
1279
|
+
throw new EciesSealedPoeError(
|
|
1280
|
+
"ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
|
|
1281
|
+
`envelope.slots[${i}].kem_ct duplicates an earlier slot \u2014 per-slot KEK uniqueness is violated`
|
|
1282
|
+
);
|
|
1283
|
+
}
|
|
1284
|
+
seenKemMaterial.add(key);
|
|
1134
1285
|
}
|
|
1135
1286
|
}
|
|
1287
|
+
const perSlotBytes = envelope.kem === "x25519" ? X25519_PUBLIC_KEY_LENGTH2 + WRAP_LENGTH2 : MLKEM768X25519_ENC_LENGTH + WRAP_LENGTH2;
|
|
1288
|
+
const decodedEnvelopeBytes = NONCE_LENGTH2 + SLOTS_MAC_LENGTH2 + n * perSlotBytes;
|
|
1289
|
+
if (decodedEnvelopeBytes > MAX_DECODED_ENVELOPE_BYTES) {
|
|
1290
|
+
throw new EciesSealedPoeError(
|
|
1291
|
+
"ENC_ENVELOPE_TOO_LARGE",
|
|
1292
|
+
`decoded envelope size ${decodedEnvelopeBytes} exceeds MAX_DECODED_ENVELOPE_BYTES=${MAX_DECODED_ENVELOPE_BYTES}`
|
|
1293
|
+
);
|
|
1294
|
+
}
|
|
1136
1295
|
if (multiPrivKeys !== void 0) {
|
|
1137
1296
|
for (let i = 0; i < multiPrivKeys.length; i++) {
|
|
1138
1297
|
if (multiPrivKeys[i].length !== X25519_SECRET_KEY_LENGTH2) {
|
|
@@ -1151,60 +1310,42 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1151
1310
|
}
|
|
1152
1311
|
}
|
|
1153
1312
|
}
|
|
1313
|
+
var ZERO_IKM_32 = new Uint8Array(32);
|
|
1154
1314
|
function tryX25519Slot(args) {
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
const shared = x25519Ecdh({
|
|
1158
|
-
secretKey: args.recipientSecretKey,
|
|
1159
|
-
theirPublicKey: args.slot.epk
|
|
1160
|
-
});
|
|
1161
|
-
const kek = hkdfSha256({
|
|
1162
|
-
ikm: shared,
|
|
1163
|
-
salt: concat2(args.slot.epk, args.pubRLocal),
|
|
1164
|
-
info: CARDANO_POE_HKDF_INFO_KEK,
|
|
1165
|
-
length: 32
|
|
1166
|
-
});
|
|
1167
|
-
return chacha20Poly1305Decrypt({
|
|
1168
|
-
key: kek,
|
|
1169
|
-
nonce: ZERO_NONCE_122,
|
|
1170
|
-
aad: CARDANO_POE_HKDF_INFO_KEK,
|
|
1171
|
-
ciphertext: args.slot.wrap
|
|
1172
|
-
});
|
|
1173
|
-
} catch (e) {
|
|
1174
|
-
if (!(e instanceof AeadVerificationError) && !(e instanceof X25519LowOrderPointError)) {
|
|
1175
|
-
throw e;
|
|
1176
|
-
}
|
|
1177
|
-
return null;
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1315
|
+
const salt = concat2(args.slot.epk, args.pubRLocal);
|
|
1316
|
+
let shared;
|
|
1180
1317
|
try {
|
|
1181
|
-
|
|
1318
|
+
shared = x25519Ecdh({
|
|
1182
1319
|
secretKey: args.recipientSecretKey,
|
|
1183
1320
|
theirPublicKey: args.slot.epk
|
|
1184
1321
|
});
|
|
1185
|
-
hkdfSha256({
|
|
1186
|
-
ikm: shared,
|
|
1187
|
-
salt: concat2(args.slot.epk, args.pubRLocal),
|
|
1188
|
-
info: CARDANO_POE_HKDF_INFO_KEK,
|
|
1189
|
-
length: 32
|
|
1190
|
-
});
|
|
1191
1322
|
} catch (e) {
|
|
1192
1323
|
if (!(e instanceof X25519LowOrderPointError)) throw e;
|
|
1324
|
+
hkdfSha256({ ikm: ZERO_IKM_32, salt, info: CARDANO_POE_HKDF_INFO_KEK, length: 32 });
|
|
1325
|
+
return null;
|
|
1326
|
+
}
|
|
1327
|
+
const kek = hkdfSha256({ ikm: shared, salt, info: CARDANO_POE_HKDF_INFO_KEK, length: 32 });
|
|
1328
|
+
try {
|
|
1329
|
+
return chacha20Poly1305Decrypt({
|
|
1330
|
+
key: kek,
|
|
1331
|
+
nonce: ZERO_NONCE_122,
|
|
1332
|
+
aad: CARDANO_POE_HKDF_INFO_KEK,
|
|
1333
|
+
ciphertext: args.slot.wrap
|
|
1334
|
+
});
|
|
1335
|
+
} catch (e) {
|
|
1336
|
+
if (!(e instanceof AeadVerificationError)) throw e;
|
|
1337
|
+
return null;
|
|
1193
1338
|
}
|
|
1194
|
-
return null;
|
|
1195
1339
|
}
|
|
1196
1340
|
function tryMlkem768X25519Slot(args) {
|
|
1197
1341
|
const enc = joinKemCt(args.slot.kem_ct);
|
|
1198
1342
|
const ss = mlkem768x25519Decapsulate({ secretSeed: args.recipientSecretKey, enc });
|
|
1199
1343
|
const kek = hkdfSha256({
|
|
1200
1344
|
ikm: ss,
|
|
1201
|
-
salt:
|
|
1345
|
+
salt: xwingKekSalt({ kemCt: enc, pubR: args.pubR }),
|
|
1202
1346
|
info: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
|
|
1203
1347
|
length: 32
|
|
1204
1348
|
});
|
|
1205
|
-
if (!args.liveSlot) {
|
|
1206
|
-
return null;
|
|
1207
|
-
}
|
|
1208
1349
|
try {
|
|
1209
1350
|
return chacha20Poly1305Decrypt({
|
|
1210
1351
|
key: kek,
|
|
@@ -1221,51 +1362,43 @@ function tryRecipientUnwrapWithIdx(envelope, recipientSecretKey, constantTimeN,
|
|
|
1221
1362
|
const n = envelope.slots.length;
|
|
1222
1363
|
let cek = null;
|
|
1223
1364
|
let matchedSlotIdx = -1;
|
|
1365
|
+
let cekConflict = false;
|
|
1366
|
+
const recordMatch = (candidate, i) => {
|
|
1367
|
+
if (candidate === null) return;
|
|
1368
|
+
if (cek === null) {
|
|
1369
|
+
cek = candidate;
|
|
1370
|
+
matchedSlotIdx = i;
|
|
1371
|
+
} else if (!compareCt(candidate, cek)) {
|
|
1372
|
+
cekConflict = true;
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1224
1375
|
if (envelope.kem === "x25519") {
|
|
1225
1376
|
const pubRLocal = x25519PublicKey({ secretKey: recipientSecretKey });
|
|
1226
1377
|
for (let i = 0; i < n; i++) {
|
|
1227
1378
|
if (slotsAttemptedOut !== void 0) {
|
|
1228
1379
|
slotsAttemptedOut.count = i + 1;
|
|
1229
1380
|
}
|
|
1230
|
-
|
|
1231
|
-
slot: envelope.slots[i],
|
|
1232
|
-
recipientSecretKey,
|
|
1233
|
-
pubRLocal,
|
|
1234
|
-
liveSlot: cek === null
|
|
1235
|
-
});
|
|
1236
|
-
if (cek === null && candidate !== null) {
|
|
1237
|
-
cek = candidate;
|
|
1238
|
-
matchedSlotIdx = i;
|
|
1239
|
-
}
|
|
1381
|
+
recordMatch(tryX25519Slot({ slot: envelope.slots[i], recipientSecretKey, pubRLocal }), i);
|
|
1240
1382
|
if (cek !== null && !constantTimeN) break;
|
|
1241
1383
|
}
|
|
1242
1384
|
} else {
|
|
1385
|
+
const pubR = mlkem768x25519Keygen(recipientSecretKey).publicKey;
|
|
1243
1386
|
for (let i = 0; i < n; i++) {
|
|
1244
1387
|
if (slotsAttemptedOut !== void 0) {
|
|
1245
1388
|
slotsAttemptedOut.count = i + 1;
|
|
1246
1389
|
}
|
|
1247
|
-
|
|
1248
|
-
slot: envelope.slots[i],
|
|
1249
|
-
recipientSecretKey,
|
|
1250
|
-
liveSlot: cek === null
|
|
1251
|
-
});
|
|
1252
|
-
if (cek === null && candidate !== null) {
|
|
1253
|
-
cek = candidate;
|
|
1254
|
-
matchedSlotIdx = i;
|
|
1255
|
-
}
|
|
1390
|
+
recordMatch(tryMlkem768X25519Slot({ slot: envelope.slots[i], recipientSecretKey, pubR }), i);
|
|
1256
1391
|
if (cek !== null && !constantTimeN) break;
|
|
1257
1392
|
}
|
|
1258
1393
|
}
|
|
1259
|
-
return cek === null ? null : { cek, slotIdx: matchedSlotIdx };
|
|
1260
|
-
}
|
|
1261
|
-
function tryRecipientUnwrap(envelope, recipientSecretKey, constantTimeN, slotsAttemptedOut) {
|
|
1262
|
-
return tryRecipientUnwrapWithIdx(envelope, recipientSecretKey, constantTimeN, slotsAttemptedOut)?.cek ?? null;
|
|
1394
|
+
return cek === null ? null : { cek, slotIdx: matchedSlotIdx, cekConflict };
|
|
1263
1395
|
}
|
|
1264
|
-
function
|
|
1265
|
-
return
|
|
1266
|
-
envelope.
|
|
1267
|
-
envelope.
|
|
1268
|
-
|
|
1396
|
+
function slotsHashBytes(envelope) {
|
|
1397
|
+
return computeSlotsHash({
|
|
1398
|
+
kem: envelope.kem,
|
|
1399
|
+
nonce: envelope.nonce,
|
|
1400
|
+
slots: envelope.slots
|
|
1401
|
+
});
|
|
1269
1402
|
}
|
|
1270
1403
|
function eciesSealedPoeUnwrap(args) {
|
|
1271
1404
|
const { envelope, ciphertext } = args;
|
|
@@ -1294,34 +1427,38 @@ function eciesSealedPoeUnwrap(args) {
|
|
|
1294
1427
|
} else {
|
|
1295
1428
|
assertEnvelopeStructure(envelope, void 0, args.recipientSecretKey);
|
|
1296
1429
|
}
|
|
1430
|
+
assertCiphertextWithinBound(ciphertext.length);
|
|
1431
|
+
const slotsHash = slotsHashBytes(envelope);
|
|
1297
1432
|
let matchedCek = null;
|
|
1298
1433
|
let anyCandidateRecovered = false;
|
|
1299
1434
|
if (hasSingle) {
|
|
1300
1435
|
const recipientSecretKey = args.recipientSecretKey;
|
|
1301
|
-
const
|
|
1436
|
+
const candidate = tryRecipientUnwrapWithIdx(
|
|
1302
1437
|
envelope,
|
|
1303
1438
|
recipientSecretKey,
|
|
1304
1439
|
constantTimeN,
|
|
1305
1440
|
args._slotsAttemptedOut
|
|
1306
1441
|
);
|
|
1307
|
-
if (
|
|
1442
|
+
if (candidate === null) {
|
|
1308
1443
|
return { matched: false, reason: "WRONG_RECIPIENT_KEY" };
|
|
1309
1444
|
}
|
|
1310
|
-
|
|
1445
|
+
if (candidate.cekConflict) {
|
|
1446
|
+
return { matched: false, reason: "TAMPERED_HEADER" };
|
|
1447
|
+
}
|
|
1311
1448
|
const hmacKey = hkdfSha256({
|
|
1312
|
-
ikm: cek,
|
|
1449
|
+
ikm: candidate.cek,
|
|
1313
1450
|
salt: EMPTY_SALT3,
|
|
1314
1451
|
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1315
1452
|
length: 32
|
|
1316
1453
|
});
|
|
1317
|
-
const slotsMacCalc = hmac(sha256$1, hmacKey,
|
|
1454
|
+
const slotsMacCalc = hmac(sha256$1, hmacKey, slotsHash);
|
|
1318
1455
|
if (!compareCt(slotsMacCalc, envelope.slots_mac)) {
|
|
1319
1456
|
return { matched: false, reason: "TAMPERED_HEADER" };
|
|
1320
1457
|
}
|
|
1321
|
-
matchedCek = cek;
|
|
1458
|
+
matchedCek = candidate.cek;
|
|
1322
1459
|
} else {
|
|
1323
|
-
const slotsCbor = slotsMacCborBytes(envelope);
|
|
1324
1460
|
const recipientSecretKeys = multiPrivKeys;
|
|
1461
|
+
let cekConflict = false;
|
|
1325
1462
|
for (let k = 0; k < recipientSecretKeys.length; k++) {
|
|
1326
1463
|
if (args._privsAttemptedOut !== void 0) {
|
|
1327
1464
|
args._privsAttemptedOut.count = k + 1;
|
|
@@ -1329,7 +1466,7 @@ function eciesSealedPoeUnwrap(args) {
|
|
|
1329
1466
|
if (args._slotsAttemptedOut !== void 0) {
|
|
1330
1467
|
args._slotsAttemptedOut.count = 0;
|
|
1331
1468
|
}
|
|
1332
|
-
const
|
|
1469
|
+
const candidate = tryRecipientUnwrapWithIdx(
|
|
1333
1470
|
envelope,
|
|
1334
1471
|
recipientSecretKeys[k],
|
|
1335
1472
|
constantTimeN,
|
|
@@ -1338,20 +1475,25 @@ function eciesSealedPoeUnwrap(args) {
|
|
|
1338
1475
|
if (args._slotsAttemptedOut?.perPrivCounts !== void 0) {
|
|
1339
1476
|
args._slotsAttemptedOut.perPrivCounts.push(args._slotsAttemptedOut.count);
|
|
1340
1477
|
}
|
|
1341
|
-
if (
|
|
1478
|
+
if (candidate === null) continue;
|
|
1479
|
+
if (candidate.cekConflict) cekConflict = true;
|
|
1480
|
+
const cek = candidate.cek;
|
|
1342
1481
|
const hmacKey = hkdfSha256({
|
|
1343
1482
|
ikm: cek,
|
|
1344
1483
|
salt: EMPTY_SALT3,
|
|
1345
1484
|
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1346
1485
|
length: 32
|
|
1347
1486
|
});
|
|
1348
|
-
const slotsMacCalc = hmac(sha256$1, hmacKey,
|
|
1487
|
+
const slotsMacCalc = hmac(sha256$1, hmacKey, slotsHash);
|
|
1349
1488
|
if (compareCt(slotsMacCalc, envelope.slots_mac)) {
|
|
1350
1489
|
matchedCek = cek;
|
|
1351
1490
|
break;
|
|
1352
1491
|
}
|
|
1353
1492
|
anyCandidateRecovered = true;
|
|
1354
1493
|
}
|
|
1494
|
+
if (matchedCek !== null && cekConflict) {
|
|
1495
|
+
return { matched: false, reason: "TAMPERED_HEADER" };
|
|
1496
|
+
}
|
|
1355
1497
|
if (matchedCek === null) {
|
|
1356
1498
|
return {
|
|
1357
1499
|
matched: false,
|
|
@@ -1359,10 +1501,16 @@ function eciesSealedPoeUnwrap(args) {
|
|
|
1359
1501
|
};
|
|
1360
1502
|
}
|
|
1361
1503
|
}
|
|
1362
|
-
const
|
|
1504
|
+
const payloadKey = slotsPayloadKey({ cek: matchedCek, nonce: envelope.nonce });
|
|
1505
|
+
const adContent = adContentSlots({
|
|
1506
|
+
kem: envelope.kem,
|
|
1507
|
+
nonce: envelope.nonce,
|
|
1508
|
+
slotsHash,
|
|
1509
|
+
slotsMac: envelope.slots_mac
|
|
1510
|
+
});
|
|
1363
1511
|
try {
|
|
1364
1512
|
const plaintext = xchacha20Poly1305Decrypt({
|
|
1365
|
-
key:
|
|
1513
|
+
key: payloadKey,
|
|
1366
1514
|
nonce: envelope.nonce,
|
|
1367
1515
|
aad: adContent,
|
|
1368
1516
|
ciphertext
|
|
@@ -1388,7 +1536,7 @@ function eciesSealedPoeTrialDecrypt(args) {
|
|
|
1388
1536
|
);
|
|
1389
1537
|
}
|
|
1390
1538
|
assertEnvelopeStructure(envelope, recipientSecretKeys, void 0);
|
|
1391
|
-
const
|
|
1539
|
+
const slotsHash = slotsHashBytes(envelope);
|
|
1392
1540
|
let anyCandidateRecovered = false;
|
|
1393
1541
|
for (let k = 0; k < recipientSecretKeys.length; k++) {
|
|
1394
1542
|
if (args._privsAttemptedOut !== void 0) {
|
|
@@ -1407,13 +1555,17 @@ function eciesSealedPoeTrialDecrypt(args) {
|
|
|
1407
1555
|
args._slotsAttemptedOut.perPrivCounts.push(args._slotsAttemptedOut.count);
|
|
1408
1556
|
}
|
|
1409
1557
|
if (candidate === null) continue;
|
|
1558
|
+
if (candidate.cekConflict) {
|
|
1559
|
+
anyCandidateRecovered = true;
|
|
1560
|
+
continue;
|
|
1561
|
+
}
|
|
1410
1562
|
const hmacKey = hkdfSha256({
|
|
1411
1563
|
ikm: candidate.cek,
|
|
1412
1564
|
salt: EMPTY_SALT3,
|
|
1413
1565
|
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1414
1566
|
length: 32
|
|
1415
1567
|
});
|
|
1416
|
-
const slotsMacCalc = hmac(sha256$1, hmacKey,
|
|
1568
|
+
const slotsMacCalc = hmac(sha256$1, hmacKey, slotsHash);
|
|
1417
1569
|
if (compareCt(slotsMacCalc, envelope.slots_mac)) {
|
|
1418
1570
|
return { kind: "match", slotIdx: candidate.slotIdx, cek: candidate.cek };
|
|
1419
1571
|
}
|
|
@@ -1754,6 +1906,6 @@ function parseAgeRecipient(recipient) {
|
|
|
1754
1906
|
throw new Error(`parseAgeRecipient: unrecognized recipient prefix "${hrp}"`);
|
|
1755
1907
|
}
|
|
1756
1908
|
|
|
1757
|
-
export { AeadVerificationError, CARDANO_POE_HKDF_INFO_KEK, CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519, CARDANO_POE_HKDF_INFO_SLOTS_MAC, CARDANO_POE_SIG_DOMAIN_PREFIX, CARDANO_POE_SIG_DOMAIN_PREFIX_BYTES, CanonicalCborError, CoseSign1BuildError, CoseVerifyError, EciesSealedPoeError, INFO_ED25519, INFO_MLKEM768X25519, INFO_X25519, LEAVES_LIST_FORMAT_V1, MERKLE_ALG_ID, MLKEM768X25519_ENC_LENGTH, MLKEM768X25519_ESEED_LENGTH, MLKEM768X25519_PUBLIC_KEY_LENGTH, MLKEM768X25519_SEED_LENGTH, MLKEM768X25519_SHARED_SECRET_LENGTH, MerkleLeavesListError, SeedDeriveError, X25519LowOrderPointError, argon2idV13, bech32DecodeNoLimit, bech32EncodeNoLimit, blake2b224, blake2b256,
|
|
1909
|
+
export { AeadVerificationError, CARDANO_POE_HKDF_INFO_KEK, CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519, CARDANO_POE_HKDF_INFO_PAYLOAD, CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE, CARDANO_POE_HKDF_INFO_SLOTS_MAC, CARDANO_POE_PW_NORM_PROFILE, CARDANO_POE_SIG_DOMAIN_PREFIX, CARDANO_POE_SIG_DOMAIN_PREFIX_BYTES, CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX, CARDANO_POE_XWING_KEK_SALT_PREFIX, CanonicalCborError, CoseSign1BuildError, CoseVerifyError, EciesSealedPoeError, INFO_ED25519, INFO_MLKEM768X25519, INFO_X25519, LEAVES_LIST_FORMAT_V1, MAX_DECODED_ENVELOPE_BYTES, MAX_SEALED_CIPHERTEXT, MAX_SEALED_PLAINTEXT, MAX_SLOTS, MERKLE_ALG_ID, MLKEM768X25519_ENC_LENGTH, MLKEM768X25519_ESEED_LENGTH, MLKEM768X25519_PUBLIC_KEY_LENGTH, MLKEM768X25519_SEED_LENGTH, MLKEM768X25519_SHARED_SECRET_LENGTH, MerkleLeavesListError, SealedPayloadTooLargeError, SeedDeriveError, X25519LowOrderPointError, adContentPassphrase, adContentSlots, argon2idV13, assertCiphertextWithinBound, assertPlaintextWithinBound, bech32DecodeNoLimit, bech32EncodeNoLimit, blake2b224, blake2b256, buildLabel309SigStructure, buildSigStructure, canonicalizeSlots, chacha20Poly1305Decrypt, chacha20Poly1305Encrypt, chunkKemCt, compareCt, computeSlotsHash, coseSign1Label309Build, coseSign1Label309Verify, decodeCanonicalCbor, decodeCbor, decodeCoseSign1, decodeLeavesList, deriveEd25519KeypairFromSeed, deriveMlKem768X25519KeypairFromSeed, deriveX25519KeypairFromSeed, dualHash, dualHashStream, eciesSealedPoeTrialDecrypt, eciesSealedPoeUnwrap, eciesSealedPoeWrap, encodeAgeX25519Recipient, encodeAgeXWingRecipient, encodeCanonicalCbor, encodeCoseSign1, encodeLeavesList, getPublicKeyEd25519, hexToBytes, hkdfSha256, joinKemCt, merkleSha2256InclusionProof, merkleSha2256Root, merkleSha2256VerifyInclusion, mlkem768x25519Decapsulate, mlkem768x25519Encapsulate, mlkem768x25519Keygen, parseAgeRecipient, parseCoseKeyEd25519, passphrasePayloadKey, sealedEnvelopeFromParsed, sha256, signEd25519, slotsPayloadKey, uniformIndexBelow, verifyEd25519, x25519Ecdh, x25519Keygen, x25519PublicKey, xchacha20Poly1305Decrypt, xchacha20Poly1305Encrypt, xwingKekSalt };
|
|
1758
1910
|
//# sourceMappingURL=index.js.map
|
|
1759
1911
|
//# sourceMappingURL=index.js.map
|