@calimero-network/mero-js 21.3.0 → 21.4.1

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
@@ -25,6 +25,7 @@ __export(index_exports, {
25
25
  CAPABILITIES: () => CAPABILITIES,
26
26
  CloudClient: () => CloudClient,
27
27
  DEFAULT_LOCAL_NODE_PORTS: () => DEFAULT_LOCAL_NODE_PORTS,
28
+ DEFAULT_RELEASE_MIRROR: () => DEFAULT_RELEASE_MIRROR,
28
29
  EphemeralClient: () => EphemeralClient,
29
30
  HANDSHAKE_PATH: () => HANDSHAKE_PATH,
30
31
  HTTPError: () => HTTPError,
@@ -32,6 +33,7 @@ __export(index_exports, {
32
33
  LocalStorageTokenStore: () => LocalStorageTokenStore,
33
34
  MemoryTokenStore: () => MemoryTokenStore,
34
35
  MeroJs: () => MeroJs,
36
+ NODE_RELEASE_SIGNER: () => NODE_RELEASE_SIGNER,
35
37
  RelayClient: () => RelayClient,
36
38
  RpcClient: () => RpcClient,
37
39
  RpcError: () => RpcError,
@@ -50,6 +52,7 @@ __export(index_exports, {
50
52
  buildAuthLoginUrl: () => buildAuthLoginUrl,
51
53
  callerProof: () => callerProof,
52
54
  classifyError: () => classifyError,
55
+ cloudNodeReleaseUrl: () => cloudNodeReleaseUrl,
53
56
  combineSignals: () => combineSignals,
54
57
  compareSemver: () => compareSemver,
55
58
  connectCloud: () => connectCloud,
@@ -72,12 +75,16 @@ __export(index_exports, {
72
75
  createQuoteVerifier: () => createQuoteVerifier,
73
76
  createRetryableMethod: () => createRetryableMethod,
74
77
  createSealedFetch: () => createSealedFetch,
78
+ createSignedReleaseSealedFetch: () => createSignedReleaseSealedFetch,
79
+ createSignedReleaseVerifier: () => createSignedReleaseVerifier,
75
80
  createTimeoutSignal: () => createTimeoutSignal,
76
81
  createUniversalHttpClient: () => createUniversalHttpClient,
77
82
  deviceCertPayload: () => deviceCertPayload,
78
83
  discoverLocalNodes: () => discoverLocalNodes,
79
84
  encodeSignedInvitation: () => encodeSignedInvitation,
80
85
  fetchAttestedTransportKey: () => fetchAttestedTransportKey,
86
+ fetchNodeRelease: () => fetchNodeRelease,
87
+ fetchNodeReleaseVersion: () => fetchNodeReleaseVersion,
81
88
  generateAccountRoot: () => generateAccountRoot,
82
89
  generateSessionKey: () => generateSessionKey,
83
90
  hasCap: () => hasCap,
@@ -89,6 +96,7 @@ __export(index_exports, {
89
96
  login: () => login,
90
97
  mintDeviceId: () => mintDeviceId,
91
98
  nodeEndpoint: () => nodeEndpoint,
99
+ nodeReleaseVersionFromImage: () => nodeReleaseVersionFromImage,
92
100
  parseAuthCallback: () => parseAuthCallback,
93
101
  probeNodeHealth: () => probeNodeHealth,
94
102
  redeemInvitation: () => redeemInvitation,
@@ -104,7 +112,9 @@ __export(index_exports, {
104
112
  signRoutingChallenge: () => signRoutingChallenge,
105
113
  signWarrant: () => signWarrant,
106
114
  transportKeyBinding: () => transportKeyBinding,
115
+ trustSignedRelease: () => trustSignedRelease,
107
116
  trustedMeasurementsFromReleases: () => trustedMeasurementsFromReleases,
117
+ verifySignedNodeRelease: () => verifySignedNodeRelease,
108
118
  withCap: () => withCap,
109
119
  withRetry: () => withRetry,
110
120
  withoutCap: () => withoutCap
@@ -7489,8 +7499,525 @@ function tcbStatus(status, release) {
7489
7499
  return known;
7490
7500
  }
7491
7501
 
7502
+ // src/sealed/sigstore-trust-root.ts
7503
+ var FULCIO_AUTHORITIES = [
7504
+ {
7505
+ uri: "https://fulcio.sigstore.dev",
7506
+ validFor: { start: "2021-03-07T03:20:29Z", end: "2022-12-31T23:59:59.999Z" },
7507
+ certChain: [
7508
+ "MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIxMDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSyA7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0JcastaRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6NmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2uSu1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJxVe/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uupHr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ=="
7509
+ ]
7510
+ },
7511
+ {
7512
+ uri: "https://fulcio.sigstore.dev",
7513
+ validFor: { start: "2022-04-13T20:06:15Z" },
7514
+ certChain: [
7515
+ "MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=",
7516
+ "MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ"
7517
+ ]
7518
+ }
7519
+ ];
7520
+ var REKOR_LOGS = [
7521
+ {
7522
+ baseUrl: "https://rekor.sigstore.dev",
7523
+ logId: "c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d",
7524
+ publicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwrkBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw==",
7525
+ validFor: { start: "2021-01-12T11:53:27Z" }
7526
+ }
7527
+ ];
7528
+
7529
+ // src/sealed/x509.ts
7530
+ var SEQUENCE = 48;
7531
+ var OID = 6;
7532
+ var INTEGER = 2;
7533
+ var BIT_STRING = 3;
7534
+ var OCTET_STRING = 4;
7535
+ var BOOLEAN = 1;
7536
+ var UTC_TIME = 23;
7537
+ var GENERALIZED_TIME = 24;
7538
+ var CONTEXT_0 = 160;
7539
+ var CONTEXT_3 = 163;
7540
+ var EC_PUBLIC_KEY = "1.2.840.10045.2.1";
7541
+ var CURVES = {
7542
+ "1.2.840.10045.3.1.7": "P-256",
7543
+ "1.3.132.0.34": "P-384"
7544
+ };
7545
+ var CURVE_SIZE = { "P-256": 32, "P-384": 48 };
7546
+ var SIGNATURE_HASHES = {
7547
+ "1.2.840.10045.4.3.2": "SHA-256",
7548
+ "1.2.840.10045.4.3.3": "SHA-384"
7549
+ };
7550
+ function read(bytes, offset, limit) {
7551
+ if (offset + 2 > limit) throw new Error("it is truncated");
7552
+ const tag = bytes[offset];
7553
+ if ((tag & 31) === 31) throw new Error("it uses a tag this parser does not read");
7554
+ let length = bytes[offset + 1];
7555
+ let start = offset + 2;
7556
+ if (length & 128) {
7557
+ const count = length & 127;
7558
+ if (count === 0 || count > 4 || start + count > limit) throw new Error("it has a length this parser does not read");
7559
+ length = 0;
7560
+ for (let i = 0; i < count; i += 1) length = length * 256 + bytes[start + i];
7561
+ start += count;
7562
+ }
7563
+ const end = start + length;
7564
+ if (end > limit) throw new Error("it is truncated");
7565
+ return { tag, bytes, offset, start, end };
7566
+ }
7567
+ function parseDer(bytes) {
7568
+ const element = read(bytes, 0, bytes.length);
7569
+ if (element.end !== bytes.length) throw new Error("it has trailing bytes");
7570
+ return element;
7571
+ }
7572
+ function children(element) {
7573
+ const out = [];
7574
+ for (let offset = element.start; offset < element.end; ) {
7575
+ const child = read(element.bytes, offset, element.end);
7576
+ out.push(child);
7577
+ offset = child.end;
7578
+ }
7579
+ return out;
7580
+ }
7581
+ function expectTag(element, tag, what) {
7582
+ if (!element || element.tag !== tag) throw new Error(`${what} is not where it should be`);
7583
+ return element;
7584
+ }
7585
+ function contents(element) {
7586
+ return element.bytes.slice(element.start, element.end);
7587
+ }
7588
+ function whole(element) {
7589
+ return element.bytes.slice(element.offset, element.end);
7590
+ }
7591
+ function oid(element) {
7592
+ const value = contents(expectTag(element, OID, "An object identifier"));
7593
+ if (value.length === 0 || value[value.length - 1] & 128) throw new Error("it has a malformed object identifier");
7594
+ const arcs = [];
7595
+ let arc = 0;
7596
+ for (const byte of value) {
7597
+ arc = arc * 128 + (byte & 127);
7598
+ if (!(byte & 128)) {
7599
+ arcs.push(arc);
7600
+ arc = 0;
7601
+ }
7602
+ }
7603
+ const first = arcs.shift();
7604
+ const head = first < 80 ? [Math.floor(first / 40), first % 40] : [2, first - 80];
7605
+ return [...head, ...arcs].join(".");
7606
+ }
7607
+ function time(element) {
7608
+ const text = new TextDecoder().decode(contents(element));
7609
+ const utc = element.tag === UTC_TIME ? /^(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})Z$/.exec(text) : null;
7610
+ const generalized = element.tag === GENERALIZED_TIME ? /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})Z$/.exec(text) : null;
7611
+ const parts = utc ?? generalized;
7612
+ if (!parts) throw new Error(`it holds the time ${JSON.stringify(text)}, which is not one a certificate may hold`);
7613
+ let year = Number(parts[1]);
7614
+ if (utc) year += year < 50 ? 2e3 : 1900;
7615
+ const [month, day, hour, minute, second] = parts.slice(2).map(Number);
7616
+ return Date.UTC(year, month - 1, day, hour, minute, second);
7617
+ }
7618
+ function parseCertificate(der) {
7619
+ try {
7620
+ const [tbsElement, algorithm, signature, ...rest] = children(
7621
+ expectTag(parseDer(der), SEQUENCE, "The certificate")
7622
+ );
7623
+ if (rest.length > 0) throw new Error("it has fields after its signature");
7624
+ const fields = children(expectTag(tbsElement, SEQUENCE, "The signed part"));
7625
+ const first = fields[0]?.tag === CONTEXT_0 ? 1 : 0;
7626
+ const [serial, innerAlgorithm, issuer, validity, subject, spki, ...optional] = fields.slice(first);
7627
+ expectTag(serial, INTEGER, "The serial number");
7628
+ const algorithmId = oid(children(expectTag(algorithm, SEQUENCE, "The signature algorithm"))[0]);
7629
+ const innerId = oid(children(expectTag(innerAlgorithm, SEQUENCE, "The signature algorithm"))[0]);
7630
+ if (innerId !== algorithmId) throw new Error("it names two different signature algorithms");
7631
+ const signatureHash = SIGNATURE_HASHES[algorithmId];
7632
+ if (!signatureHash) throw new Error(`it is signed with ${algorithmId}, not ECDSA with SHA-256 or SHA-384`);
7633
+ const [notBefore, notAfter] = children(expectTag(validity, SEQUENCE, "The validity")).map(time);
7634
+ if (notAfter === void 0) throw new Error("its validity is incomplete");
7635
+ const [keyAlgorithm, keyBits] = children(expectTag(spki, SEQUENCE, "The public key"));
7636
+ expectTag(keyBits, BIT_STRING, "The public key");
7637
+ const [keyType, keyCurve] = children(expectTag(keyAlgorithm, SEQUENCE, "The key algorithm"));
7638
+ if (oid(keyType) !== EC_PUBLIC_KEY) throw new Error("its key is not an elliptic-curve key");
7639
+ const curve = CURVES[oid(keyCurve)];
7640
+ if (!curve) throw new Error("its key is on a curve other than P-256 or P-384");
7641
+ const extensions = /* @__PURE__ */ new Map();
7642
+ const extensionsField = optional.find((field) => field.tag === CONTEXT_3);
7643
+ if (extensionsField) {
7644
+ const [list] = children(extensionsField);
7645
+ for (const extension of children(expectTag(list, SEQUENCE, "The extensions"))) {
7646
+ const parts = children(expectTag(extension, SEQUENCE, "An extension"));
7647
+ if (parts.length === 3) expectTag(parts[1], BOOLEAN, "An extension's critical flag");
7648
+ else if (parts.length !== 2) throw new Error("it has a malformed extension");
7649
+ const id = oid(parts[0]);
7650
+ if (extensions.has(id)) throw new Error(`it has extension ${id} twice`);
7651
+ extensions.set(id, contents(expectTag(parts[parts.length - 1], OCTET_STRING, "An extension value")));
7652
+ }
7653
+ }
7654
+ const signatureBits = contents(expectTag(signature, BIT_STRING, "The signature"));
7655
+ if (signatureBits[0] !== 0) throw new Error("its signature is not a whole number of bytes");
7656
+ return {
7657
+ der,
7658
+ tbs: whole(tbsElement),
7659
+ signatureHash,
7660
+ signature: signatureBits.slice(1),
7661
+ issuer: whole(expectTag(issuer, SEQUENCE, "The issuer")),
7662
+ subject: whole(expectTag(subject, SEQUENCE, "The subject")),
7663
+ notBefore,
7664
+ notAfter,
7665
+ spki: whole(spki),
7666
+ curve,
7667
+ extensions
7668
+ };
7669
+ } catch (error) {
7670
+ throw new Error(`The certificate is malformed: ${error.message}`);
7671
+ }
7672
+ }
7673
+ function objectIdentifiers(der) {
7674
+ return children(expectTag(parseDer(der), SEQUENCE, "The list")).map(oid);
7675
+ }
7676
+ function ecdsaSignatureToP1363(der, curve) {
7677
+ const size = CURVE_SIZE[curve];
7678
+ const integers = children(expectTag(parseDer(der), SEQUENCE, "The signature"));
7679
+ if (integers.length !== 2) throw new Error("The signature is not an ECDSA signature");
7680
+ const out = new Uint8Array(size * 2);
7681
+ integers.forEach((integer, index) => {
7682
+ let value = contents(expectTag(integer, INTEGER, "The signature"));
7683
+ if (value.length === 0 || value[0] & 128) throw new Error("The signature is not an ECDSA signature");
7684
+ while (value.length > 1 && value[0] === 0) value = value.subarray(1);
7685
+ if (value.length > size) throw new Error("The signature is not an ECDSA signature");
7686
+ out.set(value, index * size + size - value.length);
7687
+ });
7688
+ return out;
7689
+ }
7690
+ async function verifyEcdsa(spki, curve, hash, signature, data) {
7691
+ let p1363;
7692
+ try {
7693
+ p1363 = ecdsaSignatureToP1363(signature, curve);
7694
+ } catch {
7695
+ return false;
7696
+ }
7697
+ const key = await crypto.subtle.importKey("spki", spki, { name: "ECDSA", namedCurve: curve }, false, ["verify"]);
7698
+ return crypto.subtle.verify({ name: "ECDSA", hash }, key, p1363, data);
7699
+ }
7700
+ function fromBase642(value, what) {
7701
+ const clean = value.replace(/\s+/g, "");
7702
+ if (!/^[A-Za-z0-9+/]*={0,2}$/.test(clean) || clean.length % 4 !== 0) {
7703
+ throw new Error(`${what} is not base64`);
7704
+ }
7705
+ const binary = atob(clean);
7706
+ const bytes = new Uint8Array(binary.length);
7707
+ for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
7708
+ return bytes;
7709
+ }
7710
+ function pemCertificate(pem, what) {
7711
+ const blocks = [...pem.matchAll(/-----BEGIN CERTIFICATE-----([^-]*)-----END CERTIFICATE-----/g)];
7712
+ if (blocks.length !== 1 || pem.replace(blocks[0][0], "").trim() !== "") {
7713
+ throw new Error(`${what} is not exactly one PEM certificate`);
7714
+ }
7715
+ return fromBase642(blocks[0][1], what);
7716
+ }
7717
+
7718
+ // src/sealed/sigstore.ts
7719
+ var SUBJECT_ALT_NAME = "2.5.29.17";
7720
+ var EXTENDED_KEY_USAGE = "2.5.29.37";
7721
+ var CODE_SIGNING = "1.3.6.1.5.5.7.3.3";
7722
+ var FULCIO_ISSUER_V2 = "1.3.6.1.4.1.57264.1.8";
7723
+ var FULCIO_ISSUER_V1 = "1.3.6.1.4.1.57264.1.1";
7724
+ var SAN_URI = 134;
7725
+ var UTF8_STRING = 12;
7726
+ var SEQUENCE2 = 48;
7727
+ var utf82 = new TextEncoder();
7728
+ var fromUtf8 = (bytes) => new TextDecoder().decode(bytes);
7729
+ async function verifyCosignBundle(body, bundle, signer, signature) {
7730
+ const { base64Signature, cert, rekorBundle } = cosignBundle(bundle);
7731
+ const payload = rekorBundle.Payload;
7732
+ if (signature !== void 0 && signature.trim() !== base64Signature.trim()) {
7733
+ throw new Error("The detached signature is not the one in the bundle");
7734
+ }
7735
+ const sig = fromBase642(base64Signature, "The bundle signature");
7736
+ const leafDer = pemCertificate(fromUtf8(fromBase642(cert, "The bundle certificate")), "The bundle certificate");
7737
+ const leaf = parseCertificate(leafDer);
7738
+ const log = REKOR_LOGS.find((candidate) => candidate.logId === payload.logID.toLowerCase());
7739
+ if (!log) throw new Error(`The bundle names Rekor log ${payload.logID}, which the Sigstore trust root does not know`);
7740
+ const canonical = utf82.encode(
7741
+ `{"body":${JSON.stringify(payload.body)},"integratedTime":${payload.integratedTime},"logID":${JSON.stringify(payload.logID)},"logIndex":${payload.logIndex}}`
7742
+ );
7743
+ const setVerifies = await verifyEcdsa(
7744
+ fromBase642(log.publicKey, "The Rekor key"),
7745
+ "P-256",
7746
+ "SHA-256",
7747
+ fromBase642(rekorBundle.SignedEntryTimestamp, "The signed entry timestamp"),
7748
+ canonical
7749
+ );
7750
+ if (!setVerifies) {
7751
+ throw new Error("Rekor's signed entry timestamp does not verify: the log entry, its time or its index was altered");
7752
+ }
7753
+ const at = payload.integratedTime * 1e3;
7754
+ if (!within(at, log.validFor)) {
7755
+ throw new Error(`Rekor log ${log.baseUrl} was not in use at ${iso(at)}, when the entry says it was logged`);
7756
+ }
7757
+ const entry = hashedRekord(payload.body);
7758
+ const digest = hex(new Uint8Array(await crypto.subtle.digest("SHA-256", body)));
7759
+ if (entry.hash !== digest) throw new Error("The file's SHA-256 is not the one Rekor logged: the file is not the one signed");
7760
+ if (!equal(fromBase642(entry.signature, "The logged signature"), sig)) {
7761
+ throw new Error("The bundle's signature is not the one Rekor logged");
7762
+ }
7763
+ const loggedCert = pemCertificate(fromUtf8(fromBase642(entry.publicKey, "The logged certificate")), "The logged certificate");
7764
+ if (!equal(loggedCert, leafDer)) throw new Error("The bundle's certificate is not the one Rekor logged");
7765
+ if (leaf.curve !== "P-256" || !await verifyEcdsa(leaf.spki, "P-256", "SHA-256", sig, body)) {
7766
+ throw new Error("The signature over the file does not verify with the bundle's certificate");
7767
+ }
7768
+ await chainsToFulcio(leaf, at);
7769
+ checkSigner(leaf, signer);
7770
+ return { integratedTime: payload.integratedTime };
7771
+ }
7772
+ async function chainsToFulcio(leaf, at) {
7773
+ if (!validAt(leaf, at)) {
7774
+ throw new Error(
7775
+ `The signing certificate was valid ${iso(leaf.notBefore)} to ${iso(leaf.notAfter)}, not at ${iso(at)} when Rekor logged it`
7776
+ );
7777
+ }
7778
+ const reasons = [];
7779
+ for (const authority of FULCIO_AUTHORITIES) {
7780
+ const chain = authority.certChain.map((der) => parseCertificate(fromBase642(der, "A Fulcio certificate")));
7781
+ if (!await issuedBy(leaf, chain[0])) continue;
7782
+ const reason = await authorityRefusal(chain, authority.validFor, at);
7783
+ if (!reason) return;
7784
+ reasons.push(`${authority.uri} (valid from ${authority.validFor.start}): ${reason}`);
7785
+ }
7786
+ throw new Error(
7787
+ reasons.length > 0 ? `The signing certificate's CA is not trusted at ${iso(at)}: ${reasons.join("; ")}` : "The signing certificate was not issued by a Fulcio CA in the Sigstore trust root"
7788
+ );
7789
+ }
7790
+ async function authorityRefusal(chain, validFor, at) {
7791
+ if (!within(at, validFor)) return "the trust root says it was not issuing certificates then";
7792
+ for (const [index, certificate] of chain.entries()) {
7793
+ if (!validAt(certificate, at)) return `its certificate ${index} was not valid then`;
7794
+ const parent = chain[index + 1] ?? certificate;
7795
+ if (!await issuedBy(certificate, parent)) return `its certificate ${index} is not signed by the next`;
7796
+ }
7797
+ return void 0;
7798
+ }
7799
+ async function issuedBy(child, parent) {
7800
+ if (!equal(child.issuer, parent.subject)) return false;
7801
+ return verifyEcdsa(parent.spki, parent.curve, child.signatureHash, child.signature, child.tbs);
7802
+ }
7803
+ function checkSigner(leaf, signer) {
7804
+ const san = leaf.extensions.get(SUBJECT_ALT_NAME);
7805
+ if (!san) throw new Error("The signing certificate names no identity");
7806
+ const names = children(expectTag(parseDer(san), SEQUENCE2, "The subject alternative name"));
7807
+ const uris = names.filter((name) => name.tag === SAN_URI).map((name) => fromUtf8(contents(name)));
7808
+ if (uris.length !== 1 || names.length !== 1 || uris[0] !== signer.identity) {
7809
+ throw new Error(
7810
+ `The release was signed by ${JSON.stringify(uris.length === 1 ? uris[0] : uris)}, not by ${signer.identity}`
7811
+ );
7812
+ }
7813
+ const v2 = leaf.extensions.get(FULCIO_ISSUER_V2);
7814
+ const v1 = leaf.extensions.get(FULCIO_ISSUER_V1);
7815
+ const issuer = v2 ? fromUtf8(contents(expectTag(parseDer(v2), UTF8_STRING, "The OIDC issuer"))) : v1 ? fromUtf8(v1) : void 0;
7816
+ if (issuer !== signer.issuer) {
7817
+ throw new Error(
7818
+ `The signing certificate's OIDC issuer is ${issuer === void 0 ? "missing" : JSON.stringify(issuer)}, not ${signer.issuer}`
7819
+ );
7820
+ }
7821
+ const usage = leaf.extensions.get(EXTENDED_KEY_USAGE);
7822
+ if (!usage || !objectIdentifiers(usage).includes(CODE_SIGNING)) {
7823
+ throw new Error("The signing certificate is not for code signing");
7824
+ }
7825
+ }
7826
+ function cosignBundle(value) {
7827
+ let bundle = value;
7828
+ if (typeof bundle === "string") {
7829
+ try {
7830
+ bundle = JSON.parse(bundle);
7831
+ } catch {
7832
+ throw new Error("The bundle is not JSON");
7833
+ }
7834
+ }
7835
+ const b = bundle;
7836
+ const p = b?.rekorBundle?.Payload;
7837
+ const ok = typeof b?.base64Signature === "string" && typeof b.cert === "string" && typeof b.rekorBundle?.SignedEntryTimestamp === "string" && typeof p?.body === "string" && typeof p.logID === "string" && /^[0-9a-fA-F]{64}$/.test(p.logID) && Number.isSafeInteger(p.integratedTime) && p.integratedTime >= 0 && Number.isSafeInteger(p.logIndex) && p.logIndex >= 0;
7838
+ if (!ok) throw new Error("The bundle is not a cosign bundle with a Rekor entry");
7839
+ return b;
7840
+ }
7841
+ function hashedRekord(body) {
7842
+ let entry;
7843
+ try {
7844
+ entry = JSON.parse(fromUtf8(fromBase642(body, "The logged entry")));
7845
+ } catch {
7846
+ throw new Error("The logged entry is not JSON");
7847
+ }
7848
+ const hash = entry?.spec?.data?.hash;
7849
+ const signature = entry?.spec?.signature;
7850
+ if (entry?.kind !== "hashedrekord" || hash?.algorithm !== "sha256" || typeof hash.value !== "string" || typeof signature?.content !== "string" || typeof signature.publicKey?.content !== "string") {
7851
+ throw new Error("The logged entry is not a SHA-256 hashedrekord");
7852
+ }
7853
+ return { hash: hash.value.toLowerCase(), signature: signature.content, publicKey: signature.publicKey.content };
7854
+ }
7855
+ function validAt(certificate, at) {
7856
+ return certificate.notBefore <= at && at <= certificate.notAfter;
7857
+ }
7858
+ function within(at, window2) {
7859
+ return Date.parse(window2.start) <= at && (window2.end === void 0 || at <= Date.parse(window2.end));
7860
+ }
7861
+ function iso(ms) {
7862
+ return new Date(ms).toISOString();
7863
+ }
7864
+ function equal(a, b) {
7865
+ return a.length === b.length && a.every((byte, index) => byte === b[index]);
7866
+ }
7867
+
7868
+ // src/sealed/release.ts
7869
+ var NODE_RELEASE_SIGNER = Object.freeze({
7870
+ identity: "https://github.com/calimero-network/mero-tee/.github/workflows/release-node-image-gcp.yaml@refs/heads/master",
7871
+ issuer: "https://token.actions.githubusercontent.com"
7872
+ });
7873
+ var MEASUREMENTS = ["mrtd", "rtmr0", "rtmr1", "rtmr2", "rtmr3"];
7874
+ var DEFAULT_RELEASE_MIRROR = "https://cloud.calimero.network";
7875
+ async function verifySignedNodeRelease(publishedMrtds, bundle, options = {}) {
7876
+ const body = typeof publishedMrtds === "string" ? new TextEncoder().encode(publishedMrtds) : publishedMrtds;
7877
+ await verifyCosignBundle(
7878
+ body,
7879
+ bundle,
7880
+ {
7881
+ identity: options.identity ?? NODE_RELEASE_SIGNER.identity,
7882
+ issuer: options.issuer ?? NODE_RELEASE_SIGNER.issuer
7883
+ },
7884
+ options.signature
7885
+ );
7886
+ let release;
7887
+ try {
7888
+ release = JSON.parse(new TextDecoder().decode(body));
7889
+ } catch {
7890
+ throw new Error("The signed release is not JSON");
7891
+ }
7892
+ if (release?.role !== "node") {
7893
+ throw new Error(`The signed release is a ${JSON.stringify(release?.role)} release, not a node one`);
7894
+ }
7895
+ const profiles = Object.values(release.profiles ?? {});
7896
+ const measured = (image) => MEASUREMENTS.every((register) => /^[0-9a-f]{96}$/i.test(String(image?.[register])));
7897
+ if (typeof release.tag !== "string" || profiles.length === 0 || !profiles.every(measured)) {
7898
+ throw new Error("The signed file is not a release's published-mrtds.json: it lists no measured profiles");
7899
+ }
7900
+ if (options.expectedVersion !== void 0 && release.tag !== options.expectedVersion) {
7901
+ throw new Error(`The signed release is ${release.tag}, not ${options.expectedVersion}`);
7902
+ }
7903
+ return release;
7904
+ }
7905
+ async function fetchNodeRelease(url, options = {}) {
7906
+ const response = await (options.fetch ?? fetch)(url, { headers: { accept: "application/json" } });
7907
+ if (!response.ok) throw new Error(`Fetching the node release from ${url} failed: HTTP ${response.status}`);
7908
+ let release;
7909
+ try {
7910
+ release = (await response.json())?.data;
7911
+ } catch {
7912
+ throw new Error(`${url} did not answer with JSON`);
7913
+ }
7914
+ if (typeof release?.version !== "string" || typeof release.publishedMrtds !== "string" || typeof release.bundle !== "string") {
7915
+ throw new Error(`${url} did not answer with a signed node release`);
7916
+ }
7917
+ return { version: release.version, publishedMrtds: release.publishedMrtds, bundle: release.bundle };
7918
+ }
7919
+ function nodeReleaseVersionFromImage(osImage) {
7920
+ const match = /^merotee-.+-(\d+)-(\d+)-(\d+)$/.exec(osImage);
7921
+ if (!match) throw new Error(`${JSON.stringify(osImage)} is not the image of a mero-tee node release`);
7922
+ return `${Number(match[1])}.${Number(match[2])}.${Number(match[3])}`;
7923
+ }
7924
+ async function fetchNodeReleaseVersion(baseUrl, options = {}) {
7925
+ const url = `${baseUrl.replace(/\/+$/, "")}/admin-api/tee/info`;
7926
+ const response = await (options.fetch ?? fetch)(url, { headers: { accept: "application/json" } });
7927
+ if (!response.ok) throw new Error(`Fetching ${url} failed: HTTP ${response.status}`);
7928
+ let osImage;
7929
+ try {
7930
+ osImage = (await response.json())?.data?.osImage;
7931
+ } catch {
7932
+ throw new Error(`${url} did not answer with JSON`);
7933
+ }
7934
+ if (typeof osImage !== "string") throw new Error(`${url} did not name the node's image`);
7935
+ return nodeReleaseVersionFromImage(osImage);
7936
+ }
7937
+ function cloudNodeReleaseUrl(cloudUrl, version) {
7938
+ return `${cloudUrl.replace(/\/+$/, "")}/api/tee/node-releases/${releaseVersion(version, "version")}`;
7939
+ }
7940
+ async function trustSignedRelease(options) {
7941
+ const minimum = releaseVersion(options.minReleaseVersion, "minReleaseVersion");
7942
+ const version = releaseVersion(options.release?.version, "The release version");
7943
+ const published = await verifySignedNodeRelease(options.release.publishedMrtds, options.release.bundle, {
7944
+ expectedVersion: version,
7945
+ identity: options.identity,
7946
+ issuer: options.issuer
7947
+ });
7948
+ if (compareReleaseVersions(version, minimum) < 0) {
7949
+ throw new Error(`Release ${version} is older than the minimum trusted, ${minimum}`);
7950
+ }
7951
+ return trustedMeasurementsFromReleases([published], { profile: options.profile });
7952
+ }
7953
+ function createSignedReleaseVerifier(options) {
7954
+ releaseVersion(options.minReleaseVersion, "minReleaseVersion");
7955
+ const verifier = async (args) => {
7956
+ const trusted = await trustSignedRelease({
7957
+ release: await options.release(),
7958
+ profile: options.profile,
7959
+ minReleaseVersion: options.minReleaseVersion,
7960
+ identity: options.identity,
7961
+ issuer: options.issuer
7962
+ });
7963
+ return createQuoteVerifier({
7964
+ dcapVerify: options.dcapVerify,
7965
+ fetchCollateral: options.fetchCollateral,
7966
+ now: options.now,
7967
+ ...trusted
7968
+ })(args);
7969
+ };
7970
+ return Object.assign(verifier, { includeCollateral: true });
7971
+ }
7972
+ function createSignedReleaseSealedFetch(options) {
7973
+ const { baseUrl, releaseSource, applicationId, applicationHash, fetch: givenFetch, ...verifierOptions } = options;
7974
+ const baseFetch = givenFetch ?? ((input, init) => fetch(input, init));
7975
+ const release = async () => {
7976
+ const version = await fetchNodeReleaseVersion(baseUrl, { fetch: baseFetch });
7977
+ return typeof releaseSource === "function" ? releaseSource({ baseUrl, version, fetch: baseFetch }) : fetchNodeRelease(cloudNodeReleaseUrl(releaseSource ?? DEFAULT_RELEASE_MIRROR, version), { fetch: baseFetch });
7978
+ };
7979
+ return createAttestedSealedFetch({
7980
+ baseUrl,
7981
+ verify: createSignedReleaseVerifier({ ...verifierOptions, release }),
7982
+ applicationId,
7983
+ applicationHash,
7984
+ fetch: baseFetch
7985
+ });
7986
+ }
7987
+ var RELEASE_VERSION = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
7988
+ function releaseVersion(value, what) {
7989
+ const match = typeof value === "string" ? RELEASE_VERSION.exec(value) : null;
7990
+ const pre = match?.[4];
7991
+ if (!match || pre !== void 0 && pre.split(".").some((id) => id === "")) {
7992
+ throw new Error(`${what} is ${JSON.stringify(value)}, not a release version like 2.3.87`);
7993
+ }
7994
+ return value;
7995
+ }
7996
+ function compareReleaseVersions(a, b) {
7997
+ const [aCore, aPre] = splitVersion2(a);
7998
+ const [bCore, bPre] = splitVersion2(b);
7999
+ for (let i = 0; i < 3; i += 1) {
8000
+ if (aCore[i] !== bCore[i]) return aCore[i] < bCore[i] ? -1 : 1;
8001
+ }
8002
+ if (aPre === void 0 || bPre === void 0) return aPre === bPre ? 0 : aPre === void 0 ? 1 : -1;
8003
+ const aIds = aPre.split(".");
8004
+ const bIds = bPre.split(".");
8005
+ for (let i = 0; i < Math.max(aIds.length, bIds.length); i += 1) {
8006
+ const [x, y] = [aIds[i], bIds[i]];
8007
+ if (x === void 0 || y === void 0) return x === void 0 ? -1 : 1;
8008
+ const [xNum, yNum] = [/^\d+$/.test(x), /^\d+$/.test(y)];
8009
+ const order = xNum && yNum ? Math.sign(Number(x) - Number(y)) : xNum !== yNum ? xNum ? -1 : 1 : x < y ? -1 : x > y ? 1 : 0;
8010
+ if (order !== 0) return order;
8011
+ }
8012
+ return 0;
8013
+ }
8014
+ function splitVersion2(version) {
8015
+ const match = RELEASE_VERSION.exec(version);
8016
+ return [[Number(match[1]), Number(match[2]), Number(match[3])], match[4]];
8017
+ }
8018
+
7492
8019
  // src/namespace-op/namespace-op.ts
7493
- var SIGNED_NAMESPACE_OP_SCHEMA_VERSION = 9;
8020
+ var SIGNED_NAMESPACE_OP_SCHEMA_VERSION = 11;
7494
8021
  var NAMESPACE_SIGN_DOMAIN = new TextEncoder().encode("calimero.namespace.v1");
7495
8022
  var NAMESPACE_OP_ROOT = 0;
7496
8023
  var ROOT_OP = {
@@ -7503,7 +8030,7 @@ var ROOT_OP = {
7503
8030
  KeyDelivery: 6,
7504
8031
  MemberJoinedOpen: 7,
7505
8032
  MemberJoinedAt: 8,
7506
- NamespaceCreated: 9,
8033
+ NamespaceCreatedV2: 9,
7507
8034
  MemberJoinedViaTeeAttestation: 10
7508
8035
  };
7509
8036
  function bytes32(value, label) {