@cardanowall/sdk-ts 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.cjs +1140 -363
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +42 -5
- package/dist/client/index.d.ts +42 -5
- package/dist/client/index.js +1138 -365
- package/dist/client/index.js.map +1 -1
- package/dist/conformance/cli.cjs +4400 -2121
- package/dist/conformance/cli.cjs.map +1 -1
- package/dist/conformance/cli.js +4401 -2122
- package/dist/conformance/cli.js.map +1 -1
- package/dist/fetch/index.cjs +33 -14
- package/dist/fetch/index.cjs.map +1 -1
- package/dist/fetch/index.d.cts +2 -2
- package/dist/fetch/index.d.ts +2 -2
- package/dist/fetch/index.js +32 -15
- package/dist/fetch/index.js.map +1 -1
- package/dist/{fetch-outbound-BT5-NiYN.d.cts → fetch-outbound-dOK3ZxYa.d.cts} +7 -3
- package/dist/{fetch-outbound-BT5-NiYN.d.ts → fetch-outbound-dOK3ZxYa.d.ts} +7 -3
- package/dist/hash/index.cjs +1 -1
- package/dist/hash/index.cjs.map +1 -1
- package/dist/hash/index.js +1 -1
- package/dist/hash/index.js.map +1 -1
- package/dist/identity/index.cjs +356 -230
- package/dist/identity/index.cjs.map +1 -1
- package/dist/identity/index.d.cts +3 -2
- package/dist/identity/index.d.ts +3 -2
- package/dist/identity/index.js +356 -230
- package/dist/identity/index.js.map +1 -1
- package/dist/index.cjs +5474 -2518
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +5454 -2514
- package/dist/index.js.map +1 -1
- package/dist/merkle/index.cjs +1 -1
- package/dist/merkle/index.js +1 -1
- package/dist/types-Cexm4VH9.d.cts +119 -0
- package/dist/types-CgoBub9J.d.ts +119 -0
- package/dist/{types-DGsZTMuZ.d.cts → types-Dp4wUSFI.d.cts} +220 -1
- package/dist/{types-DGsZTMuZ.d.ts → types-Dp4wUSFI.d.ts} +220 -1
- package/dist/verifier/index.cjs +4419 -2147
- package/dist/verifier/index.cjs.map +1 -1
- package/dist/verifier/index.d.cts +159 -111
- package/dist/verifier/index.d.ts +159 -111
- package/dist/verifier/index.js +4407 -2143
- package/dist/verifier/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/types-B8Q3gW54.d.ts +0 -123
- package/dist/types-CLXdbjqr.d.cts +0 -123
package/dist/identity/index.cjs
CHANGED
|
@@ -13,10 +13,11 @@ var utils_js = require('@noble/hashes/utils.js');
|
|
|
13
13
|
var fft_js = require('@noble/curves/abstract/fft.js');
|
|
14
14
|
var ed = require('@noble/ed25519');
|
|
15
15
|
require('@noble/ciphers/utils.js');
|
|
16
|
-
var hmac_js = require('@noble/hashes/hmac.js');
|
|
17
16
|
var chacha_js = require('@noble/ciphers/chacha.js');
|
|
17
|
+
var hmac_js = require('@noble/hashes/hmac.js');
|
|
18
18
|
var cbor2 = require('cbor2');
|
|
19
19
|
var sorts = require('cbor2/sorts');
|
|
20
|
+
require('hash-wasm');
|
|
20
21
|
|
|
21
22
|
function _interopNamespace(e) {
|
|
22
23
|
if (e && e.__esModule) return e;
|
|
@@ -867,16 +868,6 @@ function chacha20Poly1305Decrypt(opts2) {
|
|
|
867
868
|
throw new AeadVerificationError("chacha20-poly1305 decrypt failed", { cause });
|
|
868
869
|
}
|
|
869
870
|
}
|
|
870
|
-
function xchacha20Poly1305Decrypt(opts2) {
|
|
871
|
-
try {
|
|
872
|
-
return chacha_js.xchacha20poly1305(opts2.key, opts2.nonce, opts2.aad).decrypt(opts2.ciphertext);
|
|
873
|
-
} catch (cause) {
|
|
874
|
-
throw new AeadVerificationError("xchacha20-poly1305 decrypt failed", { cause });
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
function hkdfSha2562(opts2) {
|
|
878
|
-
return hkdf_js.hkdf(sha2_js.sha256, opts2.ikm, opts2.salt, opts2.info, opts2.length);
|
|
879
|
-
}
|
|
880
871
|
var MLKEM768X25519_ENC_LENGTH = 1120;
|
|
881
872
|
var MLKEM768X25519_SEED_LENGTH2 = 32;
|
|
882
873
|
function mlkem768x25519Keygen2(seed) {
|
|
@@ -922,6 +913,9 @@ function x25519Ecdh(opts2) {
|
|
|
922
913
|
throw e;
|
|
923
914
|
}
|
|
924
915
|
}
|
|
916
|
+
function hkdfSha2562(opts2) {
|
|
917
|
+
return hkdf_js.hkdf(sha2_js.sha256, opts2.ikm, opts2.salt, opts2.info, opts2.length);
|
|
918
|
+
}
|
|
925
919
|
var EciesSealedPoeError = class extends Error {
|
|
926
920
|
code;
|
|
927
921
|
constructor(code, message, options) {
|
|
@@ -930,36 +924,138 @@ var EciesSealedPoeError = class extends Error {
|
|
|
930
924
|
this.code = code;
|
|
931
925
|
}
|
|
932
926
|
};
|
|
933
|
-
var
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
927
|
+
var CHUNK_SIZE = 65536;
|
|
928
|
+
var TAG_SIZE = 16;
|
|
929
|
+
var NONCE_LENGTH = 12;
|
|
930
|
+
var COUNTER_LENGTH = 11;
|
|
931
|
+
var SEALED_CHUNK_SIZE = CHUNK_SIZE + TAG_SIZE;
|
|
932
|
+
var PAYLOAD_KEY_LENGTH = 32;
|
|
933
|
+
var EMPTY_AAD = new Uint8Array(0);
|
|
934
|
+
var StreamTamperedError = class extends Error {
|
|
935
|
+
code = "TAMPERED_CIPHERTEXT";
|
|
936
|
+
constructor(message, options) {
|
|
937
|
+
super(message, options);
|
|
938
|
+
this.name = "StreamTamperedError";
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
var ChunkNonce = class {
|
|
942
|
+
nonce = new Uint8Array(NONCE_LENGTH);
|
|
943
|
+
finished = false;
|
|
944
|
+
next(final) {
|
|
945
|
+
if (this.finished) {
|
|
946
|
+
throw new Error("STREAM: no chunks may follow the final chunk");
|
|
947
|
+
}
|
|
948
|
+
if (final) {
|
|
949
|
+
this.finished = true;
|
|
950
|
+
this.nonce[COUNTER_LENGTH] = 1;
|
|
951
|
+
}
|
|
952
|
+
const out = this.nonce.slice();
|
|
953
|
+
this.increment();
|
|
954
|
+
return out;
|
|
937
955
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
chunks.push(value.subarray(i, Math.min(i + CHUNK_MAX_BYTES, value.length)));
|
|
956
|
+
get done() {
|
|
957
|
+
return this.finished;
|
|
941
958
|
}
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
959
|
+
increment() {
|
|
960
|
+
for (let i = COUNTER_LENGTH - 1; i >= 0; i--) {
|
|
961
|
+
const v = this.nonce[i] + 1 & 255;
|
|
962
|
+
this.nonce[i] = v;
|
|
963
|
+
if (v !== 0) return;
|
|
964
|
+
}
|
|
965
|
+
throw new Error("STREAM: chunk counter overflow");
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
function assertPayloadKey(payloadKey) {
|
|
969
|
+
if (payloadKey.length !== PAYLOAD_KEY_LENGTH) {
|
|
970
|
+
throw new Error(
|
|
971
|
+
`STREAM: payloadKey MUST be exactly ${PAYLOAD_KEY_LENGTH} bytes, got ${payloadKey.length}`
|
|
972
|
+
);
|
|
952
973
|
}
|
|
953
|
-
return out;
|
|
954
974
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
975
|
+
var StreamOpener = class {
|
|
976
|
+
payloadKey;
|
|
977
|
+
nonce = new ChunkNonce();
|
|
978
|
+
chunkIndex = 0;
|
|
979
|
+
constructor(payloadKey) {
|
|
980
|
+
assertPayloadKey(payloadKey);
|
|
981
|
+
this.payloadKey = payloadKey;
|
|
958
982
|
}
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
983
|
+
openChunk(sealedChunk, final) {
|
|
984
|
+
if (sealedChunk.length < TAG_SIZE) {
|
|
985
|
+
throw new StreamTamperedError(
|
|
986
|
+
`STREAM: sealed chunk shorter than the ${TAG_SIZE}-byte tag floor`
|
|
987
|
+
);
|
|
988
|
+
}
|
|
989
|
+
if (!final && sealedChunk.length !== SEALED_CHUNK_SIZE) {
|
|
990
|
+
throw new StreamTamperedError(
|
|
991
|
+
`STREAM: non-final sealed chunk MUST be exactly ${SEALED_CHUNK_SIZE} bytes, got ${sealedChunk.length}`
|
|
992
|
+
);
|
|
993
|
+
}
|
|
994
|
+
if (final && sealedChunk.length > SEALED_CHUNK_SIZE) {
|
|
995
|
+
throw new StreamTamperedError(
|
|
996
|
+
`STREAM: final sealed chunk MUST be at most ${SEALED_CHUNK_SIZE} bytes, got ${sealedChunk.length}`
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
if (final && sealedChunk.length === TAG_SIZE && this.chunkIndex > 0) {
|
|
1000
|
+
throw new StreamTamperedError("STREAM: zero-length final chunk on a non-empty stream");
|
|
1001
|
+
}
|
|
1002
|
+
let plaintext;
|
|
1003
|
+
try {
|
|
1004
|
+
plaintext = chacha20Poly1305Decrypt({
|
|
1005
|
+
key: this.payloadKey,
|
|
1006
|
+
nonce: this.nonce.next(final),
|
|
1007
|
+
aad: EMPTY_AAD,
|
|
1008
|
+
ciphertext: sealedChunk
|
|
1009
|
+
});
|
|
1010
|
+
} catch (e) {
|
|
1011
|
+
if (!(e instanceof AeadVerificationError)) throw e;
|
|
1012
|
+
throw new StreamTamperedError(`STREAM: chunk ${this.chunkIndex} tag verification failed`, {
|
|
1013
|
+
cause: e
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
this.chunkIndex += 1;
|
|
1017
|
+
return plaintext;
|
|
1018
|
+
}
|
|
1019
|
+
};
|
|
1020
|
+
function streamOpen(args) {
|
|
1021
|
+
const { ciphertext } = args;
|
|
1022
|
+
const total = ciphertext.length;
|
|
1023
|
+
if (total < TAG_SIZE) {
|
|
1024
|
+
throw new StreamTamperedError(
|
|
1025
|
+
`STREAM: ciphertext shorter than the ${TAG_SIZE}-byte single-tag floor`
|
|
1026
|
+
);
|
|
1027
|
+
}
|
|
1028
|
+
const rem = total % SEALED_CHUNK_SIZE;
|
|
1029
|
+
let nonFinalCount;
|
|
1030
|
+
let finalSealedLength;
|
|
1031
|
+
if (rem === 0) {
|
|
1032
|
+
nonFinalCount = total / SEALED_CHUNK_SIZE - 1;
|
|
1033
|
+
finalSealedLength = SEALED_CHUNK_SIZE;
|
|
1034
|
+
} else if (rem >= TAG_SIZE) {
|
|
1035
|
+
nonFinalCount = (total - rem) / SEALED_CHUNK_SIZE;
|
|
1036
|
+
finalSealedLength = rem;
|
|
1037
|
+
} else {
|
|
1038
|
+
throw new StreamTamperedError("STREAM: trailing bytes cannot form a well-formed final chunk");
|
|
1039
|
+
}
|
|
1040
|
+
if (nonFinalCount > 0 && finalSealedLength === TAG_SIZE) {
|
|
1041
|
+
throw new StreamTamperedError("STREAM: zero-length final chunk on a non-empty stream");
|
|
1042
|
+
}
|
|
1043
|
+
const opener = new StreamOpener(args.payloadKey);
|
|
1044
|
+
const out = new Uint8Array(nonFinalCount * CHUNK_SIZE + finalSealedLength - TAG_SIZE);
|
|
1045
|
+
let readOffset = 0;
|
|
1046
|
+
let writeOffset = 0;
|
|
1047
|
+
for (let i = 0; i < nonFinalCount; i++) {
|
|
1048
|
+
const plaintext = opener.openChunk(
|
|
1049
|
+
ciphertext.subarray(readOffset, readOffset + SEALED_CHUNK_SIZE),
|
|
1050
|
+
false
|
|
1051
|
+
);
|
|
1052
|
+
out.set(plaintext, writeOffset);
|
|
1053
|
+
readOffset += SEALED_CHUNK_SIZE;
|
|
1054
|
+
writeOffset += CHUNK_SIZE;
|
|
1055
|
+
}
|
|
1056
|
+
const finalPlaintext = opener.openChunk(ciphertext.subarray(readOffset), true);
|
|
1057
|
+
out.set(finalPlaintext, writeOffset);
|
|
1058
|
+
return out;
|
|
963
1059
|
}
|
|
964
1060
|
function encodeCanonicalCbor(value) {
|
|
965
1061
|
return cbor2.encode(value, {
|
|
@@ -969,23 +1065,54 @@ function encodeCanonicalCbor(value) {
|
|
|
969
1065
|
sortKeys: sorts.sortCoreDeterministic
|
|
970
1066
|
});
|
|
971
1067
|
}
|
|
1068
|
+
var CARDANO_POE_ITEM_HASHES_PREFIX = new TextEncoder().encode(
|
|
1069
|
+
"cardano-poe-item-hashes-v1"
|
|
1070
|
+
);
|
|
972
1071
|
var CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX = new TextEncoder().encode(
|
|
973
1072
|
"cardano-poe-slots-transcript-v1"
|
|
974
1073
|
);
|
|
1074
|
+
var CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX = new TextEncoder().encode(
|
|
1075
|
+
"cardano-poe-passphrase-transcript-v1"
|
|
1076
|
+
);
|
|
1077
|
+
var CARDANO_POE_HKDF_INFO_SLOTS_MAC = new TextEncoder().encode(
|
|
1078
|
+
"cardano-poe-slots-mac-v1"
|
|
1079
|
+
);
|
|
1080
|
+
var CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC = new TextEncoder().encode(
|
|
1081
|
+
"cardano-poe-passphrase-mac-v1"
|
|
1082
|
+
);
|
|
975
1083
|
var CARDANO_POE_HKDF_INFO_PAYLOAD = new TextEncoder().encode(
|
|
976
1084
|
"cardano-poe-payload-v1"
|
|
977
1085
|
);
|
|
978
1086
|
var CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE = new TextEncoder().encode(
|
|
979
1087
|
"cardano-poe-payload-passphrase-v1"
|
|
980
1088
|
);
|
|
1089
|
+
var CARDANO_POE_X25519_KEK_SALT_PREFIX = new TextEncoder().encode(
|
|
1090
|
+
"cardano-poe-x25519-kek-salt-v1"
|
|
1091
|
+
);
|
|
981
1092
|
var CARDANO_POE_XWING_KEK_SALT_PREFIX = new TextEncoder().encode(
|
|
982
1093
|
"cardano-poe-xwing-kek-salt-v1"
|
|
983
1094
|
);
|
|
1095
|
+
if (CARDANO_POE_ITEM_HASHES_PREFIX.length !== 26) {
|
|
1096
|
+
throw new Error("CARDANO_POE_ITEM_HASHES_PREFIX byte-length invariant violated (expected 26)");
|
|
1097
|
+
}
|
|
984
1098
|
if (CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length !== 31) {
|
|
985
1099
|
throw new Error(
|
|
986
1100
|
"CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX byte-length invariant violated (expected 31)"
|
|
987
1101
|
);
|
|
988
1102
|
}
|
|
1103
|
+
if (CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX.length !== 36) {
|
|
1104
|
+
throw new Error(
|
|
1105
|
+
"CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX byte-length invariant violated (expected 36)"
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
if (CARDANO_POE_HKDF_INFO_SLOTS_MAC.length !== 24) {
|
|
1109
|
+
throw new Error("CARDANO_POE_HKDF_INFO_SLOTS_MAC byte-length invariant violated (expected 24)");
|
|
1110
|
+
}
|
|
1111
|
+
if (CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC.length !== 29) {
|
|
1112
|
+
throw new Error(
|
|
1113
|
+
"CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC byte-length invariant violated (expected 29)"
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
989
1116
|
if (CARDANO_POE_HKDF_INFO_PAYLOAD.length !== 22) {
|
|
990
1117
|
throw new Error("CARDANO_POE_HKDF_INFO_PAYLOAD byte-length invariant violated (expected 22)");
|
|
991
1118
|
}
|
|
@@ -994,52 +1121,62 @@ if (CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE.length !== 33) {
|
|
|
994
1121
|
"CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE byte-length invariant violated (expected 33)"
|
|
995
1122
|
);
|
|
996
1123
|
}
|
|
1124
|
+
if (CARDANO_POE_X25519_KEK_SALT_PREFIX.length !== 30) {
|
|
1125
|
+
throw new Error(
|
|
1126
|
+
"CARDANO_POE_X25519_KEK_SALT_PREFIX byte-length invariant violated (expected 30)"
|
|
1127
|
+
);
|
|
1128
|
+
}
|
|
997
1129
|
if (CARDANO_POE_XWING_KEK_SALT_PREFIX.length !== 29) {
|
|
998
1130
|
throw new Error("CARDANO_POE_XWING_KEK_SALT_PREFIX byte-length invariant violated (expected 29)");
|
|
999
1131
|
}
|
|
1000
1132
|
var MAX_SLOTS = 1024;
|
|
1001
1133
|
var MAX_DECODED_ENVELOPE_BYTES = 65536;
|
|
1002
|
-
var
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1134
|
+
var EMPTY_SALT2 = new Uint8Array(0);
|
|
1135
|
+
function labelledSha256(prefix, ...parts) {
|
|
1136
|
+
let total = prefix.length;
|
|
1137
|
+
for (const p of parts) total += p.length;
|
|
1138
|
+
const message = new Uint8Array(total);
|
|
1139
|
+
message.set(prefix, 0);
|
|
1140
|
+
let offset = prefix.length;
|
|
1141
|
+
for (const p of parts) {
|
|
1142
|
+
message.set(p, offset);
|
|
1143
|
+
offset += p.length;
|
|
1009
1144
|
}
|
|
1145
|
+
return sha2_js.sha256(message);
|
|
1010
1146
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1147
|
+
function itemHashesHash(hashes3) {
|
|
1148
|
+
if (Object.keys(hashes3).length === 0) {
|
|
1149
|
+
throw new EciesSealedPoeError(
|
|
1150
|
+
"ENC_REQUIRES_CONTENT_HASH",
|
|
1151
|
+
"hashes MUST carry at least one content-hash entry"
|
|
1152
|
+
);
|
|
1015
1153
|
}
|
|
1016
|
-
|
|
1154
|
+
return labelledSha256(CARDANO_POE_ITEM_HASHES_PREFIX, encodeCanonicalCbor(hashes3));
|
|
1155
|
+
}
|
|
1017
1156
|
function computeSlotsHash(args) {
|
|
1157
|
+
const slots = args.kem === "x25519" ? args.slots.map((s) => ({ epk: s.epk, wrap: s.wrap })) : args.slots.map((s) => ({
|
|
1158
|
+
kem_ct: s.kem_ct,
|
|
1159
|
+
wrap: s.wrap
|
|
1160
|
+
}));
|
|
1018
1161
|
const transcript = {
|
|
1019
1162
|
scheme: 1,
|
|
1020
1163
|
path: "slots",
|
|
1021
|
-
aead:
|
|
1164
|
+
aead: args.aead,
|
|
1022
1165
|
kem: args.kem,
|
|
1023
1166
|
nonce: args.nonce,
|
|
1024
|
-
slots
|
|
1167
|
+
slots,
|
|
1168
|
+
hashes_hash: args.hashesHash
|
|
1025
1169
|
};
|
|
1026
|
-
|
|
1027
|
-
const message = new Uint8Array(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length + encoded.length);
|
|
1028
|
-
message.set(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX, 0);
|
|
1029
|
-
message.set(encoded, CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length);
|
|
1030
|
-
return sha2_js.sha256(message);
|
|
1170
|
+
return labelledSha256(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX, encodeCanonicalCbor(transcript));
|
|
1031
1171
|
}
|
|
1032
|
-
function
|
|
1033
|
-
const
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
slots_mac: args.slotsMac
|
|
1041
|
-
};
|
|
1042
|
-
return encodeCanonicalCbor(ad);
|
|
1172
|
+
function computeSlotsMac(args) {
|
|
1173
|
+
const macKey = hkdfSha2562({
|
|
1174
|
+
ikm: args.cek,
|
|
1175
|
+
salt: EMPTY_SALT2,
|
|
1176
|
+
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1177
|
+
length: 32
|
|
1178
|
+
});
|
|
1179
|
+
return hmac_js.hmac(sha2_js.sha256, macKey, args.slotsHash);
|
|
1043
1180
|
}
|
|
1044
1181
|
function slotsPayloadKey(args) {
|
|
1045
1182
|
return hkdfSha2562({
|
|
@@ -1049,25 +1186,17 @@ function slotsPayloadKey(args) {
|
|
|
1049
1186
|
length: 32
|
|
1050
1187
|
});
|
|
1051
1188
|
}
|
|
1189
|
+
function x25519KekSalt(args) {
|
|
1190
|
+
return labelledSha256(CARDANO_POE_X25519_KEK_SALT_PREFIX, args.nonce, args.epk, args.pubR);
|
|
1191
|
+
}
|
|
1052
1192
|
function xwingKekSalt(args) {
|
|
1053
|
-
|
|
1054
|
-
CARDANO_POE_XWING_KEK_SALT_PREFIX.length + args.kemCt.length + args.pubR.length
|
|
1055
|
-
);
|
|
1056
|
-
let offset = 0;
|
|
1057
|
-
message.set(CARDANO_POE_XWING_KEK_SALT_PREFIX, offset);
|
|
1058
|
-
offset += CARDANO_POE_XWING_KEK_SALT_PREFIX.length;
|
|
1059
|
-
message.set(args.kemCt, offset);
|
|
1060
|
-
offset += args.kemCt.length;
|
|
1061
|
-
message.set(args.pubR, offset);
|
|
1062
|
-
return sha2_js.sha256(message);
|
|
1193
|
+
return labelledSha256(CARDANO_POE_XWING_KEK_SALT_PREFIX, args.nonce, args.kemCt, args.pubR);
|
|
1063
1194
|
}
|
|
1195
|
+
var SEALED_POE_AEAD = "chacha20-poly1305-stream64k";
|
|
1064
1196
|
var CARDANO_POE_HKDF_INFO_KEK = new TextEncoder().encode("cardano-poe-kek-v1");
|
|
1065
1197
|
var CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 = new TextEncoder().encode(
|
|
1066
1198
|
"cardano-poe-kek-mlkem768x25519-v1"
|
|
1067
1199
|
);
|
|
1068
|
-
var CARDANO_POE_HKDF_INFO_SLOTS_MAC = new TextEncoder().encode(
|
|
1069
|
-
"cardano-poe-slots-mac-v1"
|
|
1070
|
-
);
|
|
1071
1200
|
var ZERO_NONCE_12 = new Uint8Array(12);
|
|
1072
1201
|
if (CARDANO_POE_HKDF_INFO_KEK.length !== 18) {
|
|
1073
1202
|
throw new Error("CARDANO_POE_HKDF_INFO_KEK byte-length invariant violated (expected 18)");
|
|
@@ -1077,9 +1206,6 @@ if (CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519.length !== 33) {
|
|
|
1077
1206
|
"CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 byte-length invariant violated (expected 33)"
|
|
1078
1207
|
);
|
|
1079
1208
|
}
|
|
1080
|
-
if (CARDANO_POE_HKDF_INFO_SLOTS_MAC.length !== 24) {
|
|
1081
|
-
throw new Error("CARDANO_POE_HKDF_INFO_SLOTS_MAC byte-length invariant violated (expected 24)");
|
|
1082
|
-
}
|
|
1083
1209
|
if (ZERO_NONCE_12.length !== 12) {
|
|
1084
1210
|
throw new Error("ZERO_NONCE_12 byte-length invariant violated (expected 12)");
|
|
1085
1211
|
}
|
|
@@ -1089,22 +1215,56 @@ function compareCt(a, b) {
|
|
|
1089
1215
|
for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
|
|
1090
1216
|
return diff === 0;
|
|
1091
1217
|
}
|
|
1218
|
+
var CEK_LENGTH2 = 32;
|
|
1219
|
+
function newSlotAcceptanceState() {
|
|
1220
|
+
return {
|
|
1221
|
+
foundBit: 0,
|
|
1222
|
+
conflictBit: 0,
|
|
1223
|
+
selectedCek: new Uint8Array(CEK_LENGTH2),
|
|
1224
|
+
selectedSlotIdx: -1
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
function foldSlotAcceptance(state, ok, candidateCek, slotIdx) {
|
|
1228
|
+
if (candidateCek.length !== CEK_LENGTH2) {
|
|
1229
|
+
throw new Error(
|
|
1230
|
+
`candidate CEK MUST be exactly ${CEK_LENGTH2} bytes, got ${candidateCek.length}`
|
|
1231
|
+
);
|
|
1232
|
+
}
|
|
1233
|
+
const okBit = ok & 1;
|
|
1234
|
+
const firstBit = okBit & (state.foundBit ^ 1);
|
|
1235
|
+
const firstByteMask = -firstBit & 255;
|
|
1236
|
+
const firstWordMask = -firstBit | 0;
|
|
1237
|
+
let diff = 0;
|
|
1238
|
+
for (let i = 0; i < CEK_LENGTH2; i++) {
|
|
1239
|
+
diff |= candidateCek[i] ^ state.selectedCek[i];
|
|
1240
|
+
}
|
|
1241
|
+
const neqBit = (diff | -diff) >>> 31 & 1;
|
|
1242
|
+
state.conflictBit |= okBit & state.foundBit & neqBit;
|
|
1243
|
+
for (let i = 0; i < CEK_LENGTH2; i++) {
|
|
1244
|
+
state.selectedCek[i] = candidateCek[i] & firstByteMask | state.selectedCek[i] & ~firstByteMask & 255;
|
|
1245
|
+
}
|
|
1246
|
+
state.selectedSlotIdx = slotIdx & firstWordMask | state.selectedSlotIdx & ~firstWordMask;
|
|
1247
|
+
state.foundBit |= okBit;
|
|
1248
|
+
}
|
|
1249
|
+
function finishSlotAcceptance(state) {
|
|
1250
|
+
const found = state.foundBit === 1;
|
|
1251
|
+
return {
|
|
1252
|
+
found,
|
|
1253
|
+
cekConflict: state.conflictBit === 1,
|
|
1254
|
+
selectedCek: found ? state.selectedCek : null,
|
|
1255
|
+
selectedSlotIdx: state.selectedSlotIdx
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1092
1258
|
function selectBundleSecrets(envelope, bundle) {
|
|
1093
1259
|
return envelope.kem === "x25519" ? bundle.x25519PrivateKeys : bundle.mlkem768x25519SecretSeeds;
|
|
1094
1260
|
}
|
|
1095
1261
|
var ZERO_NONCE_122 = new Uint8Array(12);
|
|
1096
|
-
var
|
|
1262
|
+
var CEK_LENGTH3 = 32;
|
|
1097
1263
|
var X25519_SECRET_KEY_LENGTH2 = 32;
|
|
1098
1264
|
var X25519_PUBLIC_KEY_LENGTH2 = 32;
|
|
1099
|
-
var
|
|
1265
|
+
var NONCE_LENGTH3 = 24;
|
|
1100
1266
|
var WRAP_LENGTH2 = 48;
|
|
1101
1267
|
var SLOTS_MAC_LENGTH2 = 32;
|
|
1102
|
-
function concat2(a, b) {
|
|
1103
|
-
const out = new Uint8Array(a.length + b.length);
|
|
1104
|
-
out.set(a, 0);
|
|
1105
|
-
out.set(b, a.length);
|
|
1106
|
-
return out;
|
|
1107
|
-
}
|
|
1108
1268
|
function bytesKey(bytes) {
|
|
1109
1269
|
let s = "";
|
|
1110
1270
|
for (let i = 0; i < bytes.length; i++) {
|
|
@@ -1115,14 +1275,14 @@ function bytesKey(bytes) {
|
|
|
1115
1275
|
function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
1116
1276
|
if (envelope.scheme !== 1) {
|
|
1117
1277
|
throw new EciesSealedPoeError(
|
|
1118
|
-
"
|
|
1278
|
+
"UNSUPPORTED_ENVELOPE_SCHEME",
|
|
1119
1279
|
`envelope.scheme=${String(envelope.scheme)} unsupported (expected 1)`
|
|
1120
1280
|
);
|
|
1121
1281
|
}
|
|
1122
|
-
if (envelope.aead !==
|
|
1282
|
+
if (envelope.aead !== SEALED_POE_AEAD) {
|
|
1123
1283
|
throw new EciesSealedPoeError(
|
|
1124
1284
|
"UNSUPPORTED_AEAD_ALG",
|
|
1125
|
-
`envelope.aead=${String(envelope.aead)} unsupported (expected '
|
|
1285
|
+
`envelope.aead=${String(envelope.aead)} unsupported (expected '${SEALED_POE_AEAD}')`
|
|
1126
1286
|
);
|
|
1127
1287
|
}
|
|
1128
1288
|
if (envelope.kem !== "x25519" && envelope.kem !== "mlkem768x25519") {
|
|
@@ -1141,10 +1301,10 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1141
1301
|
`envelope.slots.length=${n} exceeds MAX_SLOTS=${MAX_SLOTS}`
|
|
1142
1302
|
);
|
|
1143
1303
|
}
|
|
1144
|
-
if (envelope.nonce.length !==
|
|
1304
|
+
if (envelope.nonce.length !== NONCE_LENGTH3) {
|
|
1145
1305
|
throw new EciesSealedPoeError(
|
|
1146
1306
|
"NONCE_LENGTH_MISMATCH",
|
|
1147
|
-
`envelope.nonce MUST be exactly ${
|
|
1307
|
+
`envelope.nonce MUST be exactly ${NONCE_LENGTH3} bytes, got ${envelope.nonce.length}`
|
|
1148
1308
|
);
|
|
1149
1309
|
}
|
|
1150
1310
|
if (envelope.slots_mac.length !== SLOTS_MAC_LENGTH2) {
|
|
@@ -1181,11 +1341,10 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1181
1341
|
} else {
|
|
1182
1342
|
for (let i = 0; i < n; i++) {
|
|
1183
1343
|
const slot = envelope.slots[i];
|
|
1184
|
-
|
|
1185
|
-
if (enc.length !== MLKEM768X25519_ENC_LENGTH) {
|
|
1344
|
+
if (slot.kem_ct.length !== MLKEM768X25519_ENC_LENGTH) {
|
|
1186
1345
|
throw new EciesSealedPoeError(
|
|
1187
1346
|
"KEM_CT_LENGTH_MISMATCH",
|
|
1188
|
-
`envelope.slots[${i}].kem_ct MUST
|
|
1347
|
+
`envelope.slots[${i}].kem_ct MUST be exactly ${MLKEM768X25519_ENC_LENGTH} bytes, got ${slot.kem_ct.length}`
|
|
1189
1348
|
);
|
|
1190
1349
|
}
|
|
1191
1350
|
if (slot.wrap.length !== WRAP_LENGTH2) {
|
|
@@ -1194,7 +1353,7 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1194
1353
|
`envelope.slots[${i}].wrap MUST be exactly ${WRAP_LENGTH2} bytes, got ${slot.wrap.length}`
|
|
1195
1354
|
);
|
|
1196
1355
|
}
|
|
1197
|
-
const key = bytesKey(
|
|
1356
|
+
const key = bytesKey(slot.kem_ct);
|
|
1198
1357
|
if (seenKemMaterial.has(key)) {
|
|
1199
1358
|
throw new EciesSealedPoeError(
|
|
1200
1359
|
"ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
|
|
@@ -1205,7 +1364,7 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1205
1364
|
}
|
|
1206
1365
|
}
|
|
1207
1366
|
const perSlotBytes = envelope.kem === "x25519" ? X25519_PUBLIC_KEY_LENGTH2 + WRAP_LENGTH2 : MLKEM768X25519_ENC_LENGTH + WRAP_LENGTH2;
|
|
1208
|
-
const decodedEnvelopeBytes =
|
|
1367
|
+
const decodedEnvelopeBytes = NONCE_LENGTH3 + SLOTS_MAC_LENGTH2 + n * perSlotBytes;
|
|
1209
1368
|
if (decodedEnvelopeBytes > MAX_DECODED_ENVELOPE_BYTES) {
|
|
1210
1369
|
throw new EciesSealedPoeError(
|
|
1211
1370
|
"ENC_ENVELOPE_TOO_LARGE",
|
|
@@ -1231,66 +1390,63 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1231
1390
|
}
|
|
1232
1391
|
}
|
|
1233
1392
|
var ZERO_IKM_32 = new Uint8Array(32);
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
let shared;
|
|
1393
|
+
var DUMMY_CEK_32 = new Uint8Array(32);
|
|
1394
|
+
function wrapOpenOrDummy(kek, aad, wrap) {
|
|
1237
1395
|
try {
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1396
|
+
const plaintext = chacha20Poly1305Decrypt({
|
|
1397
|
+
key: kek,
|
|
1398
|
+
nonce: ZERO_NONCE_122,
|
|
1399
|
+
aad,
|
|
1400
|
+
ciphertext: wrap
|
|
1241
1401
|
});
|
|
1402
|
+
if (plaintext.length === CEK_LENGTH3) {
|
|
1403
|
+
return { ok: 1, candidate: plaintext };
|
|
1404
|
+
}
|
|
1405
|
+
return { ok: 0, candidate: DUMMY_CEK_32 };
|
|
1242
1406
|
} catch (e) {
|
|
1243
|
-
if (!(e instanceof
|
|
1244
|
-
|
|
1245
|
-
return null;
|
|
1407
|
+
if (!(e instanceof AeadVerificationError)) throw e;
|
|
1408
|
+
return { ok: 0, candidate: DUMMY_CEK_32 };
|
|
1246
1409
|
}
|
|
1247
|
-
|
|
1410
|
+
}
|
|
1411
|
+
function tryX25519Slot(args) {
|
|
1412
|
+
const salt = x25519KekSalt({ nonce: args.nonce, epk: args.slot.epk, pubR: args.pubRLocal });
|
|
1413
|
+
let kemOk = 1;
|
|
1414
|
+
let kek;
|
|
1248
1415
|
try {
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
aad: CARDANO_POE_HKDF_INFO_KEK,
|
|
1253
|
-
ciphertext: args.slot.wrap
|
|
1416
|
+
const shared = x25519Ecdh({
|
|
1417
|
+
secretKey: args.recipientSecretKey,
|
|
1418
|
+
theirPublicKey: args.slot.epk
|
|
1254
1419
|
});
|
|
1420
|
+
kek = hkdfSha2562({ ikm: shared, salt, info: CARDANO_POE_HKDF_INFO_KEK, length: 32 });
|
|
1255
1421
|
} catch (e) {
|
|
1256
|
-
if (!(e instanceof
|
|
1257
|
-
|
|
1422
|
+
if (!(e instanceof X25519LowOrderPointError)) throw e;
|
|
1423
|
+
kemOk = 0;
|
|
1424
|
+
kek = hkdfSha2562({ ikm: ZERO_IKM_32, salt, info: CARDANO_POE_HKDF_INFO_KEK, length: 32 });
|
|
1258
1425
|
}
|
|
1426
|
+
const opened = wrapOpenOrDummy(kek, CARDANO_POE_HKDF_INFO_KEK, args.slot.wrap);
|
|
1427
|
+
return { ok: kemOk & opened.ok, candidate: opened.candidate };
|
|
1259
1428
|
}
|
|
1260
1429
|
function tryMlkem768X25519Slot(args) {
|
|
1261
|
-
const
|
|
1262
|
-
|
|
1430
|
+
const ss = mlkem768x25519Decapsulate({
|
|
1431
|
+
secretSeed: args.recipientSecretKey,
|
|
1432
|
+
enc: args.slot.kem_ct
|
|
1433
|
+
});
|
|
1263
1434
|
const kek = hkdfSha2562({
|
|
1264
1435
|
ikm: ss,
|
|
1265
|
-
salt: xwingKekSalt({ kemCt:
|
|
1436
|
+
salt: xwingKekSalt({ nonce: args.nonce, kemCt: args.slot.kem_ct, pubR: args.pubR }),
|
|
1266
1437
|
info: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
|
|
1267
1438
|
length: 32
|
|
1268
1439
|
});
|
|
1269
|
-
|
|
1270
|
-
return chacha20Poly1305Decrypt({
|
|
1271
|
-
key: kek,
|
|
1272
|
-
nonce: ZERO_NONCE_122,
|
|
1273
|
-
aad: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
|
|
1274
|
-
ciphertext: args.slot.wrap
|
|
1275
|
-
});
|
|
1276
|
-
} catch (e) {
|
|
1277
|
-
if (!(e instanceof AeadVerificationError)) throw e;
|
|
1278
|
-
return null;
|
|
1279
|
-
}
|
|
1440
|
+
return wrapOpenOrDummy(kek, CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519, args.slot.wrap);
|
|
1280
1441
|
}
|
|
1281
|
-
function
|
|
1442
|
+
function runPrivPass(envelope, recipientSecretKey, slotsHash, slotsAttemptedOut) {
|
|
1282
1443
|
const n = envelope.slots.length;
|
|
1283
|
-
|
|
1284
|
-
let
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
cek = candidate;
|
|
1290
|
-
matchedSlotIdx = i;
|
|
1291
|
-
} else if (!compareCt(candidate, cek)) {
|
|
1292
|
-
cekConflict = true;
|
|
1293
|
-
}
|
|
1444
|
+
const state = newSlotAcceptanceState();
|
|
1445
|
+
let anyOpenedBit = 0;
|
|
1446
|
+
const acceptSlot = (slot, i) => {
|
|
1447
|
+
anyOpenedBit |= slot.ok;
|
|
1448
|
+
const macOk = Number(compareCt(computeSlotsMac({ cek: slot.candidate, slotsHash }), envelope.slots_mac)) & 1;
|
|
1449
|
+
foldSlotAcceptance(state, slot.ok & macOk, slot.candidate, i);
|
|
1294
1450
|
};
|
|
1295
1451
|
if (envelope.kem === "x25519") {
|
|
1296
1452
|
const pubRLocal = x25519PublicKey2({ secretKey: recipientSecretKey });
|
|
@@ -1298,8 +1454,15 @@ function tryRecipientUnwrapWithIdx(envelope, recipientSecretKey, constantTimeN,
|
|
|
1298
1454
|
if (slotsAttemptedOut !== void 0) {
|
|
1299
1455
|
slotsAttemptedOut.count = i + 1;
|
|
1300
1456
|
}
|
|
1301
|
-
|
|
1302
|
-
|
|
1457
|
+
acceptSlot(
|
|
1458
|
+
tryX25519Slot({
|
|
1459
|
+
slot: envelope.slots[i],
|
|
1460
|
+
nonce: envelope.nonce,
|
|
1461
|
+
recipientSecretKey,
|
|
1462
|
+
pubRLocal
|
|
1463
|
+
}),
|
|
1464
|
+
i
|
|
1465
|
+
);
|
|
1303
1466
|
}
|
|
1304
1467
|
} else {
|
|
1305
1468
|
const pubR = mlkem768x25519Keygen2(recipientSecretKey).publicKey;
|
|
@@ -1307,22 +1470,37 @@ function tryRecipientUnwrapWithIdx(envelope, recipientSecretKey, constantTimeN,
|
|
|
1307
1470
|
if (slotsAttemptedOut !== void 0) {
|
|
1308
1471
|
slotsAttemptedOut.count = i + 1;
|
|
1309
1472
|
}
|
|
1310
|
-
|
|
1311
|
-
|
|
1473
|
+
acceptSlot(
|
|
1474
|
+
tryMlkem768X25519Slot({
|
|
1475
|
+
slot: envelope.slots[i],
|
|
1476
|
+
nonce: envelope.nonce,
|
|
1477
|
+
recipientSecretKey,
|
|
1478
|
+
pubR
|
|
1479
|
+
}),
|
|
1480
|
+
i
|
|
1481
|
+
);
|
|
1312
1482
|
}
|
|
1313
1483
|
}
|
|
1314
|
-
|
|
1484
|
+
const outcome = finishSlotAcceptance(state);
|
|
1485
|
+
return {
|
|
1486
|
+
found: outcome.found,
|
|
1487
|
+
selectedCek: outcome.selectedCek,
|
|
1488
|
+
selectedSlotIdx: outcome.selectedSlotIdx,
|
|
1489
|
+
cekConflict: outcome.cekConflict,
|
|
1490
|
+
anyOpened: anyOpenedBit === 1
|
|
1491
|
+
};
|
|
1315
1492
|
}
|
|
1316
|
-
function slotsHashBytes(envelope) {
|
|
1493
|
+
function slotsHashBytes(envelope, hashes3) {
|
|
1317
1494
|
return computeSlotsHash({
|
|
1495
|
+
aead: envelope.aead,
|
|
1318
1496
|
kem: envelope.kem,
|
|
1319
1497
|
nonce: envelope.nonce,
|
|
1320
|
-
slots: envelope.slots
|
|
1498
|
+
slots: envelope.slots,
|
|
1499
|
+
hashesHash: itemHashesHash(hashes3)
|
|
1321
1500
|
});
|
|
1322
1501
|
}
|
|
1323
1502
|
function eciesSealedPoeUnwrap(args) {
|
|
1324
1503
|
const { envelope, ciphertext } = args;
|
|
1325
|
-
const constantTimeN = args.constantTimeN ?? true;
|
|
1326
1504
|
const hasSingle = "recipientSecretKey" in args;
|
|
1327
1505
|
const hasBundle = "recipientKeyBundle" in args;
|
|
1328
1506
|
const multiPrivKeys = hasBundle ? selectBundleSecrets(envelope, args.recipientKeyBundle) : "recipientSecretKeys" in args ? args.recipientSecretKeys : void 0;
|
|
@@ -1347,100 +1525,47 @@ function eciesSealedPoeUnwrap(args) {
|
|
|
1347
1525
|
} else {
|
|
1348
1526
|
assertEnvelopeStructure(envelope, void 0, args.recipientSecretKey);
|
|
1349
1527
|
}
|
|
1350
|
-
|
|
1351
|
-
const slotsHash = slotsHashBytes(envelope);
|
|
1528
|
+
const slotsHash = slotsHashBytes(envelope, args.hashes);
|
|
1352
1529
|
let matchedCek = null;
|
|
1353
|
-
let
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
recipientSecretKey,
|
|
1359
|
-
constantTimeN,
|
|
1360
|
-
args._slotsAttemptedOut
|
|
1361
|
-
);
|
|
1362
|
-
if (candidate === null) {
|
|
1363
|
-
return { matched: false, reason: "WRONG_RECIPIENT_KEY" };
|
|
1364
|
-
}
|
|
1365
|
-
if (candidate.cekConflict) {
|
|
1366
|
-
return { matched: false, reason: "TAMPERED_HEADER" };
|
|
1530
|
+
let anyOpenedAcrossPrivs = false;
|
|
1531
|
+
const privKeys = hasMulti ? multiPrivKeys : [args.recipientSecretKey];
|
|
1532
|
+
for (let k = 0; k < privKeys.length; k++) {
|
|
1533
|
+
if (args._privsAttemptedOut !== void 0) {
|
|
1534
|
+
args._privsAttemptedOut.count = k + 1;
|
|
1367
1535
|
}
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
salt: EMPTY_SALT22,
|
|
1371
|
-
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1372
|
-
length: 32
|
|
1373
|
-
});
|
|
1374
|
-
const slotsMacCalc = hmac_js.hmac(sha2_js.sha256, hmacKey, slotsHash);
|
|
1375
|
-
if (!compareCt(slotsMacCalc, envelope.slots_mac)) {
|
|
1376
|
-
return { matched: false, reason: "TAMPERED_HEADER" };
|
|
1536
|
+
if (args._slotsAttemptedOut !== void 0) {
|
|
1537
|
+
args._slotsAttemptedOut.count = 0;
|
|
1377
1538
|
}
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
let cekConflict = false;
|
|
1382
|
-
for (let k = 0; k < recipientSecretKeys.length; k++) {
|
|
1383
|
-
if (args._privsAttemptedOut !== void 0) {
|
|
1384
|
-
args._privsAttemptedOut.count = k + 1;
|
|
1385
|
-
}
|
|
1386
|
-
if (args._slotsAttemptedOut !== void 0) {
|
|
1387
|
-
args._slotsAttemptedOut.count = 0;
|
|
1388
|
-
}
|
|
1389
|
-
const candidate = tryRecipientUnwrapWithIdx(
|
|
1390
|
-
envelope,
|
|
1391
|
-
recipientSecretKeys[k],
|
|
1392
|
-
constantTimeN,
|
|
1393
|
-
args._slotsAttemptedOut
|
|
1394
|
-
);
|
|
1395
|
-
if (args._slotsAttemptedOut?.perPrivCounts !== void 0) {
|
|
1396
|
-
args._slotsAttemptedOut.perPrivCounts.push(args._slotsAttemptedOut.count);
|
|
1397
|
-
}
|
|
1398
|
-
if (candidate === null) continue;
|
|
1399
|
-
if (candidate.cekConflict) cekConflict = true;
|
|
1400
|
-
const cek = candidate.cek;
|
|
1401
|
-
const hmacKey = hkdfSha2562({
|
|
1402
|
-
ikm: cek,
|
|
1403
|
-
salt: EMPTY_SALT22,
|
|
1404
|
-
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1405
|
-
length: 32
|
|
1406
|
-
});
|
|
1407
|
-
const slotsMacCalc = hmac_js.hmac(sha2_js.sha256, hmacKey, slotsHash);
|
|
1408
|
-
if (compareCt(slotsMacCalc, envelope.slots_mac)) {
|
|
1409
|
-
matchedCek = cek;
|
|
1410
|
-
break;
|
|
1411
|
-
}
|
|
1412
|
-
anyCandidateRecovered = true;
|
|
1539
|
+
const pass = runPrivPass(envelope, privKeys[k], slotsHash, args._slotsAttemptedOut);
|
|
1540
|
+
if (args._slotsAttemptedOut?.perPrivCounts !== void 0) {
|
|
1541
|
+
args._slotsAttemptedOut.perPrivCounts.push(args._slotsAttemptedOut.count);
|
|
1413
1542
|
}
|
|
1414
|
-
|
|
1543
|
+
anyOpenedAcrossPrivs = anyOpenedAcrossPrivs || pass.anyOpened;
|
|
1544
|
+
if (!pass.found) continue;
|
|
1545
|
+
if (pass.cekConflict) {
|
|
1415
1546
|
return { matched: false, reason: "TAMPERED_HEADER" };
|
|
1416
1547
|
}
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1548
|
+
matchedCek = pass.selectedCek;
|
|
1549
|
+
break;
|
|
1550
|
+
}
|
|
1551
|
+
if (matchedCek === null) {
|
|
1552
|
+
return {
|
|
1553
|
+
matched: false,
|
|
1554
|
+
reason: anyOpenedAcrossPrivs ? "TAMPERED_HEADER" : "WRONG_RECIPIENT_KEY"
|
|
1555
|
+
};
|
|
1423
1556
|
}
|
|
1424
|
-
const payloadKey = slotsPayloadKey({ cek: matchedCek, nonce: envelope.nonce });
|
|
1425
|
-
const adContent = adContentSlots({
|
|
1426
|
-
kem: envelope.kem,
|
|
1427
|
-
nonce: envelope.nonce,
|
|
1428
|
-
slotsHash,
|
|
1429
|
-
slotsMac: envelope.slots_mac
|
|
1430
|
-
});
|
|
1431
1557
|
try {
|
|
1432
|
-
const plaintext =
|
|
1433
|
-
|
|
1434
|
-
nonce: envelope.nonce,
|
|
1435
|
-
aad: adContent,
|
|
1558
|
+
const plaintext = streamOpen({
|
|
1559
|
+
payloadKey: slotsPayloadKey({ cek: matchedCek, nonce: envelope.nonce }),
|
|
1436
1560
|
ciphertext
|
|
1437
1561
|
});
|
|
1438
1562
|
return { matched: true, plaintext };
|
|
1439
1563
|
} catch (e) {
|
|
1440
|
-
if (!(e instanceof
|
|
1564
|
+
if (!(e instanceof StreamTamperedError)) throw e;
|
|
1441
1565
|
return { matched: false, reason: "TAMPERED_CIPHERTEXT" };
|
|
1442
1566
|
}
|
|
1443
1567
|
}
|
|
1568
|
+
new TextEncoder();
|
|
1444
1569
|
ed__namespace.hashes.sha512 = sha2_js.sha512;
|
|
1445
1570
|
ed__namespace.Point.CURVE().n;
|
|
1446
1571
|
function signEd25519(opts2) {
|
|
@@ -1482,6 +1607,7 @@ function decryptSealedFromSeed(args) {
|
|
|
1482
1607
|
return eciesSealedPoeUnwrap({
|
|
1483
1608
|
envelope: args.envelope,
|
|
1484
1609
|
ciphertext: args.ciphertext,
|
|
1610
|
+
hashes: args.hashes,
|
|
1485
1611
|
recipientKeyBundle: recipientKeyBundleFromSeed(args.seed)
|
|
1486
1612
|
});
|
|
1487
1613
|
}
|