@cardanowall/crypto-core 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/canonical-DHeJLYDR.d.cts +7 -0
- package/dist/canonical-DHeJLYDR.d.ts +7 -0
- package/dist/cbor.d.cts +2 -6
- package/dist/cbor.d.ts +2 -6
- package/dist/index.cjs +280 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +262 -110
- package/dist/index.js.map +1 -1
- package/dist/kem.cjs.map +1 -1
- package/dist/kem.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 +1 -1
package/dist/index.cjs
CHANGED
|
@@ -797,27 +797,141 @@ function joinKemCt(chunks) {
|
|
|
797
797
|
}
|
|
798
798
|
return out;
|
|
799
799
|
}
|
|
800
|
-
function
|
|
801
|
-
let value;
|
|
800
|
+
function canonicalizeSlots(slots, kem) {
|
|
802
801
|
if (kem === "x25519") {
|
|
803
|
-
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
802
|
+
return slots.map((s) => ({ epk: s.epk, wrap: s.wrap }));
|
|
803
|
+
}
|
|
804
|
+
return slots.map((s) => ({
|
|
805
|
+
kem_ct: chunkKemCt(joinKemCt(s.kem_ct)),
|
|
806
|
+
wrap: s.wrap
|
|
807
|
+
}));
|
|
808
|
+
}
|
|
809
|
+
var CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX = new TextEncoder().encode(
|
|
810
|
+
"cardano-poe-slots-transcript-v1"
|
|
811
|
+
);
|
|
812
|
+
var CARDANO_POE_HKDF_INFO_PAYLOAD = new TextEncoder().encode(
|
|
813
|
+
"cardano-poe-payload-v1"
|
|
814
|
+
);
|
|
815
|
+
var CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE = new TextEncoder().encode(
|
|
816
|
+
"cardano-poe-payload-passphrase-v1"
|
|
817
|
+
);
|
|
818
|
+
var CARDANO_POE_XWING_KEK_SALT_PREFIX = new TextEncoder().encode(
|
|
819
|
+
"cardano-poe-xwing-kek-salt-v1"
|
|
820
|
+
);
|
|
821
|
+
if (CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length !== 31) {
|
|
822
|
+
throw new Error(
|
|
823
|
+
"CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX byte-length invariant violated (expected 31)"
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
if (CARDANO_POE_HKDF_INFO_PAYLOAD.length !== 22) {
|
|
827
|
+
throw new Error("CARDANO_POE_HKDF_INFO_PAYLOAD byte-length invariant violated (expected 22)");
|
|
828
|
+
}
|
|
829
|
+
if (CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE.length !== 33) {
|
|
830
|
+
throw new Error(
|
|
831
|
+
"CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE byte-length invariant violated (expected 33)"
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
if (CARDANO_POE_XWING_KEK_SALT_PREFIX.length !== 29) {
|
|
835
|
+
throw new Error("CARDANO_POE_XWING_KEK_SALT_PREFIX byte-length invariant violated (expected 29)");
|
|
836
|
+
}
|
|
837
|
+
var CARDANO_POE_PW_NORM_PROFILE = "cardano-poe-pw-norm-v1";
|
|
838
|
+
var MAX_SLOTS = 1024;
|
|
839
|
+
var MAX_DECODED_ENVELOPE_BYTES = 65536;
|
|
840
|
+
var MAX_SEALED_PLAINTEXT = 274877906880;
|
|
841
|
+
var MAX_SEALED_CIPHERTEXT = MAX_SEALED_PLAINTEXT + 16;
|
|
842
|
+
function assertPlaintextWithinBound(plaintextLength) {
|
|
843
|
+
if (plaintextLength >= MAX_SEALED_PLAINTEXT) {
|
|
844
|
+
throw new SealedPayloadTooLargeError(
|
|
845
|
+
`plaintext length ${plaintextLength} is at or above the maximum sealed payload size ${MAX_SEALED_PLAINTEXT}`
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
function assertCiphertextWithinBound(ciphertextLength) {
|
|
850
|
+
if (ciphertextLength >= MAX_SEALED_CIPHERTEXT) {
|
|
851
|
+
throw new SealedPayloadTooLargeError(
|
|
852
|
+
`ciphertext length ${ciphertextLength} is at or above the maximum sealed ciphertext size ${MAX_SEALED_CIPHERTEXT}`
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
var SealedPayloadTooLargeError = class extends Error {
|
|
857
|
+
constructor(message) {
|
|
858
|
+
super(message);
|
|
859
|
+
this.name = "SealedPayloadTooLargeError";
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
function computeSlotsHash(args) {
|
|
863
|
+
const transcript = {
|
|
864
|
+
scheme: 1,
|
|
865
|
+
path: "slots",
|
|
866
|
+
aead: "xchacha20-poly1305",
|
|
867
|
+
kem: args.kem,
|
|
868
|
+
nonce: args.nonce,
|
|
869
|
+
slots: canonicalizeSlots(args.slots, args.kem)
|
|
870
|
+
};
|
|
871
|
+
const encoded = encodeCanonicalCbor(transcript);
|
|
872
|
+
const message = new Uint8Array(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length + encoded.length);
|
|
873
|
+
message.set(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX, 0);
|
|
874
|
+
message.set(encoded, CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length);
|
|
875
|
+
return sha2_js.sha256(message);
|
|
876
|
+
}
|
|
877
|
+
function adContentSlots(args) {
|
|
878
|
+
const ad = {
|
|
879
|
+
scheme: 1,
|
|
880
|
+
path: "slots",
|
|
881
|
+
aead: "xchacha20-poly1305",
|
|
882
|
+
kem: args.kem,
|
|
883
|
+
nonce: args.nonce,
|
|
884
|
+
slots_hash: args.slotsHash,
|
|
885
|
+
slots_mac: args.slotsMac
|
|
886
|
+
};
|
|
887
|
+
return encodeCanonicalCbor(ad);
|
|
888
|
+
}
|
|
889
|
+
function adContentPassphrase(args) {
|
|
890
|
+
const ad = {
|
|
891
|
+
scheme: 1,
|
|
892
|
+
path: "passphrase",
|
|
893
|
+
aead: "xchacha20-poly1305",
|
|
894
|
+
nonce: args.nonce,
|
|
895
|
+
passphrase: {
|
|
896
|
+
alg: args.passphrase.alg,
|
|
897
|
+
salt: args.passphrase.salt,
|
|
898
|
+
params: {
|
|
899
|
+
m: args.passphrase.params.m,
|
|
900
|
+
t: args.passphrase.params.t,
|
|
901
|
+
p: args.passphrase.params.p
|
|
902
|
+
},
|
|
903
|
+
normalization: CARDANO_POE_PW_NORM_PROFILE
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
return encodeCanonicalCbor(ad);
|
|
907
|
+
}
|
|
908
|
+
function slotsPayloadKey(args) {
|
|
909
|
+
return hkdfSha256({
|
|
910
|
+
ikm: args.cek,
|
|
911
|
+
salt: args.nonce,
|
|
912
|
+
info: CARDANO_POE_HKDF_INFO_PAYLOAD,
|
|
913
|
+
length: 32
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
function passphrasePayloadKey(args) {
|
|
917
|
+
return hkdfSha256({
|
|
918
|
+
ikm: args.cek,
|
|
919
|
+
salt: args.nonce,
|
|
920
|
+
info: CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE,
|
|
921
|
+
length: 32
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
function xwingKekSalt(args) {
|
|
925
|
+
const message = new Uint8Array(
|
|
926
|
+
CARDANO_POE_XWING_KEK_SALT_PREFIX.length + args.kemCt.length + args.pubR.length
|
|
927
|
+
);
|
|
928
|
+
let offset = 0;
|
|
929
|
+
message.set(CARDANO_POE_XWING_KEK_SALT_PREFIX, offset);
|
|
930
|
+
offset += CARDANO_POE_XWING_KEK_SALT_PREFIX.length;
|
|
931
|
+
message.set(args.kemCt, offset);
|
|
932
|
+
offset += args.kemCt.length;
|
|
933
|
+
message.set(args.pubR, offset);
|
|
934
|
+
return sha2_js.sha256(message);
|
|
821
935
|
}
|
|
822
936
|
|
|
823
937
|
// src/sealed-poe/wrap.ts
|
|
@@ -911,7 +1025,7 @@ function wrapSlotMlkem768X25519(args) {
|
|
|
911
1025
|
}
|
|
912
1026
|
const kek = hkdfSha256({
|
|
913
1027
|
ikm: ss,
|
|
914
|
-
salt:
|
|
1028
|
+
salt: xwingKekSalt({ kemCt: enc, pubR: args.pubR }),
|
|
915
1029
|
info: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
|
|
916
1030
|
length: 32
|
|
917
1031
|
});
|
|
@@ -930,6 +1044,7 @@ function eciesSealedPoeWrap(args) {
|
|
|
930
1044
|
const { plaintext, recipientPublicKeys } = args;
|
|
931
1045
|
const kem = args.kem ?? "x25519";
|
|
932
1046
|
const n = recipientPublicKeys.length;
|
|
1047
|
+
assertPlaintextWithinBound(plaintext.length);
|
|
933
1048
|
if (n < 1) {
|
|
934
1049
|
throw new EciesSealedPoeError(
|
|
935
1050
|
"ENC_SLOTS_EMPTY",
|
|
@@ -999,6 +1114,7 @@ function eciesSealedPoeWrap(args) {
|
|
|
999
1114
|
);
|
|
1000
1115
|
}
|
|
1001
1116
|
let envelope;
|
|
1117
|
+
let slotsHash;
|
|
1002
1118
|
if (kem === "x25519") {
|
|
1003
1119
|
const slots = [];
|
|
1004
1120
|
for (let i = 0; i < n; i++) {
|
|
@@ -1014,14 +1130,14 @@ function eciesSealedPoeWrap(args) {
|
|
|
1014
1130
|
if (args.skipShuffle !== true) {
|
|
1015
1131
|
csprngShuffle(slots);
|
|
1016
1132
|
}
|
|
1017
|
-
|
|
1133
|
+
slotsHash = computeSlotsHash({ kem: "x25519", nonce, slots });
|
|
1018
1134
|
envelope = {
|
|
1019
1135
|
scheme: 1,
|
|
1020
1136
|
aead: "xchacha20-poly1305",
|
|
1021
1137
|
kem: "x25519",
|
|
1022
1138
|
nonce,
|
|
1023
1139
|
slots,
|
|
1024
|
-
slots_mac:
|
|
1140
|
+
slots_mac: computeSlotsMac(cek, slotsHash)
|
|
1025
1141
|
};
|
|
1026
1142
|
} else {
|
|
1027
1143
|
const slots = [];
|
|
@@ -1037,34 +1153,39 @@ function eciesSealedPoeWrap(args) {
|
|
|
1037
1153
|
if (args.skipShuffle !== true) {
|
|
1038
1154
|
csprngShuffle(slots);
|
|
1039
1155
|
}
|
|
1040
|
-
|
|
1156
|
+
slotsHash = computeSlotsHash({ kem: "mlkem768x25519", nonce, slots });
|
|
1041
1157
|
envelope = {
|
|
1042
1158
|
scheme: 1,
|
|
1043
1159
|
aead: "xchacha20-poly1305",
|
|
1044
1160
|
kem: "mlkem768x25519",
|
|
1045
1161
|
nonce,
|
|
1046
1162
|
slots,
|
|
1047
|
-
slots_mac:
|
|
1163
|
+
slots_mac: computeSlotsMac(cek, slotsHash)
|
|
1048
1164
|
};
|
|
1049
1165
|
}
|
|
1050
|
-
const
|
|
1166
|
+
const payloadKey = slotsPayloadKey({ cek, nonce });
|
|
1167
|
+
const adContent = adContentSlots({
|
|
1168
|
+
kem: envelope.kem,
|
|
1169
|
+
nonce,
|
|
1170
|
+
slotsHash,
|
|
1171
|
+
slotsMac: envelope.slots_mac
|
|
1172
|
+
});
|
|
1051
1173
|
const ciphertext = xchacha20Poly1305Encrypt({
|
|
1052
|
-
key:
|
|
1174
|
+
key: payloadKey,
|
|
1053
1175
|
nonce,
|
|
1054
1176
|
aad: adContent,
|
|
1055
1177
|
plaintext
|
|
1056
1178
|
});
|
|
1057
1179
|
return { envelope, ciphertext };
|
|
1058
1180
|
}
|
|
1059
|
-
function computeSlotsMac(cek,
|
|
1181
|
+
function computeSlotsMac(cek, slotsHash) {
|
|
1060
1182
|
const hmacKey = hkdfSha256({
|
|
1061
1183
|
ikm: cek,
|
|
1062
1184
|
salt: EMPTY_SALT2,
|
|
1063
1185
|
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1064
1186
|
length: 32
|
|
1065
1187
|
});
|
|
1066
|
-
const
|
|
1067
|
-
const slotsMac = hmac_js.hmac(sha2_js.sha256, hmacKey, slotsCbor);
|
|
1188
|
+
const slotsMac = hmac_js.hmac(sha2_js.sha256, hmacKey, slotsHash);
|
|
1068
1189
|
if (slotsMac.length !== SLOTS_MAC_LENGTH) {
|
|
1069
1190
|
throw new Error(`internal: slots_mac.length=${slotsMac.length}, expected ${SLOTS_MAC_LENGTH}`);
|
|
1070
1191
|
}
|
|
@@ -1086,6 +1207,13 @@ function concat2(a, b) {
|
|
|
1086
1207
|
out.set(b, a.length);
|
|
1087
1208
|
return out;
|
|
1088
1209
|
}
|
|
1210
|
+
function bytesKey(bytes) {
|
|
1211
|
+
let s = "";
|
|
1212
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
1213
|
+
s += String.fromCharCode(bytes[i]);
|
|
1214
|
+
}
|
|
1215
|
+
return s;
|
|
1216
|
+
}
|
|
1089
1217
|
function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
1090
1218
|
if (envelope.scheme !== 1) {
|
|
1091
1219
|
throw new EciesSealedPoeError(
|
|
@@ -1109,6 +1237,12 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1109
1237
|
if (n < 1) {
|
|
1110
1238
|
throw new EciesSealedPoeError("ENC_SLOTS_EMPTY", `envelope.slots.length=${n} must be >= 1`);
|
|
1111
1239
|
}
|
|
1240
|
+
if (n > MAX_SLOTS) {
|
|
1241
|
+
throw new EciesSealedPoeError(
|
|
1242
|
+
"ENC_SLOTS_TOO_MANY",
|
|
1243
|
+
`envelope.slots.length=${n} exceeds MAX_SLOTS=${MAX_SLOTS}`
|
|
1244
|
+
);
|
|
1245
|
+
}
|
|
1112
1246
|
if (envelope.nonce.length !== NONCE_LENGTH2) {
|
|
1113
1247
|
throw new EciesSealedPoeError(
|
|
1114
1248
|
"NONCE_LENGTH_MISMATCH",
|
|
@@ -1121,6 +1255,7 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1121
1255
|
`envelope.slots_mac MUST be exactly ${SLOTS_MAC_LENGTH2} bytes, got ${envelope.slots_mac.length}`
|
|
1122
1256
|
);
|
|
1123
1257
|
}
|
|
1258
|
+
const seenKemMaterial = /* @__PURE__ */ new Set();
|
|
1124
1259
|
if (envelope.kem === "x25519") {
|
|
1125
1260
|
for (let i = 0; i < n; i++) {
|
|
1126
1261
|
const slot = envelope.slots[i];
|
|
@@ -1136,6 +1271,14 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1136
1271
|
`envelope.slots[${i}].wrap MUST be exactly ${WRAP_LENGTH2} bytes, got ${slot.wrap.length}`
|
|
1137
1272
|
);
|
|
1138
1273
|
}
|
|
1274
|
+
const key = bytesKey(slot.epk);
|
|
1275
|
+
if (seenKemMaterial.has(key)) {
|
|
1276
|
+
throw new EciesSealedPoeError(
|
|
1277
|
+
"ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
|
|
1278
|
+
`envelope.slots[${i}].epk duplicates an earlier slot \u2014 per-slot KEK uniqueness is violated`
|
|
1279
|
+
);
|
|
1280
|
+
}
|
|
1281
|
+
seenKemMaterial.add(key);
|
|
1139
1282
|
}
|
|
1140
1283
|
} else {
|
|
1141
1284
|
for (let i = 0; i < n; i++) {
|
|
@@ -1153,8 +1296,24 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1153
1296
|
`envelope.slots[${i}].wrap MUST be exactly ${WRAP_LENGTH2} bytes, got ${slot.wrap.length}`
|
|
1154
1297
|
);
|
|
1155
1298
|
}
|
|
1299
|
+
const key = bytesKey(enc);
|
|
1300
|
+
if (seenKemMaterial.has(key)) {
|
|
1301
|
+
throw new EciesSealedPoeError(
|
|
1302
|
+
"ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
|
|
1303
|
+
`envelope.slots[${i}].kem_ct duplicates an earlier slot \u2014 per-slot KEK uniqueness is violated`
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1306
|
+
seenKemMaterial.add(key);
|
|
1156
1307
|
}
|
|
1157
1308
|
}
|
|
1309
|
+
const perSlotBytes = envelope.kem === "x25519" ? X25519_PUBLIC_KEY_LENGTH2 + WRAP_LENGTH2 : MLKEM768X25519_ENC_LENGTH + WRAP_LENGTH2;
|
|
1310
|
+
const decodedEnvelopeBytes = NONCE_LENGTH2 + SLOTS_MAC_LENGTH2 + n * perSlotBytes;
|
|
1311
|
+
if (decodedEnvelopeBytes > MAX_DECODED_ENVELOPE_BYTES) {
|
|
1312
|
+
throw new EciesSealedPoeError(
|
|
1313
|
+
"ENC_ENVELOPE_TOO_LARGE",
|
|
1314
|
+
`decoded envelope size ${decodedEnvelopeBytes} exceeds MAX_DECODED_ENVELOPE_BYTES=${MAX_DECODED_ENVELOPE_BYTES}`
|
|
1315
|
+
);
|
|
1316
|
+
}
|
|
1158
1317
|
if (multiPrivKeys !== void 0) {
|
|
1159
1318
|
for (let i = 0; i < multiPrivKeys.length; i++) {
|
|
1160
1319
|
if (multiPrivKeys[i].length !== X25519_SECRET_KEY_LENGTH2) {
|
|
@@ -1173,60 +1332,42 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1173
1332
|
}
|
|
1174
1333
|
}
|
|
1175
1334
|
}
|
|
1335
|
+
var ZERO_IKM_32 = new Uint8Array(32);
|
|
1176
1336
|
function tryX25519Slot(args) {
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
const shared = x25519Ecdh({
|
|
1180
|
-
secretKey: args.recipientSecretKey,
|
|
1181
|
-
theirPublicKey: args.slot.epk
|
|
1182
|
-
});
|
|
1183
|
-
const kek = hkdfSha256({
|
|
1184
|
-
ikm: shared,
|
|
1185
|
-
salt: concat2(args.slot.epk, args.pubRLocal),
|
|
1186
|
-
info: CARDANO_POE_HKDF_INFO_KEK,
|
|
1187
|
-
length: 32
|
|
1188
|
-
});
|
|
1189
|
-
return chacha20Poly1305Decrypt({
|
|
1190
|
-
key: kek,
|
|
1191
|
-
nonce: ZERO_NONCE_122,
|
|
1192
|
-
aad: CARDANO_POE_HKDF_INFO_KEK,
|
|
1193
|
-
ciphertext: args.slot.wrap
|
|
1194
|
-
});
|
|
1195
|
-
} catch (e) {
|
|
1196
|
-
if (!(e instanceof AeadVerificationError) && !(e instanceof X25519LowOrderPointError)) {
|
|
1197
|
-
throw e;
|
|
1198
|
-
}
|
|
1199
|
-
return null;
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1337
|
+
const salt = concat2(args.slot.epk, args.pubRLocal);
|
|
1338
|
+
let shared;
|
|
1202
1339
|
try {
|
|
1203
|
-
|
|
1340
|
+
shared = x25519Ecdh({
|
|
1204
1341
|
secretKey: args.recipientSecretKey,
|
|
1205
1342
|
theirPublicKey: args.slot.epk
|
|
1206
1343
|
});
|
|
1207
|
-
hkdfSha256({
|
|
1208
|
-
ikm: shared,
|
|
1209
|
-
salt: concat2(args.slot.epk, args.pubRLocal),
|
|
1210
|
-
info: CARDANO_POE_HKDF_INFO_KEK,
|
|
1211
|
-
length: 32
|
|
1212
|
-
});
|
|
1213
1344
|
} catch (e) {
|
|
1214
1345
|
if (!(e instanceof X25519LowOrderPointError)) throw e;
|
|
1346
|
+
hkdfSha256({ ikm: ZERO_IKM_32, salt, info: CARDANO_POE_HKDF_INFO_KEK, length: 32 });
|
|
1347
|
+
return null;
|
|
1348
|
+
}
|
|
1349
|
+
const kek = hkdfSha256({ ikm: shared, salt, info: CARDANO_POE_HKDF_INFO_KEK, length: 32 });
|
|
1350
|
+
try {
|
|
1351
|
+
return chacha20Poly1305Decrypt({
|
|
1352
|
+
key: kek,
|
|
1353
|
+
nonce: ZERO_NONCE_122,
|
|
1354
|
+
aad: CARDANO_POE_HKDF_INFO_KEK,
|
|
1355
|
+
ciphertext: args.slot.wrap
|
|
1356
|
+
});
|
|
1357
|
+
} catch (e) {
|
|
1358
|
+
if (!(e instanceof AeadVerificationError)) throw e;
|
|
1359
|
+
return null;
|
|
1215
1360
|
}
|
|
1216
|
-
return null;
|
|
1217
1361
|
}
|
|
1218
1362
|
function tryMlkem768X25519Slot(args) {
|
|
1219
1363
|
const enc = joinKemCt(args.slot.kem_ct);
|
|
1220
1364
|
const ss = mlkem768x25519Decapsulate({ secretSeed: args.recipientSecretKey, enc });
|
|
1221
1365
|
const kek = hkdfSha256({
|
|
1222
1366
|
ikm: ss,
|
|
1223
|
-
salt:
|
|
1367
|
+
salt: xwingKekSalt({ kemCt: enc, pubR: args.pubR }),
|
|
1224
1368
|
info: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
|
|
1225
1369
|
length: 32
|
|
1226
1370
|
});
|
|
1227
|
-
if (!args.liveSlot) {
|
|
1228
|
-
return null;
|
|
1229
|
-
}
|
|
1230
1371
|
try {
|
|
1231
1372
|
return chacha20Poly1305Decrypt({
|
|
1232
1373
|
key: kek,
|
|
@@ -1243,51 +1384,43 @@ function tryRecipientUnwrapWithIdx(envelope, recipientSecretKey, constantTimeN,
|
|
|
1243
1384
|
const n = envelope.slots.length;
|
|
1244
1385
|
let cek = null;
|
|
1245
1386
|
let matchedSlotIdx = -1;
|
|
1387
|
+
let cekConflict = false;
|
|
1388
|
+
const recordMatch = (candidate, i) => {
|
|
1389
|
+
if (candidate === null) return;
|
|
1390
|
+
if (cek === null) {
|
|
1391
|
+
cek = candidate;
|
|
1392
|
+
matchedSlotIdx = i;
|
|
1393
|
+
} else if (!compareCt(candidate, cek)) {
|
|
1394
|
+
cekConflict = true;
|
|
1395
|
+
}
|
|
1396
|
+
};
|
|
1246
1397
|
if (envelope.kem === "x25519") {
|
|
1247
1398
|
const pubRLocal = x25519PublicKey({ secretKey: recipientSecretKey });
|
|
1248
1399
|
for (let i = 0; i < n; i++) {
|
|
1249
1400
|
if (slotsAttemptedOut !== void 0) {
|
|
1250
1401
|
slotsAttemptedOut.count = i + 1;
|
|
1251
1402
|
}
|
|
1252
|
-
|
|
1253
|
-
slot: envelope.slots[i],
|
|
1254
|
-
recipientSecretKey,
|
|
1255
|
-
pubRLocal,
|
|
1256
|
-
liveSlot: cek === null
|
|
1257
|
-
});
|
|
1258
|
-
if (cek === null && candidate !== null) {
|
|
1259
|
-
cek = candidate;
|
|
1260
|
-
matchedSlotIdx = i;
|
|
1261
|
-
}
|
|
1403
|
+
recordMatch(tryX25519Slot({ slot: envelope.slots[i], recipientSecretKey, pubRLocal }), i);
|
|
1262
1404
|
if (cek !== null && !constantTimeN) break;
|
|
1263
1405
|
}
|
|
1264
1406
|
} else {
|
|
1407
|
+
const pubR = mlkem768x25519Keygen(recipientSecretKey).publicKey;
|
|
1265
1408
|
for (let i = 0; i < n; i++) {
|
|
1266
1409
|
if (slotsAttemptedOut !== void 0) {
|
|
1267
1410
|
slotsAttemptedOut.count = i + 1;
|
|
1268
1411
|
}
|
|
1269
|
-
|
|
1270
|
-
slot: envelope.slots[i],
|
|
1271
|
-
recipientSecretKey,
|
|
1272
|
-
liveSlot: cek === null
|
|
1273
|
-
});
|
|
1274
|
-
if (cek === null && candidate !== null) {
|
|
1275
|
-
cek = candidate;
|
|
1276
|
-
matchedSlotIdx = i;
|
|
1277
|
-
}
|
|
1412
|
+
recordMatch(tryMlkem768X25519Slot({ slot: envelope.slots[i], recipientSecretKey, pubR }), i);
|
|
1278
1413
|
if (cek !== null && !constantTimeN) break;
|
|
1279
1414
|
}
|
|
1280
1415
|
}
|
|
1281
|
-
return cek === null ? null : { cek, slotIdx: matchedSlotIdx };
|
|
1282
|
-
}
|
|
1283
|
-
function tryRecipientUnwrap(envelope, recipientSecretKey, constantTimeN, slotsAttemptedOut) {
|
|
1284
|
-
return tryRecipientUnwrapWithIdx(envelope, recipientSecretKey, constantTimeN, slotsAttemptedOut)?.cek ?? null;
|
|
1416
|
+
return cek === null ? null : { cek, slotIdx: matchedSlotIdx, cekConflict };
|
|
1285
1417
|
}
|
|
1286
|
-
function
|
|
1287
|
-
return
|
|
1288
|
-
envelope.
|
|
1289
|
-
envelope.
|
|
1290
|
-
|
|
1418
|
+
function slotsHashBytes(envelope) {
|
|
1419
|
+
return computeSlotsHash({
|
|
1420
|
+
kem: envelope.kem,
|
|
1421
|
+
nonce: envelope.nonce,
|
|
1422
|
+
slots: envelope.slots
|
|
1423
|
+
});
|
|
1291
1424
|
}
|
|
1292
1425
|
function eciesSealedPoeUnwrap(args) {
|
|
1293
1426
|
const { envelope, ciphertext } = args;
|
|
@@ -1316,34 +1449,38 @@ function eciesSealedPoeUnwrap(args) {
|
|
|
1316
1449
|
} else {
|
|
1317
1450
|
assertEnvelopeStructure(envelope, void 0, args.recipientSecretKey);
|
|
1318
1451
|
}
|
|
1452
|
+
assertCiphertextWithinBound(ciphertext.length);
|
|
1453
|
+
const slotsHash = slotsHashBytes(envelope);
|
|
1319
1454
|
let matchedCek = null;
|
|
1320
1455
|
let anyCandidateRecovered = false;
|
|
1321
1456
|
if (hasSingle) {
|
|
1322
1457
|
const recipientSecretKey = args.recipientSecretKey;
|
|
1323
|
-
const
|
|
1458
|
+
const candidate = tryRecipientUnwrapWithIdx(
|
|
1324
1459
|
envelope,
|
|
1325
1460
|
recipientSecretKey,
|
|
1326
1461
|
constantTimeN,
|
|
1327
1462
|
args._slotsAttemptedOut
|
|
1328
1463
|
);
|
|
1329
|
-
if (
|
|
1464
|
+
if (candidate === null) {
|
|
1330
1465
|
return { matched: false, reason: "WRONG_RECIPIENT_KEY" };
|
|
1331
1466
|
}
|
|
1332
|
-
|
|
1467
|
+
if (candidate.cekConflict) {
|
|
1468
|
+
return { matched: false, reason: "TAMPERED_HEADER" };
|
|
1469
|
+
}
|
|
1333
1470
|
const hmacKey = hkdfSha256({
|
|
1334
|
-
ikm: cek,
|
|
1471
|
+
ikm: candidate.cek,
|
|
1335
1472
|
salt: EMPTY_SALT3,
|
|
1336
1473
|
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1337
1474
|
length: 32
|
|
1338
1475
|
});
|
|
1339
|
-
const slotsMacCalc = hmac_js.hmac(sha2_js.sha256, hmacKey,
|
|
1476
|
+
const slotsMacCalc = hmac_js.hmac(sha2_js.sha256, hmacKey, slotsHash);
|
|
1340
1477
|
if (!compareCt(slotsMacCalc, envelope.slots_mac)) {
|
|
1341
1478
|
return { matched: false, reason: "TAMPERED_HEADER" };
|
|
1342
1479
|
}
|
|
1343
|
-
matchedCek = cek;
|
|
1480
|
+
matchedCek = candidate.cek;
|
|
1344
1481
|
} else {
|
|
1345
|
-
const slotsCbor = slotsMacCborBytes(envelope);
|
|
1346
1482
|
const recipientSecretKeys = multiPrivKeys;
|
|
1483
|
+
let cekConflict = false;
|
|
1347
1484
|
for (let k = 0; k < recipientSecretKeys.length; k++) {
|
|
1348
1485
|
if (args._privsAttemptedOut !== void 0) {
|
|
1349
1486
|
args._privsAttemptedOut.count = k + 1;
|
|
@@ -1351,7 +1488,7 @@ function eciesSealedPoeUnwrap(args) {
|
|
|
1351
1488
|
if (args._slotsAttemptedOut !== void 0) {
|
|
1352
1489
|
args._slotsAttemptedOut.count = 0;
|
|
1353
1490
|
}
|
|
1354
|
-
const
|
|
1491
|
+
const candidate = tryRecipientUnwrapWithIdx(
|
|
1355
1492
|
envelope,
|
|
1356
1493
|
recipientSecretKeys[k],
|
|
1357
1494
|
constantTimeN,
|
|
@@ -1360,20 +1497,25 @@ function eciesSealedPoeUnwrap(args) {
|
|
|
1360
1497
|
if (args._slotsAttemptedOut?.perPrivCounts !== void 0) {
|
|
1361
1498
|
args._slotsAttemptedOut.perPrivCounts.push(args._slotsAttemptedOut.count);
|
|
1362
1499
|
}
|
|
1363
|
-
if (
|
|
1500
|
+
if (candidate === null) continue;
|
|
1501
|
+
if (candidate.cekConflict) cekConflict = true;
|
|
1502
|
+
const cek = candidate.cek;
|
|
1364
1503
|
const hmacKey = hkdfSha256({
|
|
1365
1504
|
ikm: cek,
|
|
1366
1505
|
salt: EMPTY_SALT3,
|
|
1367
1506
|
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1368
1507
|
length: 32
|
|
1369
1508
|
});
|
|
1370
|
-
const slotsMacCalc = hmac_js.hmac(sha2_js.sha256, hmacKey,
|
|
1509
|
+
const slotsMacCalc = hmac_js.hmac(sha2_js.sha256, hmacKey, slotsHash);
|
|
1371
1510
|
if (compareCt(slotsMacCalc, envelope.slots_mac)) {
|
|
1372
1511
|
matchedCek = cek;
|
|
1373
1512
|
break;
|
|
1374
1513
|
}
|
|
1375
1514
|
anyCandidateRecovered = true;
|
|
1376
1515
|
}
|
|
1516
|
+
if (matchedCek !== null && cekConflict) {
|
|
1517
|
+
return { matched: false, reason: "TAMPERED_HEADER" };
|
|
1518
|
+
}
|
|
1377
1519
|
if (matchedCek === null) {
|
|
1378
1520
|
return {
|
|
1379
1521
|
matched: false,
|
|
@@ -1381,10 +1523,16 @@ function eciesSealedPoeUnwrap(args) {
|
|
|
1381
1523
|
};
|
|
1382
1524
|
}
|
|
1383
1525
|
}
|
|
1384
|
-
const
|
|
1526
|
+
const payloadKey = slotsPayloadKey({ cek: matchedCek, nonce: envelope.nonce });
|
|
1527
|
+
const adContent = adContentSlots({
|
|
1528
|
+
kem: envelope.kem,
|
|
1529
|
+
nonce: envelope.nonce,
|
|
1530
|
+
slotsHash,
|
|
1531
|
+
slotsMac: envelope.slots_mac
|
|
1532
|
+
});
|
|
1385
1533
|
try {
|
|
1386
1534
|
const plaintext = xchacha20Poly1305Decrypt({
|
|
1387
|
-
key:
|
|
1535
|
+
key: payloadKey,
|
|
1388
1536
|
nonce: envelope.nonce,
|
|
1389
1537
|
aad: adContent,
|
|
1390
1538
|
ciphertext
|
|
@@ -1410,7 +1558,7 @@ function eciesSealedPoeTrialDecrypt(args) {
|
|
|
1410
1558
|
);
|
|
1411
1559
|
}
|
|
1412
1560
|
assertEnvelopeStructure(envelope, recipientSecretKeys, void 0);
|
|
1413
|
-
const
|
|
1561
|
+
const slotsHash = slotsHashBytes(envelope);
|
|
1414
1562
|
let anyCandidateRecovered = false;
|
|
1415
1563
|
for (let k = 0; k < recipientSecretKeys.length; k++) {
|
|
1416
1564
|
if (args._privsAttemptedOut !== void 0) {
|
|
@@ -1429,13 +1577,17 @@ function eciesSealedPoeTrialDecrypt(args) {
|
|
|
1429
1577
|
args._slotsAttemptedOut.perPrivCounts.push(args._slotsAttemptedOut.count);
|
|
1430
1578
|
}
|
|
1431
1579
|
if (candidate === null) continue;
|
|
1580
|
+
if (candidate.cekConflict) {
|
|
1581
|
+
anyCandidateRecovered = true;
|
|
1582
|
+
continue;
|
|
1583
|
+
}
|
|
1432
1584
|
const hmacKey = hkdfSha256({
|
|
1433
1585
|
ikm: candidate.cek,
|
|
1434
1586
|
salt: EMPTY_SALT3,
|
|
1435
1587
|
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1436
1588
|
length: 32
|
|
1437
1589
|
});
|
|
1438
|
-
const slotsMacCalc = hmac_js.hmac(sha2_js.sha256, hmacKey,
|
|
1590
|
+
const slotsMacCalc = hmac_js.hmac(sha2_js.sha256, hmacKey, slotsHash);
|
|
1439
1591
|
if (compareCt(slotsMacCalc, envelope.slots_mac)) {
|
|
1440
1592
|
return { kind: "match", slotIdx: candidate.slotIdx, cek: candidate.cek };
|
|
1441
1593
|
}
|
|
@@ -1779,9 +1931,14 @@ function parseAgeRecipient(recipient) {
|
|
|
1779
1931
|
exports.AeadVerificationError = AeadVerificationError;
|
|
1780
1932
|
exports.CARDANO_POE_HKDF_INFO_KEK = CARDANO_POE_HKDF_INFO_KEK;
|
|
1781
1933
|
exports.CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 = CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519;
|
|
1934
|
+
exports.CARDANO_POE_HKDF_INFO_PAYLOAD = CARDANO_POE_HKDF_INFO_PAYLOAD;
|
|
1935
|
+
exports.CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE = CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE;
|
|
1782
1936
|
exports.CARDANO_POE_HKDF_INFO_SLOTS_MAC = CARDANO_POE_HKDF_INFO_SLOTS_MAC;
|
|
1937
|
+
exports.CARDANO_POE_PW_NORM_PROFILE = CARDANO_POE_PW_NORM_PROFILE;
|
|
1783
1938
|
exports.CARDANO_POE_SIG_DOMAIN_PREFIX = CARDANO_POE_SIG_DOMAIN_PREFIX;
|
|
1784
1939
|
exports.CARDANO_POE_SIG_DOMAIN_PREFIX_BYTES = CARDANO_POE_SIG_DOMAIN_PREFIX_BYTES;
|
|
1940
|
+
exports.CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX = CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX;
|
|
1941
|
+
exports.CARDANO_POE_XWING_KEK_SALT_PREFIX = CARDANO_POE_XWING_KEK_SALT_PREFIX;
|
|
1785
1942
|
exports.CanonicalCborError = CanonicalCborError;
|
|
1786
1943
|
exports.CoseSign1BuildError = CoseSign1BuildError;
|
|
1787
1944
|
exports.CoseVerifyError = CoseVerifyError;
|
|
@@ -1790,6 +1947,10 @@ exports.INFO_ED25519 = INFO_ED25519;
|
|
|
1790
1947
|
exports.INFO_MLKEM768X25519 = INFO_MLKEM768X25519;
|
|
1791
1948
|
exports.INFO_X25519 = INFO_X25519;
|
|
1792
1949
|
exports.LEAVES_LIST_FORMAT_V1 = LEAVES_LIST_FORMAT_V1;
|
|
1950
|
+
exports.MAX_DECODED_ENVELOPE_BYTES = MAX_DECODED_ENVELOPE_BYTES;
|
|
1951
|
+
exports.MAX_SEALED_CIPHERTEXT = MAX_SEALED_CIPHERTEXT;
|
|
1952
|
+
exports.MAX_SEALED_PLAINTEXT = MAX_SEALED_PLAINTEXT;
|
|
1953
|
+
exports.MAX_SLOTS = MAX_SLOTS;
|
|
1793
1954
|
exports.MERKLE_ALG_ID = MERKLE_ALG_ID;
|
|
1794
1955
|
exports.MLKEM768X25519_ENC_LENGTH = MLKEM768X25519_ENC_LENGTH;
|
|
1795
1956
|
exports.MLKEM768X25519_ESEED_LENGTH = MLKEM768X25519_ESEED_LENGTH;
|
|
@@ -1797,19 +1958,26 @@ exports.MLKEM768X25519_PUBLIC_KEY_LENGTH = MLKEM768X25519_PUBLIC_KEY_LENGTH;
|
|
|
1797
1958
|
exports.MLKEM768X25519_SEED_LENGTH = MLKEM768X25519_SEED_LENGTH;
|
|
1798
1959
|
exports.MLKEM768X25519_SHARED_SECRET_LENGTH = MLKEM768X25519_SHARED_SECRET_LENGTH;
|
|
1799
1960
|
exports.MerkleLeavesListError = MerkleLeavesListError;
|
|
1961
|
+
exports.SealedPayloadTooLargeError = SealedPayloadTooLargeError;
|
|
1800
1962
|
exports.SeedDeriveError = SeedDeriveError;
|
|
1801
1963
|
exports.X25519LowOrderPointError = X25519LowOrderPointError;
|
|
1964
|
+
exports.adContentPassphrase = adContentPassphrase;
|
|
1965
|
+
exports.adContentSlots = adContentSlots;
|
|
1802
1966
|
exports.argon2idV13 = argon2idV13;
|
|
1967
|
+
exports.assertCiphertextWithinBound = assertCiphertextWithinBound;
|
|
1968
|
+
exports.assertPlaintextWithinBound = assertPlaintextWithinBound;
|
|
1803
1969
|
exports.bech32DecodeNoLimit = bech32DecodeNoLimit;
|
|
1804
1970
|
exports.bech32EncodeNoLimit = bech32EncodeNoLimit;
|
|
1805
1971
|
exports.blake2b224 = blake2b224;
|
|
1806
1972
|
exports.blake2b256 = blake2b256;
|
|
1807
1973
|
exports.buildLabel309SigStructure = buildLabel309SigStructure;
|
|
1808
1974
|
exports.buildSigStructure = buildSigStructure;
|
|
1975
|
+
exports.canonicalizeSlots = canonicalizeSlots;
|
|
1809
1976
|
exports.chacha20Poly1305Decrypt = chacha20Poly1305Decrypt;
|
|
1810
1977
|
exports.chacha20Poly1305Encrypt = chacha20Poly1305Encrypt;
|
|
1811
1978
|
exports.chunkKemCt = chunkKemCt;
|
|
1812
1979
|
exports.compareCt = compareCt;
|
|
1980
|
+
exports.computeSlotsHash = computeSlotsHash;
|
|
1813
1981
|
exports.coseSign1Label309Build = coseSign1Label309Build;
|
|
1814
1982
|
exports.coseSign1Label309Verify = coseSign1Label309Verify;
|
|
1815
1983
|
exports.decodeCanonicalCbor = decodeCanonicalCbor;
|
|
@@ -1841,10 +2009,11 @@ exports.mlkem768x25519Encapsulate = mlkem768x25519Encapsulate;
|
|
|
1841
2009
|
exports.mlkem768x25519Keygen = mlkem768x25519Keygen;
|
|
1842
2010
|
exports.parseAgeRecipient = parseAgeRecipient;
|
|
1843
2011
|
exports.parseCoseKeyEd25519 = parseCoseKeyEd25519;
|
|
2012
|
+
exports.passphrasePayloadKey = passphrasePayloadKey;
|
|
1844
2013
|
exports.sealedEnvelopeFromParsed = sealedEnvelopeFromParsed;
|
|
1845
2014
|
exports.sha256 = sha256;
|
|
1846
2015
|
exports.signEd25519 = signEd25519;
|
|
1847
|
-
exports.
|
|
2016
|
+
exports.slotsPayloadKey = slotsPayloadKey;
|
|
1848
2017
|
exports.uniformIndexBelow = uniformIndexBelow;
|
|
1849
2018
|
exports.verifyEd25519 = verifyEd25519;
|
|
1850
2019
|
exports.x25519Ecdh = x25519Ecdh;
|
|
@@ -1852,5 +2021,6 @@ exports.x25519Keygen = x25519Keygen;
|
|
|
1852
2021
|
exports.x25519PublicKey = x25519PublicKey;
|
|
1853
2022
|
exports.xchacha20Poly1305Decrypt = xchacha20Poly1305Decrypt;
|
|
1854
2023
|
exports.xchacha20Poly1305Encrypt = xchacha20Poly1305Encrypt;
|
|
2024
|
+
exports.xwingKekSalt = xwingKekSalt;
|
|
1855
2025
|
//# sourceMappingURL=index.cjs.map
|
|
1856
2026
|
//# sourceMappingURL=index.cjs.map
|