@calimero-network/mero-js 19.25.0 → 19.26.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/index.cjs CHANGED
@@ -67,6 +67,7 @@ __export(index_exports, {
67
67
  createNodeAuthApiClient: () => createNodeAuthApiClient,
68
68
  createNodeHttpClient: () => createNodeHttpClient,
69
69
  createProofSigner: () => createProofSigner,
70
+ createQuoteVerifier: () => createQuoteVerifier,
70
71
  createRetryableMethod: () => createRetryableMethod,
71
72
  createSealedFetch: () => createSealedFetch,
72
73
  createTimeoutSignal: () => createTimeoutSignal,
@@ -1954,6 +1955,10 @@ var AdminApiClient = class {
1954
1955
  async teeAttest(request) {
1955
1956
  return unwrap(await this.httpClient.post("/admin-api/tee/attest", request));
1956
1957
  }
1958
+ /**
1959
+ * @deprecated Nodes no longer serve `/admin-api/tee/verify-quote` (removed in
1960
+ * core#3262), so this fails. Verify quotes with `createQuoteVerifier`.
1961
+ */
1957
1962
  async teeVerifyQuote(request) {
1958
1963
  return unwrap(
1959
1964
  await this.httpClient.post("/admin-api/tee/verify-quote", request)
@@ -6752,9 +6757,16 @@ async function initiate(remoteStatic, prologue, fixedEphemeral) {
6752
6757
  }
6753
6758
  async function x25519KeyPair(secret) {
6754
6759
  if (!secret) {
6755
- const pair = await crypto.subtle.generateKey({ name: "X25519" }, true, [
6756
- "deriveBits"
6757
- ]);
6760
+ let pair;
6761
+ try {
6762
+ pair = await crypto.subtle.generateKey({ name: "X25519" }, true, [
6763
+ "deriveBits"
6764
+ ]);
6765
+ } catch (error) {
6766
+ throw new Error(
6767
+ `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.`
6768
+ );
6769
+ }
6758
6770
  const publicKey = new Uint8Array(await crypto.subtle.exportKey("raw", pair.publicKey));
6759
6771
  return { privateKey: pair.privateKey, publicKey };
6760
6772
  }
@@ -6847,7 +6859,8 @@ async function fetchAttestedTransportKey(admin, verify, options = {}) {
6847
6859
  const attested = await admin.teeAttest({
6848
6860
  nonce: nonce2,
6849
6861
  applicationId: options.applicationId,
6850
- bindTransportKey: true
6862
+ bindTransportKey: true,
6863
+ ...verify.includeCollateral ? { includeCollateral: true } : {}
6851
6864
  });
6852
6865
  if (!attested.transportPublicKey) {
6853
6866
  throw new Error("The node did not report a transport key; it predates sealed transport");
@@ -6855,7 +6868,13 @@ async function fetchAttestedTransportKey(admin, verify, options = {}) {
6855
6868
  const transportKey = fromHex(attested.transportPublicKey, "transportPublicKey", 32);
6856
6869
  const inner = options.applicationHash ? fromHex(options.applicationHash, "applicationHash", 32) : new Uint8Array(32);
6857
6870
  const reportDataSuffix = hex(await transportKeyBinding(inner, transportKey));
6858
- if (!await verify({ quoteB64: attested.quoteB64, nonce: nonce2, reportDataSuffix })) {
6871
+ const verified = await verify({
6872
+ quoteB64: attested.quoteB64,
6873
+ nonce: nonce2,
6874
+ reportDataSuffix,
6875
+ collateral: attested.collateral
6876
+ });
6877
+ if (!verified) {
6859
6878
  throw new Error("The attestation did not verify, so its transport key is not trusted");
6860
6879
  }
6861
6880
  return transportKey;
@@ -7117,6 +7136,83 @@ async function envelopeRefusal(response) {
7117
7136
  return new SealedTransportError(response.status, code, message, retryAfterMs);
7118
7137
  }
7119
7138
 
7139
+ // src/sealed/verify.ts
7140
+ var MEASUREMENT_HEX = /^[0-9a-f]{96}$/;
7141
+ function createQuoteVerifier(options) {
7142
+ const allowedMrtd = measurements(options.allowedMrtd, "allowedMrtd");
7143
+ if (allowedMrtd.length === 0) {
7144
+ throw new Error("allowedMrtd is empty, so no quote would ever be accepted");
7145
+ }
7146
+ const allowedRtmr = [
7147
+ measurements(options.allowedRtmr0 ?? [], "allowedRtmr0"),
7148
+ measurements(options.allowedRtmr1 ?? [], "allowedRtmr1"),
7149
+ measurements(options.allowedRtmr2 ?? [], "allowedRtmr2"),
7150
+ measurements(options.allowedRtmr3 ?? [], "allowedRtmr3")
7151
+ ];
7152
+ const allowedStatuses = options.allowedTcbStatuses ?? ["UpToDate"];
7153
+ const now = options.now ?? Date.now;
7154
+ const verifier = async ({
7155
+ quoteB64,
7156
+ nonce: nonce2,
7157
+ reportDataSuffix,
7158
+ collateral
7159
+ }) => {
7160
+ const quote = fromBase64(quoteB64);
7161
+ const against = collateral ?? await options.fetchCollateral?.(quote);
7162
+ if (!against) {
7163
+ throw new Error(
7164
+ "The node sent no collateral for its quote, and no fetchCollateral was given to get it"
7165
+ );
7166
+ }
7167
+ let verified;
7168
+ try {
7169
+ verified = options.dcapVerify(quote, against, Math.floor(now() / 1e3));
7170
+ } catch (error) {
7171
+ throw new Error(`The quote did not verify: ${error?.message ?? String(error)}`);
7172
+ }
7173
+ if (verified.status === "Revoked" || !allowedStatuses.includes(verified.status)) {
7174
+ throw new Error(`The platform's TCB status is ${verified.status}, which is not accepted`);
7175
+ }
7176
+ const td = tdReport(verified.report);
7177
+ if (!allowedMrtd.includes(hex(td.mrTd))) {
7178
+ throw new Error(`MRTD ${hex(td.mrTd)} is not an image this verifier trusts`);
7179
+ }
7180
+ const registers = [td.rtMr0, td.rtMr1, td.rtMr2, td.rtMr3];
7181
+ registers.forEach((register, index) => {
7182
+ const allowed = allowedRtmr[index];
7183
+ if (allowed.length > 0 && !allowed.includes(hex(register))) {
7184
+ throw new Error(`RTMR${index} ${hex(register)} is not one this verifier trusts`);
7185
+ }
7186
+ });
7187
+ if (hex(td.reportData) !== `${nonce2}${reportDataSuffix}`.toLowerCase()) {
7188
+ throw new Error("The quote does not commit to this nonce and binding");
7189
+ }
7190
+ return true;
7191
+ };
7192
+ return Object.assign(verifier, { includeCollateral: true });
7193
+ }
7194
+ function measurements(values, label) {
7195
+ return values.map((value) => {
7196
+ const normalized = value.trim().toLowerCase();
7197
+ if (!MEASUREMENT_HEX.test(normalized)) {
7198
+ throw new Error(`${label} holds ${JSON.stringify(value)}, which is not a 48-byte hex measurement`);
7199
+ }
7200
+ return normalized;
7201
+ });
7202
+ }
7203
+ function tdReport(report) {
7204
+ const data = report.data;
7205
+ if (report.type === "td10" && data) return data;
7206
+ if (report.type === "td15" && data?.base) return data.base;
7207
+ throw new Error(`The quote is a ${report.type} quote, not a TDX one`);
7208
+ }
7209
+ function fromBase64(value) {
7210
+ const binary = atob(value);
7211
+ const bytes = new Uint8Array(binary.length);
7212
+ for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
7213
+ return bytes;
7214
+ }
7215
+
7120
7216
  // src/namespace-op/namespace-op.ts
7121
7217
  var SIGNED_NAMESPACE_OP_SCHEMA_VERSION = 8;
7122
7218
  var NAMESPACE_SIGN_DOMAIN = new TextEncoder().encode("calimero.namespace.v1");