@calimero-network/mero-js 19.25.0 → 19.27.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/admin-api/admin-client.d.ts +4 -0
- package/dist/admin-api/admin-client.js +4 -0
- package/dist/admin-api/admin-types.d.ts +18 -0
- package/dist/admin-api/admin-types.js +0 -2
- package/dist/cloud/connect.d.ts +19 -0
- package/dist/cloud/connect.js +17 -1
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +4 -4
- package/dist/index.cjs +222 -91
- package/dist/index.cjs.map +4 -4
- package/dist/index.mjs +222 -91
- package/dist/index.mjs.map +4 -4
- package/dist/sealed/index.d.ts +4 -2
- package/dist/sealed/index.js +2 -1
- package/dist/sealed/noise.js +11 -3
- package/dist/sealed/sealed.d.ts +46 -6
- package/dist/sealed/sealed.js +57 -1
- package/dist/sealed/verify.d.ts +109 -0
- package/dist/sealed/verify.js +116 -0
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -54,6 +54,7 @@ __export(index_exports, {
|
|
|
54
54
|
connectCloud: () => connectCloud,
|
|
55
55
|
createAdminApiClient: () => createAdminApiClient,
|
|
56
56
|
createAdminApiClientFromHttpClient: () => createAdminApiClientFromHttpClient,
|
|
57
|
+
createAttestedSealedFetch: () => createAttestedSealedFetch,
|
|
57
58
|
createAuthApiClient: () => createAuthApiClient,
|
|
58
59
|
createAuthApiClientFromHttpClient: () => createAuthApiClientFromHttpClient,
|
|
59
60
|
createBrowserAdminApiClient: () => createBrowserAdminApiClient,
|
|
@@ -67,6 +68,7 @@ __export(index_exports, {
|
|
|
67
68
|
createNodeAuthApiClient: () => createNodeAuthApiClient,
|
|
68
69
|
createNodeHttpClient: () => createNodeHttpClient,
|
|
69
70
|
createProofSigner: () => createProofSigner,
|
|
71
|
+
createQuoteVerifier: () => createQuoteVerifier,
|
|
70
72
|
createRetryableMethod: () => createRetryableMethod,
|
|
71
73
|
createSealedFetch: () => createSealedFetch,
|
|
72
74
|
createTimeoutSignal: () => createTimeoutSignal,
|
|
@@ -1954,6 +1956,10 @@ var AdminApiClient = class {
|
|
|
1954
1956
|
async teeAttest(request) {
|
|
1955
1957
|
return unwrap(await this.httpClient.post("/admin-api/tee/attest", request));
|
|
1956
1958
|
}
|
|
1959
|
+
/**
|
|
1960
|
+
* @deprecated Nodes no longer serve `/admin-api/tee/verify-quote` (removed in
|
|
1961
|
+
* core#3262), so this fails. Verify quotes with `createQuoteVerifier`.
|
|
1962
|
+
*/
|
|
1957
1963
|
async teeVerifyQuote(request) {
|
|
1958
1964
|
return unwrap(
|
|
1959
1965
|
await this.httpClient.post("/admin-api/tee/verify-quote", request)
|
|
@@ -6569,92 +6575,6 @@ function createLocalStorageNonceSource(key, storage) {
|
|
|
6569
6575
|
};
|
|
6570
6576
|
}
|
|
6571
6577
|
|
|
6572
|
-
// src/cloud/connect.ts
|
|
6573
|
-
async function connectCloud(options) {
|
|
6574
|
-
if (!options.sessionToken && !options.googleIdToken) {
|
|
6575
|
-
throw new Error("connectCloud needs either a sessionToken or a googleIdToken");
|
|
6576
|
-
}
|
|
6577
|
-
const cloud = new CloudClient({
|
|
6578
|
-
cloudBaseUrl: options.cloudBaseUrl,
|
|
6579
|
-
sessionToken: options.sessionToken,
|
|
6580
|
-
onSession: options.onSession,
|
|
6581
|
-
fetch: options.fetch,
|
|
6582
|
-
timeoutMs: options.timeoutMs
|
|
6583
|
-
});
|
|
6584
|
-
if (!options.sessionToken && options.googleIdToken) {
|
|
6585
|
-
await cloud.signInWithGoogle(options.googleIdToken);
|
|
6586
|
-
}
|
|
6587
|
-
const namespaces = await cloud.getMyNamespaces();
|
|
6588
|
-
const namespace = pickNamespace(namespaces, options.namespaceId);
|
|
6589
|
-
const relays = await cloud.getNamespaceRelays(namespace.namespaceId);
|
|
6590
|
-
const relayInfo = relays.find((r) => r.authorshipReady && r.relayUrl !== null && r.executorAccount !== null) ?? null;
|
|
6591
|
-
if (!relayInfo) {
|
|
6592
|
-
throw new Error(explainNoRelay(namespace, relays));
|
|
6593
|
-
}
|
|
6594
|
-
const relay = new RelayClient({
|
|
6595
|
-
// Checked by `findExecutingRelay`, which is the only thing that returns a
|
|
6596
|
-
// relay — narrowed here rather than re-validated, so the invariant lives in
|
|
6597
|
-
// one place.
|
|
6598
|
-
relayUrl: relayInfo.relayUrl,
|
|
6599
|
-
executorAccount: relayInfo.executorAccount,
|
|
6600
|
-
authorAccount: options.authorAccount,
|
|
6601
|
-
authorProof: options.authorProof,
|
|
6602
|
-
deviceSecret: options.deviceSecret,
|
|
6603
|
-
nonces: options.nonces ?? await defaultNonceSource(options.deviceSecret),
|
|
6604
|
-
ttlSeconds: options.ttlSeconds,
|
|
6605
|
-
fetch: options.fetch,
|
|
6606
|
-
timeoutMs: options.timeoutMs
|
|
6607
|
-
});
|
|
6608
|
-
return {
|
|
6609
|
-
cloud,
|
|
6610
|
-
relay,
|
|
6611
|
-
namespaceId: namespace.namespaceId,
|
|
6612
|
-
namespace,
|
|
6613
|
-
relayInfo,
|
|
6614
|
-
execute: (contextId, method, argsJson) => relay.execute(contextId, method, argsJson)
|
|
6615
|
-
};
|
|
6616
|
-
}
|
|
6617
|
-
function pickNamespace(namespaces, requested) {
|
|
6618
|
-
if (requested) {
|
|
6619
|
-
const found = namespaces.find((n) => n.namespaceId === requested);
|
|
6620
|
-
if (!found) {
|
|
6621
|
-
throw new Error(
|
|
6622
|
-
`this account owns no namespace ${requested}; it owns: ${namespaces.map((n) => n.namespaceId).join(", ") || "(none)"}`
|
|
6623
|
-
);
|
|
6624
|
-
}
|
|
6625
|
-
return found;
|
|
6626
|
-
}
|
|
6627
|
-
if (namespaces.length === 0) {
|
|
6628
|
-
throw new Error(
|
|
6629
|
-
"this account owns no namespaces in the cloud: claim one first, then enable HA on it so a relay is assigned"
|
|
6630
|
-
);
|
|
6631
|
-
}
|
|
6632
|
-
if (namespaces.length > 1) {
|
|
6633
|
-
throw new Error(
|
|
6634
|
-
`this account owns ${namespaces.length} namespaces \u2014 pass namespaceId to say which: ${namespaces.map((n) => n.namespaceId).join(", ")}`
|
|
6635
|
-
);
|
|
6636
|
-
}
|
|
6637
|
-
return namespaces[0];
|
|
6638
|
-
}
|
|
6639
|
-
function explainNoRelay(namespace, relays) {
|
|
6640
|
-
if (relays.length === 0) {
|
|
6641
|
-
return namespace.haStatus === "enabled" ? `namespace ${namespace.namespaceId} has HA enabled but no relay has been assigned to it yet \u2014 retry shortly` : `namespace ${namespace.namespaceId} has no relays: enable HA on it in the cloud to have an attested node assigned`;
|
|
6642
|
-
}
|
|
6643
|
-
const waitingOnGrant = relays.filter((r) => !r.authorshipReady);
|
|
6644
|
-
if (waitingOnGrant.length === relays.length) {
|
|
6645
|
-
return `every relay on namespace ${namespace.namespaceId} is waiting for the authorship grant: an admin of the namespace must grant CAN_AUTHOR_ON_BEHALF to ${waitingOnGrant.map((r) => r.executorAccount ?? r.peerId).join(", ") || "the relay account"} \u2014 with AdminClient.grantAuthorship(namespaceId, account) for these relays, and AdminClient.openToDelegatedExecution(namespaceId) so relays assigned later need no further op`;
|
|
6646
|
-
}
|
|
6647
|
-
return `namespace ${namespace.namespaceId} has ${relays.length} relay(s), but none reported both a URL and an executor account yet \u2014 retry shortly`;
|
|
6648
|
-
}
|
|
6649
|
-
async function defaultNonceSource(deviceSecret) {
|
|
6650
|
-
const key = `mero-js:warrant-nonce:${hex(await derivePublicKey(deviceSecret))}`;
|
|
6651
|
-
try {
|
|
6652
|
-
return createLocalStorageNonceSource(key);
|
|
6653
|
-
} catch {
|
|
6654
|
-
return createMemoryNonceSource(1);
|
|
6655
|
-
}
|
|
6656
|
-
}
|
|
6657
|
-
|
|
6658
6578
|
// src/sealed/noise.ts
|
|
6659
6579
|
var PROTOCOL_NAME = "Noise_NK_25519_AESGCM_SHA256";
|
|
6660
6580
|
var DH_LEN = 32;
|
|
@@ -6752,9 +6672,16 @@ async function initiate(remoteStatic, prologue, fixedEphemeral) {
|
|
|
6752
6672
|
}
|
|
6753
6673
|
async function x25519KeyPair(secret) {
|
|
6754
6674
|
if (!secret) {
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6675
|
+
let pair;
|
|
6676
|
+
try {
|
|
6677
|
+
pair = await crypto.subtle.generateKey({ name: "X25519" }, true, [
|
|
6678
|
+
"deriveBits"
|
|
6679
|
+
]);
|
|
6680
|
+
} catch (error) {
|
|
6681
|
+
throw new Error(
|
|
6682
|
+
`Sealed transport needs WebCrypto with X25519, which this runtime lacks (${error?.name ?? "no crypto.subtle"}). Current browsers and Node have it; in React Native, install react-native-quick-crypto as the global crypto.`
|
|
6683
|
+
);
|
|
6684
|
+
}
|
|
6758
6685
|
const publicKey = new Uint8Array(await crypto.subtle.exportKey("raw", pair.publicKey));
|
|
6759
6686
|
return { privateKey: pair.privateKey, publicKey };
|
|
6760
6687
|
}
|
|
@@ -6847,7 +6774,8 @@ async function fetchAttestedTransportKey(admin, verify, options = {}) {
|
|
|
6847
6774
|
const attested = await admin.teeAttest({
|
|
6848
6775
|
nonce: nonce2,
|
|
6849
6776
|
applicationId: options.applicationId,
|
|
6850
|
-
bindTransportKey: true
|
|
6777
|
+
bindTransportKey: true,
|
|
6778
|
+
...verify.includeCollateral ? { includeCollateral: true } : {}
|
|
6851
6779
|
});
|
|
6852
6780
|
if (!attested.transportPublicKey) {
|
|
6853
6781
|
throw new Error("The node did not report a transport key; it predates sealed transport");
|
|
@@ -6855,11 +6783,44 @@ async function fetchAttestedTransportKey(admin, verify, options = {}) {
|
|
|
6855
6783
|
const transportKey = fromHex(attested.transportPublicKey, "transportPublicKey", 32);
|
|
6856
6784
|
const inner = options.applicationHash ? fromHex(options.applicationHash, "applicationHash", 32) : new Uint8Array(32);
|
|
6857
6785
|
const reportDataSuffix = hex(await transportKeyBinding(inner, transportKey));
|
|
6858
|
-
|
|
6786
|
+
const verified = await verify({
|
|
6787
|
+
quoteB64: attested.quoteB64,
|
|
6788
|
+
nonce: nonce2,
|
|
6789
|
+
reportDataSuffix,
|
|
6790
|
+
collateral: attested.collateral
|
|
6791
|
+
});
|
|
6792
|
+
if (!verified) {
|
|
6859
6793
|
throw new Error("The attestation did not verify, so its transport key is not trusted");
|
|
6860
6794
|
}
|
|
6861
6795
|
return transportKey;
|
|
6862
6796
|
}
|
|
6797
|
+
function createAttestedSealedFetch(options) {
|
|
6798
|
+
const baseFetch = options.fetch ?? ((input, init) => fetch(input, init));
|
|
6799
|
+
const attestUrl = `${options.baseUrl.replace(/\/+$/, "")}/admin-api/tee/attest`;
|
|
6800
|
+
const admin = {
|
|
6801
|
+
async teeAttest(request) {
|
|
6802
|
+
const response = await baseFetch(attestUrl, {
|
|
6803
|
+
method: "POST",
|
|
6804
|
+
headers: { "content-type": "application/json" },
|
|
6805
|
+
body: JSON.stringify(request)
|
|
6806
|
+
});
|
|
6807
|
+
if (!response.ok) {
|
|
6808
|
+
throw new Error(`The node refused to attest: HTTP ${response.status}`);
|
|
6809
|
+
}
|
|
6810
|
+
const body = await response.json();
|
|
6811
|
+
if (!body.data) throw new Error("The attestation response is malformed");
|
|
6812
|
+
return body.data;
|
|
6813
|
+
}
|
|
6814
|
+
};
|
|
6815
|
+
return createSealedFetch({
|
|
6816
|
+
baseUrl: options.baseUrl,
|
|
6817
|
+
transportPublicKey: () => fetchAttestedTransportKey(admin, options.verify, {
|
|
6818
|
+
applicationId: options.applicationId,
|
|
6819
|
+
applicationHash: options.applicationHash
|
|
6820
|
+
}),
|
|
6821
|
+
fetch: baseFetch
|
|
6822
|
+
});
|
|
6823
|
+
}
|
|
6863
6824
|
function createSealedFetch(options) {
|
|
6864
6825
|
const base = new URL(options.baseUrl);
|
|
6865
6826
|
const basePath = base.pathname.replace(/\/+$/, "");
|
|
@@ -6867,6 +6828,7 @@ function createSealedFetch(options) {
|
|
|
6867
6828
|
const baseFetch = options.fetch ?? ((input, init) => fetch(input, init));
|
|
6868
6829
|
const attest = typeof options.transportPublicKey === "function" ? options.transportPublicKey : void 0;
|
|
6869
6830
|
let transportKey = attest ? Promise.resolve().then(attest) : Promise.resolve(options.transportPublicKey);
|
|
6831
|
+
transportKey.catch(() => void 0);
|
|
6870
6832
|
let session;
|
|
6871
6833
|
const openSession = async () => {
|
|
6872
6834
|
try {
|
|
@@ -7117,6 +7079,175 @@ async function envelopeRefusal(response) {
|
|
|
7117
7079
|
return new SealedTransportError(response.status, code, message, retryAfterMs);
|
|
7118
7080
|
}
|
|
7119
7081
|
|
|
7082
|
+
// src/cloud/connect.ts
|
|
7083
|
+
async function connectCloud(options) {
|
|
7084
|
+
if (!options.sessionToken && !options.googleIdToken) {
|
|
7085
|
+
throw new Error("connectCloud needs either a sessionToken or a googleIdToken");
|
|
7086
|
+
}
|
|
7087
|
+
const cloud = new CloudClient({
|
|
7088
|
+
cloudBaseUrl: options.cloudBaseUrl,
|
|
7089
|
+
sessionToken: options.sessionToken,
|
|
7090
|
+
onSession: options.onSession,
|
|
7091
|
+
fetch: options.fetch,
|
|
7092
|
+
timeoutMs: options.timeoutMs
|
|
7093
|
+
});
|
|
7094
|
+
if (!options.sessionToken && options.googleIdToken) {
|
|
7095
|
+
await cloud.signInWithGoogle(options.googleIdToken);
|
|
7096
|
+
}
|
|
7097
|
+
const namespaces = await cloud.getMyNamespaces();
|
|
7098
|
+
const namespace = pickNamespace(namespaces, options.namespaceId);
|
|
7099
|
+
const relays = await cloud.getNamespaceRelays(namespace.namespaceId);
|
|
7100
|
+
const relayInfo = relays.find((r) => r.authorshipReady && r.relayUrl !== null && r.executorAccount !== null) ?? null;
|
|
7101
|
+
if (!relayInfo) {
|
|
7102
|
+
throw new Error(explainNoRelay(namespace, relays));
|
|
7103
|
+
}
|
|
7104
|
+
const relay = new RelayClient({
|
|
7105
|
+
// Checked by `findExecutingRelay`, which is the only thing that returns a
|
|
7106
|
+
// relay — narrowed here rather than re-validated, so the invariant lives in
|
|
7107
|
+
// one place.
|
|
7108
|
+
relayUrl: relayInfo.relayUrl,
|
|
7109
|
+
executorAccount: relayInfo.executorAccount,
|
|
7110
|
+
authorAccount: options.authorAccount,
|
|
7111
|
+
authorProof: options.authorProof,
|
|
7112
|
+
deviceSecret: options.deviceSecret,
|
|
7113
|
+
nonces: options.nonces ?? await defaultNonceSource(options.deviceSecret),
|
|
7114
|
+
ttlSeconds: options.ttlSeconds,
|
|
7115
|
+
// Only the relay is sealed: a sealed fetch is bound to one node and refuses
|
|
7116
|
+
// any other URL, and the cloud calls above carry no intent.
|
|
7117
|
+
fetch: options.seal ? createAttestedSealedFetch({
|
|
7118
|
+
baseUrl: relayInfo.relayUrl,
|
|
7119
|
+
verify: options.seal,
|
|
7120
|
+
fetch: options.fetch
|
|
7121
|
+
}) : options.fetch,
|
|
7122
|
+
timeoutMs: options.timeoutMs
|
|
7123
|
+
});
|
|
7124
|
+
return {
|
|
7125
|
+
cloud,
|
|
7126
|
+
relay,
|
|
7127
|
+
namespaceId: namespace.namespaceId,
|
|
7128
|
+
namespace,
|
|
7129
|
+
relayInfo,
|
|
7130
|
+
execute: (contextId, method, argsJson) => relay.execute(contextId, method, argsJson)
|
|
7131
|
+
};
|
|
7132
|
+
}
|
|
7133
|
+
function pickNamespace(namespaces, requested) {
|
|
7134
|
+
if (requested) {
|
|
7135
|
+
const found = namespaces.find((n) => n.namespaceId === requested);
|
|
7136
|
+
if (!found) {
|
|
7137
|
+
throw new Error(
|
|
7138
|
+
`this account owns no namespace ${requested}; it owns: ${namespaces.map((n) => n.namespaceId).join(", ") || "(none)"}`
|
|
7139
|
+
);
|
|
7140
|
+
}
|
|
7141
|
+
return found;
|
|
7142
|
+
}
|
|
7143
|
+
if (namespaces.length === 0) {
|
|
7144
|
+
throw new Error(
|
|
7145
|
+
"this account owns no namespaces in the cloud: claim one first, then enable HA on it so a relay is assigned"
|
|
7146
|
+
);
|
|
7147
|
+
}
|
|
7148
|
+
if (namespaces.length > 1) {
|
|
7149
|
+
throw new Error(
|
|
7150
|
+
`this account owns ${namespaces.length} namespaces \u2014 pass namespaceId to say which: ${namespaces.map((n) => n.namespaceId).join(", ")}`
|
|
7151
|
+
);
|
|
7152
|
+
}
|
|
7153
|
+
return namespaces[0];
|
|
7154
|
+
}
|
|
7155
|
+
function explainNoRelay(namespace, relays) {
|
|
7156
|
+
if (relays.length === 0) {
|
|
7157
|
+
return namespace.haStatus === "enabled" ? `namespace ${namespace.namespaceId} has HA enabled but no relay has been assigned to it yet \u2014 retry shortly` : `namespace ${namespace.namespaceId} has no relays: enable HA on it in the cloud to have an attested node assigned`;
|
|
7158
|
+
}
|
|
7159
|
+
const waitingOnGrant = relays.filter((r) => !r.authorshipReady);
|
|
7160
|
+
if (waitingOnGrant.length === relays.length) {
|
|
7161
|
+
return `every relay on namespace ${namespace.namespaceId} is waiting for the authorship grant: an admin of the namespace must grant CAN_AUTHOR_ON_BEHALF to ${waitingOnGrant.map((r) => r.executorAccount ?? r.peerId).join(", ") || "the relay account"} \u2014 with AdminClient.grantAuthorship(namespaceId, account) for these relays, and AdminClient.openToDelegatedExecution(namespaceId) so relays assigned later need no further op`;
|
|
7162
|
+
}
|
|
7163
|
+
return `namespace ${namespace.namespaceId} has ${relays.length} relay(s), but none reported both a URL and an executor account yet \u2014 retry shortly`;
|
|
7164
|
+
}
|
|
7165
|
+
async function defaultNonceSource(deviceSecret) {
|
|
7166
|
+
const key = `mero-js:warrant-nonce:${hex(await derivePublicKey(deviceSecret))}`;
|
|
7167
|
+
try {
|
|
7168
|
+
return createLocalStorageNonceSource(key);
|
|
7169
|
+
} catch {
|
|
7170
|
+
return createMemoryNonceSource(1);
|
|
7171
|
+
}
|
|
7172
|
+
}
|
|
7173
|
+
|
|
7174
|
+
// src/sealed/verify.ts
|
|
7175
|
+
var MEASUREMENT_HEX = /^[0-9a-f]{96}$/;
|
|
7176
|
+
function createQuoteVerifier(options) {
|
|
7177
|
+
const allowedMrtd = measurements(options.allowedMrtd, "allowedMrtd");
|
|
7178
|
+
if (allowedMrtd.length === 0) {
|
|
7179
|
+
throw new Error("allowedMrtd is empty, so no quote would ever be accepted");
|
|
7180
|
+
}
|
|
7181
|
+
const allowedRtmr = [
|
|
7182
|
+
measurements(options.allowedRtmr0 ?? [], "allowedRtmr0"),
|
|
7183
|
+
measurements(options.allowedRtmr1 ?? [], "allowedRtmr1"),
|
|
7184
|
+
measurements(options.allowedRtmr2 ?? [], "allowedRtmr2"),
|
|
7185
|
+
measurements(options.allowedRtmr3 ?? [], "allowedRtmr3")
|
|
7186
|
+
];
|
|
7187
|
+
const allowedStatuses = options.allowedTcbStatuses ?? ["UpToDate"];
|
|
7188
|
+
const now = options.now ?? Date.now;
|
|
7189
|
+
const verifier = async ({
|
|
7190
|
+
quoteB64,
|
|
7191
|
+
nonce: nonce2,
|
|
7192
|
+
reportDataSuffix,
|
|
7193
|
+
collateral
|
|
7194
|
+
}) => {
|
|
7195
|
+
const quote = fromBase64(quoteB64);
|
|
7196
|
+
const against = collateral ?? await options.fetchCollateral?.(quote);
|
|
7197
|
+
if (!against) {
|
|
7198
|
+
throw new Error(
|
|
7199
|
+
"The node sent no collateral for its quote, and no fetchCollateral was given to get it"
|
|
7200
|
+
);
|
|
7201
|
+
}
|
|
7202
|
+
let verified;
|
|
7203
|
+
try {
|
|
7204
|
+
verified = options.dcapVerify(quote, against, Math.floor(now() / 1e3));
|
|
7205
|
+
} catch (error) {
|
|
7206
|
+
throw new Error(`The quote did not verify: ${error?.message ?? String(error)}`);
|
|
7207
|
+
}
|
|
7208
|
+
if (verified.status === "Revoked" || !allowedStatuses.includes(verified.status)) {
|
|
7209
|
+
throw new Error(`The platform's TCB status is ${verified.status}, which is not accepted`);
|
|
7210
|
+
}
|
|
7211
|
+
const td = tdReport(verified.report);
|
|
7212
|
+
if (!allowedMrtd.includes(hex(td.mrTd))) {
|
|
7213
|
+
throw new Error(`MRTD ${hex(td.mrTd)} is not an image this verifier trusts`);
|
|
7214
|
+
}
|
|
7215
|
+
const registers = [td.rtMr0, td.rtMr1, td.rtMr2, td.rtMr3];
|
|
7216
|
+
registers.forEach((register, index) => {
|
|
7217
|
+
const allowed = allowedRtmr[index];
|
|
7218
|
+
if (allowed.length > 0 && !allowed.includes(hex(register))) {
|
|
7219
|
+
throw new Error(`RTMR${index} ${hex(register)} is not one this verifier trusts`);
|
|
7220
|
+
}
|
|
7221
|
+
});
|
|
7222
|
+
if (hex(td.reportData) !== `${nonce2}${reportDataSuffix}`.toLowerCase()) {
|
|
7223
|
+
throw new Error("The quote does not commit to this nonce and binding");
|
|
7224
|
+
}
|
|
7225
|
+
return true;
|
|
7226
|
+
};
|
|
7227
|
+
return Object.assign(verifier, { includeCollateral: true });
|
|
7228
|
+
}
|
|
7229
|
+
function measurements(values, label) {
|
|
7230
|
+
return values.map((value) => {
|
|
7231
|
+
const normalized = value.trim().toLowerCase();
|
|
7232
|
+
if (!MEASUREMENT_HEX.test(normalized)) {
|
|
7233
|
+
throw new Error(`${label} holds ${JSON.stringify(value)}, which is not a 48-byte hex measurement`);
|
|
7234
|
+
}
|
|
7235
|
+
return normalized;
|
|
7236
|
+
});
|
|
7237
|
+
}
|
|
7238
|
+
function tdReport(report) {
|
|
7239
|
+
const data = report.data;
|
|
7240
|
+
if (report.type === "td10" && data) return data;
|
|
7241
|
+
if (report.type === "td15" && data?.base) return data.base;
|
|
7242
|
+
throw new Error(`The quote is a ${report.type} quote, not a TDX one`);
|
|
7243
|
+
}
|
|
7244
|
+
function fromBase64(value) {
|
|
7245
|
+
const binary = atob(value);
|
|
7246
|
+
const bytes = new Uint8Array(binary.length);
|
|
7247
|
+
for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
|
|
7248
|
+
return bytes;
|
|
7249
|
+
}
|
|
7250
|
+
|
|
7120
7251
|
// src/namespace-op/namespace-op.ts
|
|
7121
7252
|
var SIGNED_NAMESPACE_OP_SCHEMA_VERSION = 8;
|
|
7122
7253
|
var NAMESPACE_SIGN_DOMAIN = new TextEncoder().encode("calimero.namespace.v1");
|