@calimero-network/mero-js 26.11.0 → 27.0.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/README.md +73 -288
- package/dist/cloud-v2/attestation.d.ts +91 -0
- package/dist/cloud-v2/attestation.js +135 -0
- package/dist/cloud-v2/capabilities.d.ts +30 -0
- package/dist/cloud-v2/capabilities.js +31 -0
- package/dist/cloud-v2/client.d.ts +426 -0
- package/dist/cloud-v2/client.js +646 -0
- package/dist/cloud-v2/errors.d.ts +98 -0
- package/dist/cloud-v2/errors.js +165 -0
- package/dist/cloud-v2/index.d.ts +10 -0
- package/dist/cloud-v2/index.js +5 -0
- package/dist/cloud-v2/passkey.d.ts +27 -0
- package/dist/cloud-v2/passkey.js +73 -0
- package/dist/crypto/internal.d.ts +3 -37
- package/dist/crypto/internal.js +3 -72
- package/dist/index.browser.mjs +4 -2
- package/dist/index.browser.mjs.map +4 -4
- package/dist/index.cjs +1750 -12772
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +3 -56
- package/dist/index.js +14 -75
- package/dist/index.mjs +1750 -12772
- package/dist/index.mjs.map +4 -4
- package/dist/sealed/index.d.ts +4 -4
- package/dist/sealed/index.js +2 -2
- package/dist/sealed/release.d.ts +21 -76
- package/dist/sealed/release.js +24 -108
- package/dist/sealed/sealed.d.ts +8 -56
- package/dist/sealed/sealed.js +11 -89
- package/dist/sealed/verify.d.ts +7 -13
- package/dist/sealed/verify.js +3 -7
- package/package.json +8 -13
- package/dist/account/account-admin.d.ts +0 -80
- package/dist/account/account-admin.js +0 -346
- package/dist/account/account.d.ts +0 -196
- package/dist/account/account.js +0 -269
- package/dist/account/application-id.d.ts +0 -50
- package/dist/account/application-id.js +0 -83
- package/dist/account/bootstrap-from-invitation.d.ts +0 -110
- package/dist/account/bootstrap-from-invitation.js +0 -80
- package/dist/account/create-context.d.ts +0 -103
- package/dist/account/create-context.js +0 -267
- package/dist/account/govern.d.ts +0 -24
- package/dist/account/govern.js +0 -50
- package/dist/account/index.d.ts +0 -26
- package/dist/account/index.js +0 -15
- package/dist/account/join-as-account.d.ts +0 -23
- package/dist/account/join-as-account.js +0 -34
- package/dist/account/join-with-node.d.ts +0 -115
- package/dist/account/join-with-node.js +0 -161
- package/dist/account/node-admin.d.ts +0 -20
- package/dist/account/node-admin.js +0 -33
- package/dist/account/relay-from-invitation.d.ts +0 -137
- package/dist/account/relay-from-invitation.js +0 -180
- package/dist/account/relay-login-error.d.ts +0 -32
- package/dist/account/relay-login-error.js +0 -34
- package/dist/account/session.d.ts +0 -266
- package/dist/account/session.js +0 -915
- package/dist/account/wordlist.d.ts +0 -16
- package/dist/account/wordlist.js +0 -273
- package/dist/admin-api/admin-client.d.ts +0 -688
- package/dist/admin-api/admin-client.js +0 -1230
- package/dist/admin-api/admin-factory.d.ts +0 -14
- package/dist/admin-api/admin-factory.js +0 -22
- package/dist/admin-api/admin-types.d.ts +0 -2046
- package/dist/admin-api/admin-types.js +0 -12
- package/dist/admin-api/index.d.ts +0 -4
- package/dist/admin-api/index.js +0 -5
- package/dist/admin-api/redeem-invitation.d.ts +0 -101
- package/dist/admin-api/redeem-invitation.js +0 -200
- package/dist/auth/index.d.ts +0 -34
- package/dist/auth/index.js +0 -67
- package/dist/auth-api/auth-client.d.ts +0 -31
- package/dist/auth-api/auth-client.js +0 -117
- package/dist/auth-api/auth-factory.d.ts +0 -7
- package/dist/auth-api/auth-factory.js +0 -41
- package/dist/auth-api/auth-types.d.ts +0 -130
- package/dist/auth-api/auth-types.js +0 -2
- package/dist/auth-api/index.d.ts +0 -3
- package/dist/auth-api/index.js +0 -4
- package/dist/capabilities.d.ts +0 -84
- package/dist/capabilities.js +0 -117
- package/dist/cloud/account-ownership.d.ts +0 -63
- package/dist/cloud/account-ownership.js +0 -87
- package/dist/cloud/cloud-client.d.ts +0 -949
- package/dist/cloud/cloud-client.js +0 -1068
- package/dist/cloud/connect.d.ts +0 -213
- package/dist/cloud/connect.js +0 -260
- package/dist/cloud/enrol-redirect.d.ts +0 -181
- package/dist/cloud/enrol-redirect.js +0 -244
- package/dist/cloud/index.d.ts +0 -12
- package/dist/cloud/index.js +0 -10
- package/dist/cloud/link-redirect.d.ts +0 -95
- package/dist/cloud/link-redirect.js +0 -121
- package/dist/cloud/link-state.d.ts +0 -36
- package/dist/cloud/link-state.js +0 -104
- package/dist/cloud/routing-proof.d.ts +0 -113
- package/dist/cloud/routing-proof.js +0 -96
- package/dist/device-cert/device-cert.d.ts +0 -189
- package/dist/device-cert/device-cert.js +0 -239
- package/dist/device-cert/device-scope.d.ts +0 -58
- package/dist/device-cert/device-scope.js +0 -76
- package/dist/device-cert/index.d.ts +0 -4
- package/dist/device-cert/index.js +0 -3
- package/dist/ephemeral/codec.d.ts +0 -4
- package/dist/ephemeral/codec.js +0 -12
- package/dist/ephemeral/index.d.ts +0 -57
- package/dist/ephemeral/index.js +0 -101
- package/dist/ephemeral/types.d.ts +0 -27
- package/dist/ephemeral/types.js +0 -1
- package/dist/errors/classify.d.ts +0 -65
- package/dist/errors/classify.js +0 -117
- package/dist/errors/index.d.ts +0 -2
- package/dist/errors/index.js +0 -1
- package/dist/events/group.d.ts +0 -95
- package/dist/events/group.js +0 -10
- package/dist/events/index.d.ts +0 -5
- package/dist/events/index.js +0 -2
- package/dist/events/sse.d.ts +0 -127
- package/dist/events/sse.js +0 -469
- package/dist/events/ws.d.ts +0 -57
- package/dist/events/ws.js +0 -212
- package/dist/http-client/api-response.d.ts +0 -15
- package/dist/http-client/api-response.js +0 -1
- package/dist/http-client/http-factory.d.ts +0 -71
- package/dist/http-client/http-factory.js +0 -68
- package/dist/http-client/http-types.d.ts +0 -87
- package/dist/http-client/http-types.js +0 -1
- package/dist/http-client/index.d.ts +0 -7
- package/dist/http-client/index.js +0 -11
- package/dist/http-client/path-segment.d.ts +0 -1
- package/dist/http-client/path-segment.js +0 -8
- package/dist/http-client/retry.d.ts +0 -5
- package/dist/http-client/retry.js +0 -82
- package/dist/http-client/signal-utils.d.ts +0 -2
- package/dist/http-client/signal-utils.js +0 -41
- package/dist/http-client/web-client.d.ts +0 -80
- package/dist/http-client/web-client.js +0 -557
- package/dist/invitation/index.d.ts +0 -2
- package/dist/invitation/index.js +0 -2
- package/dist/invitation/invitation.d.ts +0 -106
- package/dist/invitation/invitation.js +0 -104
- package/dist/login/index.d.ts +0 -4
- package/dist/login/index.js +0 -3
- package/dist/login/login.d.ts +0 -111
- package/dist/login/login.js +0 -110
- package/dist/login/session.d.ts +0 -124
- package/dist/login/session.js +0 -141
- package/dist/mero-js.d.ts +0 -231
- package/dist/mero-js.js +0 -469
- package/dist/namespace-op/index.d.ts +0 -2
- package/dist/namespace-op/index.js +0 -3
- package/dist/namespace-op/namespace-op.d.ts +0 -150
- package/dist/namespace-op/namespace-op.js +0 -191
- package/dist/nodeDiscovery.d.ts +0 -47
- package/dist/nodeDiscovery.js +0 -101
- package/dist/owner-op/index.d.ts +0 -2
- package/dist/owner-op/index.js +0 -3
- package/dist/owner-op/owner-op.d.ts +0 -119
- package/dist/owner-op/owner-op.js +0 -214
- package/dist/presence/relay-presence.d.ts +0 -56
- package/dist/presence/relay-presence.js +0 -107
- package/dist/presence/statement.d.ts +0 -12
- package/dist/presence/statement.js +0 -17
- package/dist/relay/index.d.ts +0 -4
- package/dist/relay/index.js +0 -2
- package/dist/relay/nonce-source.d.ts +0 -72
- package/dist/relay/nonce-source.js +0 -111
- package/dist/relay/relay-client.d.ts +0 -472
- package/dist/relay/relay-client.js +0 -518
- package/dist/relay-attestation/attest-node-key.d.ts +0 -66
- package/dist/relay-attestation/attest-node-key.js +0 -110
- package/dist/relay-attestation/index.d.ts +0 -1
- package/dist/relay-attestation/index.js +0 -1
- package/dist/request/caller-proof.d.ts +0 -43
- package/dist/request/caller-proof.js +0 -36
- package/dist/request/index.d.ts +0 -3
- package/dist/request/index.js +0 -3
- package/dist/request/proof-signer.d.ts +0 -49
- package/dist/request/proof-signer.js +0 -59
- package/dist/request/request-sig.d.ts +0 -99
- package/dist/request/request-sig.js +0 -95
- package/dist/request-proof/authorizer.d.ts +0 -41
- package/dist/request-proof/authorizer.js +0 -60
- package/dist/request-proof/index.d.ts +0 -4
- package/dist/request-proof/index.js +0 -2
- package/dist/request-proof/request-proof.d.ts +0 -53
- package/dist/request-proof/request-proof.js +0 -95
- package/dist/rpc/index.d.ts +0 -64
- package/dist/rpc/index.js +0 -78
- package/dist/signer/index.d.ts +0 -2
- package/dist/signer/index.js +0 -3
- package/dist/signer/signer.d.ts +0 -92
- package/dist/signer/signer.js +0 -147
- package/dist/token-store/index.d.ts +0 -27
- package/dist/token-store/index.js +0 -62
- package/dist/transport/index.d.ts +0 -6
- package/dist/transport/index.js +0 -3
- package/dist/transport/mero-client.d.ts +0 -294
- package/dist/transport/mero-client.js +0 -386
- package/dist/transport/relay-observer.d.ts +0 -152
- package/dist/transport/relay-observer.js +0 -173
- package/dist/transport/relay-transport.d.ts +0 -73
- package/dist/transport/relay-transport.js +0 -94
- package/dist/transport/types.d.ts +0 -100
- package/dist/transport/types.js +0 -32
- package/dist/types/api-response.d.ts +0 -15
- package/dist/types/api-response.js +0 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.js +0 -1
- package/dist/warrant/creation-warrant.d.ts +0 -163
- package/dist/warrant/creation-warrant.js +0 -194
- package/dist/warrant/governance-op.d.ts +0 -202
- package/dist/warrant/governance-op.js +0 -355
- package/dist/warrant/governance-warrant.d.ts +0 -124
- package/dist/warrant/governance-warrant.js +0 -164
- package/dist/warrant/index.d.ts +0 -8
- package/dist/warrant/index.js +0 -5
- package/dist/warrant/warrant.d.ts +0 -188
- package/dist/warrant/warrant.js +0 -202
package/dist/sealed/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { HANDSHAKE_PATH, SEALED_CONTENT_TYPE, SEALED_PATH, SealedTransportError, StaleTransportKeyError,
|
|
2
|
-
export type {
|
|
1
|
+
export { HANDSHAKE_PATH, SEALED_CONTENT_TYPE, SEALED_PATH, SealedTransportError, StaleTransportKeyError, createSealedFetch, transportKeyBinding, } from './sealed.js';
|
|
2
|
+
export type { SealedFetchOptions, VerifyTransportQuote } from './sealed.js';
|
|
3
3
|
export { PRODUCTION_PROFILE, createQuoteVerifier, trustedMeasurementsFromReleases } from './verify.js';
|
|
4
4
|
export type { DcapCollateral, DcapVerifiedReport, DcapVerify, PublishedMrtds, QuoteVerifier, QuoteVerifierOptions, TrustedMeasurements, TrustedProfileOptions, } from './verify.js';
|
|
5
|
-
export { DEFAULT_RELEASE_MIRROR, NODE_RELEASE_SIGNER, cloudNodeReleaseUrl,
|
|
6
|
-
export type { SignedNodeRelease,
|
|
5
|
+
export { DEFAULT_RELEASE_MIRROR, GITHUB_RELEASES, NODE_RELEASE_SIGNER, cloudNodeReleaseUrl, createSignedReleaseVerifier, fetchGithubNodeRelease, fetchNodeRelease, trustSignedRelease, verifySignedNodeRelease, } from './release.js';
|
|
6
|
+
export type { SignedNodeRelease, SignedReleaseVerifierOptions, TrustSignedReleaseOptions, VerifySignedNodeReleaseOptions, } from './release.js';
|
|
7
7
|
export type { SignerIdentity } from './sigstore.js';
|
package/dist/sealed/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { HANDSHAKE_PATH, SEALED_CONTENT_TYPE, SEALED_PATH, SealedTransportError, StaleTransportKeyError,
|
|
1
|
+
export { HANDSHAKE_PATH, SEALED_CONTENT_TYPE, SEALED_PATH, SealedTransportError, StaleTransportKeyError, createSealedFetch, transportKeyBinding, } from './sealed.js';
|
|
2
2
|
export { PRODUCTION_PROFILE, createQuoteVerifier, trustedMeasurementsFromReleases } from './verify.js';
|
|
3
|
-
export { DEFAULT_RELEASE_MIRROR, NODE_RELEASE_SIGNER, cloudNodeReleaseUrl,
|
|
3
|
+
export { DEFAULT_RELEASE_MIRROR, GITHUB_RELEASES, NODE_RELEASE_SIGNER, cloudNodeReleaseUrl, createSignedReleaseVerifier, fetchGithubNodeRelease, fetchNodeRelease, trustSignedRelease, verifySignedNodeRelease, } from './release.js';
|
package/dist/sealed/release.d.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* - the minimum release you pass, below which no release is accepted.
|
|
22
22
|
*
|
|
23
23
|
* What is not: the transport, the server that serves the release, and the
|
|
24
|
-
*
|
|
24
|
+
* node's choice of release. A node can present any genuinely signed release
|
|
25
25
|
* at or above the minimum, and no other; its quote must then match that
|
|
26
26
|
* release's measurements exactly, so it cannot claim one release and run
|
|
27
27
|
* another.
|
|
@@ -86,25 +86,19 @@ export declare function verifySignedNodeRelease(publishedMrtds: string | Uint8Ar
|
|
|
86
86
|
export declare function fetchNodeRelease(url: string, options?: {
|
|
87
87
|
fetch?: typeof fetch;
|
|
88
88
|
}): Promise<SignedNodeRelease>;
|
|
89
|
+
/** Where mero-tee publishes its releases' assets, as core's `calimero-tee-release` fetches them. */
|
|
90
|
+
export declare const GITHUB_RELEASES = "https://github.com/calimero-network/mero-tee/releases/download";
|
|
89
91
|
/**
|
|
90
|
-
*
|
|
91
|
-
* `
|
|
92
|
-
*
|
|
92
|
+
* Node release `version` straight from its GitHub release (`mero-tee-v{version}`):
|
|
93
|
+
* `published-mrtds.json` and its cosign bundle, the files core's Rust client
|
|
94
|
+
* verifies. Only fetched: verify it with {@link trustSignedRelease}. GitHub's
|
|
95
|
+
* downloads send no CORS headers, so in a browser use a mirror
|
|
96
|
+
* ({@link cloudNodeReleaseUrl}) instead.
|
|
93
97
|
*/
|
|
94
|
-
export declare function
|
|
95
|
-
/**
|
|
96
|
-
* The release the TEE node at `baseUrl` says it runs, from the image name its
|
|
97
|
-
* `GET /admin-api/tee/info` reports. The node's own claim, and not trusted:
|
|
98
|
-
* it only picks which signed release to fetch, and the quote must then match
|
|
99
|
-
* that release's measurements.
|
|
100
|
-
*
|
|
101
|
-
* `/tee/info` is read in the clear, before any session exists. A node that
|
|
102
|
-
* requires sealing serves it unsealed from core#4196 on; an older one refuses
|
|
103
|
-
* it with `403 sealed_required`, and then this throws saying so.
|
|
104
|
-
*/
|
|
105
|
-
export declare function fetchNodeReleaseVersion(baseUrl: string, options?: {
|
|
98
|
+
export declare function fetchGithubNodeRelease(version: string, options?: {
|
|
106
99
|
fetch?: typeof fetch;
|
|
107
|
-
|
|
100
|
+
base?: string;
|
|
101
|
+
}): Promise<SignedNodeRelease>;
|
|
108
102
|
/** Where the cloud at `cloudUrl` (e.g. `https://cloud.calimero.network`) mirrors node release `version`. */
|
|
109
103
|
export declare function cloudNodeReleaseUrl(cloudUrl: string, version: string): string;
|
|
110
104
|
export interface TrustSignedReleaseOptions extends TrustedProfileOptions {
|
|
@@ -112,7 +106,7 @@ export interface TrustSignedReleaseOptions extends TrustedProfileOptions {
|
|
|
112
106
|
release: SignedNodeRelease;
|
|
113
107
|
/**
|
|
114
108
|
* The oldest release accepted, e.g. `2.3.87`. Required: every release ever
|
|
115
|
-
* signed stays validly signed, so without a floor a
|
|
109
|
+
* signed stays validly signed, so without a floor a node could present an
|
|
116
110
|
* old release, with a flaw since fixed, and a quote from that old image
|
|
117
111
|
* would pass. Raise it when a release must no longer be trusted.
|
|
118
112
|
*/
|
|
@@ -129,7 +123,7 @@ export interface TrustSignedReleaseOptions extends TrustedProfileOptions {
|
|
|
129
123
|
* file.
|
|
130
124
|
*
|
|
131
125
|
* ```ts
|
|
132
|
-
* const version =
|
|
126
|
+
* const version = claimedRelease(attestationDoc, '2.3.109'); // the release the node names
|
|
133
127
|
* const release = await fetchNodeRelease(cloudNodeReleaseUrl(DEFAULT_RELEASE_MIRROR, version));
|
|
134
128
|
* const verifier = createQuoteVerifier({
|
|
135
129
|
* dcapVerify,
|
|
@@ -160,64 +154,15 @@ export interface SignedReleaseVerifierOptions extends Omit<TrustSignedReleaseOpt
|
|
|
160
154
|
*
|
|
161
155
|
* The node's choice of release is the one thing taken from it, and it is
|
|
162
156
|
* bounded: only a genuinely signed release at or above `minReleaseVersion`
|
|
163
|
-
* passes, and the quote must then be of exactly that release's image.
|
|
164
|
-
* as `
|
|
165
|
-
* {@link createSignedReleaseSealedFetch} does the latter for you.
|
|
157
|
+
* passes, and the quote must then be of exactly that release's image. Pass it
|
|
158
|
+
* as `CloudPolicy.verifyQuote` to choose where the release comes from yourself.
|
|
166
159
|
*/
|
|
167
160
|
export declare function createSignedReleaseVerifier(options: SignedReleaseVerifierOptions): QuoteVerifier;
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
baseUrl: string;
|
|
171
|
-
/**
|
|
172
|
-
* Where to get the release the node says it runs, the version read from its
|
|
173
|
-
* `GET /admin-api/tee/info` each time it is attested. A string is the base
|
|
174
|
-
* URL of a mirror serving {@link cloudNodeReleaseUrl}'s path, defaulting to
|
|
175
|
-
* {@link DEFAULT_RELEASE_MIRROR}; a function gets the version and returns
|
|
176
|
-
* the release, e.g. from a file bundled with the app. Whichever it is, it is
|
|
177
|
-
* not trusted: the release is verified. `/tee/info` is read in the clear;
|
|
178
|
-
* for a node that refuses that, see `releaseVersion`.
|
|
179
|
-
*/
|
|
180
|
-
releaseSource?: string | ((context: {
|
|
181
|
-
baseUrl: string;
|
|
182
|
-
version: string;
|
|
183
|
-
fetch: typeof fetch;
|
|
184
|
-
}) => Promise<SignedNodeRelease>);
|
|
185
|
-
/**
|
|
186
|
-
* The release to fetch when the node will not name one: a node that
|
|
187
|
-
* requires sealing (`[server.sealed] required`) and predates core#4196
|
|
188
|
-
* refuses `/tee/info` unsealed. Used only then; a node that answers is
|
|
189
|
-
* taken at its word. Either way untrusted: the quote must match the signed
|
|
190
|
-
* release, so a wrong version only fails the attestation.
|
|
191
|
-
*/
|
|
192
|
-
releaseVersion?: string;
|
|
193
|
-
/** Also require the node to run this application with this bytecode hash. */
|
|
194
|
-
applicationId?: string;
|
|
195
|
-
applicationHash?: string;
|
|
196
|
-
/** Fetch for the release, the attestation and the envelopes. Defaults to global `fetch`. */
|
|
197
|
-
fetch?: typeof fetch;
|
|
198
|
-
}
|
|
161
|
+
/** `value` if it is a release version (`2.3.87`, `2.3.88-rc.1`); throws otherwise. */
|
|
162
|
+
export declare function releaseVersion(value: unknown, what: string): string;
|
|
199
163
|
/**
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
* when a node too old to serve that unsealed requires sealing), gets that release from
|
|
204
|
-
* `releaseSource` (the public mirror unless you say otherwise), verifies its
|
|
205
|
-
* signature, builds the quote verifier from its measurements, attests the node
|
|
206
|
-
* and seals everything to it.
|
|
207
|
-
*
|
|
208
|
-
* ```ts
|
|
209
|
-
* import { verify as dcapVerify } from '@phala/dcap-qvl';
|
|
210
|
-
*
|
|
211
|
-
* const relay = new RelayClient({
|
|
212
|
-
* relayUrl,
|
|
213
|
-
* fetch: createSignedReleaseSealedFetch({
|
|
214
|
-
* baseUrl: relayUrl,
|
|
215
|
-
* dcapVerify,
|
|
216
|
-
* profile: 'locked-read-only',
|
|
217
|
-
* minReleaseVersion: '2.3.87',
|
|
218
|
-
* }),
|
|
219
|
-
* ...
|
|
220
|
-
* });
|
|
221
|
-
* ```
|
|
164
|
+
* Order two release versions as semver does, and as core's
|
|
165
|
+
* `compare_release_versions`: numerically by `X.Y.Z`, a pre-release before its
|
|
166
|
+
* release, build metadata ignored.
|
|
222
167
|
*/
|
|
223
|
-
export declare function
|
|
168
|
+
export declare function compareReleaseVersions(a: string, b: string): number;
|
package/dist/sealed/release.js
CHANGED
|
@@ -21,12 +21,11 @@
|
|
|
21
21
|
* - the minimum release you pass, below which no release is accepted.
|
|
22
22
|
*
|
|
23
23
|
* What is not: the transport, the server that serves the release, and the
|
|
24
|
-
*
|
|
24
|
+
* node's choice of release. A node can present any genuinely signed release
|
|
25
25
|
* at or above the minimum, and no other; its quote must then match that
|
|
26
26
|
* release's measurements exactly, so it cannot claim one release and run
|
|
27
27
|
* another.
|
|
28
28
|
*/
|
|
29
|
-
import { createAttestedSealedFetch } from './sealed.js';
|
|
30
29
|
import { verifyCosignBundle } from './sigstore.js';
|
|
31
30
|
import { createQuoteVerifier, trustedMeasurementsFromReleases, } from './verify.js';
|
|
32
31
|
/** The workflow that signs every mero-tee node release, and the issuer of its identity. */
|
|
@@ -111,63 +110,26 @@ export async function fetchNodeRelease(url, options = {}) {
|
|
|
111
110
|
}
|
|
112
111
|
return { version: release.version, publishedMrtds: release.publishedMrtds, bundle: release.bundle };
|
|
113
112
|
}
|
|
113
|
+
/** Where mero-tee publishes its releases' assets, as core's `calimero-tee-release` fetches them. */
|
|
114
|
+
export const GITHUB_RELEASES = 'https://github.com/calimero-network/mero-tee/releases/download';
|
|
114
115
|
/**
|
|
115
|
-
*
|
|
116
|
-
* `
|
|
117
|
-
*
|
|
116
|
+
* Node release `version` straight from its GitHub release (`mero-tee-v{version}`):
|
|
117
|
+
* `published-mrtds.json` and its cosign bundle, the files core's Rust client
|
|
118
|
+
* verifies. Only fetched: verify it with {@link trustSignedRelease}. GitHub's
|
|
119
|
+
* downloads send no CORS headers, so in a browser use a mirror
|
|
120
|
+
* ({@link cloudNodeReleaseUrl}) instead.
|
|
118
121
|
*/
|
|
119
|
-
export function
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
*
|
|
131
|
-
* `/tee/info` is read in the clear, before any session exists. A node that
|
|
132
|
-
* requires sealing serves it unsealed from core#4196 on; an older one refuses
|
|
133
|
-
* it with `403 sealed_required`, and then this throws saying so.
|
|
134
|
-
*/
|
|
135
|
-
export async function fetchNodeReleaseVersion(baseUrl, options = {}) {
|
|
136
|
-
const version = await readNodeReleaseVersion(baseUrl, options.fetch ?? fetch);
|
|
137
|
-
if (version === undefined) {
|
|
138
|
-
throw new Error(`${infoUrl(baseUrl)} was refused unsealed: the node requires sealing and is too old to name its ` +
|
|
139
|
-
"release before a session exists. Name the release it runs yourself (createSignedReleaseSealedFetch's " +
|
|
140
|
-
'`releaseVersion`), or upgrade the node');
|
|
141
|
-
}
|
|
142
|
-
return version;
|
|
143
|
-
}
|
|
144
|
-
function infoUrl(baseUrl) {
|
|
145
|
-
return `${baseUrl.replace(/\/+$/, '')}/admin-api/tee/info`;
|
|
146
|
-
}
|
|
147
|
-
/** As {@link fetchNodeReleaseVersion}, but `undefined` when the node refuses `/tee/info` as `sealed_required`. */
|
|
148
|
-
async function readNodeReleaseVersion(baseUrl, givenFetch) {
|
|
149
|
-
const url = infoUrl(baseUrl);
|
|
150
|
-
const response = await givenFetch(url, { headers: { accept: 'application/json' } });
|
|
151
|
-
if (response.status === 403) {
|
|
152
|
-
const code = await response
|
|
153
|
-
.json()
|
|
154
|
-
.then((body) => body?.error?.code)
|
|
155
|
-
.catch(() => undefined);
|
|
156
|
-
if (code === 'sealed_required')
|
|
157
|
-
return undefined;
|
|
158
|
-
}
|
|
159
|
-
if (!response.ok)
|
|
160
|
-
throw new Error(`Fetching ${url} failed: HTTP ${response.status}`);
|
|
161
|
-
let osImage;
|
|
162
|
-
try {
|
|
163
|
-
osImage = (await response.json())?.data?.osImage;
|
|
164
|
-
}
|
|
165
|
-
catch {
|
|
166
|
-
throw new Error(`${url} did not answer with JSON`);
|
|
167
|
-
}
|
|
168
|
-
if (typeof osImage !== 'string')
|
|
169
|
-
throw new Error(`${url} did not name the node's image`);
|
|
170
|
-
return nodeReleaseVersionFromImage(osImage);
|
|
122
|
+
export async function fetchGithubNodeRelease(version, options = {}) {
|
|
123
|
+
const tag = `mero-tee-v${releaseVersion(version, 'version')}`;
|
|
124
|
+
const base = `${(options.base ?? GITHUB_RELEASES).replace(/\/+$/, '')}/${tag}/published-mrtds.json`;
|
|
125
|
+
const get = async (url) => {
|
|
126
|
+
const response = await (options.fetch ?? fetch)(url);
|
|
127
|
+
if (!response.ok)
|
|
128
|
+
throw new Error(`Fetching ${url} failed: HTTP ${response.status}`);
|
|
129
|
+
return response.text();
|
|
130
|
+
};
|
|
131
|
+
const [publishedMrtds, bundle] = await Promise.all([get(base), get(`${base}.bundle.json`)]);
|
|
132
|
+
return { version, publishedMrtds, bundle };
|
|
171
133
|
}
|
|
172
134
|
/** Where the cloud at `cloudUrl` (e.g. `https://cloud.calimero.network`) mirrors node release `version`. */
|
|
173
135
|
export function cloudNodeReleaseUrl(cloudUrl, version) {
|
|
@@ -181,7 +143,7 @@ export function cloudNodeReleaseUrl(cloudUrl, version) {
|
|
|
181
143
|
* file.
|
|
182
144
|
*
|
|
183
145
|
* ```ts
|
|
184
|
-
* const version =
|
|
146
|
+
* const version = claimedRelease(attestationDoc, '2.3.109'); // the release the node names
|
|
185
147
|
* const release = await fetchNodeRelease(cloudNodeReleaseUrl(DEFAULT_RELEASE_MIRROR, version));
|
|
186
148
|
* const verifier = createQuoteVerifier({
|
|
187
149
|
* dcapVerify,
|
|
@@ -213,9 +175,8 @@ export async function trustSignedRelease(options) {
|
|
|
213
175
|
*
|
|
214
176
|
* The node's choice of release is the one thing taken from it, and it is
|
|
215
177
|
* bounded: only a genuinely signed release at or above `minReleaseVersion`
|
|
216
|
-
* passes, and the quote must then be of exactly that release's image.
|
|
217
|
-
* as `
|
|
218
|
-
* {@link createSignedReleaseSealedFetch} does the latter for you.
|
|
178
|
+
* passes, and the quote must then be of exactly that release's image. Pass it
|
|
179
|
+
* as `CloudPolicy.verifyQuote` to choose where the release comes from yourself.
|
|
219
180
|
*/
|
|
220
181
|
export function createSignedReleaseVerifier(options) {
|
|
221
182
|
releaseVersion(options.minReleaseVersion, 'minReleaseVersion');
|
|
@@ -237,54 +198,9 @@ export function createSignedReleaseVerifier(options) {
|
|
|
237
198
|
};
|
|
238
199
|
return Object.assign(verifier, { includeCollateral: true });
|
|
239
200
|
}
|
|
240
|
-
/**
|
|
241
|
-
* {@link createAttestedSealedFetch} for a TEE node known only by URL, trusting
|
|
242
|
-
* whatever signed release at or above `minReleaseVersion` it runs: it reads
|
|
243
|
-
* the release the node names from its `/tee/info` (or takes `releaseVersion`
|
|
244
|
-
* when a node too old to serve that unsealed requires sealing), gets that release from
|
|
245
|
-
* `releaseSource` (the public mirror unless you say otherwise), verifies its
|
|
246
|
-
* signature, builds the quote verifier from its measurements, attests the node
|
|
247
|
-
* and seals everything to it.
|
|
248
|
-
*
|
|
249
|
-
* ```ts
|
|
250
|
-
* import { verify as dcapVerify } from '@phala/dcap-qvl';
|
|
251
|
-
*
|
|
252
|
-
* const relay = new RelayClient({
|
|
253
|
-
* relayUrl,
|
|
254
|
-
* fetch: createSignedReleaseSealedFetch({
|
|
255
|
-
* baseUrl: relayUrl,
|
|
256
|
-
* dcapVerify,
|
|
257
|
-
* profile: 'locked-read-only',
|
|
258
|
-
* minReleaseVersion: '2.3.87',
|
|
259
|
-
* }),
|
|
260
|
-
* ...
|
|
261
|
-
* });
|
|
262
|
-
* ```
|
|
263
|
-
*/
|
|
264
|
-
export function createSignedReleaseSealedFetch(options) {
|
|
265
|
-
const { baseUrl, releaseSource, releaseVersion: fallbackVersion, applicationId, applicationHash, fetch: givenFetch, ...verifierOptions } = options;
|
|
266
|
-
if (fallbackVersion !== undefined)
|
|
267
|
-
releaseVersion(fallbackVersion, 'releaseVersion');
|
|
268
|
-
const baseFetch = givenFetch ?? ((input, init) => fetch(input, init));
|
|
269
|
-
const release = async () => {
|
|
270
|
-
const version = fallbackVersion === undefined
|
|
271
|
-
? await fetchNodeReleaseVersion(baseUrl, { fetch: baseFetch })
|
|
272
|
-
: ((await readNodeReleaseVersion(baseUrl, baseFetch)) ?? fallbackVersion);
|
|
273
|
-
return typeof releaseSource === 'function'
|
|
274
|
-
? releaseSource({ baseUrl, version, fetch: baseFetch })
|
|
275
|
-
: fetchNodeRelease(cloudNodeReleaseUrl(releaseSource ?? DEFAULT_RELEASE_MIRROR, version), { fetch: baseFetch });
|
|
276
|
-
};
|
|
277
|
-
return createAttestedSealedFetch({
|
|
278
|
-
baseUrl,
|
|
279
|
-
verify: createSignedReleaseVerifier({ ...verifierOptions, release }),
|
|
280
|
-
applicationId,
|
|
281
|
-
applicationHash,
|
|
282
|
-
fetch: baseFetch,
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
201
|
const RELEASE_VERSION = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
286
202
|
/** `value` if it is a release version (`2.3.87`, `2.3.88-rc.1`); throws otherwise. */
|
|
287
|
-
function releaseVersion(value, what) {
|
|
203
|
+
export function releaseVersion(value, what) {
|
|
288
204
|
const match = typeof value === 'string' ? RELEASE_VERSION.exec(value) : null;
|
|
289
205
|
const pre = match?.[4];
|
|
290
206
|
if (!match || (pre !== undefined && pre.split('.').some((id) => id === ''))) {
|
|
@@ -297,7 +213,7 @@ function releaseVersion(value, what) {
|
|
|
297
213
|
* `compare_release_versions`: numerically by `X.Y.Z`, a pre-release before its
|
|
298
214
|
* release, build metadata ignored.
|
|
299
215
|
*/
|
|
300
|
-
function compareReleaseVersions(a, b) {
|
|
216
|
+
export function compareReleaseVersions(a, b) {
|
|
301
217
|
const [aCore, aPre] = splitVersion(a);
|
|
302
218
|
const [bCore, bPre] = splitVersion(b);
|
|
303
219
|
for (let i = 0; i < 3; i += 1) {
|
package/dist/sealed/sealed.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* produce a response the client accepts.
|
|
9
9
|
*
|
|
10
10
|
* The client opens a session with a Noise NK handshake to the X25519 transport
|
|
11
|
-
* key the node's quote commits to (
|
|
11
|
+
* key the node's quote commits to (`fetchAttestedKey`, `GET /attestation`). The
|
|
12
12
|
* transport key only authenticates the node: the session keys come from both
|
|
13
13
|
* sides' ephemeral keys, so once a session is gone (an hour at most) what was
|
|
14
14
|
* sent in it cannot be opened, not even with the transport key. Each request
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* refuses one inside the envelope with a 501.
|
|
18
18
|
*
|
|
19
19
|
* The node's side, and the wire format this implements byte for byte, are in
|
|
20
|
-
* core's `crates/
|
|
20
|
+
* core's `crates/sealed` (`calimero-sealed`):
|
|
21
21
|
*
|
|
22
22
|
* ```text
|
|
23
23
|
* handshake Noise_NK_25519_AESGCM_SHA256, prologue "calimero/sealed-http/v2"
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
* inner u32 BE head length || head (JSON) || body
|
|
34
34
|
* ```
|
|
35
35
|
*
|
|
36
|
-
*
|
|
36
|
+
* `CalimeroClient` seals every request this way; {@link createSealedFetch} is
|
|
37
|
+
* the same channel as a plain `fetch`.
|
|
37
38
|
*/
|
|
38
|
-
import type { TeeAttestRequest, TeeAttestResponseData } from '../admin-api/admin-types.js';
|
|
39
39
|
import { initiate } from './noise.js';
|
|
40
40
|
import type { DcapCollateral } from './verify.js';
|
|
41
41
|
/** Where a session is opened, relative to the node's base URL. */
|
|
@@ -83,64 +83,17 @@ export interface VerifyTransportQuote {
|
|
|
83
83
|
collateral?: DcapCollateral;
|
|
84
84
|
}): Promise<boolean>;
|
|
85
85
|
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* refuses the request.
|
|
86
|
+
* Set by the verifiers this package builds. A Calimero 1.0 node sends no
|
|
87
|
+
* collateral with its quote; the verifier fetches it itself.
|
|
89
88
|
*/
|
|
90
89
|
readonly includeCollateral?: boolean;
|
|
91
90
|
}
|
|
92
|
-
/**
|
|
93
|
-
* Ask a node for its transport key and accept it only if a quote you verified
|
|
94
|
-
* commits to it.
|
|
95
|
-
*
|
|
96
|
-
* `applicationHash` (hex), when given, is the bytecode hash you expect the
|
|
97
|
-
* attested application to have; the node is asked to bind that application,
|
|
98
|
-
* and the quote only verifies if it runs that exact bytecode.
|
|
99
|
-
*/
|
|
100
|
-
export declare function fetchAttestedTransportKey(admin: {
|
|
101
|
-
teeAttest(request: TeeAttestRequest): Promise<TeeAttestResponseData>;
|
|
102
|
-
}, verify: VerifyTransportQuote, options?: {
|
|
103
|
-
applicationId?: string;
|
|
104
|
-
applicationHash?: string;
|
|
105
|
-
}): Promise<Uint8Array>;
|
|
106
|
-
export interface AttestedSealedFetchOptions {
|
|
107
|
-
/** The node's base URL. */
|
|
108
|
-
baseUrl: string;
|
|
109
|
-
/**
|
|
110
|
-
* Decides whether the node's quote is one you trust — typically
|
|
111
|
-
* {@link createQuoteVerifier}. Asked again whenever the node restarts.
|
|
112
|
-
*/
|
|
113
|
-
verify: VerifyTransportQuote;
|
|
114
|
-
/** Also require the node to run this application with this bytecode hash. */
|
|
115
|
-
applicationId?: string;
|
|
116
|
-
applicationHash?: string;
|
|
117
|
-
/** Fetch that carries the attestation and the envelopes. Defaults to global `fetch`. */
|
|
118
|
-
fetch?: typeof fetch;
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* {@link createSealedFetch} for a node you know only by URL: it attests the
|
|
122
|
-
* node's transport key with `verify`, over `POST /admin-api/tee/attest` (the
|
|
123
|
-
* one call that needs no credential and is never sealed), and seals everything
|
|
124
|
-
* else to it. A restarted node is attested again, through the same verifier.
|
|
125
|
-
*
|
|
126
|
-
* This is what to hand a {@link RelayClient} for a TEE relay, so the warrant
|
|
127
|
-
* and the method's arguments reach only the attested TD:
|
|
128
|
-
*
|
|
129
|
-
* ```ts
|
|
130
|
-
* const relay = new RelayClient({
|
|
131
|
-
* relayUrl,
|
|
132
|
-
* fetch: createAttestedSealedFetch({ baseUrl: relayUrl, verify: createQuoteVerifier(...) }),
|
|
133
|
-
* ...
|
|
134
|
-
* });
|
|
135
|
-
* ```
|
|
136
|
-
*/
|
|
137
|
-
export declare function createAttestedSealedFetch(options: AttestedSealedFetchOptions): typeof fetch;
|
|
138
91
|
export interface SealedFetchOptions {
|
|
139
|
-
/** The node's base URL
|
|
92
|
+
/** The node's base URL. */
|
|
140
93
|
baseUrl: string;
|
|
141
94
|
/**
|
|
142
95
|
* The node's attested transport key, or a function that attests and returns
|
|
143
|
-
* it — typically `() =>
|
|
96
|
+
* it — typically `() => fetchAttestedKey(baseUrl, policy)`. Given a
|
|
144
97
|
* function, a restarted node is handled: the key is attested again, through
|
|
145
98
|
* your verifier, and the request retried once. Given bytes, a restart
|
|
146
99
|
* surfaces as {@link StaleTransportKeyError}.
|
|
@@ -151,7 +104,6 @@ export interface SealedFetchOptions {
|
|
|
151
104
|
}
|
|
152
105
|
/**
|
|
153
106
|
* A `fetch` that seals every request to the node's attested transport key.
|
|
154
|
-
* Pass it as `MeroJsConfig.fetch`.
|
|
155
107
|
*
|
|
156
108
|
* A request for any URL outside `baseUrl` is refused rather than sent in the
|
|
157
109
|
* clear: falling back to plaintext would silently undo the point of sealing.
|
package/dist/sealed/sealed.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* produce a response the client accepts.
|
|
9
9
|
*
|
|
10
10
|
* The client opens a session with a Noise NK handshake to the X25519 transport
|
|
11
|
-
* key the node's quote commits to (
|
|
11
|
+
* key the node's quote commits to (`fetchAttestedKey`, `GET /attestation`). The
|
|
12
12
|
* transport key only authenticates the node: the session keys come from both
|
|
13
13
|
* sides' ephemeral keys, so once a session is gone (an hour at most) what was
|
|
14
14
|
* sent in it cannot be opened, not even with the transport key. Each request
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* refuses one inside the envelope with a 501.
|
|
18
18
|
*
|
|
19
19
|
* The node's side, and the wire format this implements byte for byte, are in
|
|
20
|
-
* core's `crates/
|
|
20
|
+
* core's `crates/sealed` (`calimero-sealed`):
|
|
21
21
|
*
|
|
22
22
|
* ```text
|
|
23
23
|
* handshake Noise_NK_25519_AESGCM_SHA256, prologue "calimero/sealed-http/v2"
|
|
@@ -33,9 +33,10 @@
|
|
|
33
33
|
* inner u32 BE head length || head (JSON) || body
|
|
34
34
|
* ```
|
|
35
35
|
*
|
|
36
|
-
*
|
|
36
|
+
* `CalimeroClient` seals every request this way; {@link createSealedFetch} is
|
|
37
|
+
* the same channel as a plain `fetch`.
|
|
37
38
|
*/
|
|
38
|
-
import { concat
|
|
39
|
+
import { concat } from '../crypto/internal.js';
|
|
39
40
|
import { initiate } from './noise.js';
|
|
40
41
|
/** Where a session is opened, relative to the node's base URL. */
|
|
41
42
|
export const HANDSHAKE_PATH = '/sealed/v2/handshake';
|
|
@@ -92,92 +93,8 @@ export async function transportKeyBinding(inner, transportKey) {
|
|
|
92
93
|
const digest = await crypto.subtle.digest('SHA-256', concat(utf8.encode(TRANSPORT_BINDING_DOMAIN), inner, transportKey));
|
|
93
94
|
return new Uint8Array(digest);
|
|
94
95
|
}
|
|
95
|
-
/**
|
|
96
|
-
* Ask a node for its transport key and accept it only if a quote you verified
|
|
97
|
-
* commits to it.
|
|
98
|
-
*
|
|
99
|
-
* `applicationHash` (hex), when given, is the bytecode hash you expect the
|
|
100
|
-
* attested application to have; the node is asked to bind that application,
|
|
101
|
-
* and the quote only verifies if it runs that exact bytecode.
|
|
102
|
-
*/
|
|
103
|
-
export async function fetchAttestedTransportKey(admin, verify, options = {}) {
|
|
104
|
-
if ((options.applicationId === undefined) !== (options.applicationHash === undefined)) {
|
|
105
|
-
throw new Error('applicationId and applicationHash are given together or not at all');
|
|
106
|
-
}
|
|
107
|
-
const nonce = hex(crypto.getRandomValues(new Uint8Array(32)));
|
|
108
|
-
const attested = await admin.teeAttest({
|
|
109
|
-
nonce,
|
|
110
|
-
applicationId: options.applicationId,
|
|
111
|
-
bindTransportKey: true,
|
|
112
|
-
...(verify.includeCollateral ? { includeCollateral: true } : {}),
|
|
113
|
-
});
|
|
114
|
-
if (!attested.transportPublicKey) {
|
|
115
|
-
throw new Error('The node did not report a transport key; it predates sealed transport');
|
|
116
|
-
}
|
|
117
|
-
const transportKey = fromHex(attested.transportPublicKey, 'transportPublicKey', 32);
|
|
118
|
-
const inner = options.applicationHash
|
|
119
|
-
? fromHex(options.applicationHash, 'applicationHash', 32)
|
|
120
|
-
: new Uint8Array(32);
|
|
121
|
-
const reportDataSuffix = hex(await transportKeyBinding(inner, transportKey));
|
|
122
|
-
const verified = await verify({
|
|
123
|
-
quoteB64: attested.quoteB64,
|
|
124
|
-
nonce,
|
|
125
|
-
reportDataSuffix,
|
|
126
|
-
collateral: attested.collateral,
|
|
127
|
-
});
|
|
128
|
-
if (!verified) {
|
|
129
|
-
throw new Error('The attestation did not verify, so its transport key is not trusted');
|
|
130
|
-
}
|
|
131
|
-
return transportKey;
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* {@link createSealedFetch} for a node you know only by URL: it attests the
|
|
135
|
-
* node's transport key with `verify`, over `POST /admin-api/tee/attest` (the
|
|
136
|
-
* one call that needs no credential and is never sealed), and seals everything
|
|
137
|
-
* else to it. A restarted node is attested again, through the same verifier.
|
|
138
|
-
*
|
|
139
|
-
* This is what to hand a {@link RelayClient} for a TEE relay, so the warrant
|
|
140
|
-
* and the method's arguments reach only the attested TD:
|
|
141
|
-
*
|
|
142
|
-
* ```ts
|
|
143
|
-
* const relay = new RelayClient({
|
|
144
|
-
* relayUrl,
|
|
145
|
-
* fetch: createAttestedSealedFetch({ baseUrl: relayUrl, verify: createQuoteVerifier(...) }),
|
|
146
|
-
* ...
|
|
147
|
-
* });
|
|
148
|
-
* ```
|
|
149
|
-
*/
|
|
150
|
-
export function createAttestedSealedFetch(options) {
|
|
151
|
-
const baseFetch = options.fetch ?? ((input, init) => fetch(input, init));
|
|
152
|
-
const attestUrl = `${options.baseUrl.replace(/\/+$/, '')}/admin-api/tee/attest`;
|
|
153
|
-
const admin = {
|
|
154
|
-
async teeAttest(request) {
|
|
155
|
-
const response = await baseFetch(attestUrl, {
|
|
156
|
-
method: 'POST',
|
|
157
|
-
headers: { 'content-type': 'application/json' },
|
|
158
|
-
body: JSON.stringify(request),
|
|
159
|
-
});
|
|
160
|
-
if (!response.ok) {
|
|
161
|
-
throw new Error(`The node refused to attest: HTTP ${response.status}`);
|
|
162
|
-
}
|
|
163
|
-
const body = (await response.json());
|
|
164
|
-
if (!body.data)
|
|
165
|
-
throw new Error('The attestation response is malformed');
|
|
166
|
-
return body.data;
|
|
167
|
-
},
|
|
168
|
-
};
|
|
169
|
-
return createSealedFetch({
|
|
170
|
-
baseUrl: options.baseUrl,
|
|
171
|
-
transportPublicKey: () => fetchAttestedTransportKey(admin, options.verify, {
|
|
172
|
-
applicationId: options.applicationId,
|
|
173
|
-
applicationHash: options.applicationHash,
|
|
174
|
-
}),
|
|
175
|
-
fetch: baseFetch,
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
96
|
/**
|
|
179
97
|
* A `fetch` that seals every request to the node's attested transport key.
|
|
180
|
-
* Pass it as `MeroJsConfig.fetch`.
|
|
181
98
|
*
|
|
182
99
|
* A request for any URL outside `baseUrl` is refused rather than sent in the
|
|
183
100
|
* clear: falling back to plaintext would silently undo the point of sealing.
|
|
@@ -239,13 +156,18 @@ export function createSealedFetch(options) {
|
|
|
239
156
|
request.headers.forEach((value, name) => headers.push([name, value]));
|
|
240
157
|
const head = { method: request.method, path: url.pathname.slice(basePath.length) + url.search, headers };
|
|
241
158
|
const body = new Uint8Array(await request.arrayBuffer());
|
|
159
|
+
// The caller's own signal, not `request.signal`: a Request's signal follows
|
|
160
|
+
// the one it was built from only through a weak reference (undici), so once
|
|
161
|
+
// the Request is garbage collected (it is, while a stream stays open) an
|
|
162
|
+
// abort no longer reaches the fetch, and an event stream never closes.
|
|
163
|
+
const signal = init?.signal ?? (input instanceof Request ? input.signal : undefined);
|
|
242
164
|
const send = async (open) => {
|
|
243
165
|
const { requestId, header, envelope } = await sealRequest(open, head, body);
|
|
244
166
|
const response = await baseFetch(`${nodeUrl}${SEALED_PATH}`, {
|
|
245
167
|
method: 'POST',
|
|
246
168
|
headers: { 'content-type': SEALED_CONTENT_TYPE },
|
|
247
169
|
body: envelope,
|
|
248
|
-
signal
|
|
170
|
+
signal,
|
|
249
171
|
});
|
|
250
172
|
if (response.headers.get('content-type') !== SEALED_CONTENT_TYPE) {
|
|
251
173
|
throw await envelopeRefusal(response);
|
package/dist/sealed/verify.d.ts
CHANGED
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
* signatures against Intel's root, the TCB status of the platform, the
|
|
6
6
|
* measurements of the image, and that its report data is the nonce and the
|
|
7
7
|
* binding this client asked for. {@link createQuoteVerifier} does all of that
|
|
8
|
-
* here
|
|
9
|
-
*
|
|
10
|
-
* collateral, only choose which valid collateral to serve.
|
|
8
|
+
* here. A Calimero 1.0 node sends no collateral: the verifier fetches Intel's
|
|
9
|
+
* (`fetchCollateral`), which the node can neither forge nor choose.
|
|
11
10
|
*
|
|
12
11
|
* The DCAP verification itself — parsing the quote, checking its signature
|
|
13
12
|
* chain and appraising its TCB level against the collateral — comes from a
|
|
@@ -20,8 +19,7 @@ import type { VerifyTransportQuote } from './sealed.js';
|
|
|
20
19
|
/**
|
|
21
20
|
* The collateral a quote is verified against — TCB info, QE identity, CRLs and
|
|
22
21
|
* their chains — in dcap-qvl's form: byte fields are hex strings or byte
|
|
23
|
-
* arrays. What
|
|
24
|
-
* what `@phala/dcap-qvl`'s `getCollateral` fetches.
|
|
22
|
+
* arrays. What `@phala/dcap-qvl`'s `getCollateral` fetches.
|
|
25
23
|
*/
|
|
26
24
|
export interface DcapCollateral {
|
|
27
25
|
pck_crl_issuer_chain: string;
|
|
@@ -87,16 +85,15 @@ export interface QuoteVerifierOptions {
|
|
|
87
85
|
*/
|
|
88
86
|
allowedTcbStatuses?: string[];
|
|
89
87
|
/**
|
|
90
|
-
* Where to get collateral
|
|
91
|
-
* `includeCollateral`): for example
|
|
88
|
+
* Where to get collateral (a Calimero 1.0 node sends none): for example
|
|
92
89
|
* `(quote) => getCollateral(PHALA_PCCS_URL, quote)` from `@phala/dcap-qvl`.
|
|
93
|
-
* Without it,
|
|
90
|
+
* Without it, a quote that came without collateral is refused.
|
|
94
91
|
*/
|
|
95
92
|
fetchCollateral?: (quote: Uint8Array) => Promise<DcapCollateral>;
|
|
96
93
|
/** The current time in milliseconds; defaults to `Date.now`. For tests. */
|
|
97
94
|
now?: () => number;
|
|
98
95
|
}
|
|
99
|
-
/** A verifier
|
|
96
|
+
/** A quote verifier as this package builds one ({@link createQuoteVerifier}). */
|
|
100
97
|
export type QuoteVerifier = VerifyTransportQuote & {
|
|
101
98
|
readonly includeCollateral: true;
|
|
102
99
|
};
|
|
@@ -122,10 +119,7 @@ export type QuoteVerifier = VerifyTransportQuote & {
|
|
|
122
119
|
* dcapVerify,
|
|
123
120
|
* ...trustedMeasurementsFromReleases([publishedMrtds], { profile: 'locked-read-only' }),
|
|
124
121
|
* });
|
|
125
|
-
* const
|
|
126
|
-
* baseUrl,
|
|
127
|
-
* transportPublicKey: () => fetchAttestedTransportKey(admin, verifier),
|
|
128
|
-
* });
|
|
122
|
+
* const client = new CalimeroClient({ baseUrl, policy: { verifyQuote: verifier } });
|
|
129
123
|
* ```
|
|
130
124
|
*/
|
|
131
125
|
export declare function createQuoteVerifier(options: QuoteVerifierOptions): QuoteVerifier;
|