@cardanowall/sdk-ts 0.3.0 → 0.5.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 +1146 -365
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +48 -7
- package/dist/client/index.d.ts +48 -7
- package/dist/client/index.js +1144 -367
- 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 +5480 -2520
- 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 +5460 -2516
- 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-DNu_IrWZ.d.cts} +236 -7
- package/dist/{types-DGsZTMuZ.d.ts → types-DNu_IrWZ.d.ts} +236 -7
- 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.js
CHANGED
|
@@ -11,10 +11,11 @@ import { anumber, abytes, randomBytes as randomBytes$1, u32, swap32IfBE } from '
|
|
|
11
11
|
import { FFTCore, reverseBits } from '@noble/curves/abstract/fft.js';
|
|
12
12
|
import * as ed from '@noble/ed25519';
|
|
13
13
|
import '@noble/ciphers/utils.js';
|
|
14
|
+
import { chacha20poly1305 } from '@noble/ciphers/chacha.js';
|
|
14
15
|
import { hmac } from '@noble/hashes/hmac.js';
|
|
15
|
-
import { xchacha20poly1305, chacha20poly1305 } from '@noble/ciphers/chacha.js';
|
|
16
16
|
import { encode } from 'cbor2';
|
|
17
17
|
import { sortCoreDeterministic } from 'cbor2/sorts';
|
|
18
|
+
import 'hash-wasm';
|
|
18
19
|
|
|
19
20
|
// ../crypto-core/dist/seed-derive.js
|
|
20
21
|
var abytesDoc = abytes;
|
|
@@ -845,16 +846,6 @@ function chacha20Poly1305Decrypt(opts2) {
|
|
|
845
846
|
throw new AeadVerificationError("chacha20-poly1305 decrypt failed", { cause });
|
|
846
847
|
}
|
|
847
848
|
}
|
|
848
|
-
function xchacha20Poly1305Decrypt(opts2) {
|
|
849
|
-
try {
|
|
850
|
-
return xchacha20poly1305(opts2.key, opts2.nonce, opts2.aad).decrypt(opts2.ciphertext);
|
|
851
|
-
} catch (cause) {
|
|
852
|
-
throw new AeadVerificationError("xchacha20-poly1305 decrypt failed", { cause });
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
function hkdfSha2562(opts2) {
|
|
856
|
-
return hkdf(sha256, opts2.ikm, opts2.salt, opts2.info, opts2.length);
|
|
857
|
-
}
|
|
858
849
|
var MLKEM768X25519_ENC_LENGTH = 1120;
|
|
859
850
|
var MLKEM768X25519_SEED_LENGTH2 = 32;
|
|
860
851
|
function mlkem768x25519Keygen2(seed) {
|
|
@@ -900,6 +891,9 @@ function x25519Ecdh(opts2) {
|
|
|
900
891
|
throw e;
|
|
901
892
|
}
|
|
902
893
|
}
|
|
894
|
+
function hkdfSha2562(opts2) {
|
|
895
|
+
return hkdf(sha256, opts2.ikm, opts2.salt, opts2.info, opts2.length);
|
|
896
|
+
}
|
|
903
897
|
var EciesSealedPoeError = class extends Error {
|
|
904
898
|
code;
|
|
905
899
|
constructor(code, message, options) {
|
|
@@ -908,36 +902,138 @@ var EciesSealedPoeError = class extends Error {
|
|
|
908
902
|
this.code = code;
|
|
909
903
|
}
|
|
910
904
|
};
|
|
911
|
-
var
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
905
|
+
var CHUNK_SIZE = 65536;
|
|
906
|
+
var TAG_SIZE = 16;
|
|
907
|
+
var NONCE_LENGTH = 12;
|
|
908
|
+
var COUNTER_LENGTH = 11;
|
|
909
|
+
var SEALED_CHUNK_SIZE = CHUNK_SIZE + TAG_SIZE;
|
|
910
|
+
var PAYLOAD_KEY_LENGTH = 32;
|
|
911
|
+
var EMPTY_AAD = new Uint8Array(0);
|
|
912
|
+
var StreamTamperedError = class extends Error {
|
|
913
|
+
code = "TAMPERED_CIPHERTEXT";
|
|
914
|
+
constructor(message, options) {
|
|
915
|
+
super(message, options);
|
|
916
|
+
this.name = "StreamTamperedError";
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
var ChunkNonce = class {
|
|
920
|
+
nonce = new Uint8Array(NONCE_LENGTH);
|
|
921
|
+
finished = false;
|
|
922
|
+
next(final) {
|
|
923
|
+
if (this.finished) {
|
|
924
|
+
throw new Error("STREAM: no chunks may follow the final chunk");
|
|
925
|
+
}
|
|
926
|
+
if (final) {
|
|
927
|
+
this.finished = true;
|
|
928
|
+
this.nonce[COUNTER_LENGTH] = 1;
|
|
929
|
+
}
|
|
930
|
+
const out = this.nonce.slice();
|
|
931
|
+
this.increment();
|
|
932
|
+
return out;
|
|
915
933
|
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
chunks.push(value.subarray(i, Math.min(i + CHUNK_MAX_BYTES, value.length)));
|
|
934
|
+
get done() {
|
|
935
|
+
return this.finished;
|
|
919
936
|
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
937
|
+
increment() {
|
|
938
|
+
for (let i = COUNTER_LENGTH - 1; i >= 0; i--) {
|
|
939
|
+
const v = this.nonce[i] + 1 & 255;
|
|
940
|
+
this.nonce[i] = v;
|
|
941
|
+
if (v !== 0) return;
|
|
942
|
+
}
|
|
943
|
+
throw new Error("STREAM: chunk counter overflow");
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
function assertPayloadKey(payloadKey) {
|
|
947
|
+
if (payloadKey.length !== PAYLOAD_KEY_LENGTH) {
|
|
948
|
+
throw new Error(
|
|
949
|
+
`STREAM: payloadKey MUST be exactly ${PAYLOAD_KEY_LENGTH} bytes, got ${payloadKey.length}`
|
|
950
|
+
);
|
|
930
951
|
}
|
|
931
|
-
return out;
|
|
932
952
|
}
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
953
|
+
var StreamOpener = class {
|
|
954
|
+
payloadKey;
|
|
955
|
+
nonce = new ChunkNonce();
|
|
956
|
+
chunkIndex = 0;
|
|
957
|
+
constructor(payloadKey) {
|
|
958
|
+
assertPayloadKey(payloadKey);
|
|
959
|
+
this.payloadKey = payloadKey;
|
|
936
960
|
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
961
|
+
openChunk(sealedChunk, final) {
|
|
962
|
+
if (sealedChunk.length < TAG_SIZE) {
|
|
963
|
+
throw new StreamTamperedError(
|
|
964
|
+
`STREAM: sealed chunk shorter than the ${TAG_SIZE}-byte tag floor`
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
if (!final && sealedChunk.length !== SEALED_CHUNK_SIZE) {
|
|
968
|
+
throw new StreamTamperedError(
|
|
969
|
+
`STREAM: non-final sealed chunk MUST be exactly ${SEALED_CHUNK_SIZE} bytes, got ${sealedChunk.length}`
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
if (final && sealedChunk.length > SEALED_CHUNK_SIZE) {
|
|
973
|
+
throw new StreamTamperedError(
|
|
974
|
+
`STREAM: final sealed chunk MUST be at most ${SEALED_CHUNK_SIZE} bytes, got ${sealedChunk.length}`
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
if (final && sealedChunk.length === TAG_SIZE && this.chunkIndex > 0) {
|
|
978
|
+
throw new StreamTamperedError("STREAM: zero-length final chunk on a non-empty stream");
|
|
979
|
+
}
|
|
980
|
+
let plaintext;
|
|
981
|
+
try {
|
|
982
|
+
plaintext = chacha20Poly1305Decrypt({
|
|
983
|
+
key: this.payloadKey,
|
|
984
|
+
nonce: this.nonce.next(final),
|
|
985
|
+
aad: EMPTY_AAD,
|
|
986
|
+
ciphertext: sealedChunk
|
|
987
|
+
});
|
|
988
|
+
} catch (e) {
|
|
989
|
+
if (!(e instanceof AeadVerificationError)) throw e;
|
|
990
|
+
throw new StreamTamperedError(`STREAM: chunk ${this.chunkIndex} tag verification failed`, {
|
|
991
|
+
cause: e
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
this.chunkIndex += 1;
|
|
995
|
+
return plaintext;
|
|
996
|
+
}
|
|
997
|
+
};
|
|
998
|
+
function streamOpen(args) {
|
|
999
|
+
const { ciphertext } = args;
|
|
1000
|
+
const total = ciphertext.length;
|
|
1001
|
+
if (total < TAG_SIZE) {
|
|
1002
|
+
throw new StreamTamperedError(
|
|
1003
|
+
`STREAM: ciphertext shorter than the ${TAG_SIZE}-byte single-tag floor`
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
1006
|
+
const rem = total % SEALED_CHUNK_SIZE;
|
|
1007
|
+
let nonFinalCount;
|
|
1008
|
+
let finalSealedLength;
|
|
1009
|
+
if (rem === 0) {
|
|
1010
|
+
nonFinalCount = total / SEALED_CHUNK_SIZE - 1;
|
|
1011
|
+
finalSealedLength = SEALED_CHUNK_SIZE;
|
|
1012
|
+
} else if (rem >= TAG_SIZE) {
|
|
1013
|
+
nonFinalCount = (total - rem) / SEALED_CHUNK_SIZE;
|
|
1014
|
+
finalSealedLength = rem;
|
|
1015
|
+
} else {
|
|
1016
|
+
throw new StreamTamperedError("STREAM: trailing bytes cannot form a well-formed final chunk");
|
|
1017
|
+
}
|
|
1018
|
+
if (nonFinalCount > 0 && finalSealedLength === TAG_SIZE) {
|
|
1019
|
+
throw new StreamTamperedError("STREAM: zero-length final chunk on a non-empty stream");
|
|
1020
|
+
}
|
|
1021
|
+
const opener = new StreamOpener(args.payloadKey);
|
|
1022
|
+
const out = new Uint8Array(nonFinalCount * CHUNK_SIZE + finalSealedLength - TAG_SIZE);
|
|
1023
|
+
let readOffset = 0;
|
|
1024
|
+
let writeOffset = 0;
|
|
1025
|
+
for (let i = 0; i < nonFinalCount; i++) {
|
|
1026
|
+
const plaintext = opener.openChunk(
|
|
1027
|
+
ciphertext.subarray(readOffset, readOffset + SEALED_CHUNK_SIZE),
|
|
1028
|
+
false
|
|
1029
|
+
);
|
|
1030
|
+
out.set(plaintext, writeOffset);
|
|
1031
|
+
readOffset += SEALED_CHUNK_SIZE;
|
|
1032
|
+
writeOffset += CHUNK_SIZE;
|
|
1033
|
+
}
|
|
1034
|
+
const finalPlaintext = opener.openChunk(ciphertext.subarray(readOffset), true);
|
|
1035
|
+
out.set(finalPlaintext, writeOffset);
|
|
1036
|
+
return out;
|
|
941
1037
|
}
|
|
942
1038
|
function encodeCanonicalCbor(value) {
|
|
943
1039
|
return encode(value, {
|
|
@@ -947,23 +1043,54 @@ function encodeCanonicalCbor(value) {
|
|
|
947
1043
|
sortKeys: sortCoreDeterministic
|
|
948
1044
|
});
|
|
949
1045
|
}
|
|
1046
|
+
var CARDANO_POE_ITEM_HASHES_PREFIX = new TextEncoder().encode(
|
|
1047
|
+
"cardano-poe-item-hashes-v1"
|
|
1048
|
+
);
|
|
950
1049
|
var CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX = new TextEncoder().encode(
|
|
951
1050
|
"cardano-poe-slots-transcript-v1"
|
|
952
1051
|
);
|
|
1052
|
+
var CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX = new TextEncoder().encode(
|
|
1053
|
+
"cardano-poe-passphrase-transcript-v1"
|
|
1054
|
+
);
|
|
1055
|
+
var CARDANO_POE_HKDF_INFO_SLOTS_MAC = new TextEncoder().encode(
|
|
1056
|
+
"cardano-poe-slots-mac-v1"
|
|
1057
|
+
);
|
|
1058
|
+
var CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC = new TextEncoder().encode(
|
|
1059
|
+
"cardano-poe-passphrase-mac-v1"
|
|
1060
|
+
);
|
|
953
1061
|
var CARDANO_POE_HKDF_INFO_PAYLOAD = new TextEncoder().encode(
|
|
954
1062
|
"cardano-poe-payload-v1"
|
|
955
1063
|
);
|
|
956
1064
|
var CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE = new TextEncoder().encode(
|
|
957
1065
|
"cardano-poe-payload-passphrase-v1"
|
|
958
1066
|
);
|
|
1067
|
+
var CARDANO_POE_X25519_KEK_SALT_PREFIX = new TextEncoder().encode(
|
|
1068
|
+
"cardano-poe-x25519-kek-salt-v1"
|
|
1069
|
+
);
|
|
959
1070
|
var CARDANO_POE_XWING_KEK_SALT_PREFIX = new TextEncoder().encode(
|
|
960
1071
|
"cardano-poe-xwing-kek-salt-v1"
|
|
961
1072
|
);
|
|
1073
|
+
if (CARDANO_POE_ITEM_HASHES_PREFIX.length !== 26) {
|
|
1074
|
+
throw new Error("CARDANO_POE_ITEM_HASHES_PREFIX byte-length invariant violated (expected 26)");
|
|
1075
|
+
}
|
|
962
1076
|
if (CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length !== 31) {
|
|
963
1077
|
throw new Error(
|
|
964
1078
|
"CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX byte-length invariant violated (expected 31)"
|
|
965
1079
|
);
|
|
966
1080
|
}
|
|
1081
|
+
if (CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX.length !== 36) {
|
|
1082
|
+
throw new Error(
|
|
1083
|
+
"CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX byte-length invariant violated (expected 36)"
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
1086
|
+
if (CARDANO_POE_HKDF_INFO_SLOTS_MAC.length !== 24) {
|
|
1087
|
+
throw new Error("CARDANO_POE_HKDF_INFO_SLOTS_MAC byte-length invariant violated (expected 24)");
|
|
1088
|
+
}
|
|
1089
|
+
if (CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC.length !== 29) {
|
|
1090
|
+
throw new Error(
|
|
1091
|
+
"CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC byte-length invariant violated (expected 29)"
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
967
1094
|
if (CARDANO_POE_HKDF_INFO_PAYLOAD.length !== 22) {
|
|
968
1095
|
throw new Error("CARDANO_POE_HKDF_INFO_PAYLOAD byte-length invariant violated (expected 22)");
|
|
969
1096
|
}
|
|
@@ -972,52 +1099,62 @@ if (CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE.length !== 33) {
|
|
|
972
1099
|
"CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE byte-length invariant violated (expected 33)"
|
|
973
1100
|
);
|
|
974
1101
|
}
|
|
1102
|
+
if (CARDANO_POE_X25519_KEK_SALT_PREFIX.length !== 30) {
|
|
1103
|
+
throw new Error(
|
|
1104
|
+
"CARDANO_POE_X25519_KEK_SALT_PREFIX byte-length invariant violated (expected 30)"
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
975
1107
|
if (CARDANO_POE_XWING_KEK_SALT_PREFIX.length !== 29) {
|
|
976
1108
|
throw new Error("CARDANO_POE_XWING_KEK_SALT_PREFIX byte-length invariant violated (expected 29)");
|
|
977
1109
|
}
|
|
978
1110
|
var MAX_SLOTS = 1024;
|
|
979
1111
|
var MAX_DECODED_ENVELOPE_BYTES = 65536;
|
|
980
|
-
var
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
1112
|
+
var EMPTY_SALT2 = new Uint8Array(0);
|
|
1113
|
+
function labelledSha256(prefix, ...parts) {
|
|
1114
|
+
let total = prefix.length;
|
|
1115
|
+
for (const p of parts) total += p.length;
|
|
1116
|
+
const message = new Uint8Array(total);
|
|
1117
|
+
message.set(prefix, 0);
|
|
1118
|
+
let offset = prefix.length;
|
|
1119
|
+
for (const p of parts) {
|
|
1120
|
+
message.set(p, offset);
|
|
1121
|
+
offset += p.length;
|
|
987
1122
|
}
|
|
1123
|
+
return sha256(message);
|
|
988
1124
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1125
|
+
function itemHashesHash(hashes3) {
|
|
1126
|
+
if (Object.keys(hashes3).length === 0) {
|
|
1127
|
+
throw new EciesSealedPoeError(
|
|
1128
|
+
"ENC_REQUIRES_CONTENT_HASH",
|
|
1129
|
+
"hashes MUST carry at least one content-hash entry"
|
|
1130
|
+
);
|
|
993
1131
|
}
|
|
994
|
-
|
|
1132
|
+
return labelledSha256(CARDANO_POE_ITEM_HASHES_PREFIX, encodeCanonicalCbor(hashes3));
|
|
1133
|
+
}
|
|
995
1134
|
function computeSlotsHash(args) {
|
|
1135
|
+
const slots = args.kem === "x25519" ? args.slots.map((s) => ({ epk: s.epk, wrap: s.wrap })) : args.slots.map((s) => ({
|
|
1136
|
+
kem_ct: s.kem_ct,
|
|
1137
|
+
wrap: s.wrap
|
|
1138
|
+
}));
|
|
996
1139
|
const transcript = {
|
|
997
1140
|
scheme: 1,
|
|
998
1141
|
path: "slots",
|
|
999
|
-
aead:
|
|
1142
|
+
aead: args.aead,
|
|
1000
1143
|
kem: args.kem,
|
|
1001
1144
|
nonce: args.nonce,
|
|
1002
|
-
slots
|
|
1145
|
+
slots,
|
|
1146
|
+
hashes_hash: args.hashesHash
|
|
1003
1147
|
};
|
|
1004
|
-
|
|
1005
|
-
const message = new Uint8Array(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length + encoded.length);
|
|
1006
|
-
message.set(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX, 0);
|
|
1007
|
-
message.set(encoded, CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length);
|
|
1008
|
-
return sha256(message);
|
|
1148
|
+
return labelledSha256(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX, encodeCanonicalCbor(transcript));
|
|
1009
1149
|
}
|
|
1010
|
-
function
|
|
1011
|
-
const
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
slots_mac: args.slotsMac
|
|
1019
|
-
};
|
|
1020
|
-
return encodeCanonicalCbor(ad);
|
|
1150
|
+
function computeSlotsMac(args) {
|
|
1151
|
+
const macKey = hkdfSha2562({
|
|
1152
|
+
ikm: args.cek,
|
|
1153
|
+
salt: EMPTY_SALT2,
|
|
1154
|
+
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1155
|
+
length: 32
|
|
1156
|
+
});
|
|
1157
|
+
return hmac(sha256, macKey, args.slotsHash);
|
|
1021
1158
|
}
|
|
1022
1159
|
function slotsPayloadKey(args) {
|
|
1023
1160
|
return hkdfSha2562({
|
|
@@ -1027,25 +1164,17 @@ function slotsPayloadKey(args) {
|
|
|
1027
1164
|
length: 32
|
|
1028
1165
|
});
|
|
1029
1166
|
}
|
|
1167
|
+
function x25519KekSalt(args) {
|
|
1168
|
+
return labelledSha256(CARDANO_POE_X25519_KEK_SALT_PREFIX, args.nonce, args.epk, args.pubR);
|
|
1169
|
+
}
|
|
1030
1170
|
function xwingKekSalt(args) {
|
|
1031
|
-
|
|
1032
|
-
CARDANO_POE_XWING_KEK_SALT_PREFIX.length + args.kemCt.length + args.pubR.length
|
|
1033
|
-
);
|
|
1034
|
-
let offset = 0;
|
|
1035
|
-
message.set(CARDANO_POE_XWING_KEK_SALT_PREFIX, offset);
|
|
1036
|
-
offset += CARDANO_POE_XWING_KEK_SALT_PREFIX.length;
|
|
1037
|
-
message.set(args.kemCt, offset);
|
|
1038
|
-
offset += args.kemCt.length;
|
|
1039
|
-
message.set(args.pubR, offset);
|
|
1040
|
-
return sha256(message);
|
|
1171
|
+
return labelledSha256(CARDANO_POE_XWING_KEK_SALT_PREFIX, args.nonce, args.kemCt, args.pubR);
|
|
1041
1172
|
}
|
|
1173
|
+
var SEALED_POE_AEAD = "chacha20-poly1305-stream64k";
|
|
1042
1174
|
var CARDANO_POE_HKDF_INFO_KEK = new TextEncoder().encode("cardano-poe-kek-v1");
|
|
1043
1175
|
var CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 = new TextEncoder().encode(
|
|
1044
1176
|
"cardano-poe-kek-mlkem768x25519-v1"
|
|
1045
1177
|
);
|
|
1046
|
-
var CARDANO_POE_HKDF_INFO_SLOTS_MAC = new TextEncoder().encode(
|
|
1047
|
-
"cardano-poe-slots-mac-v1"
|
|
1048
|
-
);
|
|
1049
1178
|
var ZERO_NONCE_12 = new Uint8Array(12);
|
|
1050
1179
|
if (CARDANO_POE_HKDF_INFO_KEK.length !== 18) {
|
|
1051
1180
|
throw new Error("CARDANO_POE_HKDF_INFO_KEK byte-length invariant violated (expected 18)");
|
|
@@ -1055,9 +1184,6 @@ if (CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519.length !== 33) {
|
|
|
1055
1184
|
"CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 byte-length invariant violated (expected 33)"
|
|
1056
1185
|
);
|
|
1057
1186
|
}
|
|
1058
|
-
if (CARDANO_POE_HKDF_INFO_SLOTS_MAC.length !== 24) {
|
|
1059
|
-
throw new Error("CARDANO_POE_HKDF_INFO_SLOTS_MAC byte-length invariant violated (expected 24)");
|
|
1060
|
-
}
|
|
1061
1187
|
if (ZERO_NONCE_12.length !== 12) {
|
|
1062
1188
|
throw new Error("ZERO_NONCE_12 byte-length invariant violated (expected 12)");
|
|
1063
1189
|
}
|
|
@@ -1067,22 +1193,56 @@ function compareCt(a, b) {
|
|
|
1067
1193
|
for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
|
|
1068
1194
|
return diff === 0;
|
|
1069
1195
|
}
|
|
1196
|
+
var CEK_LENGTH2 = 32;
|
|
1197
|
+
function newSlotAcceptanceState() {
|
|
1198
|
+
return {
|
|
1199
|
+
foundBit: 0,
|
|
1200
|
+
conflictBit: 0,
|
|
1201
|
+
selectedCek: new Uint8Array(CEK_LENGTH2),
|
|
1202
|
+
selectedSlotIdx: -1
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
function foldSlotAcceptance(state, ok, candidateCek, slotIdx) {
|
|
1206
|
+
if (candidateCek.length !== CEK_LENGTH2) {
|
|
1207
|
+
throw new Error(
|
|
1208
|
+
`candidate CEK MUST be exactly ${CEK_LENGTH2} bytes, got ${candidateCek.length}`
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1211
|
+
const okBit = ok & 1;
|
|
1212
|
+
const firstBit = okBit & (state.foundBit ^ 1);
|
|
1213
|
+
const firstByteMask = -firstBit & 255;
|
|
1214
|
+
const firstWordMask = -firstBit | 0;
|
|
1215
|
+
let diff = 0;
|
|
1216
|
+
for (let i = 0; i < CEK_LENGTH2; i++) {
|
|
1217
|
+
diff |= candidateCek[i] ^ state.selectedCek[i];
|
|
1218
|
+
}
|
|
1219
|
+
const neqBit = (diff | -diff) >>> 31 & 1;
|
|
1220
|
+
state.conflictBit |= okBit & state.foundBit & neqBit;
|
|
1221
|
+
for (let i = 0; i < CEK_LENGTH2; i++) {
|
|
1222
|
+
state.selectedCek[i] = candidateCek[i] & firstByteMask | state.selectedCek[i] & ~firstByteMask & 255;
|
|
1223
|
+
}
|
|
1224
|
+
state.selectedSlotIdx = slotIdx & firstWordMask | state.selectedSlotIdx & ~firstWordMask;
|
|
1225
|
+
state.foundBit |= okBit;
|
|
1226
|
+
}
|
|
1227
|
+
function finishSlotAcceptance(state) {
|
|
1228
|
+
const found = state.foundBit === 1;
|
|
1229
|
+
return {
|
|
1230
|
+
found,
|
|
1231
|
+
cekConflict: state.conflictBit === 1,
|
|
1232
|
+
selectedCek: found ? state.selectedCek : null,
|
|
1233
|
+
selectedSlotIdx: state.selectedSlotIdx
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1070
1236
|
function selectBundleSecrets(envelope, bundle) {
|
|
1071
1237
|
return envelope.kem === "x25519" ? bundle.x25519PrivateKeys : bundle.mlkem768x25519SecretSeeds;
|
|
1072
1238
|
}
|
|
1073
1239
|
var ZERO_NONCE_122 = new Uint8Array(12);
|
|
1074
|
-
var
|
|
1240
|
+
var CEK_LENGTH3 = 32;
|
|
1075
1241
|
var X25519_SECRET_KEY_LENGTH2 = 32;
|
|
1076
1242
|
var X25519_PUBLIC_KEY_LENGTH2 = 32;
|
|
1077
|
-
var
|
|
1243
|
+
var NONCE_LENGTH3 = 24;
|
|
1078
1244
|
var WRAP_LENGTH2 = 48;
|
|
1079
1245
|
var SLOTS_MAC_LENGTH2 = 32;
|
|
1080
|
-
function concat2(a, b) {
|
|
1081
|
-
const out = new Uint8Array(a.length + b.length);
|
|
1082
|
-
out.set(a, 0);
|
|
1083
|
-
out.set(b, a.length);
|
|
1084
|
-
return out;
|
|
1085
|
-
}
|
|
1086
1246
|
function bytesKey(bytes) {
|
|
1087
1247
|
let s = "";
|
|
1088
1248
|
for (let i = 0; i < bytes.length; i++) {
|
|
@@ -1093,14 +1253,14 @@ function bytesKey(bytes) {
|
|
|
1093
1253
|
function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
1094
1254
|
if (envelope.scheme !== 1) {
|
|
1095
1255
|
throw new EciesSealedPoeError(
|
|
1096
|
-
"
|
|
1256
|
+
"UNSUPPORTED_ENVELOPE_SCHEME",
|
|
1097
1257
|
`envelope.scheme=${String(envelope.scheme)} unsupported (expected 1)`
|
|
1098
1258
|
);
|
|
1099
1259
|
}
|
|
1100
|
-
if (envelope.aead !==
|
|
1260
|
+
if (envelope.aead !== SEALED_POE_AEAD) {
|
|
1101
1261
|
throw new EciesSealedPoeError(
|
|
1102
1262
|
"UNSUPPORTED_AEAD_ALG",
|
|
1103
|
-
`envelope.aead=${String(envelope.aead)} unsupported (expected '
|
|
1263
|
+
`envelope.aead=${String(envelope.aead)} unsupported (expected '${SEALED_POE_AEAD}')`
|
|
1104
1264
|
);
|
|
1105
1265
|
}
|
|
1106
1266
|
if (envelope.kem !== "x25519" && envelope.kem !== "mlkem768x25519") {
|
|
@@ -1119,10 +1279,10 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1119
1279
|
`envelope.slots.length=${n} exceeds MAX_SLOTS=${MAX_SLOTS}`
|
|
1120
1280
|
);
|
|
1121
1281
|
}
|
|
1122
|
-
if (envelope.nonce.length !==
|
|
1282
|
+
if (envelope.nonce.length !== NONCE_LENGTH3) {
|
|
1123
1283
|
throw new EciesSealedPoeError(
|
|
1124
1284
|
"NONCE_LENGTH_MISMATCH",
|
|
1125
|
-
`envelope.nonce MUST be exactly ${
|
|
1285
|
+
`envelope.nonce MUST be exactly ${NONCE_LENGTH3} bytes, got ${envelope.nonce.length}`
|
|
1126
1286
|
);
|
|
1127
1287
|
}
|
|
1128
1288
|
if (envelope.slots_mac.length !== SLOTS_MAC_LENGTH2) {
|
|
@@ -1159,11 +1319,10 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1159
1319
|
} else {
|
|
1160
1320
|
for (let i = 0; i < n; i++) {
|
|
1161
1321
|
const slot = envelope.slots[i];
|
|
1162
|
-
|
|
1163
|
-
if (enc.length !== MLKEM768X25519_ENC_LENGTH) {
|
|
1322
|
+
if (slot.kem_ct.length !== MLKEM768X25519_ENC_LENGTH) {
|
|
1164
1323
|
throw new EciesSealedPoeError(
|
|
1165
1324
|
"KEM_CT_LENGTH_MISMATCH",
|
|
1166
|
-
`envelope.slots[${i}].kem_ct MUST
|
|
1325
|
+
`envelope.slots[${i}].kem_ct MUST be exactly ${MLKEM768X25519_ENC_LENGTH} bytes, got ${slot.kem_ct.length}`
|
|
1167
1326
|
);
|
|
1168
1327
|
}
|
|
1169
1328
|
if (slot.wrap.length !== WRAP_LENGTH2) {
|
|
@@ -1172,7 +1331,7 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1172
1331
|
`envelope.slots[${i}].wrap MUST be exactly ${WRAP_LENGTH2} bytes, got ${slot.wrap.length}`
|
|
1173
1332
|
);
|
|
1174
1333
|
}
|
|
1175
|
-
const key = bytesKey(
|
|
1334
|
+
const key = bytesKey(slot.kem_ct);
|
|
1176
1335
|
if (seenKemMaterial.has(key)) {
|
|
1177
1336
|
throw new EciesSealedPoeError(
|
|
1178
1337
|
"ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
|
|
@@ -1183,7 +1342,7 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1183
1342
|
}
|
|
1184
1343
|
}
|
|
1185
1344
|
const perSlotBytes = envelope.kem === "x25519" ? X25519_PUBLIC_KEY_LENGTH2 + WRAP_LENGTH2 : MLKEM768X25519_ENC_LENGTH + WRAP_LENGTH2;
|
|
1186
|
-
const decodedEnvelopeBytes =
|
|
1345
|
+
const decodedEnvelopeBytes = NONCE_LENGTH3 + SLOTS_MAC_LENGTH2 + n * perSlotBytes;
|
|
1187
1346
|
if (decodedEnvelopeBytes > MAX_DECODED_ENVELOPE_BYTES) {
|
|
1188
1347
|
throw new EciesSealedPoeError(
|
|
1189
1348
|
"ENC_ENVELOPE_TOO_LARGE",
|
|
@@ -1209,66 +1368,63 @@ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
|
|
|
1209
1368
|
}
|
|
1210
1369
|
}
|
|
1211
1370
|
var ZERO_IKM_32 = new Uint8Array(32);
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
let shared;
|
|
1371
|
+
var DUMMY_CEK_32 = new Uint8Array(32);
|
|
1372
|
+
function wrapOpenOrDummy(kek, aad, wrap) {
|
|
1215
1373
|
try {
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1374
|
+
const plaintext = chacha20Poly1305Decrypt({
|
|
1375
|
+
key: kek,
|
|
1376
|
+
nonce: ZERO_NONCE_122,
|
|
1377
|
+
aad,
|
|
1378
|
+
ciphertext: wrap
|
|
1219
1379
|
});
|
|
1380
|
+
if (plaintext.length === CEK_LENGTH3) {
|
|
1381
|
+
return { ok: 1, candidate: plaintext };
|
|
1382
|
+
}
|
|
1383
|
+
return { ok: 0, candidate: DUMMY_CEK_32 };
|
|
1220
1384
|
} catch (e) {
|
|
1221
|
-
if (!(e instanceof
|
|
1222
|
-
|
|
1223
|
-
return null;
|
|
1385
|
+
if (!(e instanceof AeadVerificationError)) throw e;
|
|
1386
|
+
return { ok: 0, candidate: DUMMY_CEK_32 };
|
|
1224
1387
|
}
|
|
1225
|
-
|
|
1388
|
+
}
|
|
1389
|
+
function tryX25519Slot(args) {
|
|
1390
|
+
const salt = x25519KekSalt({ nonce: args.nonce, epk: args.slot.epk, pubR: args.pubRLocal });
|
|
1391
|
+
let kemOk = 1;
|
|
1392
|
+
let kek;
|
|
1226
1393
|
try {
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
aad: CARDANO_POE_HKDF_INFO_KEK,
|
|
1231
|
-
ciphertext: args.slot.wrap
|
|
1394
|
+
const shared = x25519Ecdh({
|
|
1395
|
+
secretKey: args.recipientSecretKey,
|
|
1396
|
+
theirPublicKey: args.slot.epk
|
|
1232
1397
|
});
|
|
1398
|
+
kek = hkdfSha2562({ ikm: shared, salt, info: CARDANO_POE_HKDF_INFO_KEK, length: 32 });
|
|
1233
1399
|
} catch (e) {
|
|
1234
|
-
if (!(e instanceof
|
|
1235
|
-
|
|
1400
|
+
if (!(e instanceof X25519LowOrderPointError)) throw e;
|
|
1401
|
+
kemOk = 0;
|
|
1402
|
+
kek = hkdfSha2562({ ikm: ZERO_IKM_32, salt, info: CARDANO_POE_HKDF_INFO_KEK, length: 32 });
|
|
1236
1403
|
}
|
|
1404
|
+
const opened = wrapOpenOrDummy(kek, CARDANO_POE_HKDF_INFO_KEK, args.slot.wrap);
|
|
1405
|
+
return { ok: kemOk & opened.ok, candidate: opened.candidate };
|
|
1237
1406
|
}
|
|
1238
1407
|
function tryMlkem768X25519Slot(args) {
|
|
1239
|
-
const
|
|
1240
|
-
|
|
1408
|
+
const ss = mlkem768x25519Decapsulate({
|
|
1409
|
+
secretSeed: args.recipientSecretKey,
|
|
1410
|
+
enc: args.slot.kem_ct
|
|
1411
|
+
});
|
|
1241
1412
|
const kek = hkdfSha2562({
|
|
1242
1413
|
ikm: ss,
|
|
1243
|
-
salt: xwingKekSalt({ kemCt:
|
|
1414
|
+
salt: xwingKekSalt({ nonce: args.nonce, kemCt: args.slot.kem_ct, pubR: args.pubR }),
|
|
1244
1415
|
info: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
|
|
1245
1416
|
length: 32
|
|
1246
1417
|
});
|
|
1247
|
-
|
|
1248
|
-
return chacha20Poly1305Decrypt({
|
|
1249
|
-
key: kek,
|
|
1250
|
-
nonce: ZERO_NONCE_122,
|
|
1251
|
-
aad: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
|
|
1252
|
-
ciphertext: args.slot.wrap
|
|
1253
|
-
});
|
|
1254
|
-
} catch (e) {
|
|
1255
|
-
if (!(e instanceof AeadVerificationError)) throw e;
|
|
1256
|
-
return null;
|
|
1257
|
-
}
|
|
1418
|
+
return wrapOpenOrDummy(kek, CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519, args.slot.wrap);
|
|
1258
1419
|
}
|
|
1259
|
-
function
|
|
1420
|
+
function runPrivPass(envelope, recipientSecretKey, slotsHash, slotsAttemptedOut) {
|
|
1260
1421
|
const n = envelope.slots.length;
|
|
1261
|
-
|
|
1262
|
-
let
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
cek = candidate;
|
|
1268
|
-
matchedSlotIdx = i;
|
|
1269
|
-
} else if (!compareCt(candidate, cek)) {
|
|
1270
|
-
cekConflict = true;
|
|
1271
|
-
}
|
|
1422
|
+
const state = newSlotAcceptanceState();
|
|
1423
|
+
let anyOpenedBit = 0;
|
|
1424
|
+
const acceptSlot = (slot, i) => {
|
|
1425
|
+
anyOpenedBit |= slot.ok;
|
|
1426
|
+
const macOk = Number(compareCt(computeSlotsMac({ cek: slot.candidate, slotsHash }), envelope.slots_mac)) & 1;
|
|
1427
|
+
foldSlotAcceptance(state, slot.ok & macOk, slot.candidate, i);
|
|
1272
1428
|
};
|
|
1273
1429
|
if (envelope.kem === "x25519") {
|
|
1274
1430
|
const pubRLocal = x25519PublicKey2({ secretKey: recipientSecretKey });
|
|
@@ -1276,8 +1432,15 @@ function tryRecipientUnwrapWithIdx(envelope, recipientSecretKey, constantTimeN,
|
|
|
1276
1432
|
if (slotsAttemptedOut !== void 0) {
|
|
1277
1433
|
slotsAttemptedOut.count = i + 1;
|
|
1278
1434
|
}
|
|
1279
|
-
|
|
1280
|
-
|
|
1435
|
+
acceptSlot(
|
|
1436
|
+
tryX25519Slot({
|
|
1437
|
+
slot: envelope.slots[i],
|
|
1438
|
+
nonce: envelope.nonce,
|
|
1439
|
+
recipientSecretKey,
|
|
1440
|
+
pubRLocal
|
|
1441
|
+
}),
|
|
1442
|
+
i
|
|
1443
|
+
);
|
|
1281
1444
|
}
|
|
1282
1445
|
} else {
|
|
1283
1446
|
const pubR = mlkem768x25519Keygen2(recipientSecretKey).publicKey;
|
|
@@ -1285,22 +1448,37 @@ function tryRecipientUnwrapWithIdx(envelope, recipientSecretKey, constantTimeN,
|
|
|
1285
1448
|
if (slotsAttemptedOut !== void 0) {
|
|
1286
1449
|
slotsAttemptedOut.count = i + 1;
|
|
1287
1450
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1451
|
+
acceptSlot(
|
|
1452
|
+
tryMlkem768X25519Slot({
|
|
1453
|
+
slot: envelope.slots[i],
|
|
1454
|
+
nonce: envelope.nonce,
|
|
1455
|
+
recipientSecretKey,
|
|
1456
|
+
pubR
|
|
1457
|
+
}),
|
|
1458
|
+
i
|
|
1459
|
+
);
|
|
1290
1460
|
}
|
|
1291
1461
|
}
|
|
1292
|
-
|
|
1462
|
+
const outcome = finishSlotAcceptance(state);
|
|
1463
|
+
return {
|
|
1464
|
+
found: outcome.found,
|
|
1465
|
+
selectedCek: outcome.selectedCek,
|
|
1466
|
+
selectedSlotIdx: outcome.selectedSlotIdx,
|
|
1467
|
+
cekConflict: outcome.cekConflict,
|
|
1468
|
+
anyOpened: anyOpenedBit === 1
|
|
1469
|
+
};
|
|
1293
1470
|
}
|
|
1294
|
-
function slotsHashBytes(envelope) {
|
|
1471
|
+
function slotsHashBytes(envelope, hashes3) {
|
|
1295
1472
|
return computeSlotsHash({
|
|
1473
|
+
aead: envelope.aead,
|
|
1296
1474
|
kem: envelope.kem,
|
|
1297
1475
|
nonce: envelope.nonce,
|
|
1298
|
-
slots: envelope.slots
|
|
1476
|
+
slots: envelope.slots,
|
|
1477
|
+
hashesHash: itemHashesHash(hashes3)
|
|
1299
1478
|
});
|
|
1300
1479
|
}
|
|
1301
1480
|
function eciesSealedPoeUnwrap(args) {
|
|
1302
1481
|
const { envelope, ciphertext } = args;
|
|
1303
|
-
const constantTimeN = args.constantTimeN ?? true;
|
|
1304
1482
|
const hasSingle = "recipientSecretKey" in args;
|
|
1305
1483
|
const hasBundle = "recipientKeyBundle" in args;
|
|
1306
1484
|
const multiPrivKeys = hasBundle ? selectBundleSecrets(envelope, args.recipientKeyBundle) : "recipientSecretKeys" in args ? args.recipientSecretKeys : void 0;
|
|
@@ -1325,100 +1503,47 @@ function eciesSealedPoeUnwrap(args) {
|
|
|
1325
1503
|
} else {
|
|
1326
1504
|
assertEnvelopeStructure(envelope, void 0, args.recipientSecretKey);
|
|
1327
1505
|
}
|
|
1328
|
-
|
|
1329
|
-
const slotsHash = slotsHashBytes(envelope);
|
|
1506
|
+
const slotsHash = slotsHashBytes(envelope, args.hashes);
|
|
1330
1507
|
let matchedCek = null;
|
|
1331
|
-
let
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
recipientSecretKey,
|
|
1337
|
-
constantTimeN,
|
|
1338
|
-
args._slotsAttemptedOut
|
|
1339
|
-
);
|
|
1340
|
-
if (candidate === null) {
|
|
1341
|
-
return { matched: false, reason: "WRONG_RECIPIENT_KEY" };
|
|
1508
|
+
let anyOpenedAcrossPrivs = false;
|
|
1509
|
+
const privKeys = hasMulti ? multiPrivKeys : [args.recipientSecretKey];
|
|
1510
|
+
for (let k = 0; k < privKeys.length; k++) {
|
|
1511
|
+
if (args._privsAttemptedOut !== void 0) {
|
|
1512
|
+
args._privsAttemptedOut.count = k + 1;
|
|
1342
1513
|
}
|
|
1343
|
-
if (
|
|
1344
|
-
|
|
1514
|
+
if (args._slotsAttemptedOut !== void 0) {
|
|
1515
|
+
args._slotsAttemptedOut.count = 0;
|
|
1345
1516
|
}
|
|
1346
|
-
const
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1350
|
-
length: 32
|
|
1351
|
-
});
|
|
1352
|
-
const slotsMacCalc = hmac(sha256, hmacKey, slotsHash);
|
|
1353
|
-
if (!compareCt(slotsMacCalc, envelope.slots_mac)) {
|
|
1354
|
-
return { matched: false, reason: "TAMPERED_HEADER" };
|
|
1355
|
-
}
|
|
1356
|
-
matchedCek = candidate.cek;
|
|
1357
|
-
} else {
|
|
1358
|
-
const recipientSecretKeys = multiPrivKeys;
|
|
1359
|
-
let cekConflict = false;
|
|
1360
|
-
for (let k = 0; k < recipientSecretKeys.length; k++) {
|
|
1361
|
-
if (args._privsAttemptedOut !== void 0) {
|
|
1362
|
-
args._privsAttemptedOut.count = k + 1;
|
|
1363
|
-
}
|
|
1364
|
-
if (args._slotsAttemptedOut !== void 0) {
|
|
1365
|
-
args._slotsAttemptedOut.count = 0;
|
|
1366
|
-
}
|
|
1367
|
-
const candidate = tryRecipientUnwrapWithIdx(
|
|
1368
|
-
envelope,
|
|
1369
|
-
recipientSecretKeys[k],
|
|
1370
|
-
constantTimeN,
|
|
1371
|
-
args._slotsAttemptedOut
|
|
1372
|
-
);
|
|
1373
|
-
if (args._slotsAttemptedOut?.perPrivCounts !== void 0) {
|
|
1374
|
-
args._slotsAttemptedOut.perPrivCounts.push(args._slotsAttemptedOut.count);
|
|
1375
|
-
}
|
|
1376
|
-
if (candidate === null) continue;
|
|
1377
|
-
if (candidate.cekConflict) cekConflict = true;
|
|
1378
|
-
const cek = candidate.cek;
|
|
1379
|
-
const hmacKey = hkdfSha2562({
|
|
1380
|
-
ikm: cek,
|
|
1381
|
-
salt: EMPTY_SALT22,
|
|
1382
|
-
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1383
|
-
length: 32
|
|
1384
|
-
});
|
|
1385
|
-
const slotsMacCalc = hmac(sha256, hmacKey, slotsHash);
|
|
1386
|
-
if (compareCt(slotsMacCalc, envelope.slots_mac)) {
|
|
1387
|
-
matchedCek = cek;
|
|
1388
|
-
break;
|
|
1389
|
-
}
|
|
1390
|
-
anyCandidateRecovered = true;
|
|
1517
|
+
const pass = runPrivPass(envelope, privKeys[k], slotsHash, args._slotsAttemptedOut);
|
|
1518
|
+
if (args._slotsAttemptedOut?.perPrivCounts !== void 0) {
|
|
1519
|
+
args._slotsAttemptedOut.perPrivCounts.push(args._slotsAttemptedOut.count);
|
|
1391
1520
|
}
|
|
1392
|
-
|
|
1521
|
+
anyOpenedAcrossPrivs = anyOpenedAcrossPrivs || pass.anyOpened;
|
|
1522
|
+
if (!pass.found) continue;
|
|
1523
|
+
if (pass.cekConflict) {
|
|
1393
1524
|
return { matched: false, reason: "TAMPERED_HEADER" };
|
|
1394
1525
|
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1526
|
+
matchedCek = pass.selectedCek;
|
|
1527
|
+
break;
|
|
1528
|
+
}
|
|
1529
|
+
if (matchedCek === null) {
|
|
1530
|
+
return {
|
|
1531
|
+
matched: false,
|
|
1532
|
+
reason: anyOpenedAcrossPrivs ? "TAMPERED_HEADER" : "WRONG_RECIPIENT_KEY"
|
|
1533
|
+
};
|
|
1401
1534
|
}
|
|
1402
|
-
const payloadKey = slotsPayloadKey({ cek: matchedCek, nonce: envelope.nonce });
|
|
1403
|
-
const adContent = adContentSlots({
|
|
1404
|
-
kem: envelope.kem,
|
|
1405
|
-
nonce: envelope.nonce,
|
|
1406
|
-
slotsHash,
|
|
1407
|
-
slotsMac: envelope.slots_mac
|
|
1408
|
-
});
|
|
1409
1535
|
try {
|
|
1410
|
-
const plaintext =
|
|
1411
|
-
|
|
1412
|
-
nonce: envelope.nonce,
|
|
1413
|
-
aad: adContent,
|
|
1536
|
+
const plaintext = streamOpen({
|
|
1537
|
+
payloadKey: slotsPayloadKey({ cek: matchedCek, nonce: envelope.nonce }),
|
|
1414
1538
|
ciphertext
|
|
1415
1539
|
});
|
|
1416
1540
|
return { matched: true, plaintext };
|
|
1417
1541
|
} catch (e) {
|
|
1418
|
-
if (!(e instanceof
|
|
1542
|
+
if (!(e instanceof StreamTamperedError)) throw e;
|
|
1419
1543
|
return { matched: false, reason: "TAMPERED_CIPHERTEXT" };
|
|
1420
1544
|
}
|
|
1421
1545
|
}
|
|
1546
|
+
new TextEncoder();
|
|
1422
1547
|
ed.hashes.sha512 = sha512;
|
|
1423
1548
|
ed.Point.CURVE().n;
|
|
1424
1549
|
function signEd25519(opts2) {
|
|
@@ -1460,6 +1585,7 @@ function decryptSealedFromSeed(args) {
|
|
|
1460
1585
|
return eciesSealedPoeUnwrap({
|
|
1461
1586
|
envelope: args.envelope,
|
|
1462
1587
|
ciphertext: args.ciphertext,
|
|
1588
|
+
hashes: args.hashes,
|
|
1463
1589
|
recipientKeyBundle: recipientKeyBundleFromSeed(args.seed)
|
|
1464
1590
|
});
|
|
1465
1591
|
}
|