@tinycloud/sdk-core 3.0.0-beta.0 → 3.0.0-beta.10
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/{credential-admission-C_cO1QqB.d.cts → credential-admission-CFjUChF8.d.cts} +122 -2
- package/dist/{credential-admission-cvHe-HUC.d.ts → credential-admission-DIEPgQ0o.d.ts} +122 -2
- package/dist/delegations/index.cjs +80 -133
- package/dist/delegations/index.cjs.map +1 -1
- package/dist/delegations/index.d.cts +3 -3
- package/dist/delegations/index.d.ts +3 -3
- package/dist/delegations/index.js +81 -138
- package/dist/delegations/index.js.map +1 -1
- package/dist/index.cjs +994 -306
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +149 -7
- package/dist/index.d.ts +149 -7
- package/dist/index.js +996 -311
- package/dist/index.js.map +1 -1
- package/dist/policy/index.cjs +323 -8
- package/dist/policy/index.cjs.map +1 -1
- package/dist/policy/index.d.cts +2 -2
- package/dist/policy/index.d.ts +2 -2
- package/dist/policy/index.js +323 -8
- package/dist/policy/index.js.map +1 -1
- package/dist/{recipient-types.schema-G8NEKRoo.d.cts → recipient-types.schema-Cz5zJzED.d.cts} +50 -77
- package/dist/{recipient-types.schema-G8NEKRoo.d.ts → recipient-types.schema-Cz5zJzED.d.ts} +50 -77
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -64,6 +64,7 @@ __export(index_exports, {
|
|
|
64
64
|
CREDENTIAL_CONTRACT_VERSION: () => CREDENTIAL_CONTRACT_VERSION,
|
|
65
65
|
CREDENTIAL_ERROR_CODES: () => CREDENTIAL_ERROR_CODES,
|
|
66
66
|
CREDENTIAL_FORMAT: () => CREDENTIAL_FORMAT,
|
|
67
|
+
CREDENTIAL_INVITATION_REQUEST_DOMAIN: () => CREDENTIAL_INVITATION_REQUEST_DOMAIN,
|
|
67
68
|
CREDENTIAL_PROTOCOL: () => CREDENTIAL_ACQUISITION_PROTOCOL,
|
|
68
69
|
CREDENTIAL_STEP_REGISTRY_VERSION: () => CREDENTIAL_STEP_REGISTRY_VERSION,
|
|
69
70
|
CapabilityKeyRegistry: () => CapabilityKeyRegistry,
|
|
@@ -87,6 +88,7 @@ __export(index_exports, {
|
|
|
87
88
|
DEFAULT_SIGNED_READ_URL_EXPIRY_MS: () => DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
|
|
88
89
|
DEFAULT_TINYCLOUD_FALLBACK_HOST: () => DEFAULT_TINYCLOUD_FALLBACK_HOST,
|
|
89
90
|
DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL: () => DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL,
|
|
91
|
+
DELIVERY_ADMISSION_DOMAIN: () => DELIVERY_ADMISSION_DOMAIN,
|
|
90
92
|
DUCKDB: () => import_bootstrap4.DUCKDB,
|
|
91
93
|
DataVaultService: () => import_sdk_services8.DataVaultService,
|
|
92
94
|
DatabaseHandle: () => import_sdk_services8.DatabaseHandle,
|
|
@@ -98,6 +100,7 @@ __export(index_exports, {
|
|
|
98
100
|
DuckDbService: () => import_sdk_services8.DuckDbService,
|
|
99
101
|
ED25519_JCS_SIGNATURE_SUITE: () => ED25519_JCS_SIGNATURE_SUITE,
|
|
100
102
|
EIP191_JCS_SIGNATURE_SUITE: () => EIP191_JCS_SIGNATURE_SUITE,
|
|
103
|
+
EMAIL_CREDENTIAL_MAX_AGE_SECONDS: () => EMAIL_CREDENTIAL_MAX_AGE_SECONDS,
|
|
101
104
|
ENCRYPTION: () => import_bootstrap4.ENCRYPTION,
|
|
102
105
|
ENCRYPTION_MANIFEST_SPACE: () => ENCRYPTION_MANIFEST_SPACE,
|
|
103
106
|
ENCRYPTION_NETWORK_URN_PREFIX: () => import_sdk_services8.ENCRYPTION_NETWORK_URN_PREFIX,
|
|
@@ -136,6 +139,8 @@ __export(index_exports, {
|
|
|
136
139
|
NETWORK_NAME_PATTERN: () => import_sdk_services8.NETWORK_NAME_PATTERN,
|
|
137
140
|
NetworkIdError: () => import_sdk_services8.NetworkIdError,
|
|
138
141
|
OPENKEY_ACTION_ID_SEPARATOR: () => OPENKEY_ACTION_ID_SEPARATOR,
|
|
142
|
+
OWNER_NODE_BINDING_TIMEOUT_MS: () => OWNER_NODE_BINDING_TIMEOUT_MS,
|
|
143
|
+
OpenKeyManageKeyError: () => OpenKeyManageKeyError,
|
|
139
144
|
POLICY_CAPABILITY_V1_DOMAIN: () => POLICY_CAPABILITY_V1_DOMAIN,
|
|
140
145
|
POLICY_CAPABILITY_V1_SCHEMA: () => POLICY_CAPABILITY_V1_SCHEMA,
|
|
141
146
|
POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE: () => POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE,
|
|
@@ -148,6 +153,8 @@ __export(index_exports, {
|
|
|
148
153
|
POLICY_ENGINE_RESOLVE_REQUEST_SCHEMA: () => POLICY_ENGINE_RESOLVE_REQUEST_SCHEMA,
|
|
149
154
|
POLICY_PRESENTATION_V3_DOMAIN: () => POLICY_PRESENTATION_V3_DOMAIN,
|
|
150
155
|
POLICY_PRESENTATION_V3_SCHEMA: () => POLICY_PRESENTATION_V3_SCHEMA,
|
|
156
|
+
POLICY_PRESENTATION_V4_DOMAIN: () => POLICY_PRESENTATION_V4_DOMAIN,
|
|
157
|
+
POLICY_PRESENTATION_V4_SCHEMA: () => POLICY_PRESENTATION_V4_SCHEMA,
|
|
151
158
|
POLICY_SCHEMA: () => POLICY_SCHEMA,
|
|
152
159
|
POLICY_SESSION_UCAN_V1_PROFILE: () => POLICY_SESSION_UCAN_V1_PROFILE,
|
|
153
160
|
POLICY_STATUS_SCHEMA: () => POLICY_STATUS_SCHEMA,
|
|
@@ -172,7 +179,6 @@ __export(index_exports, {
|
|
|
172
179
|
SECRET_RECORDS_SCHEMA: () => import_bootstrap4.SECRET_RECORDS_SCHEMA,
|
|
173
180
|
SERVICE_LONG_TO_SHORT: () => SERVICE_LONG_TO_SHORT,
|
|
174
181
|
SERVICE_SHORT_TO_LONG: () => SERVICE_SHORT_TO_LONG,
|
|
175
|
-
SHARE_DELIVERY_AUTHORIZATION_DOMAIN: () => SHARE_DELIVERY_AUTHORIZATION_DOMAIN,
|
|
176
182
|
SPACE: () => import_bootstrap4.SPACE,
|
|
177
183
|
SQL: () => import_bootstrap4.SQL,
|
|
178
184
|
SQLAction: () => import_sdk_services8.SQLAction,
|
|
@@ -206,7 +212,9 @@ __export(index_exports, {
|
|
|
206
212
|
SpaceService: () => SpaceService,
|
|
207
213
|
TINYCLOUD_ACCOUNT_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_ACCOUNT_SPACE_MANIFEST,
|
|
208
214
|
TINYCLOUD_APPLICATIONS_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_APPLICATIONS_SPACE_MANIFEST,
|
|
215
|
+
TINYCLOUD_CANONICAL_IDENTITY_CLAIM: () => TINYCLOUD_CANONICAL_IDENTITY_CLAIM,
|
|
209
216
|
TINYCLOUD_DEFAULT_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_DEFAULT_SPACE_MANIFEST,
|
|
217
|
+
TINYCLOUD_MANAGE_KEY_SCOPE: () => TINYCLOUD_MANAGE_KEY_SCOPE,
|
|
210
218
|
TINYCLOUD_PUBLIC_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_PUBLIC_SPACE_MANIFEST,
|
|
211
219
|
TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST: () => import_bootstrap4.TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST,
|
|
212
220
|
TinyCloud: () => TinyCloud,
|
|
@@ -223,6 +231,7 @@ __export(index_exports, {
|
|
|
223
231
|
activateSessionWithHost: () => activateSessionWithHost,
|
|
224
232
|
addressStorageKey: () => addressStorageKey,
|
|
225
233
|
admitPolicyCredentialV3: () => admitPolicyCredentialV3,
|
|
234
|
+
admitPolicyCredentialV4: () => admitPolicyCredentialV4,
|
|
226
235
|
applyPrefix: () => applyPrefix,
|
|
227
236
|
bootstrapEncryptionNetworkId: () => import_bootstrap4.bootstrapEncryptionNetworkId,
|
|
228
237
|
bootstrapSpaceId: () => import_bootstrap4.bootstrapSpaceId,
|
|
@@ -233,6 +242,7 @@ __export(index_exports, {
|
|
|
233
242
|
buildDecryptInvocation: () => import_sdk_services8.buildDecryptInvocation,
|
|
234
243
|
buildNetworkId: () => import_sdk_services8.buildNetworkId,
|
|
235
244
|
buildPolicyCredentialPresentationV3: () => buildPolicyCredentialPresentationV3,
|
|
245
|
+
buildPolicyCredentialPresentationV4: () => buildPolicyCredentialPresentationV4,
|
|
236
246
|
buildSpaceUri: () => buildSpaceUri,
|
|
237
247
|
canonicalDigest: () => canonicalDigest,
|
|
238
248
|
canonicalHashHex: () => import_sdk_services8.canonicalHashHex,
|
|
@@ -263,10 +273,12 @@ __export(index_exports, {
|
|
|
263
273
|
createAndSignSignedObject: () => createAndSignSignedObject,
|
|
264
274
|
createCapabilityKeyRegistry: () => createCapabilityKeyRegistry,
|
|
265
275
|
createDelegatedShareKey: () => createDelegatedShareKey,
|
|
276
|
+
createEmailCredentialRequirement: () => createEmailCredentialRequirement,
|
|
266
277
|
createHolderBinding: () => createHolderBinding,
|
|
267
278
|
createInMemoryLocalNodeIdentityStore: () => createInMemoryLocalNodeIdentityStore,
|
|
268
279
|
createLocalStorageLocalNodeIdentityStore: () => createLocalStorageLocalNodeIdentityStore,
|
|
269
280
|
createOpenKeyCallbackSigningStrategy: () => createOpenKeyCallbackSigningStrategy,
|
|
281
|
+
createOpenKeyManageKeySigningStrategy: () => createOpenKeyManageKeySigningStrategy,
|
|
270
282
|
createPolicyEnforcementDelegation: () => createPolicyEnforcementDelegation,
|
|
271
283
|
createShareArtifact: () => createShareArtifact,
|
|
272
284
|
createShareArtifactAsync: () => createShareArtifactAsync,
|
|
@@ -325,6 +337,7 @@ __export(index_exports, {
|
|
|
325
337
|
generateRandomReceiverKey: () => import_sdk_services8.generateRandomReceiverKey,
|
|
326
338
|
getPolicyRootStatusV3: () => getPolicyRootStatusV3,
|
|
327
339
|
getTinyCloudDebugLogs: () => import_sdk_services8.getTinyCloudDebugLogs,
|
|
340
|
+
hasTinyCloudManageKeyScope: () => hasTinyCloudManageKeyScope,
|
|
328
341
|
hexDecode: () => import_sdk_services8.hexDecode,
|
|
329
342
|
hexEncode: () => import_sdk_services8.hexEncode,
|
|
330
343
|
holderBindingCanonicalBytes: () => holderBindingCanonicalBytes,
|
|
@@ -339,6 +352,7 @@ __export(index_exports, {
|
|
|
339
352
|
isNetworkId: () => import_sdk_services8.isNetworkId,
|
|
340
353
|
isPlausibleOpenKeyActionId: () => isPlausibleOpenKeyActionId,
|
|
341
354
|
isV2ShareLink: () => isV2ShareLink,
|
|
355
|
+
issuedCredentialEnvelopeFromVerified: () => issuedCredentialEnvelopeFromVerified,
|
|
342
356
|
jcsCanonicalize: () => jcsCanonicalize,
|
|
343
357
|
loadManifest: () => loadManifest,
|
|
344
358
|
locationPayloadForRecord: () => locationPayloadForRecord,
|
|
@@ -357,6 +371,8 @@ __export(index_exports, {
|
|
|
357
371
|
openWrappedKey: () => import_sdk_services8.openWrappedKey,
|
|
358
372
|
parseCanonicalNetworkId: () => parseCanonicalNetworkId,
|
|
359
373
|
parseCanonicalRecapResource: () => parseCanonicalRecapResource,
|
|
374
|
+
parseCanonicalTinyCloudIdentity: () => parseCanonicalTinyCloudIdentity,
|
|
375
|
+
parseCanonicalTinyCloudIdentityClaims: () => parseCanonicalTinyCloudIdentityClaims,
|
|
360
376
|
parseCompactUcanAuthorization: () => parseCompactUcanAuthorization,
|
|
361
377
|
parseExpiry: () => parseExpiry,
|
|
362
378
|
parseNetworkId: () => import_sdk_services8.parseNetworkId,
|
|
@@ -365,6 +381,7 @@ __export(index_exports, {
|
|
|
365
381
|
parsePolicySessionUcan: () => parsePolicySessionUcan,
|
|
366
382
|
parsePortableDelegation: () => parsePortableDelegation,
|
|
367
383
|
parseRecapCapabilities: () => parseRecapCapabilities,
|
|
384
|
+
parseSecretCatalogKey: () => import_sdk_services8.parseSecretCatalogKey,
|
|
368
385
|
parseShareArtifact: () => parseShareArtifact,
|
|
369
386
|
parseShareUrl: () => parseShareUrl,
|
|
370
387
|
parseSpaceUri: () => parseSpaceUri,
|
|
@@ -377,8 +394,11 @@ __export(index_exports, {
|
|
|
377
394
|
principalDidEquals: () => principalDidEquals,
|
|
378
395
|
projectUnifiedPolicyCapabilities: () => projectUnifiedPolicyCapabilities,
|
|
379
396
|
projectUnifiedPolicyCapability: () => projectUnifiedPolicyCapability,
|
|
397
|
+
publishLocationRecord: () => publishLocationRecord,
|
|
380
398
|
recapCaveatsEqual: () => recapCaveatsEqual,
|
|
399
|
+
registerPolicyV3: () => registerPolicyV3,
|
|
381
400
|
renewPolicyRootStatusV3: () => renewPolicyRootStatusV3,
|
|
401
|
+
requestTinyCloudManageKeyScope: () => requestTinyCloudManageKeyScope,
|
|
382
402
|
resolveCloudLocation: () => resolveCloudLocation,
|
|
383
403
|
resolveManifest: () => resolveManifest,
|
|
384
404
|
resolveManifestKnowledgeRoot: () => resolveManifestKnowledgeRoot,
|
|
@@ -394,10 +414,10 @@ __export(index_exports, {
|
|
|
394
414
|
serviceError: () => import_sdk_services8.serviceError,
|
|
395
415
|
sha256Base64Url: () => sha256Base64Url,
|
|
396
416
|
shareCapabilityAllows: () => shareCapabilityAllows,
|
|
397
|
-
shareDeliveryTrustedKid: () => shareDeliveryTrustedKid,
|
|
398
417
|
signCompactUcanAuthorization: () => signCompactUcanAuthorization,
|
|
399
418
|
signCompactUcanRootAuthorization: () => signCompactUcanRootAuthorization,
|
|
400
419
|
signLocationRecord: () => signLocationRecord,
|
|
420
|
+
signPolicyCredentialPresentationV4: () => signPolicyCredentialPresentationV4,
|
|
401
421
|
signShareEnvelope: () => signShareEnvelope,
|
|
402
422
|
signedObjectIdFor: () => signedObjectIdFor,
|
|
403
423
|
submitHostDelegation: () => submitHostDelegation,
|
|
@@ -430,13 +450,14 @@ __export(index_exports, {
|
|
|
430
450
|
validatePolicyStatusSignedShape: () => validatePolicyStatusSignedShape,
|
|
431
451
|
validatePolicyStatusUnsigned: () => validatePolicyStatusUnsigned,
|
|
432
452
|
validatePolicyUnsigned: () => validatePolicyUnsigned,
|
|
433
|
-
|
|
453
|
+
validateShareDeliveryAuthorizationV3Bytes: () => validateShareDeliveryAuthorizationV3Bytes,
|
|
434
454
|
validateUnifiedPolicyV2: () => validateUnifiedPolicyV2,
|
|
435
455
|
verifyDecryptResponse: () => import_sdk_services8.verifyDecryptResponse,
|
|
436
456
|
verifyDidKeyEd25519Signature: () => verifyDidKeyEd25519Signature,
|
|
437
457
|
verifyEip191MessageSignature: () => verifyEip191MessageSignature,
|
|
438
458
|
verifyIssuedCredential: () => verifyIssuedCredential,
|
|
439
459
|
verifyLocationRecord: () => verifyLocationRecord,
|
|
460
|
+
verifyOwnerNodeBinding: () => verifyOwnerNodeBinding,
|
|
440
461
|
verifyPolicy: () => verifyPolicy,
|
|
441
462
|
verifyPolicyEngineRecord: () => verifyPolicyEngineRecord,
|
|
442
463
|
verifyPolicyRootRevocationV3: () => verifyPolicyRootRevocationV3,
|
|
@@ -1001,6 +1022,17 @@ function isDigest(value) {
|
|
|
1001
1022
|
|
|
1002
1023
|
// src/credentials/requirement.ts
|
|
1003
1024
|
var ID2 = /^[a-z0-9][a-z0-9._-]{0,127}(?:\/v1)?$/;
|
|
1025
|
+
var EMAIL_CREDENTIAL_MAX_AGE_SECONDS = 3600;
|
|
1026
|
+
function createEmailCredentialRequirement(input) {
|
|
1027
|
+
return validateCredentialRequirement({
|
|
1028
|
+
type: "TinyCloudCredentialRequirement",
|
|
1029
|
+
version: 1,
|
|
1030
|
+
profile: input.profile,
|
|
1031
|
+
credentialType: input.credentialType,
|
|
1032
|
+
claims: { email: input.email },
|
|
1033
|
+
maxAgeSeconds: EMAIL_CREDENTIAL_MAX_AGE_SECONDS
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1004
1036
|
function validateCredentialRequirement(value) {
|
|
1005
1037
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new CredentialError("UNSUPPORTED_PROFILE", "Credential requirement is invalid");
|
|
1006
1038
|
const raw = value;
|
|
@@ -2215,16 +2247,16 @@ function policyCapabilityContains(authority, request) {
|
|
|
2215
2247
|
return caveatsContain(auth.caveats, req.caveats);
|
|
2216
2248
|
}
|
|
2217
2249
|
function normalizePolicyCapabilityWithOptions(input, options2) {
|
|
2218
|
-
const
|
|
2219
|
-
assertExactKeys(
|
|
2220
|
-
const service = requiredString(
|
|
2250
|
+
const object4 = expectObject(input, "$");
|
|
2251
|
+
assertExactKeys(object4, ["service", "space", "path", "actions", "caveats"], "$");
|
|
2252
|
+
const service = requiredString(object4, "service", "$", "policy-capability-malformed-service");
|
|
2221
2253
|
if (!CEILING_SERVICES.has(service)) {
|
|
2222
2254
|
throw new PolicyCapabilityError(
|
|
2223
2255
|
"policy-capability-malformed-service",
|
|
2224
2256
|
"$.service is outside the frozen permissionsCeiling vocabulary"
|
|
2225
2257
|
);
|
|
2226
2258
|
}
|
|
2227
|
-
const space = requiredString(
|
|
2259
|
+
const space = requiredString(object4, "space", "$", "policy-capability-malformed-space");
|
|
2228
2260
|
validateConcreteSpace(space);
|
|
2229
2261
|
const canonicalSpace = space.normalize("NFC");
|
|
2230
2262
|
if (options2.requireCanonical && canonicalSpace !== space) {
|
|
@@ -2233,7 +2265,7 @@ function normalizePolicyCapabilityWithOptions(input, options2) {
|
|
|
2233
2265
|
"$.space must already be canonical NFC"
|
|
2234
2266
|
);
|
|
2235
2267
|
}
|
|
2236
|
-
const rawPath = requiredString(
|
|
2268
|
+
const rawPath = requiredString(object4, "path", "$", "policy-capability-malformed-path");
|
|
2237
2269
|
validateRawPath(rawPath, options2);
|
|
2238
2270
|
const path = normalizePath(rawPath);
|
|
2239
2271
|
if (options2.requireCanonical && path !== rawPath) {
|
|
@@ -2244,7 +2276,7 @@ function normalizePolicyCapabilityWithOptions(input, options2) {
|
|
|
2244
2276
|
}
|
|
2245
2277
|
validateRawPath(path, options2);
|
|
2246
2278
|
const actions = normalizeActions(
|
|
2247
|
-
requiredArray(
|
|
2279
|
+
requiredArray(object4, "actions", "$", "policy-capability-malformed-action"),
|
|
2248
2280
|
service,
|
|
2249
2281
|
options2
|
|
2250
2282
|
);
|
|
@@ -2253,9 +2285,9 @@ function normalizePolicyCapabilityWithOptions(input, options2) {
|
|
|
2253
2285
|
output.space = canonicalSpace;
|
|
2254
2286
|
output.path = path;
|
|
2255
2287
|
output.actions = actions;
|
|
2256
|
-
if (hasOwn(
|
|
2288
|
+
if (hasOwn(object4, "caveats")) {
|
|
2257
2289
|
output.caveats = validateCaveats(
|
|
2258
|
-
requiredValue(
|
|
2290
|
+
requiredValue(object4, "caveats", "$", "policy-capability-malformed-caveats"),
|
|
2259
2291
|
output.service
|
|
2260
2292
|
);
|
|
2261
2293
|
}
|
|
@@ -2278,9 +2310,9 @@ function expectObject(input, path) {
|
|
|
2278
2310
|
);
|
|
2279
2311
|
}
|
|
2280
2312
|
}
|
|
2281
|
-
function assertExactKeys(
|
|
2313
|
+
function assertExactKeys(object4, allowed, path) {
|
|
2282
2314
|
const allowedSet = new Set(allowed);
|
|
2283
|
-
for (const key of Object.keys(
|
|
2315
|
+
for (const key of Object.keys(object4)) {
|
|
2284
2316
|
if (!allowedSet.has(key)) {
|
|
2285
2317
|
throw new PolicyCapabilityError(
|
|
2286
2318
|
key === "id" || key === "scope" ? "policy-capability-malformed" : "policy-capability-unknown-key",
|
|
@@ -2289,21 +2321,21 @@ function assertExactKeys(object3, allowed, path) {
|
|
|
2289
2321
|
}
|
|
2290
2322
|
}
|
|
2291
2323
|
}
|
|
2292
|
-
function requiredValue(
|
|
2293
|
-
if (!hasOwn(
|
|
2324
|
+
function requiredValue(object4, key, path, code3) {
|
|
2325
|
+
if (!hasOwn(object4, key)) {
|
|
2294
2326
|
throw new PolicyCapabilityError(code3, `${path}.${key} is required`);
|
|
2295
2327
|
}
|
|
2296
|
-
return
|
|
2328
|
+
return object4[key];
|
|
2297
2329
|
}
|
|
2298
|
-
function requiredString(
|
|
2299
|
-
const value = requiredValue(
|
|
2330
|
+
function requiredString(object4, key, path, code3) {
|
|
2331
|
+
const value = requiredValue(object4, key, path, code3);
|
|
2300
2332
|
if (typeof value !== "string" || value.length === 0) {
|
|
2301
2333
|
throw new PolicyCapabilityError(code3, `${path}.${key} must be a non-empty string`);
|
|
2302
2334
|
}
|
|
2303
2335
|
return value;
|
|
2304
2336
|
}
|
|
2305
|
-
function requiredArray(
|
|
2306
|
-
const value = requiredValue(
|
|
2337
|
+
function requiredArray(object4, key, path, code3) {
|
|
2338
|
+
const value = requiredValue(object4, key, path, code3);
|
|
2307
2339
|
if (!Array.isArray(value)) {
|
|
2308
2340
|
throw new PolicyCapabilityError(code3, `${path}.${key} must be an array`);
|
|
2309
2341
|
}
|
|
@@ -2448,11 +2480,11 @@ function validateSqlStatement(input, path) {
|
|
|
2448
2480
|
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
2449
2481
|
throw new PolicyCapabilityError("policy-capability-malformed-caveats", `${path} must be an object`);
|
|
2450
2482
|
}
|
|
2451
|
-
const
|
|
2452
|
-
assertCaveatKeys(
|
|
2453
|
-
caveatString(
|
|
2454
|
-
caveatString(
|
|
2455
|
-
const fixedParams = requiredCaveat(
|
|
2483
|
+
const object4 = input;
|
|
2484
|
+
assertCaveatKeys(object4, ["name", "sql", "fixedParams"], path);
|
|
2485
|
+
caveatString(object4, "name", path);
|
|
2486
|
+
caveatString(object4, "sql", path);
|
|
2487
|
+
const fixedParams = requiredCaveat(object4, "fixedParams", path);
|
|
2456
2488
|
if (!Array.isArray(fixedParams)) {
|
|
2457
2489
|
throw new PolicyCapabilityError(
|
|
2458
2490
|
"policy-capability-malformed-caveats",
|
|
@@ -2467,20 +2499,20 @@ function validateFixedParam(input, path) {
|
|
|
2467
2499
|
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
2468
2500
|
throw new PolicyCapabilityError("policy-capability-malformed-caveats", `${path} must be an object`);
|
|
2469
2501
|
}
|
|
2470
|
-
const
|
|
2471
|
-
assertCaveatKeys(
|
|
2472
|
-
const index = requiredCaveat(
|
|
2502
|
+
const object4 = input;
|
|
2503
|
+
assertCaveatKeys(object4, ["index", "value"], path);
|
|
2504
|
+
const index = requiredCaveat(object4, "index", path);
|
|
2473
2505
|
if (typeof index !== "number" || !Number.isSafeInteger(index) || index < 0) {
|
|
2474
2506
|
throw new PolicyCapabilityError(
|
|
2475
2507
|
"policy-capability-malformed-caveats",
|
|
2476
2508
|
`${path}.index must be a non-negative integer`
|
|
2477
2509
|
);
|
|
2478
2510
|
}
|
|
2479
|
-
requiredCaveat(
|
|
2511
|
+
requiredCaveat(object4, "value", path);
|
|
2480
2512
|
}
|
|
2481
|
-
function assertCaveatKeys(
|
|
2513
|
+
function assertCaveatKeys(object4, allowed, path) {
|
|
2482
2514
|
const allowedSet = new Set(allowed);
|
|
2483
|
-
for (const key of Object.keys(
|
|
2515
|
+
for (const key of Object.keys(object4)) {
|
|
2484
2516
|
if (!allowedSet.has(key)) {
|
|
2485
2517
|
throw new PolicyCapabilityError(
|
|
2486
2518
|
"policy-capability-malformed-caveats",
|
|
@@ -2489,17 +2521,17 @@ function assertCaveatKeys(object3, allowed, path) {
|
|
|
2489
2521
|
}
|
|
2490
2522
|
}
|
|
2491
2523
|
}
|
|
2492
|
-
function requiredCaveat(
|
|
2493
|
-
if (!hasOwn(
|
|
2524
|
+
function requiredCaveat(object4, key, path) {
|
|
2525
|
+
if (!hasOwn(object4, key)) {
|
|
2494
2526
|
throw new PolicyCapabilityError(
|
|
2495
2527
|
"policy-capability-malformed-caveats",
|
|
2496
2528
|
`${path}.${key} is required`
|
|
2497
2529
|
);
|
|
2498
2530
|
}
|
|
2499
|
-
return
|
|
2531
|
+
return object4[key];
|
|
2500
2532
|
}
|
|
2501
|
-
function caveatString(
|
|
2502
|
-
const value = requiredCaveat(
|
|
2533
|
+
function caveatString(object4, key, path) {
|
|
2534
|
+
const value = requiredCaveat(object4, key, path);
|
|
2503
2535
|
if (typeof value !== "string" || value.length === 0) {
|
|
2504
2536
|
throw new PolicyCapabilityError(
|
|
2505
2537
|
"policy-capability-malformed-caveats",
|
|
@@ -2530,8 +2562,8 @@ function caveatsContain(authority, request) {
|
|
|
2530
2562
|
(statement) => authorityStatements.some((candidate) => jcsCanonicalize(candidate) === jcsCanonicalize(statement))
|
|
2531
2563
|
);
|
|
2532
2564
|
}
|
|
2533
|
-
function hasOwn(
|
|
2534
|
-
return objectHasOwn2(
|
|
2565
|
+
function hasOwn(object4, key) {
|
|
2566
|
+
return objectHasOwn2(object4, key);
|
|
2535
2567
|
}
|
|
2536
2568
|
|
|
2537
2569
|
// src/policy/signed-object.ts
|
|
@@ -2859,17 +2891,17 @@ async function createAndSignSignedObject(input, signer) {
|
|
|
2859
2891
|
}
|
|
2860
2892
|
function createAndSignPolicy(input, signer) {
|
|
2861
2893
|
return createAndSignSignedObject(input, signer).then(
|
|
2862
|
-
(
|
|
2894
|
+
(object4) => validatePolicySignedShape(object4)
|
|
2863
2895
|
);
|
|
2864
2896
|
}
|
|
2865
2897
|
function createAndSignPolicyStatus(input, signer) {
|
|
2866
2898
|
return createAndSignSignedObject(input, signer).then(
|
|
2867
|
-
(
|
|
2899
|
+
(object4) => validatePolicyStatusSignedShape(object4)
|
|
2868
2900
|
);
|
|
2869
2901
|
}
|
|
2870
2902
|
function createAndSignPolicyEngineRecord(input, signer) {
|
|
2871
2903
|
return createAndSignSignedObject(input, signer).then(
|
|
2872
|
-
(
|
|
2904
|
+
(object4) => validatePolicyEngineRecordSignedShape(object4)
|
|
2873
2905
|
);
|
|
2874
2906
|
}
|
|
2875
2907
|
async function verifySignedObject(input) {
|
|
@@ -2973,9 +3005,9 @@ function validateUnsignedForDescriptor(input, descriptor) {
|
|
|
2973
3005
|
}
|
|
2974
3006
|
}
|
|
2975
3007
|
function validatePolicyShape(input, signed) {
|
|
2976
|
-
const
|
|
3008
|
+
const object4 = expectJsonObject(normalizeJson(input), "$");
|
|
2977
3009
|
assertExactKeys2(
|
|
2978
|
-
|
|
3010
|
+
object4,
|
|
2979
3011
|
signed ? [
|
|
2980
3012
|
"schema",
|
|
2981
3013
|
"policyId",
|
|
@@ -3003,30 +3035,30 @@ function validatePolicyShape(input, signed) {
|
|
|
3003
3035
|
],
|
|
3004
3036
|
"$"
|
|
3005
3037
|
);
|
|
3006
|
-
expectConst(requiredString2(
|
|
3038
|
+
expectConst(requiredString2(object4, "schema", "$"), POLICY_SCHEMA, "$.schema");
|
|
3007
3039
|
if (signed) {
|
|
3008
|
-
requiredString2(
|
|
3009
|
-
validateSignature(requiredValue2(
|
|
3040
|
+
requiredString2(object4, "policyId", "$");
|
|
3041
|
+
validateSignature(requiredValue2(object4, "signature", "$"));
|
|
3010
3042
|
}
|
|
3011
|
-
requiredString2(
|
|
3012
|
-
requiredString2(
|
|
3013
|
-
requiredDateString(
|
|
3014
|
-
optionalDateString(
|
|
3015
|
-
validatePolicyResource(requiredValue2(
|
|
3016
|
-
validateExpression(requiredValue2(
|
|
3017
|
-
validateGrant(requiredValue2(
|
|
3018
|
-
if (hasOwn2(
|
|
3019
|
-
validateDisclosure(requiredValue2(
|
|
3043
|
+
requiredString2(object4, "ownerDid", "$");
|
|
3044
|
+
requiredString2(object4, "signingKeyDid", "$");
|
|
3045
|
+
requiredDateString(object4, "createdAt", "$");
|
|
3046
|
+
optionalDateString(object4, "expiresAt", "$");
|
|
3047
|
+
validatePolicyResource(requiredValue2(object4, "resource", "$"), "$.resource");
|
|
3048
|
+
validateExpression(requiredValue2(object4, "when", "$"), "$.when");
|
|
3049
|
+
validateGrant(requiredValue2(object4, "grant", "$"), "$.grant");
|
|
3050
|
+
if (hasOwn2(object4, "disclosure")) {
|
|
3051
|
+
validateDisclosure(requiredValue2(object4, "disclosure", "$"), "$.disclosure");
|
|
3020
3052
|
}
|
|
3021
|
-
if (hasOwn2(
|
|
3022
|
-
validateAudit(requiredValue2(
|
|
3053
|
+
if (hasOwn2(object4, "audit")) {
|
|
3054
|
+
validateAudit(requiredValue2(object4, "audit", "$"), "$.audit");
|
|
3023
3055
|
}
|
|
3024
|
-
return
|
|
3056
|
+
return object4;
|
|
3025
3057
|
}
|
|
3026
3058
|
function validatePolicyStatusShape(input, signed) {
|
|
3027
|
-
const
|
|
3059
|
+
const object4 = expectJsonObject(normalizeJson(input), "$");
|
|
3028
3060
|
assertExactKeys2(
|
|
3029
|
-
|
|
3061
|
+
object4,
|
|
3030
3062
|
signed ? [
|
|
3031
3063
|
"schema",
|
|
3032
3064
|
"statusId",
|
|
@@ -3050,28 +3082,28 @@ function validatePolicyStatusShape(input, signed) {
|
|
|
3050
3082
|
],
|
|
3051
3083
|
"$"
|
|
3052
3084
|
);
|
|
3053
|
-
expectConst(requiredString2(
|
|
3085
|
+
expectConst(requiredString2(object4, "schema", "$"), POLICY_STATUS_SCHEMA, "$.schema");
|
|
3054
3086
|
if (signed) {
|
|
3055
|
-
requiredString2(
|
|
3056
|
-
validateSignature(requiredValue2(
|
|
3087
|
+
requiredString2(object4, "statusId", "$");
|
|
3088
|
+
validateSignature(requiredValue2(object4, "signature", "$"));
|
|
3057
3089
|
}
|
|
3058
|
-
requiredString2(
|
|
3059
|
-
requiredString2(
|
|
3060
|
-
requiredInteger(
|
|
3061
|
-
expectOneOf(requiredString2(
|
|
3090
|
+
requiredString2(object4, "policyId", "$");
|
|
3091
|
+
requiredString2(object4, "ownerDid", "$");
|
|
3092
|
+
requiredInteger(object4, "sequence", "$", 0);
|
|
3093
|
+
expectOneOf(requiredString2(object4, "disposition", "$"), [
|
|
3062
3094
|
"active",
|
|
3063
3095
|
"suspended",
|
|
3064
3096
|
"revoked"
|
|
3065
3097
|
], "$.disposition");
|
|
3066
|
-
requiredDateString(
|
|
3067
|
-
optionalString(
|
|
3068
|
-
requiredString2(
|
|
3069
|
-
return
|
|
3098
|
+
requiredDateString(object4, "effectiveAt", "$");
|
|
3099
|
+
optionalString(object4, "reasonCode", "$");
|
|
3100
|
+
requiredString2(object4, "signingKeyDid", "$");
|
|
3101
|
+
return object4;
|
|
3070
3102
|
}
|
|
3071
3103
|
function validatePolicyEngineRecordShape(input, signed) {
|
|
3072
|
-
const
|
|
3104
|
+
const object4 = expectJsonObject(normalizeJson(input), "$");
|
|
3073
3105
|
assertExactKeys2(
|
|
3074
|
-
|
|
3106
|
+
object4,
|
|
3075
3107
|
signed ? [
|
|
3076
3108
|
"schema",
|
|
3077
3109
|
"engineRecordId",
|
|
@@ -3096,44 +3128,44 @@ function validatePolicyEngineRecordShape(input, signed) {
|
|
|
3096
3128
|
"$"
|
|
3097
3129
|
);
|
|
3098
3130
|
expectConst(
|
|
3099
|
-
requiredString2(
|
|
3131
|
+
requiredString2(object4, "schema", "$"),
|
|
3100
3132
|
POLICY_ENGINE_RECORD_SCHEMA,
|
|
3101
3133
|
"$.schema"
|
|
3102
3134
|
);
|
|
3103
3135
|
if (signed) {
|
|
3104
|
-
requiredString2(
|
|
3105
|
-
validateSignature(requiredValue2(
|
|
3136
|
+
requiredString2(object4, "engineRecordId", "$");
|
|
3137
|
+
validateSignature(requiredValue2(object4, "signature", "$"));
|
|
3106
3138
|
}
|
|
3107
|
-
requiredString2(
|
|
3108
|
-
requiredString2(
|
|
3109
|
-
requiredString2(
|
|
3139
|
+
requiredString2(object4, "ownerDid", "$");
|
|
3140
|
+
requiredString2(object4, "endpoint", "$");
|
|
3141
|
+
requiredString2(object4, "audience", "$");
|
|
3110
3142
|
requiredStringArray(
|
|
3111
|
-
|
|
3143
|
+
object4,
|
|
3112
3144
|
"supportedPolicyVersions",
|
|
3113
3145
|
"$",
|
|
3114
3146
|
(value, path) => expectConst(value, "v0", path)
|
|
3115
3147
|
);
|
|
3116
|
-
requiredStringArray(
|
|
3117
|
-
requiredString2(
|
|
3118
|
-
requiredDateString(
|
|
3119
|
-
return
|
|
3148
|
+
requiredStringArray(object4, "supportedEvidenceVerifiers", "$");
|
|
3149
|
+
requiredString2(object4, "grantIssuerDid", "$");
|
|
3150
|
+
requiredDateString(object4, "expiresAt", "$");
|
|
3151
|
+
return object4;
|
|
3120
3152
|
}
|
|
3121
3153
|
function validateSignature(input) {
|
|
3122
|
-
const
|
|
3123
|
-
assertExactKeys2(
|
|
3124
|
-
const suite = requiredString2(
|
|
3154
|
+
const object4 = expectJsonObject(input, "$.signature");
|
|
3155
|
+
assertExactKeys2(object4, ["suite", "signerDid", "value"], "$.signature");
|
|
3156
|
+
const suite = requiredString2(object4, "suite", "$.signature");
|
|
3125
3157
|
assertSupportedSignatureSuite(suite);
|
|
3126
|
-
const signerDid = requiredString2(
|
|
3127
|
-
const value = requiredString2(
|
|
3158
|
+
const signerDid = requiredString2(object4, "signerDid", "$.signature");
|
|
3159
|
+
const value = requiredString2(object4, "value", "$.signature");
|
|
3128
3160
|
decodeSignatureValue(value, suite);
|
|
3129
3161
|
return { suite, signerDid, value };
|
|
3130
3162
|
}
|
|
3131
3163
|
function validatePolicyResource(input, path) {
|
|
3132
|
-
const
|
|
3133
|
-
assertExactKeys2(
|
|
3134
|
-
requiredString2(
|
|
3135
|
-
requiredString2(
|
|
3136
|
-
const ceiling = requiredArray2(
|
|
3164
|
+
const object4 = expectJsonObject(input, path);
|
|
3165
|
+
assertExactKeys2(object4, ["resourceType", "resourceId", "permissionsCeiling"], path);
|
|
3166
|
+
requiredString2(object4, "resourceType", path);
|
|
3167
|
+
requiredString2(object4, "resourceId", path);
|
|
3168
|
+
const ceiling = requiredArray2(object4, "permissionsCeiling", path, 1);
|
|
3137
3169
|
for (let index = 0; index < ceiling.length; index++) {
|
|
3138
3170
|
validatePolicyCapability(ceiling[index], `${path}.permissionsCeiling[${index}]`);
|
|
3139
3171
|
}
|
|
@@ -3149,10 +3181,10 @@ function validatePolicyPermissionsCeilingForSigning(input) {
|
|
|
3149
3181
|
}
|
|
3150
3182
|
}
|
|
3151
3183
|
function validatePolicyCapability(input, path) {
|
|
3152
|
-
const
|
|
3184
|
+
const object4 = expectJsonObject(input, path);
|
|
3153
3185
|
try {
|
|
3154
|
-
const canonical = canonicalizePolicyCapability(
|
|
3155
|
-
if (jcsCanonicalize(
|
|
3186
|
+
const canonical = canonicalizePolicyCapability(object4);
|
|
3187
|
+
if (jcsCanonicalize(object4) !== jcsCanonicalize(canonical)) {
|
|
3156
3188
|
throw new SignedObjectSchemaError(`${path} must be canonical PolicyCapability JSON`);
|
|
3157
3189
|
}
|
|
3158
3190
|
} catch (error) {
|
|
@@ -3165,10 +3197,10 @@ function validatePolicyCapability(input, path) {
|
|
|
3165
3197
|
}
|
|
3166
3198
|
}
|
|
3167
3199
|
function validatePolicyCapabilityForSigning(input, path) {
|
|
3168
|
-
const
|
|
3200
|
+
const object4 = expectJsonObject(input, path);
|
|
3169
3201
|
try {
|
|
3170
|
-
const canonical = normalizePolicyCapability(
|
|
3171
|
-
if (jcsCanonicalize(
|
|
3202
|
+
const canonical = normalizePolicyCapability(object4);
|
|
3203
|
+
if (jcsCanonicalize(object4) !== jcsCanonicalize(canonical)) {
|
|
3172
3204
|
throw new SignedObjectSchemaError(`${path} must be strict canonical PolicyCapability JSON`);
|
|
3173
3205
|
}
|
|
3174
3206
|
} catch (error) {
|
|
@@ -3181,43 +3213,43 @@ function validatePolicyCapabilityForSigning(input, path) {
|
|
|
3181
3213
|
}
|
|
3182
3214
|
}
|
|
3183
3215
|
function validateExpression(input, path) {
|
|
3184
|
-
const
|
|
3185
|
-
const keys = Object.keys(
|
|
3216
|
+
const object4 = expectJsonObject(input, path);
|
|
3217
|
+
const keys = Object.keys(object4);
|
|
3186
3218
|
if (keys.length !== 1) {
|
|
3187
3219
|
throw new SignedObjectSchemaError(`${path} must have exactly one expression key`);
|
|
3188
3220
|
}
|
|
3189
3221
|
const key = keys[0];
|
|
3190
3222
|
if (key === "allOf" || key === "anyOf") {
|
|
3191
|
-
const values = requiredArray2(
|
|
3223
|
+
const values = requiredArray2(object4, key, path, 1);
|
|
3192
3224
|
for (let index = 0; index < values.length; index++) {
|
|
3193
3225
|
validateExpression(values[index], `${path}.${key}[${index}]`);
|
|
3194
3226
|
}
|
|
3195
3227
|
return;
|
|
3196
3228
|
}
|
|
3197
3229
|
if (key === "subject") {
|
|
3198
|
-
const subject = expectJsonObject(requiredValue2(
|
|
3230
|
+
const subject = expectJsonObject(requiredValue2(object4, "subject", path), `${path}.subject`);
|
|
3199
3231
|
assertExactKeys2(subject, ["did"], `${path}.subject`);
|
|
3200
3232
|
requiredString2(subject, "did", `${path}.subject`);
|
|
3201
3233
|
return;
|
|
3202
3234
|
}
|
|
3203
3235
|
if (key === "evidence") {
|
|
3204
|
-
validateEvidenceRequirement(requiredValue2(
|
|
3236
|
+
validateEvidenceRequirement(requiredValue2(object4, "evidence", path), `${path}.evidence`);
|
|
3205
3237
|
return;
|
|
3206
3238
|
}
|
|
3207
3239
|
throw new SignedObjectSchemaError(`${path} has unknown expression key ${key}`);
|
|
3208
3240
|
}
|
|
3209
3241
|
function validateEvidenceRequirement(input, path) {
|
|
3210
|
-
const
|
|
3242
|
+
const object4 = expectJsonObject(input, path);
|
|
3211
3243
|
assertExactKeys2(
|
|
3212
|
-
|
|
3244
|
+
object4,
|
|
3213
3245
|
["requirementId", "verifier", "requirements", "authority", "freshness"],
|
|
3214
3246
|
path
|
|
3215
3247
|
);
|
|
3216
|
-
requiredString2(
|
|
3217
|
-
requiredString2(
|
|
3218
|
-
requiredValue2(
|
|
3219
|
-
if (hasOwn2(
|
|
3220
|
-
const authority = expectJsonObject(requiredValue2(
|
|
3248
|
+
requiredString2(object4, "requirementId", path);
|
|
3249
|
+
requiredString2(object4, "verifier", path);
|
|
3250
|
+
requiredValue2(object4, "requirements", path);
|
|
3251
|
+
if (hasOwn2(object4, "authority")) {
|
|
3252
|
+
const authority = expectJsonObject(requiredValue2(object4, "authority", path), `${path}.authority`);
|
|
3221
3253
|
assertExactKeys2(
|
|
3222
3254
|
authority,
|
|
3223
3255
|
["profile", "acceptedIssuers", "allowOwnerAuthorizedIssuer"],
|
|
@@ -3229,35 +3261,35 @@ function validateEvidenceRequirement(input, path) {
|
|
|
3229
3261
|
}
|
|
3230
3262
|
optionalBoolean(authority, "allowOwnerAuthorizedIssuer", `${path}.authority`);
|
|
3231
3263
|
}
|
|
3232
|
-
if (hasOwn2(
|
|
3233
|
-
const freshness = expectJsonObject(requiredValue2(
|
|
3264
|
+
if (hasOwn2(object4, "freshness")) {
|
|
3265
|
+
const freshness = expectJsonObject(requiredValue2(object4, "freshness", path), `${path}.freshness`);
|
|
3234
3266
|
assertExactKeys2(freshness, ["maxStatusAgeSeconds"], `${path}.freshness`);
|
|
3235
3267
|
requiredInteger(freshness, "maxStatusAgeSeconds", `${path}.freshness`, 0);
|
|
3236
3268
|
}
|
|
3237
3269
|
}
|
|
3238
3270
|
function validateGrant(input, path) {
|
|
3239
|
-
const
|
|
3240
|
-
assertExactKeys2(
|
|
3241
|
-
expectConst(requiredString2(
|
|
3242
|
-
requiredInteger(
|
|
3243
|
-
expectOneOf(requiredString2(
|
|
3271
|
+
const object4 = expectJsonObject(input, path);
|
|
3272
|
+
assertExactKeys2(object4, ["output", "maxTtlSeconds", "delegationMode", "revocation"], path);
|
|
3273
|
+
expectConst(requiredString2(object4, "output", path), "portable-delegation", `${path}.output`);
|
|
3274
|
+
requiredInteger(object4, "maxTtlSeconds", path, 1);
|
|
3275
|
+
expectOneOf(requiredString2(object4, "delegationMode", path), [
|
|
3244
3276
|
"terminal",
|
|
3245
3277
|
"attenuable"
|
|
3246
3278
|
], `${path}.delegationMode`);
|
|
3247
|
-
expectOneOf(requiredString2(
|
|
3279
|
+
expectOneOf(requiredString2(object4, "revocation", path), [
|
|
3248
3280
|
"refresh_only",
|
|
3249
3281
|
"active_cutoff"
|
|
3250
3282
|
], `${path}.revocation`);
|
|
3251
3283
|
}
|
|
3252
3284
|
function validateDisclosure(input, path) {
|
|
3253
|
-
const
|
|
3254
|
-
assertExactKeys2(
|
|
3255
|
-
expectOneOf(requiredString2(
|
|
3285
|
+
const object4 = expectJsonObject(input, path);
|
|
3286
|
+
assertExactKeys2(object4, ["denial"], path);
|
|
3287
|
+
expectOneOf(requiredString2(object4, "denial", path), ["none", "code", "debug"], `${path}.denial`);
|
|
3256
3288
|
}
|
|
3257
3289
|
function validateAudit(input, path) {
|
|
3258
|
-
const
|
|
3259
|
-
assertExactKeys2(
|
|
3260
|
-
expectOneOf(requiredString2(
|
|
3290
|
+
const object4 = expectJsonObject(input, path);
|
|
3291
|
+
assertExactKeys2(object4, ["issuance"], path);
|
|
3292
|
+
expectOneOf(requiredString2(object4, "issuance", path), ["off", "security", "full"], `${path}.issuance`);
|
|
3261
3293
|
}
|
|
3262
3294
|
function materialForUnsigned(unsigned, descriptor) {
|
|
3263
3295
|
const jcs = jcsCanonicalize(unsigned);
|
|
@@ -3406,9 +3438,9 @@ function encodeSignatureValue(value, suite) {
|
|
|
3406
3438
|
decodeSignatureValue(encoded, suite);
|
|
3407
3439
|
return encoded;
|
|
3408
3440
|
}
|
|
3409
|
-
function stripOwnIdAndSignature(
|
|
3441
|
+
function stripOwnIdAndSignature(object4, descriptor) {
|
|
3410
3442
|
const output = /* @__PURE__ */ Object.create(null);
|
|
3411
|
-
for (const [key, value] of Object.entries(
|
|
3443
|
+
for (const [key, value] of Object.entries(object4)) {
|
|
3412
3444
|
if (key === descriptor.idField || key === "signature") {
|
|
3413
3445
|
continue;
|
|
3414
3446
|
}
|
|
@@ -3417,8 +3449,8 @@ function stripOwnIdAndSignature(object3, descriptor) {
|
|
|
3417
3449
|
return output;
|
|
3418
3450
|
}
|
|
3419
3451
|
function descriptorForUnsigned(input) {
|
|
3420
|
-
const
|
|
3421
|
-
return descriptorForSchema(requiredString2(
|
|
3452
|
+
const object4 = expectJsonObject(normalizeJson(input), "$");
|
|
3453
|
+
return descriptorForSchema(requiredString2(object4, "schema", "$"));
|
|
3422
3454
|
}
|
|
3423
3455
|
function descriptorForSchema(schema) {
|
|
3424
3456
|
if (schema === POLICY_SCHEMA) return DESCRIPTORS.Policy;
|
|
@@ -3432,28 +3464,28 @@ function expectJsonObject(input, path) {
|
|
|
3432
3464
|
}
|
|
3433
3465
|
return input;
|
|
3434
3466
|
}
|
|
3435
|
-
function assertExactKeys2(
|
|
3467
|
+
function assertExactKeys2(object4, allowed, path) {
|
|
3436
3468
|
const allowedSet = new Set(allowed);
|
|
3437
|
-
for (const key of Object.keys(
|
|
3469
|
+
for (const key of Object.keys(object4)) {
|
|
3438
3470
|
if (!allowedSet.has(key)) {
|
|
3439
3471
|
throw new SignedObjectSchemaError(`${path} has unknown field ${key}`);
|
|
3440
3472
|
}
|
|
3441
3473
|
}
|
|
3442
3474
|
}
|
|
3443
|
-
function requiredValue2(
|
|
3444
|
-
if (!hasOwn2(
|
|
3475
|
+
function requiredValue2(object4, key, path) {
|
|
3476
|
+
if (!hasOwn2(object4, key)) {
|
|
3445
3477
|
throw new SignedObjectSchemaError(`${path}.${key} is required`);
|
|
3446
3478
|
}
|
|
3447
|
-
return
|
|
3479
|
+
return object4[key];
|
|
3448
3480
|
}
|
|
3449
|
-
function requiredString2(
|
|
3450
|
-
return requireStringType(requiredValue2(
|
|
3481
|
+
function requiredString2(object4, key, path) {
|
|
3482
|
+
return requireStringType(requiredValue2(object4, key, path), `${path}.${key}`);
|
|
3451
3483
|
}
|
|
3452
|
-
function optionalString(
|
|
3453
|
-
if (!hasOwn2(
|
|
3484
|
+
function optionalString(object4, key, path) {
|
|
3485
|
+
if (!hasOwn2(object4, key)) {
|
|
3454
3486
|
return;
|
|
3455
3487
|
}
|
|
3456
|
-
requireStringType(requiredValue2(
|
|
3488
|
+
requireStringType(requiredValue2(object4, key, path), `${path}.${key}`);
|
|
3457
3489
|
}
|
|
3458
3490
|
function requireStringType(value, path) {
|
|
3459
3491
|
if (typeof value !== "string" || value.length === 0) {
|
|
@@ -3461,16 +3493,16 @@ function requireStringType(value, path) {
|
|
|
3461
3493
|
}
|
|
3462
3494
|
return value;
|
|
3463
3495
|
}
|
|
3464
|
-
function optionalBoolean(
|
|
3465
|
-
if (!hasOwn2(
|
|
3496
|
+
function optionalBoolean(object4, key, path) {
|
|
3497
|
+
if (!hasOwn2(object4, key)) {
|
|
3466
3498
|
return;
|
|
3467
3499
|
}
|
|
3468
|
-
if (typeof requiredValue2(
|
|
3500
|
+
if (typeof requiredValue2(object4, key, path) !== "boolean") {
|
|
3469
3501
|
throw new SignedObjectSchemaError(`${path}.${key} must be a boolean`);
|
|
3470
3502
|
}
|
|
3471
3503
|
}
|
|
3472
|
-
function requiredInteger(
|
|
3473
|
-
const value = requiredValue2(
|
|
3504
|
+
function requiredInteger(object4, key, path, minimum) {
|
|
3505
|
+
const value = requiredValue2(object4, key, path);
|
|
3474
3506
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum) {
|
|
3475
3507
|
throw new SignedObjectSchemaError(
|
|
3476
3508
|
`${path}.${key} must be an integer >= ${minimum}`
|
|
@@ -3478,8 +3510,8 @@ function requiredInteger(object3, key, path, minimum) {
|
|
|
3478
3510
|
}
|
|
3479
3511
|
return value;
|
|
3480
3512
|
}
|
|
3481
|
-
function requiredArray2(
|
|
3482
|
-
const value = requiredValue2(
|
|
3513
|
+
function requiredArray2(object4, key, path, minimum) {
|
|
3514
|
+
const value = requiredValue2(object4, key, path);
|
|
3483
3515
|
if (!Array.isArray(value) || value.length < minimum) {
|
|
3484
3516
|
throw new SignedObjectSchemaError(
|
|
3485
3517
|
`${path}.${key} must be an array with at least ${minimum} item(s)`
|
|
@@ -3487,22 +3519,22 @@ function requiredArray2(object3, key, path, minimum) {
|
|
|
3487
3519
|
}
|
|
3488
3520
|
return value;
|
|
3489
3521
|
}
|
|
3490
|
-
function requiredStringArray(
|
|
3491
|
-
const values = requiredArray2(
|
|
3522
|
+
function requiredStringArray(object4, key, path, check) {
|
|
3523
|
+
const values = requiredArray2(object4, key, path, 0);
|
|
3492
3524
|
for (let index = 0; index < values.length; index++) {
|
|
3493
3525
|
const itemPath = `${path}.${key}[${index}]`;
|
|
3494
3526
|
const value = requireStringType(values[index], itemPath);
|
|
3495
3527
|
check?.(value, itemPath);
|
|
3496
3528
|
}
|
|
3497
3529
|
}
|
|
3498
|
-
function requiredDateString(
|
|
3499
|
-
assertRfc3339(requiredString2(
|
|
3530
|
+
function requiredDateString(object4, key, path) {
|
|
3531
|
+
assertRfc3339(requiredString2(object4, key, path), `${path}.${key}`);
|
|
3500
3532
|
}
|
|
3501
|
-
function optionalDateString(
|
|
3502
|
-
if (!hasOwn2(
|
|
3533
|
+
function optionalDateString(object4, key, path) {
|
|
3534
|
+
if (!hasOwn2(object4, key)) {
|
|
3503
3535
|
return;
|
|
3504
3536
|
}
|
|
3505
|
-
assertRfc3339(requiredString2(
|
|
3537
|
+
assertRfc3339(requiredString2(object4, key, path), `${path}.${key}`);
|
|
3506
3538
|
}
|
|
3507
3539
|
function assertRfc3339(value, path) {
|
|
3508
3540
|
const match = value.match(
|
|
@@ -3534,8 +3566,8 @@ function expectOneOf(actual, allowed, path) {
|
|
|
3534
3566
|
}
|
|
3535
3567
|
throw new SignedObjectSchemaError(`${path} has unsupported value ${actual}`);
|
|
3536
3568
|
}
|
|
3537
|
-
function hasOwn2(
|
|
3538
|
-
return objectHasOwn3(
|
|
3569
|
+
function hasOwn2(object4, key) {
|
|
3570
|
+
return objectHasOwn3(object4, key);
|
|
3539
3571
|
}
|
|
3540
3572
|
function assertSupportedSignatureSuite(suite) {
|
|
3541
3573
|
if (suite !== ED25519_JCS_SIGNATURE_SUITE && suite !== EIP191_JCS_SIGNATURE_SUITE) {
|
|
@@ -3762,9 +3794,9 @@ function expectObject2(input, path, code3) {
|
|
|
3762
3794
|
);
|
|
3763
3795
|
}
|
|
3764
3796
|
}
|
|
3765
|
-
function assertExactKeys3(
|
|
3797
|
+
function assertExactKeys3(object4, allowed, path) {
|
|
3766
3798
|
const allowedSet = new Set(allowed);
|
|
3767
|
-
for (const key of Object.keys(
|
|
3799
|
+
for (const key of Object.keys(object4)) {
|
|
3768
3800
|
if (!allowedSet.has(key)) {
|
|
3769
3801
|
throw new PolicyAuthoringError(
|
|
3770
3802
|
"policy-authoring-unknown-key",
|
|
@@ -3773,17 +3805,17 @@ function assertExactKeys3(object3, allowed, path) {
|
|
|
3773
3805
|
}
|
|
3774
3806
|
}
|
|
3775
3807
|
}
|
|
3776
|
-
function requiredValue3(
|
|
3777
|
-
if (!hasOwn3(
|
|
3808
|
+
function requiredValue3(object4, key, path) {
|
|
3809
|
+
if (!hasOwn3(object4, key)) {
|
|
3778
3810
|
throw new PolicyAuthoringError(
|
|
3779
3811
|
"policy-authoring-malformed",
|
|
3780
3812
|
`${path}.${key} is required`
|
|
3781
3813
|
);
|
|
3782
3814
|
}
|
|
3783
|
-
return
|
|
3815
|
+
return object4[key];
|
|
3784
3816
|
}
|
|
3785
|
-
function requiredString3(
|
|
3786
|
-
const value = requiredValue3(
|
|
3817
|
+
function requiredString3(object4, key, path) {
|
|
3818
|
+
const value = requiredValue3(object4, key, path);
|
|
3787
3819
|
if (typeof value !== "string" || value.length === 0) {
|
|
3788
3820
|
throw new PolicyAuthoringError(
|
|
3789
3821
|
"policy-authoring-malformed",
|
|
@@ -3792,11 +3824,11 @@ function requiredString3(object3, key, path) {
|
|
|
3792
3824
|
}
|
|
3793
3825
|
return value;
|
|
3794
3826
|
}
|
|
3795
|
-
function fieldString(
|
|
3796
|
-
if (!hasOwn3(
|
|
3827
|
+
function fieldString(object4, key, path, code3) {
|
|
3828
|
+
if (!hasOwn3(object4, key)) {
|
|
3797
3829
|
throw new PolicyAuthoringError(code3, `${path}.${key} is required`);
|
|
3798
3830
|
}
|
|
3799
|
-
const value =
|
|
3831
|
+
const value = object4[key];
|
|
3800
3832
|
if (typeof value !== "string" || value.length === 0) {
|
|
3801
3833
|
throw new PolicyAuthoringError(
|
|
3802
3834
|
code3,
|
|
@@ -3834,8 +3866,8 @@ function parseStrictRfc3339(value, path) {
|
|
|
3834
3866
|
var objectHasOwn4 = Object.hasOwn ?? Object.prototype.hasOwnProperty.call.bind(
|
|
3835
3867
|
Object.prototype.hasOwnProperty
|
|
3836
3868
|
);
|
|
3837
|
-
function hasOwn3(
|
|
3838
|
-
return objectHasOwn4(
|
|
3869
|
+
function hasOwn3(object4, key) {
|
|
3870
|
+
return objectHasOwn4(object4, key);
|
|
3839
3871
|
}
|
|
3840
3872
|
|
|
3841
3873
|
// src/policy/unified.ts
|
|
@@ -3862,9 +3894,11 @@ var MAX_LEGACY_ENVELOPE_EXPIRES_AT = "2026-12-29T00:00:00Z";
|
|
|
3862
3894
|
var LAST_V2_READ_AT = "2027-01-05T00:00:00Z";
|
|
3863
3895
|
async function requestPolicyChallengeV3(input) {
|
|
3864
3896
|
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3865
|
-
const
|
|
3897
|
+
const policyRuntimePath = policyRuntimeBasePath(input.policyRuntimePath);
|
|
3898
|
+
const response = await fetchFn(new URL(`${policyRuntimePath}/challenges`, input.nodeOrigin), {
|
|
3866
3899
|
method: "POST",
|
|
3867
3900
|
redirect: "error",
|
|
3901
|
+
signal: input.signal,
|
|
3868
3902
|
headers: { accept: "application/json", "content-type": "application/json" },
|
|
3869
3903
|
body: JSON.stringify({ policyCid: input.policyCid, recipientDid: input.recipientDid, requestedCapabilities: input.requestedCapabilities.map(normalizeUnifiedPolicyCapability) })
|
|
3870
3904
|
});
|
|
@@ -3874,6 +3908,13 @@ async function requestPolicyChallengeV3(input) {
|
|
|
3874
3908
|
throw new Error("policy challenge binding is invalid");
|
|
3875
3909
|
return value;
|
|
3876
3910
|
}
|
|
3911
|
+
function policyRuntimeBasePath(value) {
|
|
3912
|
+
const path = value ?? "/policy/v3";
|
|
3913
|
+
if (!/^\/policy(?:\/[a-z0-9-]+)*$/.test(path)) {
|
|
3914
|
+
throw new Error("policy runtime path is invalid");
|
|
3915
|
+
}
|
|
3916
|
+
return path;
|
|
3917
|
+
}
|
|
3877
3918
|
function compactAttenuationForPolicyCapabilities(input) {
|
|
3878
3919
|
const attenuation = {};
|
|
3879
3920
|
for (const raw of input) {
|
|
@@ -3919,7 +3960,7 @@ function verifyPolicyRootStatusCheckpointV3(input) {
|
|
|
3919
3960
|
}
|
|
3920
3961
|
async function getPolicyRootStatusV3(input) {
|
|
3921
3962
|
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3922
|
-
const response = await fetchFn(new URL(`/
|
|
3963
|
+
const response = await fetchFn(new URL(`/policy/v3/status/${encodeURIComponent(input.rootCid)}`, input.nodeOrigin), {
|
|
3923
3964
|
method: "GET",
|
|
3924
3965
|
redirect: "error",
|
|
3925
3966
|
headers: { accept: "application/json" }
|
|
@@ -3978,7 +4019,7 @@ async function renewPolicyRootStatusV3(input) {
|
|
|
3978
4019
|
};
|
|
3979
4020
|
const signature = await input.sign((0, import_sha256.sha256)(new TextEncoder().encode(ROOT_STATUS_RENEWAL_V1_DOMAIN + jcsCanonicalize(unsigned))));
|
|
3980
4021
|
if (signature.length !== 64) throw new Error("policy root renewal signature must be Ed25519");
|
|
3981
|
-
const response = await fetchFn(new URL("/
|
|
4022
|
+
const response = await fetchFn(new URL("/policy/v3/status", input.nodeOrigin), {
|
|
3982
4023
|
method: "POST",
|
|
3983
4024
|
redirect: "error",
|
|
3984
4025
|
headers: { accept: "application/json", "content-type": "application/json" },
|
|
@@ -4034,6 +4075,11 @@ var POLICY_SESSION_FACT_KEYS = [
|
|
|
4034
4075
|
"issuanceAuditDigestHex",
|
|
4035
4076
|
"remainingRedelegationDepth"
|
|
4036
4077
|
];
|
|
4078
|
+
var POLICY_SESSION_V4_AUDIT_FACT_KEYS = [
|
|
4079
|
+
"credentialIdAuditDigestHex",
|
|
4080
|
+
"presentationJtiAuditDigestHex"
|
|
4081
|
+
];
|
|
4082
|
+
var LOWER_SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
4037
4083
|
var KV_ACTIONS = /* @__PURE__ */ new Set([
|
|
4038
4084
|
"tinycloud.kv/get",
|
|
4039
4085
|
"tinycloud.kv/list",
|
|
@@ -4228,6 +4274,133 @@ function contentSourceDigestHex(input) {
|
|
|
4228
4274
|
)
|
|
4229
4275
|
);
|
|
4230
4276
|
}
|
|
4277
|
+
function exactObject(value, keys, label) {
|
|
4278
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
4279
|
+
throw new Error(`${label} is invalid`);
|
|
4280
|
+
}
|
|
4281
|
+
const record2 = value;
|
|
4282
|
+
if (Object.keys(record2).length !== keys.length || keys.some((key) => !Object.prototype.hasOwnProperty.call(record2, key))) {
|
|
4283
|
+
throw new Error(`${label} is invalid`);
|
|
4284
|
+
}
|
|
4285
|
+
return record2;
|
|
4286
|
+
}
|
|
4287
|
+
function didKeyEd25519PublicKey(value) {
|
|
4288
|
+
const principal = value.split("#", 1)[0];
|
|
4289
|
+
if (!principal.startsWith("did:key:")) {
|
|
4290
|
+
throw new Error("policy enforcer binding signer is invalid");
|
|
4291
|
+
}
|
|
4292
|
+
const material = base58btc.decode(principal.slice("did:key:".length));
|
|
4293
|
+
if (material.length !== 34 || material[0] !== 237 || material[1] !== 1) {
|
|
4294
|
+
throw new Error("policy enforcer binding signer is invalid");
|
|
4295
|
+
}
|
|
4296
|
+
return material.slice(2);
|
|
4297
|
+
}
|
|
4298
|
+
async function registerPolicyV3(input) {
|
|
4299
|
+
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
4300
|
+
const rootExpiresAt = new Date(input.rootExpiresAt);
|
|
4301
|
+
const canonicalRootExpiresAt = new Date(
|
|
4302
|
+
Math.floor(rootExpiresAt.getTime() / 1e3) * 1e3
|
|
4303
|
+
).toISOString().replace(/\.000Z$/, "Z");
|
|
4304
|
+
if (!Number.isFinite(rootExpiresAt.getTime()) || canonicalRootExpiresAt !== input.rootExpiresAt || rootExpiresAt.getTime() <= Date.now()) {
|
|
4305
|
+
throw new Error("policy root expiry is invalid");
|
|
4306
|
+
}
|
|
4307
|
+
const bindingResponse = await fetchFn(
|
|
4308
|
+
new URL("/policy/v3/enforcer-bindings", input.nodeOrigin),
|
|
4309
|
+
{
|
|
4310
|
+
method: "POST",
|
|
4311
|
+
redirect: "error",
|
|
4312
|
+
signal: input.signal,
|
|
4313
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
4314
|
+
body: JSON.stringify({
|
|
4315
|
+
rootExpiresAt: input.rootExpiresAt,
|
|
4316
|
+
enforcerDid: input.enforcerDid
|
|
4317
|
+
})
|
|
4318
|
+
}
|
|
4319
|
+
);
|
|
4320
|
+
if (!bindingResponse.ok) {
|
|
4321
|
+
throw new Error(`policy enforcer binding rejected (${bindingResponse.status})`);
|
|
4322
|
+
}
|
|
4323
|
+
const binding = exactObject(
|
|
4324
|
+
await bindingResponse.json(),
|
|
4325
|
+
[
|
|
4326
|
+
"schema",
|
|
4327
|
+
"enforcerDid",
|
|
4328
|
+
"nodeAudience",
|
|
4329
|
+
"attestationBindingDigestHex",
|
|
4330
|
+
"issuedAt",
|
|
4331
|
+
"expiresAt",
|
|
4332
|
+
"signature"
|
|
4333
|
+
],
|
|
4334
|
+
"policy enforcer binding"
|
|
4335
|
+
);
|
|
4336
|
+
const signature = exactObject(
|
|
4337
|
+
binding.signature,
|
|
4338
|
+
["suite", "signerDid", "value"],
|
|
4339
|
+
"policy enforcer binding signature"
|
|
4340
|
+
);
|
|
4341
|
+
const expectedBindingDigestHex = hex(
|
|
4342
|
+
(0, import_sha256.sha256)(
|
|
4343
|
+
new TextEncoder().encode(
|
|
4344
|
+
jcsCanonicalize({
|
|
4345
|
+
enforcerDid: input.enforcerDid,
|
|
4346
|
+
nodeAudience: input.expectedNodeAudience
|
|
4347
|
+
})
|
|
4348
|
+
)
|
|
4349
|
+
)
|
|
4350
|
+
);
|
|
4351
|
+
if (binding.schema !== ATTESTED_ENFORCER_BINDING_V2_SCHEMA || binding.enforcerDid !== input.enforcerDid || binding.nodeAudience !== input.expectedNodeAudience || binding.attestationBindingDigestHex !== expectedBindingDigestHex || binding.expiresAt !== input.rootExpiresAt || typeof binding.issuedAt !== "string" || Date.parse(binding.issuedAt) > Date.now() || signature.suite !== "Ed25519" || signature.signerDid !== input.expectedNodeAudience || typeof signature.value !== "string") {
|
|
4352
|
+
throw new Error("policy enforcer binding is invalid");
|
|
4353
|
+
}
|
|
4354
|
+
const { signature: _signature, ...unsignedBinding } = binding;
|
|
4355
|
+
const signedBinding = (0, import_sha256.sha256)(
|
|
4356
|
+
new TextEncoder().encode(
|
|
4357
|
+
`xyz.tinycloud.policy/AttestedEnforcerBinding/v2\0${jcsCanonicalize(unsignedBinding)}`
|
|
4358
|
+
)
|
|
4359
|
+
);
|
|
4360
|
+
if (!import_ed255193.ed25519.verify(
|
|
4361
|
+
decodeBase64Url2(signature.value),
|
|
4362
|
+
signedBinding,
|
|
4363
|
+
didKeyEd25519PublicKey(input.expectedNodeAudience),
|
|
4364
|
+
{ zip215: false }
|
|
4365
|
+
)) {
|
|
4366
|
+
throw new Error("policy enforcer binding signature is invalid");
|
|
4367
|
+
}
|
|
4368
|
+
const response = await fetchFn(
|
|
4369
|
+
new URL("/policy/v3/policies", input.nodeOrigin),
|
|
4370
|
+
{
|
|
4371
|
+
method: "POST",
|
|
4372
|
+
redirect: "error",
|
|
4373
|
+
signal: input.signal,
|
|
4374
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
4375
|
+
body: JSON.stringify({
|
|
4376
|
+
policyCid: input.policyCid,
|
|
4377
|
+
policy: input.policy,
|
|
4378
|
+
policyRoot: input.policyRoot.authorization.replace(/^Bearer\s+/i, ""),
|
|
4379
|
+
enforcementRoot: input.enforcementRoot.authorization.replace(/^Bearer\s+/i, ""),
|
|
4380
|
+
contentSourceDigestHex: input.contentSourceDigestHex,
|
|
4381
|
+
nativeProjectionHashHex: input.nativeProjectionHashHex,
|
|
4382
|
+
attestedEnforcerBinding: binding
|
|
4383
|
+
})
|
|
4384
|
+
}
|
|
4385
|
+
);
|
|
4386
|
+
if (!response.ok) {
|
|
4387
|
+
throw new Error(`policy registration rejected (${response.status})`);
|
|
4388
|
+
}
|
|
4389
|
+
const receipt = exactObject(
|
|
4390
|
+
await response.json(),
|
|
4391
|
+
["policyCid", "policyRootCid", "enforcementRootCid"],
|
|
4392
|
+
"policy registration response"
|
|
4393
|
+
);
|
|
4394
|
+
if (receipt.policyCid !== input.policyCid || receipt.policyRootCid !== input.policyRoot.cid || receipt.enforcementRootCid !== input.enforcementRoot.cid) {
|
|
4395
|
+
throw new Error("policy registration response binding is invalid");
|
|
4396
|
+
}
|
|
4397
|
+
return {
|
|
4398
|
+
policyCid: input.policyCid,
|
|
4399
|
+
policyRootCid: input.policyRoot.cid,
|
|
4400
|
+
enforcementRootCid: input.enforcementRoot.cid,
|
|
4401
|
+
attestedEnforcerBinding: binding
|
|
4402
|
+
};
|
|
4403
|
+
}
|
|
4231
4404
|
function policyDigestHex(policy) {
|
|
4232
4405
|
const canonicalPolicy = {
|
|
4233
4406
|
...policy,
|
|
@@ -4426,9 +4599,13 @@ function parsePolicySessionUcan(authorization, expectedProofs) {
|
|
|
4426
4599
|
if (expectedProofs !== void 0 && (parsed.payload.prf[0] !== expectedProofs[0] || parsed.payload.prf[1] !== expectedProofs[1]))
|
|
4427
4600
|
throw new Error("policy session UCAN ordered proofs do not match");
|
|
4428
4601
|
const factKeys = Object.keys(fact);
|
|
4429
|
-
|
|
4602
|
+
const hasLegacyFactShape = factKeys.length === POLICY_SESSION_FACT_KEYS.length && factKeys.every((key) => POLICY_SESSION_FACT_KEYS.includes(key)) && POLICY_SESSION_FACT_KEYS.every((key) => key in fact);
|
|
4603
|
+
const hasV4AuditFactShape = factKeys.length === POLICY_SESSION_FACT_KEYS.length + POLICY_SESSION_V4_AUDIT_FACT_KEYS.length && factKeys.every((key) => POLICY_SESSION_FACT_KEYS.includes(key) || POLICY_SESSION_V4_AUDIT_FACT_KEYS.includes(key)) && POLICY_SESSION_FACT_KEYS.every((key) => key in fact) && POLICY_SESSION_V4_AUDIT_FACT_KEYS.every((key) => key in fact);
|
|
4604
|
+
if (!hasLegacyFactShape && !hasV4AuditFactShape)
|
|
4430
4605
|
throw new Error("policy session UCAN fact is incomplete");
|
|
4431
|
-
if (
|
|
4606
|
+
if (hasV4AuditFactShape && (!LOWER_SHA256_HEX.test(fact.credentialIdAuditDigestHex) || !LOWER_SHA256_HEX.test(fact.presentationJtiAuditDigestHex)))
|
|
4607
|
+
throw new Error("policy session UCAN v4 audit facts are invalid");
|
|
4608
|
+
if (fact.profile !== POLICY_SESSION_UCAN_V1_PROFILE || typeof fact.remainingRedelegationDepth !== "number" || !Number.isInteger(fact.remainingRedelegationDepth) || fact.remainingRedelegationDepth < 0 || fact.remainingRedelegationDepth > 8 || parsed.payload.exp - parsed.payload.nbf > 60 || parsed.payload.iss.split("#", 1)[0] !== fact.nodeAudience || parsed.payload.aud !== fact.recipientDid)
|
|
4432
4609
|
throw new Error("policy session UCAN fact is invalid");
|
|
4433
4610
|
return {
|
|
4434
4611
|
authorization,
|
|
@@ -4487,6 +4664,8 @@ var POLICY_V2_DOMAIN = "xyz.tinycloud.policy/policy/v2\0";
|
|
|
4487
4664
|
var POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE = "TinyCloudPolicyCredentialRequirement";
|
|
4488
4665
|
var POLICY_PRESENTATION_V3_SCHEMA = "xyz.tinycloud.policy/presentation/v3";
|
|
4489
4666
|
var POLICY_PRESENTATION_V3_DOMAIN = "xyz.tinycloud.policy/Presentation/v3\0";
|
|
4667
|
+
var POLICY_PRESENTATION_V4_SCHEMA = "xyz.tinycloud.policy/presentation/v4";
|
|
4668
|
+
var POLICY_PRESENTATION_V4_DOMAIN = "xyz.tinycloud.policy/Presentation/v4\0";
|
|
4490
4669
|
function record(value, label) {
|
|
4491
4670
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
4492
4671
|
throw new Error(`${label} must be an object`);
|
|
@@ -4661,6 +4840,97 @@ function envelopeFromVerified(verified) {
|
|
|
4661
4840
|
} = verified;
|
|
4662
4841
|
return envelope;
|
|
4663
4842
|
}
|
|
4843
|
+
function issuedCredentialEnvelopeFromVerified(verified) {
|
|
4844
|
+
return envelopeFromVerified(verified);
|
|
4845
|
+
}
|
|
4846
|
+
async function signPolicyCredentialPresentationV4(value, sign) {
|
|
4847
|
+
const item = record(value, "Policy presentation v4");
|
|
4848
|
+
exactKeys2(item, [
|
|
4849
|
+
"schema",
|
|
4850
|
+
"jti",
|
|
4851
|
+
"challengeId",
|
|
4852
|
+
"nonce",
|
|
4853
|
+
"policyCid",
|
|
4854
|
+
"nodeAudience",
|
|
4855
|
+
"holderDid",
|
|
4856
|
+
"subjectDid",
|
|
4857
|
+
"credentialDigest",
|
|
4858
|
+
"requirementDigest",
|
|
4859
|
+
"descriptorDigest",
|
|
4860
|
+
"requestedCapabilities",
|
|
4861
|
+
"issuedAt",
|
|
4862
|
+
"expiresAt"
|
|
4863
|
+
], "Policy presentation v4");
|
|
4864
|
+
if (item.schema !== POLICY_PRESENTATION_V4_SCHEMA || item.holderDid !== item.subjectDid || !Array.isArray(item.requestedCapabilities)) throw new Error("Policy presentation v4 is invalid");
|
|
4865
|
+
const holderDid = nonEmpty(item.holderDid, "Policy presentation v4 holder DID");
|
|
4866
|
+
didKeyBytes(holderDid);
|
|
4867
|
+
const unsigned = Object.freeze({
|
|
4868
|
+
schema: POLICY_PRESENTATION_V4_SCHEMA,
|
|
4869
|
+
jti: nonEmpty(item.jti, "Policy presentation v4 jti"),
|
|
4870
|
+
challengeId: nonEmpty(item.challengeId, "Policy presentation v4 challenge"),
|
|
4871
|
+
nonce: nonEmpty(item.nonce, "Policy presentation v4 nonce"),
|
|
4872
|
+
policyCid: nonEmpty(item.policyCid, "Policy presentation v4 policy CID"),
|
|
4873
|
+
nodeAudience: nonEmpty(item.nodeAudience, "Policy presentation v4 audience"),
|
|
4874
|
+
holderDid,
|
|
4875
|
+
subjectDid: holderDid,
|
|
4876
|
+
credentialDigest: nonEmpty(item.credentialDigest, "Policy presentation v4 credential digest"),
|
|
4877
|
+
requirementDigest: nonEmpty(item.requirementDigest, "Policy presentation v4 requirement digest"),
|
|
4878
|
+
descriptorDigest: nonEmpty(item.descriptorDigest, "Policy presentation v4 descriptor digest"),
|
|
4879
|
+
// The low-level signer preserves the frozen wire bytes exactly. The
|
|
4880
|
+
// policy-aware builder performs capability normalization and containment.
|
|
4881
|
+
requestedCapabilities: Object.freeze(item.requestedCapabilities),
|
|
4882
|
+
issuedAt: date(item.issuedAt, "Policy presentation v4 issuedAt"),
|
|
4883
|
+
expiresAt: date(item.expiresAt, "Policy presentation v4 expiresAt")
|
|
4884
|
+
});
|
|
4885
|
+
const digest4 = (0, import_sha2562.sha256)(new TextEncoder().encode(
|
|
4886
|
+
POLICY_PRESENTATION_V4_DOMAIN + jcsCanonicalize(unsigned)
|
|
4887
|
+
));
|
|
4888
|
+
const signature = await sign(digest4);
|
|
4889
|
+
if (signature.length !== 64 || !import_ed255194.ed25519.verify(signature, digest4, didKeyBytes(holderDid))) throw new Error("policy presentation signature is invalid");
|
|
4890
|
+
return Object.freeze({
|
|
4891
|
+
...unsigned,
|
|
4892
|
+
signature: Object.freeze({
|
|
4893
|
+
suite: "Ed25519",
|
|
4894
|
+
signerDid: holderDid,
|
|
4895
|
+
value: encodeBase64Url(signature)
|
|
4896
|
+
})
|
|
4897
|
+
});
|
|
4898
|
+
}
|
|
4899
|
+
async function buildPolicyCredentialPresentationV4(input) {
|
|
4900
|
+
const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
|
|
4901
|
+
const requirement = validateCredentialRequirement(input.requirement);
|
|
4902
|
+
const commitment = policy.credentialRequirement;
|
|
4903
|
+
const requirementDigest = await credentialRequirementDigest(requirement);
|
|
4904
|
+
const credentialDigest = await sha256Base64Url(input.credential.credential);
|
|
4905
|
+
if (requirementDigest !== commitment.requirementDigest || input.credential.descriptorDigest !== commitment.descriptorDigest || input.credential.credentialDigest !== credentialDigest || input.credential.issuerDid !== commitment.issuerDid || input.credential.issuerKid !== commitment.issuerKid || jcsCanonicalize(input.credential.profile) !== jcsCanonicalize(commitment.profile) || jcsCanonicalize(input.credential.credentialType) !== jcsCanonicalize(commitment.credentialType)) throw new Error("credential does not match the signed Policy/v2 requirement");
|
|
4906
|
+
const requestedCapabilities = input.requestedCapabilities.map(normalizeUnifiedPolicyCapability);
|
|
4907
|
+
if (requestedCapabilities.length === 0 || requestedCapabilities.some((requested) => !policy.capabilityCeiling.some((authority) => unifiedPolicyCapabilityContains(authority, requested)))) throw new Error("requested capabilities exceed the signed Policy/v2 ceiling");
|
|
4908
|
+
const holderDid = input.credential.holderDid;
|
|
4909
|
+
if (input.challenge.policyCid !== input.policyCid || input.challenge.recipientDid !== holderDid || input.credential.subjectDid !== holderDid || typeof input.challenge.nodeAudience !== "string" || input.challenge.nodeAudience.length === 0 || typeof input.challenge.expiresAt !== "string" || !Number.isFinite(Date.parse(input.challenge.expiresAt))) throw new Error("policy challenge binding is invalid");
|
|
4910
|
+
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
4911
|
+
const expiresAtMs = Math.min(
|
|
4912
|
+
now.getTime() + 6e4,
|
|
4913
|
+
Date.parse(input.challenge.expiresAt),
|
|
4914
|
+
Date.parse(input.credential.expiresAt)
|
|
4915
|
+
);
|
|
4916
|
+
if (expiresAtMs <= now.getTime()) throw new Error("policy challenge is expired");
|
|
4917
|
+
return signPolicyCredentialPresentationV4(Object.freeze({
|
|
4918
|
+
schema: POLICY_PRESENTATION_V4_SCHEMA,
|
|
4919
|
+
jti: input.jti ?? encodeBase64Url(crypto.getRandomValues(new Uint8Array(16))),
|
|
4920
|
+
challengeId: input.challenge.challengeId,
|
|
4921
|
+
nonce: input.challenge.nonce,
|
|
4922
|
+
policyCid: input.policyCid,
|
|
4923
|
+
nodeAudience: input.challenge.nodeAudience,
|
|
4924
|
+
holderDid,
|
|
4925
|
+
subjectDid: holderDid,
|
|
4926
|
+
credentialDigest,
|
|
4927
|
+
requirementDigest,
|
|
4928
|
+
descriptorDigest: input.credential.descriptorDigest,
|
|
4929
|
+
requestedCapabilities: Object.freeze(requestedCapabilities),
|
|
4930
|
+
issuedAt: rfc3339Seconds(now),
|
|
4931
|
+
expiresAt: rfc3339Seconds(new Date(expiresAtMs))
|
|
4932
|
+
}), input.sign);
|
|
4933
|
+
}
|
|
4664
4934
|
async function buildPolicyCredentialPresentationV3(input) {
|
|
4665
4935
|
const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
|
|
4666
4936
|
const requirement = validateCredentialRequirement(input.requirement);
|
|
@@ -4747,10 +5017,12 @@ async function admitPolicyCredentialV3(input) {
|
|
|
4747
5017
|
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
4748
5018
|
const challenge = await requestPolicyChallengeV3({
|
|
4749
5019
|
nodeOrigin,
|
|
5020
|
+
policyRuntimePath: input.policyRuntimePath,
|
|
4750
5021
|
policyCid: input.policyCid,
|
|
4751
5022
|
recipientDid: input.credential.holderDid,
|
|
4752
5023
|
requestedCapabilities: input.requestedCapabilities,
|
|
4753
|
-
fetch: fetchFn
|
|
5024
|
+
fetch: fetchFn,
|
|
5025
|
+
signal: input.signal
|
|
4754
5026
|
});
|
|
4755
5027
|
const presentation = await buildPolicyCredentialPresentationV3({
|
|
4756
5028
|
...input,
|
|
@@ -4758,10 +5030,11 @@ async function admitPolicyCredentialV3(input) {
|
|
|
4758
5030
|
challenge
|
|
4759
5031
|
});
|
|
4760
5032
|
const response = await fetchFn(
|
|
4761
|
-
new URL(
|
|
5033
|
+
new URL(`${policyRuntimeBasePath2(input.policyRuntimePath)}/delegations`, nodeOrigin),
|
|
4762
5034
|
{
|
|
4763
5035
|
method: "POST",
|
|
4764
5036
|
redirect: "error",
|
|
5037
|
+
signal: input.signal,
|
|
4765
5038
|
headers: { accept: "application/json", "content-type": "application/json" },
|
|
4766
5039
|
body: JSON.stringify({
|
|
4767
5040
|
policyCid: input.policyCid,
|
|
@@ -4790,8 +5063,74 @@ async function admitPolicyCredentialV3(input) {
|
|
|
4790
5063
|
if (session.cid !== value.sessionCid || session.aud !== input.credential.holderDid || session.fact.policyCid !== input.policyCid || session.fact.recipientDid !== input.credential.holderDid || session.fact.policyDelegationCid !== input.policyRootCid || session.fact.enforcementDelegationCid !== input.enforcementRootCid || jcsCanonicalize(session.att) !== jcsCanonicalize(expectedAttenuation)) {
|
|
4791
5064
|
throw new Error("policy delegation signed binding is invalid");
|
|
4792
5065
|
}
|
|
5066
|
+
input.signal?.throwIfAborted();
|
|
4793
5067
|
return Object.freeze({ challenge, presentation, session });
|
|
4794
5068
|
}
|
|
5069
|
+
function validatePolicyCredentialAdmissionV4Authority(session, expected) {
|
|
5070
|
+
if (session.iss.split("#", 1)[0] !== expected.nodeAudience || session.fact.nodeAudience !== expected.nodeAudience || session.fact.enforcerDid !== expected.enforcerDid) throw new Error("policy delegation authority binding is invalid");
|
|
5071
|
+
}
|
|
5072
|
+
async function admitPolicyCredentialV4(input) {
|
|
5073
|
+
const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
|
|
5074
|
+
const nodeOrigin = policyNodeOrigin(input.nodeOrigin);
|
|
5075
|
+
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
5076
|
+
const challenge = await requestPolicyChallengeV3({
|
|
5077
|
+
nodeOrigin,
|
|
5078
|
+
policyRuntimePath: input.policyRuntimePath,
|
|
5079
|
+
policyCid: input.policyCid,
|
|
5080
|
+
recipientDid: input.credential.holderDid,
|
|
5081
|
+
requestedCapabilities: input.requestedCapabilities,
|
|
5082
|
+
fetch: fetchFn,
|
|
5083
|
+
signal: input.signal
|
|
5084
|
+
});
|
|
5085
|
+
const presentation = await buildPolicyCredentialPresentationV4({
|
|
5086
|
+
...input,
|
|
5087
|
+
policy,
|
|
5088
|
+
challenge
|
|
5089
|
+
});
|
|
5090
|
+
const response = await fetchFn(new URL(`${policyRuntimeBasePath2(input.policyRuntimePath)}/delegations`, nodeOrigin), {
|
|
5091
|
+
method: "POST",
|
|
5092
|
+
redirect: "error",
|
|
5093
|
+
signal: input.signal,
|
|
5094
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
5095
|
+
body: JSON.stringify({
|
|
5096
|
+
policyCid: input.policyCid,
|
|
5097
|
+
challengeId: challenge.challengeId,
|
|
5098
|
+
nonce: challenge.nonce,
|
|
5099
|
+
requirement: validateCredentialRequirement(input.requirement),
|
|
5100
|
+
credential: envelopeFromVerified(input.credential),
|
|
5101
|
+
presentation
|
|
5102
|
+
})
|
|
5103
|
+
});
|
|
5104
|
+
if (!response.ok) throw new Error(`policy delegation rejected (${response.status})`);
|
|
5105
|
+
const value = record(await response.json(), "policy delegation");
|
|
5106
|
+
if (value.admitted !== true || typeof value.sessionCid !== "string" || typeof value.authorization !== "string") throw new Error("policy delegation response is invalid");
|
|
5107
|
+
const session = parsePolicySessionUcan(value.authorization, [
|
|
5108
|
+
input.policyRootCid,
|
|
5109
|
+
input.enforcementRootCid
|
|
5110
|
+
]);
|
|
5111
|
+
const expectedAttenuation = compactAttenuationForPolicyCapabilities(input.requestedCapabilities);
|
|
5112
|
+
validatePolicyCredentialAdmissionV4Authority(session, {
|
|
5113
|
+
nodeAudience: input.expectedNodeAudience,
|
|
5114
|
+
enforcerDid: input.expectedEnforcerDid
|
|
5115
|
+
});
|
|
5116
|
+
if (session.cid !== value.sessionCid || session.aud !== input.credential.holderDid || session.fact.policyCid !== input.policyCid || session.fact.recipientDid !== input.credential.holderDid || session.fact.policyDelegationCid !== input.policyRootCid || session.fact.enforcementDelegationCid !== input.enforcementRootCid || jcsCanonicalize(session.att) !== jcsCanonicalize(expectedAttenuation)) throw new Error("policy delegation signed binding is invalid");
|
|
5117
|
+
const importResponse = await fetchFn(new URL("/delegate", nodeOrigin), {
|
|
5118
|
+
method: "POST",
|
|
5119
|
+
redirect: "error",
|
|
5120
|
+
signal: input.signal,
|
|
5121
|
+
headers: { Authorization: session.authorization }
|
|
5122
|
+
});
|
|
5123
|
+
if (!importResponse.ok) throw new Error(`delegation import rejected (${importResponse.status})`);
|
|
5124
|
+
input.signal?.throwIfAborted();
|
|
5125
|
+
return Object.freeze({ challenge, presentation, session, delegationImported: true });
|
|
5126
|
+
}
|
|
5127
|
+
function policyRuntimeBasePath2(value) {
|
|
5128
|
+
const path = value ?? "/policy/v3";
|
|
5129
|
+
if (!/^\/policy(?:\/[a-z0-9-]+)*$/.test(path)) {
|
|
5130
|
+
throw new Error("policy runtime path is invalid");
|
|
5131
|
+
}
|
|
5132
|
+
return path;
|
|
5133
|
+
}
|
|
4795
5134
|
|
|
4796
5135
|
// src/requester/index.ts
|
|
4797
5136
|
var REQUESTER_NEAR_EXPIRY_SECONDS = 30;
|
|
@@ -8198,13 +8537,13 @@ function canonicalizeRecapCaveats(caveats) {
|
|
|
8198
8537
|
}
|
|
8199
8538
|
if (Array.isArray(value)) return `[${value.map(canonicalize).join(",")}]`;
|
|
8200
8539
|
if (typeof value === "object") {
|
|
8201
|
-
const
|
|
8202
|
-
const prototype = Object.getPrototypeOf(
|
|
8540
|
+
const object4 = value;
|
|
8541
|
+
const prototype = Object.getPrototypeOf(object4);
|
|
8203
8542
|
if (prototype !== Object.prototype && prototype !== null) {
|
|
8204
8543
|
throw new Error("ReCap caveats must contain only JSON values.");
|
|
8205
8544
|
}
|
|
8206
|
-
return `{${Object.keys(
|
|
8207
|
-
(key) => `${JSON.stringify(key)}:${canonicalize(
|
|
8545
|
+
return `{${Object.keys(object4).sort().map(
|
|
8546
|
+
(key) => `${JSON.stringify(key)}:${canonicalize(object4[key])}`
|
|
8208
8547
|
).join(",")}}`;
|
|
8209
8548
|
}
|
|
8210
8549
|
throw new Error("ReCap caveats must contain only JSON values.");
|
|
@@ -8288,7 +8627,6 @@ var import_ed255195 = require("@noble/curves/ed25519");
|
|
|
8288
8627
|
var import_blake33 = require("@noble/hashes/blake3");
|
|
8289
8628
|
var import_sha2563 = require("@noble/hashes/sha256");
|
|
8290
8629
|
var ENVELOPE_SCHEMA = "xyz.tinycloud.share/envelope";
|
|
8291
|
-
var INLINE_PREFIX = "tc2:";
|
|
8292
8630
|
var RAW_CID_CODEC = 85;
|
|
8293
8631
|
var BLAKE3_256_CODE = 30;
|
|
8294
8632
|
var DEFAULT_MAX_INLINE_BYTES = 256 * 1024;
|
|
@@ -8335,8 +8673,8 @@ function canonicalJson(value) {
|
|
|
8335
8673
|
}
|
|
8336
8674
|
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
8337
8675
|
if (typeof value === "object") {
|
|
8338
|
-
const
|
|
8339
|
-
return `{${Object.keys(
|
|
8676
|
+
const object4 = value;
|
|
8677
|
+
return `{${Object.keys(object4).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(object4[key])}`).join(",")}}`;
|
|
8340
8678
|
}
|
|
8341
8679
|
throw new ShareEnvelopeError("invalid-envelope", "unsupported JSON value");
|
|
8342
8680
|
}
|
|
@@ -8993,25 +9331,8 @@ function parsePublishedShareLink(link, options2 = {}) {
|
|
|
8993
9331
|
}
|
|
8994
9332
|
const origin2 = normalizeOrigin(url.origin);
|
|
8995
9333
|
if (options2.trustedOrigins !== void 0 && !options2.trustedOrigins.map(normalizeOrigin).includes(origin2)) throw new ShareEnvelopeError("origin-mismatch", "share link origin is not trusted");
|
|
8996
|
-
if (url.
|
|
8997
|
-
if (url.
|
|
8998
|
-
let payload;
|
|
8999
|
-
try {
|
|
9000
|
-
const bytes2 = base64UrlToBytes(url.hash.slice("#v=2&p=".length));
|
|
9001
|
-
if (bytes2.length > (options2.maxInlineBytes ?? DEFAULT_MAX_INLINE_BYTES) * 2) throw new Error("inline payload too large");
|
|
9002
|
-
const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes2));
|
|
9003
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("inline payload shape");
|
|
9004
|
-
payload = value;
|
|
9005
|
-
} catch (error) {
|
|
9006
|
-
throw new ShareEnvelopeError("invalid-link", error instanceof Error ? error.message : "invalid inline payload");
|
|
9007
|
-
}
|
|
9008
|
-
if (payload.v !== 2 || typeof payload.c !== "string" || typeof payload.cid !== "string" || payload.k !== void 0 && typeof payload.k !== "string" || Object.keys(payload).some((key3) => !["v", "c", "cid", "k"].includes(key3))) throw new ShareEnvelopeError("invalid-link", "inline payload fields are invalid");
|
|
9009
|
-
const sealed = base64UrlToBytes(payload.c);
|
|
9010
|
-
if (!verifyPublishedShareCid(payload.cid, sealed)) throw new ShareEnvelopeError("cid-mismatch", "inline payload CID does not match");
|
|
9011
|
-
const key2 = payload.k === void 0 ? void 0 : base64UrlToBytes(payload.k);
|
|
9012
|
-
if (key2 !== void 0 && key2.length !== 32) throw new ShareEnvelopeError("invalid-link", "inline payload key is invalid");
|
|
9013
|
-
return { origin: origin2, cid: payload.cid, ...key2 === void 0 ? {} : { key: key2 }, sealed };
|
|
9014
|
-
}
|
|
9334
|
+
if (url.username !== "" || url.password !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
|
|
9335
|
+
if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
|
|
9015
9336
|
const match = /^\/s\/([a-z2-7]+)$/.exec(url.pathname);
|
|
9016
9337
|
if (match === null || !url.hash.startsWith("#k=")) throw new ShareEnvelopeError("invalid-link", "published share link is invalid");
|
|
9017
9338
|
const key = base64UrlToBytes(url.hash.slice(3));
|
|
@@ -9031,13 +9352,10 @@ function parseShareUrl(link, options2 = {}) {
|
|
|
9031
9352
|
} catch {
|
|
9032
9353
|
throw new ShareEnvelopeError("invalid-link", "share link must be an absolute URL");
|
|
9033
9354
|
}
|
|
9034
|
-
if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share links must not contain query parameters");
|
|
9035
9355
|
const origin2 = normalizeOrigin(url.origin);
|
|
9036
9356
|
if (options2.trustedOrigins !== void 0 && !options2.trustedOrigins.map(normalizeOrigin).includes(origin2)) throw new ShareEnvelopeError("origin-mismatch", "share link origin is not trusted");
|
|
9037
|
-
if (url.pathname === "/
|
|
9038
|
-
|
|
9039
|
-
return { kind: "inline", origin: origin2, cid: published.cid, key: published.key, url: `${url.origin}${url.pathname}`, protocol: "share-envelope-v2" };
|
|
9040
|
-
}
|
|
9357
|
+
if (url.pathname === "/viewer") throw new ShareEnvelopeError("invalid-link", "retired viewer links are not accepted");
|
|
9358
|
+
if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share links must not contain query parameters");
|
|
9041
9359
|
const match = url.pathname.match(/^\/s\/(i\/)?([^/]+)$/);
|
|
9042
9360
|
if (!match) throw new ShareEnvelopeError("invalid-link", "share link path is invalid");
|
|
9043
9361
|
const fragmentParams = url.hash === "" ? void 0 : new URLSearchParams(url.hash.slice(1));
|
|
@@ -9060,9 +9378,10 @@ function parseShareUrl(link, options2 = {}) {
|
|
|
9060
9378
|
}
|
|
9061
9379
|
function isV2ShareLink(link) {
|
|
9062
9380
|
try {
|
|
9063
|
-
|
|
9381
|
+
const url = new URL(link);
|
|
9382
|
+
return url.search === "" && url.pathname.startsWith("/s/");
|
|
9064
9383
|
} catch {
|
|
9065
|
-
return
|
|
9384
|
+
return false;
|
|
9066
9385
|
}
|
|
9067
9386
|
}
|
|
9068
9387
|
async function encryptShareBytes(bytes2, key) {
|
|
@@ -9518,8 +9837,8 @@ function canonicalJsonForProof(value) {
|
|
|
9518
9837
|
}
|
|
9519
9838
|
if (Array.isArray(value)) return `[${value.map(canonicalJsonForProof).join(",")}]`;
|
|
9520
9839
|
if (typeof value === "object") {
|
|
9521
|
-
const
|
|
9522
|
-
return `{${Object.keys(
|
|
9840
|
+
const object4 = value;
|
|
9841
|
+
return `{${Object.keys(object4).sort().map((key) => `${JSON.stringify(key)}:${canonicalJsonForProof(object4[key])}`).join(",")}}`;
|
|
9523
9842
|
}
|
|
9524
9843
|
throw new ShareAccessError("SHARE_SESSION_INVALID");
|
|
9525
9844
|
}
|
|
@@ -10228,12 +10547,12 @@ var ShareRecipientClient = class {
|
|
|
10228
10547
|
this.assertDetachedProof(request.proof);
|
|
10229
10548
|
}
|
|
10230
10549
|
parseSessionCapability(value) {
|
|
10231
|
-
const
|
|
10232
|
-
if (typeof
|
|
10233
|
-
if (!Array.isArray(
|
|
10234
|
-
const resource = this.requireObject(
|
|
10550
|
+
const object4 = this.requireObject(value, "session capability");
|
|
10551
|
+
if (typeof object4.spaceId !== "string") throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability space is invalid");
|
|
10552
|
+
if (!Array.isArray(object4.actions) || object4.actions.some((action) => !["read", "list", "edit"].includes(action))) throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability actions are invalid");
|
|
10553
|
+
const resource = this.requireObject(object4.resource, "session capability resource");
|
|
10235
10554
|
if (resource.kind !== "exact" && resource.kind !== "prefix" || typeof resource.path !== "string") throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability resource is invalid");
|
|
10236
|
-
return { spaceId:
|
|
10555
|
+
return { spaceId: object4.spaceId, actions: [...new Set(object4.actions)], resource: { kind: resource.kind, path: resource.path } };
|
|
10237
10556
|
}
|
|
10238
10557
|
assertProvidedPolicySession(session, envelope) {
|
|
10239
10558
|
const sessionExpiry = dateValue(session.expiresAt);
|
|
@@ -11873,62 +12192,21 @@ function validateOwnerSharePolicyRegistrationBytes(bytesValue, expected) {
|
|
|
11873
12192
|
return validateOwnerSharePolicyRegistration(value, expected);
|
|
11874
12193
|
}
|
|
11875
12194
|
|
|
11876
|
-
// src/delegations/share-delivery.ts
|
|
12195
|
+
// src/delegations/share-delivery-v3.ts
|
|
11877
12196
|
var import_ed255199 = require("@noble/curves/ed25519");
|
|
11878
|
-
var
|
|
11879
|
-
|
|
11880
|
-
|
|
11881
|
-
|
|
11882
|
-
|
|
11883
|
-
|
|
11884
|
-
return false;
|
|
11885
|
-
}
|
|
11886
|
-
return url.protocol === "https:" && url.origin === value && url.pathname === "/" && url.search === "" && url.hash === "" && url.username === "" && url.password === "";
|
|
11887
|
-
}
|
|
11888
|
-
var AUTHORIZATION_KEYS = [
|
|
11889
|
-
"type",
|
|
11890
|
-
"version",
|
|
11891
|
-
"jti",
|
|
11892
|
-
"shareCid",
|
|
11893
|
-
"shareId",
|
|
11894
|
-
"registrationCid",
|
|
11895
|
-
"delegationCid",
|
|
11896
|
-
"enforcementDelegationCid",
|
|
11897
|
-
"envelopeCid",
|
|
11898
|
-
"policyCid",
|
|
11899
|
-
"nodeAudience",
|
|
11900
|
-
"targetOrigin",
|
|
11901
|
-
"openCredentialsAudience",
|
|
11902
|
-
"holder",
|
|
11903
|
-
"recipientMatcher",
|
|
11904
|
-
"deliveryEmail",
|
|
11905
|
-
"shareUrl",
|
|
11906
|
-
"returnOrigin",
|
|
11907
|
-
"documentName",
|
|
11908
|
-
"senderDid",
|
|
11909
|
-
"senderTrust",
|
|
11910
|
-
"contentSource",
|
|
11911
|
-
"contentSourceDigest",
|
|
11912
|
-
"shareExpiresAt",
|
|
11913
|
-
"issuedAt",
|
|
11914
|
-
"reportAbuseToken",
|
|
11915
|
-
"actions",
|
|
11916
|
-
"resource",
|
|
11917
|
-
"authorityMaterialHandle",
|
|
11918
|
-
"authorityMaterialDigest",
|
|
11919
|
-
"requestBodyDigest",
|
|
11920
|
-
"idempotencyKey",
|
|
11921
|
-
"expiresAt",
|
|
11922
|
-
"dataAuthority"
|
|
11923
|
-
];
|
|
11924
|
-
function assertObject2(value, keys, label) {
|
|
12197
|
+
var import_sha2567 = require("@noble/hashes/sha256");
|
|
12198
|
+
var CREDENTIAL_INVITATION_REQUEST_DOMAIN = "xyz.tinycloud.credentials/invitation-request/v1\0";
|
|
12199
|
+
var DELIVERY_ADMISSION_DOMAIN = "xyz.tinycloud.policy/delivery-admission/v0\0";
|
|
12200
|
+
var REQUEST_KEYS = ["schema", "policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "audience", "issuedAt", "expiresAt", "nonce"];
|
|
12201
|
+
var ADMISSION_KEYS = ["schema", "policyId", "ownerDid", "recipient", "resource", "actions", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "senderKeyDid", "audience", "issuedAt", "expiresAt", "nonce", "signature"];
|
|
12202
|
+
function exactObject2(value, keys, label) {
|
|
11925
12203
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
|
|
11926
12204
|
const record2 = value;
|
|
11927
12205
|
if (Object.keys(record2).length !== keys.length || keys.some((key) => !Object.prototype.hasOwnProperty.call(record2, key))) throw new Error(`${label} has unknown or missing fields`);
|
|
11928
12206
|
return record2;
|
|
11929
12207
|
}
|
|
11930
|
-
function
|
|
11931
|
-
if (!/^[A-Za-z0-9_-]+$/.test(value)) throw new Error("share delivery value is not canonical base64url");
|
|
12208
|
+
function decodeBase64Url3(value) {
|
|
12209
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9_-]+$/.test(value)) throw new Error("share delivery value is not canonical base64url");
|
|
11932
12210
|
if (typeof atob === "function") {
|
|
11933
12211
|
const padded = value.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - value.length % 4) % 4);
|
|
11934
12212
|
return Uint8Array.from(atob(padded), (character) => character.charCodeAt(0));
|
|
@@ -11936,42 +12214,45 @@ function fromB642(value) {
|
|
|
11936
12214
|
if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(value, "base64url"));
|
|
11937
12215
|
throw new Error("base64url decoding is unavailable");
|
|
11938
12216
|
}
|
|
11939
|
-
function
|
|
11940
|
-
|
|
12217
|
+
function object3(value, label) {
|
|
12218
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
|
|
12219
|
+
return value;
|
|
12220
|
+
}
|
|
12221
|
+
function didKeyBytes2(did) {
|
|
12222
|
+
if (!did.startsWith("did:key:z") || /[#/?]/.test(did.slice("did:key:".length))) throw new Error("v3 delivery admission signer is invalid");
|
|
12223
|
+
const decoded = base58btc.decode(did.slice("did:key:".length));
|
|
12224
|
+
if (decoded.length !== 34 || decoded[0] !== 237 || decoded[1] !== 1) throw new Error("v3 delivery admission signer is invalid");
|
|
12225
|
+
return decoded.slice(2);
|
|
12226
|
+
}
|
|
12227
|
+
function validateShareDeliveryAuthorizationV3Bytes(bytes2, expected) {
|
|
12228
|
+
let parsed;
|
|
11941
12229
|
try {
|
|
11942
|
-
|
|
12230
|
+
parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes2));
|
|
11943
12231
|
} catch {
|
|
11944
|
-
throw new Error("share delivery
|
|
11945
|
-
}
|
|
11946
|
-
const root =
|
|
11947
|
-
const
|
|
11948
|
-
const
|
|
11949
|
-
const
|
|
11950
|
-
|
|
11951
|
-
|
|
11952
|
-
|
|
11953
|
-
|
|
11954
|
-
|
|
11955
|
-
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
if (
|
|
11960
|
-
const
|
|
11961
|
-
const
|
|
11962
|
-
if (
|
|
11963
|
-
const
|
|
11964
|
-
const
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
}
|
|
11968
|
-
function shareDeliveryTrustedKid(publicKey) {
|
|
11969
|
-
if (publicKey.length !== 32) throw new Error("share delivery node key must be a raw 32-byte Ed25519 public key");
|
|
11970
|
-
const prefixed = new Uint8Array(34);
|
|
11971
|
-
prefixed.set([237, 1]);
|
|
11972
|
-
prefixed.set(publicKey, 2);
|
|
11973
|
-
const did = `did:key:${base58btc.encode(prefixed)}`;
|
|
11974
|
-
return `${did}#${did.slice("did:key:".length)}`;
|
|
12232
|
+
throw new Error("v3 share delivery response is not valid UTF-8 JSON");
|
|
12233
|
+
}
|
|
12234
|
+
const root = exactObject2(parsed, ["request", "admission"], "v3 share delivery response");
|
|
12235
|
+
const request = exactObject2(root.request, REQUEST_KEYS, "credential invitation request");
|
|
12236
|
+
const admission = exactObject2(root.admission, ADMISSION_KEYS, "delivery admission");
|
|
12237
|
+
const signature = exactObject2(admission.signature, ["suite", "signerDid", "value"], "delivery admission signature");
|
|
12238
|
+
const envelope = object3(expected.request.envelope, "v3 share envelope");
|
|
12239
|
+
const policy = object3(envelope.policy, "v3 share policy");
|
|
12240
|
+
const requirement = object3(policy.credentialRequirement, "v3 credential requirement");
|
|
12241
|
+
const credentialType = object3(requirement.credentialType, "v3 credential type");
|
|
12242
|
+
const contentSource = object3(envelope.contentSource, "v3 content source");
|
|
12243
|
+
const envelopeSignature = object3(envelope.signature, "v3 envelope signature");
|
|
12244
|
+
const target = object3(envelope.target, "v3 target");
|
|
12245
|
+
const binding = object3(envelope.attestedEnforcerBinding, "v3 attested enforcer binding");
|
|
12246
|
+
const fields = ["policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "audience", "issuedAt", "expiresAt", "nonce"];
|
|
12247
|
+
if (request.schema !== "xyz.tinycloud.credentials/invitation-request/v1" || admission.schema !== "xyz.tinycloud.policy/delivery-admission/v0" || fields.some((field) => request[field] !== admission[field]) || request.policyId !== envelope.policyCid || request.recipient !== expected.request.recipientEmail || request.resource !== contentSource.kvResource || request.credentialType !== credentialType.id || request.credentialType !== "opencredentials.email/v1" || request.returnLink !== expected.request.shareUrl || request.envelopeRef !== expected.request.shareCid || request.label !== expected.request.documentName || request.shareExpiresAt !== envelope.expiry || request.audience !== expected.deliveryAudience || request.expiresAt !== expected.request.expiresAt || request.nonce !== expected.request.jti || admission.ownerDid !== envelopeSignature.signerDid || admission.senderKeyDid !== expected.senderKeyDid || canonicalizeSignedObjectUnsigned(admission.actions) !== canonicalizeSignedObjectUnsigned(["tinycloud.kv/get"]) || signature.suite !== "eddsa-ed25519-sha256-jcs-v1" || target.nodeAudience !== binding.nodeAudience || signature.signerDid !== binding.nodeAudience) throw new Error("v3 share delivery authorization is not bound to the submitted request");
|
|
12248
|
+
const issuedAt = Date.parse(String(request.issuedAt));
|
|
12249
|
+
const expiresAt = Date.parse(String(request.expiresAt));
|
|
12250
|
+
if (!Number.isFinite(issuedAt) || !Number.isFinite(expiresAt) || expiresAt <= issuedAt || expiresAt - issuedAt > 15 * 6e4) throw new Error("v3 share delivery authorization time is invalid");
|
|
12251
|
+
const admissionSignature = decodeBase64Url3(signature.value);
|
|
12252
|
+
const { signature: _signature, ...unsignedAdmission } = admission;
|
|
12253
|
+
const digest4 = (0, import_sha2567.sha256)(new TextEncoder().encode(`${DELIVERY_ADMISSION_DOMAIN}${canonicalizeSignedObjectUnsigned(unsignedAdmission)}`));
|
|
12254
|
+
if (admissionSignature.length !== 64 || !import_ed255199.ed25519.verify(admissionSignature, digest4, didKeyBytes2(String(signature.signerDid)))) throw new Error("v3 share delivery admission signature is invalid");
|
|
12255
|
+
return { request, admission };
|
|
11975
12256
|
}
|
|
11976
12257
|
|
|
11977
12258
|
// src/delegations/sender-share-store.ts
|
|
@@ -14694,8 +14975,8 @@ function stableJson(value) {
|
|
|
14694
14975
|
if (Array.isArray(value)) {
|
|
14695
14976
|
return `[${value.map(stableJson).join(",")}]`;
|
|
14696
14977
|
}
|
|
14697
|
-
const
|
|
14698
|
-
return `{${Object.keys(
|
|
14978
|
+
const object4 = value;
|
|
14979
|
+
return `{${Object.keys(object4).sort().map((key) => `${JSON.stringify(key)}:${stableJson(object4[key])}`).join(",")}}`;
|
|
14699
14980
|
}
|
|
14700
14981
|
function indexedDelegationFromRow(row) {
|
|
14701
14982
|
const [
|
|
@@ -15367,6 +15648,236 @@ function createOpenKeyCallbackSigningStrategy(options2) {
|
|
|
15367
15648
|
}
|
|
15368
15649
|
var defaultSignStrategy = { type: "auto-sign" };
|
|
15369
15650
|
|
|
15651
|
+
// src/authorization/manage-key.ts
|
|
15652
|
+
var TINYCLOUD_MANAGE_KEY_SCOPE = "tinycloud:manage-key";
|
|
15653
|
+
var TINYCLOUD_CANONICAL_IDENTITY_CLAIM = "https://tinycloud.xyz/canonical_identity";
|
|
15654
|
+
function requestTinyCloudManageKeyScope(scopes = []) {
|
|
15655
|
+
const requested = typeof scopes === "string" ? scopes.split(/\s+/u) : scopes;
|
|
15656
|
+
return [
|
|
15657
|
+
...new Set([...requested, TINYCLOUD_MANAGE_KEY_SCOPE].filter(Boolean))
|
|
15658
|
+
].sort().join(" ");
|
|
15659
|
+
}
|
|
15660
|
+
function hasTinyCloudManageKeyScope(scopes) {
|
|
15661
|
+
const values = Array.isArray(scopes) ? scopes : typeof scopes === "string" ? scopes.split(/\s+/u) : [];
|
|
15662
|
+
return values.includes(TINYCLOUD_MANAGE_KEY_SCOPE);
|
|
15663
|
+
}
|
|
15664
|
+
var OpenKeyManageKeyError = class extends Error {
|
|
15665
|
+
constructor(code3, message, approvalUrl, retryable = false) {
|
|
15666
|
+
super(message);
|
|
15667
|
+
this.code = code3;
|
|
15668
|
+
this.approvalUrl = approvalUrl;
|
|
15669
|
+
this.retryable = retryable;
|
|
15670
|
+
this.name = "OpenKeyManageKeyError";
|
|
15671
|
+
}
|
|
15672
|
+
};
|
|
15673
|
+
function isRecord2(value) {
|
|
15674
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
15675
|
+
}
|
|
15676
|
+
function rejected(message) {
|
|
15677
|
+
return new OpenKeyManageKeyError("MESSAGE_REJECTED", message);
|
|
15678
|
+
}
|
|
15679
|
+
function identityMismatch(message) {
|
|
15680
|
+
return new OpenKeyManageKeyError("IDENTITY_MISMATCH", message);
|
|
15681
|
+
}
|
|
15682
|
+
function parseCanonicalTinyCloudIdentity(value) {
|
|
15683
|
+
if (!isRecord2(value))
|
|
15684
|
+
throw rejected("Canonical TinyCloud identity is missing");
|
|
15685
|
+
const expectedKeys = [
|
|
15686
|
+
"address",
|
|
15687
|
+
"chainId",
|
|
15688
|
+
"did",
|
|
15689
|
+
"keyId",
|
|
15690
|
+
"spaceId",
|
|
15691
|
+
"version"
|
|
15692
|
+
];
|
|
15693
|
+
const actualKeys = Object.keys(value).sort();
|
|
15694
|
+
if (actualKeys.length !== expectedKeys.length || actualKeys.some((key, index) => key !== expectedKeys[index])) {
|
|
15695
|
+
throw rejected("Canonical TinyCloud identity has an invalid shape");
|
|
15696
|
+
}
|
|
15697
|
+
if (value.version !== "v1")
|
|
15698
|
+
throw rejected("Canonical TinyCloud identity version is unsupported");
|
|
15699
|
+
if (typeof value.keyId !== "string" || value.keyId.trim() === "") {
|
|
15700
|
+
throw rejected("Canonical TinyCloud identity key ID is invalid");
|
|
15701
|
+
}
|
|
15702
|
+
if (typeof value.address !== "string") {
|
|
15703
|
+
throw rejected("Canonical TinyCloud identity address is invalid");
|
|
15704
|
+
}
|
|
15705
|
+
let address;
|
|
15706
|
+
try {
|
|
15707
|
+
address = canonicalizeAddress(value.address);
|
|
15708
|
+
} catch {
|
|
15709
|
+
throw rejected("Canonical TinyCloud identity address is invalid");
|
|
15710
|
+
}
|
|
15711
|
+
if (address !== value.address) {
|
|
15712
|
+
throw rejected(
|
|
15713
|
+
"Canonical TinyCloud identity address must use EIP-55 checksum casing"
|
|
15714
|
+
);
|
|
15715
|
+
}
|
|
15716
|
+
if (typeof value.chainId !== "number" || !Number.isSafeInteger(value.chainId) || value.chainId <= 0) {
|
|
15717
|
+
throw rejected("Canonical TinyCloud identity chain ID is invalid");
|
|
15718
|
+
}
|
|
15719
|
+
const did = pkhDid(address, value.chainId);
|
|
15720
|
+
const spacePrefix = `tinycloud:pkh:eip155:${value.chainId}:${address}:`;
|
|
15721
|
+
if (typeof value.spaceId !== "string" || !value.spaceId.startsWith(spacePrefix)) {
|
|
15722
|
+
throw rejected("Canonical TinyCloud identity space is invalid");
|
|
15723
|
+
}
|
|
15724
|
+
const spaceName = value.spaceId.slice(spacePrefix.length);
|
|
15725
|
+
let spaceId;
|
|
15726
|
+
try {
|
|
15727
|
+
spaceId = makePkhSpaceId(address, value.chainId, spaceName);
|
|
15728
|
+
} catch {
|
|
15729
|
+
throw rejected("Canonical TinyCloud identity space is invalid");
|
|
15730
|
+
}
|
|
15731
|
+
if (value.did !== did || value.spaceId !== spaceId) {
|
|
15732
|
+
throw rejected("Canonical TinyCloud identity does not match its address");
|
|
15733
|
+
}
|
|
15734
|
+
return {
|
|
15735
|
+
version: "v1",
|
|
15736
|
+
keyId: value.keyId,
|
|
15737
|
+
address,
|
|
15738
|
+
chainId: value.chainId,
|
|
15739
|
+
did,
|
|
15740
|
+
spaceId
|
|
15741
|
+
};
|
|
15742
|
+
}
|
|
15743
|
+
function parseCanonicalTinyCloudIdentityClaims(claims) {
|
|
15744
|
+
if (!isRecord2(claims)) throw rejected("OAuth claims are missing");
|
|
15745
|
+
return parseCanonicalTinyCloudIdentity(
|
|
15746
|
+
claims[TINYCLOUD_CANONICAL_IDENTITY_CLAIM]
|
|
15747
|
+
);
|
|
15748
|
+
}
|
|
15749
|
+
function identitiesEqual(left, right) {
|
|
15750
|
+
return left.version === right.version && left.keyId === right.keyId && left.address === right.address && left.chainId === right.chainId && left.did === right.did && left.spaceId === right.spaceId;
|
|
15751
|
+
}
|
|
15752
|
+
function errorForResponse(body, status) {
|
|
15753
|
+
const message = body?.reason ?? body?.error ?? `OpenKey manage-key signing failed with HTTP ${status}`;
|
|
15754
|
+
switch (body?.code ?? body?.error) {
|
|
15755
|
+
case "missing_scope":
|
|
15756
|
+
case "consent_required":
|
|
15757
|
+
return new OpenKeyManageKeyError("CONSENT_REQUIRED", message, body?.approvalUrl);
|
|
15758
|
+
case "client_disabled":
|
|
15759
|
+
case "signing_disabled":
|
|
15760
|
+
case "grant_disabled":
|
|
15761
|
+
return new OpenKeyManageKeyError("GRANT_DISABLED", message, body?.approvalUrl);
|
|
15762
|
+
case "user_exclusive":
|
|
15763
|
+
return new OpenKeyManageKeyError("USER_EXCLUSIVE", message, body?.approvalUrl);
|
|
15764
|
+
case "token_expired":
|
|
15765
|
+
case "token_too_old":
|
|
15766
|
+
case "invalid_token":
|
|
15767
|
+
case "expired_token":
|
|
15768
|
+
return new OpenKeyManageKeyError("TOKEN_EXPIRED", message, body?.approvalUrl);
|
|
15769
|
+
case "canonical_key_unavailable":
|
|
15770
|
+
case "signer_failed":
|
|
15771
|
+
return new OpenKeyManageKeyError(
|
|
15772
|
+
"SIGNER_UNAVAILABLE",
|
|
15773
|
+
"The canonical key signer is temporarily unavailable",
|
|
15774
|
+
void 0,
|
|
15775
|
+
true
|
|
15776
|
+
);
|
|
15777
|
+
default:
|
|
15778
|
+
return new OpenKeyManageKeyError(
|
|
15779
|
+
status === 401 ? "TOKEN_EXPIRED" : "MESSAGE_REJECTED",
|
|
15780
|
+
message,
|
|
15781
|
+
body?.approvalUrl
|
|
15782
|
+
);
|
|
15783
|
+
}
|
|
15784
|
+
}
|
|
15785
|
+
async function resolveToken(token) {
|
|
15786
|
+
const value = typeof token === "function" ? await token() : token;
|
|
15787
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
15788
|
+
throw new OpenKeyManageKeyError(
|
|
15789
|
+
"CONSENT_REQUIRED",
|
|
15790
|
+
"A manage-key OAuth bearer token is required"
|
|
15791
|
+
);
|
|
15792
|
+
}
|
|
15793
|
+
return value;
|
|
15794
|
+
}
|
|
15795
|
+
function normalizeEndpoint(value) {
|
|
15796
|
+
let endpoint;
|
|
15797
|
+
try {
|
|
15798
|
+
endpoint = new URL(value);
|
|
15799
|
+
} catch {
|
|
15800
|
+
throw rejected("OpenKey manage-key signing endpoint must be an absolute URL");
|
|
15801
|
+
}
|
|
15802
|
+
const rawHttpAuthority = value.match(/^http:\/\/([^/?#]*)/iu)?.[1];
|
|
15803
|
+
const loopback = rawHttpAuthority !== void 0 && /^(?:localhost|127\.0\.0\.1|\[::1\])(?::[0-9]+)?$/u.test(rawHttpAuthority);
|
|
15804
|
+
if (endpoint.protocol !== "https:" && !(endpoint.protocol === "http:" && loopback) || endpoint.username !== "" || endpoint.password !== "" || endpoint.search !== "" || endpoint.hash !== "") {
|
|
15805
|
+
throw rejected("OpenKey manage-key signing endpoint must use HTTPS or exact loopback HTTP");
|
|
15806
|
+
}
|
|
15807
|
+
return endpoint.href;
|
|
15808
|
+
}
|
|
15809
|
+
function assertSignInRequest(request, identity3) {
|
|
15810
|
+
if (request.type !== "siwe" || request.purpose !== "sign-in" || request.chainId !== identity3.chainId || request.address !== identity3.address || typeof request.message !== "string" || request.message.length === 0) {
|
|
15811
|
+
throw rejected(
|
|
15812
|
+
"Manage-key signing only permits the canonical sign-in SIWE request"
|
|
15813
|
+
);
|
|
15814
|
+
}
|
|
15815
|
+
}
|
|
15816
|
+
function createOpenKeyManageKeySigningStrategy(options2) {
|
|
15817
|
+
const identity3 = parseCanonicalTinyCloudIdentity(options2.identity);
|
|
15818
|
+
const endpoint = normalizeEndpoint(options2.endpoint);
|
|
15819
|
+
return {
|
|
15820
|
+
type: "callback",
|
|
15821
|
+
openKeyAutoSign: true,
|
|
15822
|
+
handler: async (request) => {
|
|
15823
|
+
assertSignInRequest(request, identity3);
|
|
15824
|
+
const fetchImpl = options2.fetch ?? globalThis.fetch;
|
|
15825
|
+
if (!fetchImpl)
|
|
15826
|
+
throw rejected("OpenKey manage-key signing requires fetch");
|
|
15827
|
+
if (!hasTinyCloudManageKeyScope(options2.scopes)) {
|
|
15828
|
+
throw new OpenKeyManageKeyError(
|
|
15829
|
+
"CONSENT_REQUIRED",
|
|
15830
|
+
"The OAuth bearer token was not granted tinycloud:manage-key"
|
|
15831
|
+
);
|
|
15832
|
+
}
|
|
15833
|
+
const token = await resolveToken(options2.token);
|
|
15834
|
+
const response = await fetchImpl(endpoint, {
|
|
15835
|
+
method: "POST",
|
|
15836
|
+
credentials: "omit",
|
|
15837
|
+
headers: {
|
|
15838
|
+
authorization: `Bearer ${token}`,
|
|
15839
|
+
"content-type": "application/json"
|
|
15840
|
+
},
|
|
15841
|
+
body: JSON.stringify({
|
|
15842
|
+
address: request.address,
|
|
15843
|
+
chainId: request.chainId,
|
|
15844
|
+
message: request.message,
|
|
15845
|
+
type: "siwe"
|
|
15846
|
+
})
|
|
15847
|
+
});
|
|
15848
|
+
let body;
|
|
15849
|
+
try {
|
|
15850
|
+
const value = await response.json();
|
|
15851
|
+
body = isRecord2(value) ? value : void 0;
|
|
15852
|
+
} catch {
|
|
15853
|
+
body = void 0;
|
|
15854
|
+
}
|
|
15855
|
+
if (!response.ok || body?.approved !== true)
|
|
15856
|
+
throw errorForResponse(body, response.status);
|
|
15857
|
+
if (typeof body.signature !== "string")
|
|
15858
|
+
throw rejected("OpenKey manage-key signer did not return a signature");
|
|
15859
|
+
const responseIdentity = parseCanonicalTinyCloudIdentity(
|
|
15860
|
+
body.canonicalIdentity
|
|
15861
|
+
);
|
|
15862
|
+
if (!identitiesEqual(identity3, responseIdentity)) {
|
|
15863
|
+
throw identityMismatch(
|
|
15864
|
+
"OpenKey manage-key signer returned a different canonical identity"
|
|
15865
|
+
);
|
|
15866
|
+
}
|
|
15867
|
+
if (!await verifyEip191MessageSignature(
|
|
15868
|
+
request.message,
|
|
15869
|
+
body.signature,
|
|
15870
|
+
identity3.address
|
|
15871
|
+
)) {
|
|
15872
|
+
throw rejected(
|
|
15873
|
+
"OpenKey manage-key signer returned an invalid signature"
|
|
15874
|
+
);
|
|
15875
|
+
}
|
|
15876
|
+
return { approved: true, signature: body.signature };
|
|
15877
|
+
}
|
|
15878
|
+
};
|
|
15879
|
+
}
|
|
15880
|
+
|
|
15370
15881
|
// src/authorization/spaceCreation.ts
|
|
15371
15882
|
var AutoApproveSpaceCreationHandler = class {
|
|
15372
15883
|
/**
|
|
@@ -17373,6 +17884,7 @@ var DEFAULT_LOCAL_NODE_URL = "http://127.0.0.1:8000";
|
|
|
17373
17884
|
var LOCAL_LOOPBACK_PROBE_TIMEOUT_MS = 250;
|
|
17374
17885
|
var LOCAL_LINK_PROBE_TIMEOUT_MS = 750;
|
|
17375
17886
|
var LOCAL_LINK_HOST_SUFFIX = ".local.tinycloud.link";
|
|
17887
|
+
var OWNER_NODE_BINDING_TIMEOUT_MS = 5e3;
|
|
17376
17888
|
function createInMemoryLocalNodeIdentityStore() {
|
|
17377
17889
|
const pins = /* @__PURE__ */ new Map();
|
|
17378
17890
|
return {
|
|
@@ -17505,21 +18017,80 @@ async function verifyLocationRecord(input) {
|
|
|
17505
18017
|
}
|
|
17506
18018
|
return false;
|
|
17507
18019
|
}
|
|
17508
|
-
async function fetchLocationRecord(registryUrl, subject, fetchFn = globalThis.fetch) {
|
|
18020
|
+
async function fetchLocationRecord(registryUrl, subject, fetchFn = globalThis.fetch, signal) {
|
|
17509
18021
|
const url = `${registryUrl.replace(/\/$/, "")}/v1/locations/${encodeURIComponent(subject)}`;
|
|
17510
|
-
const response = await fetchFn(url
|
|
18022
|
+
const response = await fetchFn(url, {
|
|
18023
|
+
redirect: "error",
|
|
18024
|
+
headers: { accept: "application/json" },
|
|
18025
|
+
...signal === void 0 ? {} : { signal }
|
|
18026
|
+
});
|
|
17511
18027
|
if (response.status === 404) {
|
|
17512
18028
|
return null;
|
|
17513
18029
|
}
|
|
17514
18030
|
if (!response.ok) {
|
|
17515
18031
|
throw new Error(`location registry returned HTTP ${response.status}`);
|
|
17516
18032
|
}
|
|
17517
|
-
const
|
|
18033
|
+
const text2 = await boundedResponseText(response, "location registry", 64 * 1024);
|
|
18034
|
+
let body;
|
|
18035
|
+
try {
|
|
18036
|
+
body = JSON.parse(text2);
|
|
18037
|
+
} catch {
|
|
18038
|
+
throw new LocationRecordValidationError("registry response is not JSON");
|
|
18039
|
+
}
|
|
17518
18040
|
if (body.record === void 0) {
|
|
17519
18041
|
throw new LocationRecordValidationError("registry response missing record");
|
|
17520
18042
|
}
|
|
17521
18043
|
return validateLocationRecord(body.record);
|
|
17522
18044
|
}
|
|
18045
|
+
async function publishLocationRecord(options2) {
|
|
18046
|
+
validateSubject(options2.subject);
|
|
18047
|
+
const registryUrl = exactHttpOrigin(options2.registryUrl, "registry URL");
|
|
18048
|
+
const nodeOrigin = exactHttpOrigin(options2.nodeOrigin, "node origin");
|
|
18049
|
+
const fetchFn = options2.fetch ?? globalThis.fetch.bind(globalThis);
|
|
18050
|
+
const current = await fetchLocationRecord(registryUrl, options2.subject, fetchFn);
|
|
18051
|
+
if (current !== null) {
|
|
18052
|
+
if (current.subject !== options2.subject || !await verifyLocationRecord(current)) {
|
|
18053
|
+
throw new LocationRecordValidationError("existing location record signature is invalid");
|
|
18054
|
+
}
|
|
18055
|
+
if (publishedHttpOrigins(current).includes(nodeOrigin)) {
|
|
18056
|
+
return current;
|
|
18057
|
+
}
|
|
18058
|
+
}
|
|
18059
|
+
const record2 = await signLocationRecord({
|
|
18060
|
+
version: 1,
|
|
18061
|
+
subject: options2.subject,
|
|
18062
|
+
multiaddrs: [...current?.multiaddrs ?? [], httpUrlToMultiaddr(nodeOrigin)],
|
|
18063
|
+
updated_at: (options2.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
18064
|
+
sequence: (current?.sequence ?? -1) + 1
|
|
18065
|
+
}, options2.signer);
|
|
18066
|
+
const response = await fetchFn(
|
|
18067
|
+
`${registryUrl}/v1/locations/${encodeURIComponent(options2.subject)}`,
|
|
18068
|
+
{
|
|
18069
|
+
method: "PUT",
|
|
18070
|
+
redirect: "error",
|
|
18071
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
18072
|
+
body: JSON.stringify(record2)
|
|
18073
|
+
}
|
|
18074
|
+
);
|
|
18075
|
+
if (!response.ok) {
|
|
18076
|
+
throw new Error(`location registry publish returned HTTP ${response.status}`);
|
|
18077
|
+
}
|
|
18078
|
+
const text2 = await boundedResponseText(response, "location registry", 64 * 1024);
|
|
18079
|
+
let body;
|
|
18080
|
+
try {
|
|
18081
|
+
body = JSON.parse(text2);
|
|
18082
|
+
} catch {
|
|
18083
|
+
throw new LocationRecordValidationError("registry publish response is not JSON");
|
|
18084
|
+
}
|
|
18085
|
+
if (body.record === void 0) {
|
|
18086
|
+
throw new LocationRecordValidationError("registry publish response missing record");
|
|
18087
|
+
}
|
|
18088
|
+
const published = validateLocationRecord(body.record);
|
|
18089
|
+
if (canonicalLocationPayload(locationPayloadForRecord(published)) !== canonicalLocationPayload(locationPayloadForRecord(record2)) || published.signature !== record2.signature || !await verifyLocationRecord(published)) {
|
|
18090
|
+
throw new LocationRecordValidationError("registry publish response does not match the signed record");
|
|
18091
|
+
}
|
|
18092
|
+
return published;
|
|
18093
|
+
}
|
|
17523
18094
|
async function resolveCloudLocation(subject, options2 = {}) {
|
|
17524
18095
|
validateSubject(subject);
|
|
17525
18096
|
const verifyRecords = options2.verifyRecords ?? true;
|
|
@@ -17770,6 +18341,123 @@ function multiaddrToHttpUrl(input) {
|
|
|
17770
18341
|
}
|
|
17771
18342
|
return uri;
|
|
17772
18343
|
}
|
|
18344
|
+
function exactHttpOrigin(value, label) {
|
|
18345
|
+
let url;
|
|
18346
|
+
try {
|
|
18347
|
+
url = new URL(value);
|
|
18348
|
+
} catch {
|
|
18349
|
+
throw new LocationRecordValidationError(`${label} must be an absolute URL origin`);
|
|
18350
|
+
}
|
|
18351
|
+
const loopback = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
|
|
18352
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) {
|
|
18353
|
+
throw new LocationRecordValidationError(`${label} must use HTTPS (or loopback HTTP)`);
|
|
18354
|
+
}
|
|
18355
|
+
if (url.username !== "" || url.password !== "" || url.pathname !== "/" || url.search !== "" || url.hash !== "") {
|
|
18356
|
+
throw new LocationRecordValidationError(`${label} must be an origin without credentials, path, query, or fragment`);
|
|
18357
|
+
}
|
|
18358
|
+
return url.origin;
|
|
18359
|
+
}
|
|
18360
|
+
function publishedHttpOrigins(record2) {
|
|
18361
|
+
const origins = [];
|
|
18362
|
+
for (const address of record2.multiaddrs) {
|
|
18363
|
+
try {
|
|
18364
|
+
origins.push(exactHttpOrigin(multiaddrToHttpUrl(address), "published node URL"));
|
|
18365
|
+
} catch {
|
|
18366
|
+
}
|
|
18367
|
+
}
|
|
18368
|
+
return origins;
|
|
18369
|
+
}
|
|
18370
|
+
async function boundedResponseText(response, label, maxBytes) {
|
|
18371
|
+
const declaredLength = response.headers.get("content-length");
|
|
18372
|
+
if (declaredLength !== null && (!/^\d+$/.test(declaredLength) || Number(declaredLength) > maxBytes)) {
|
|
18373
|
+
throw new LocationRecordValidationError(`${label} response is too large`);
|
|
18374
|
+
}
|
|
18375
|
+
if (response.body === null) return "";
|
|
18376
|
+
const reader = response.body.getReader();
|
|
18377
|
+
const chunks = [];
|
|
18378
|
+
let total = 0;
|
|
18379
|
+
try {
|
|
18380
|
+
while (true) {
|
|
18381
|
+
const { done, value } = await reader.read();
|
|
18382
|
+
if (done) break;
|
|
18383
|
+
if (value === void 0) continue;
|
|
18384
|
+
total += value.byteLength;
|
|
18385
|
+
if (total > maxBytes) {
|
|
18386
|
+
await reader.cancel().catch(() => void 0);
|
|
18387
|
+
throw new LocationRecordValidationError(`${label} response is too large`);
|
|
18388
|
+
}
|
|
18389
|
+
chunks.push(value);
|
|
18390
|
+
}
|
|
18391
|
+
} finally {
|
|
18392
|
+
reader.releaseLock();
|
|
18393
|
+
}
|
|
18394
|
+
const bytes2 = new Uint8Array(total);
|
|
18395
|
+
let offset = 0;
|
|
18396
|
+
for (const chunk of chunks) {
|
|
18397
|
+
bytes2.set(chunk, offset);
|
|
18398
|
+
offset += chunk.byteLength;
|
|
18399
|
+
}
|
|
18400
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes2);
|
|
18401
|
+
}
|
|
18402
|
+
function boundedSignal(parent, timeoutMs) {
|
|
18403
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
18404
|
+
throw new LocationRecordValidationError("owner-node binding timeout must be positive");
|
|
18405
|
+
}
|
|
18406
|
+
const controller = new AbortController();
|
|
18407
|
+
const abortFromParent = () => controller.abort(parent?.reason);
|
|
18408
|
+
if (parent?.aborted) abortFromParent();
|
|
18409
|
+
else parent?.addEventListener("abort", abortFromParent, { once: true });
|
|
18410
|
+
const timer = setTimeout(() => controller.abort(new DOMException("owner-node binding timed out", "TimeoutError")), timeoutMs);
|
|
18411
|
+
return {
|
|
18412
|
+
signal: controller.signal,
|
|
18413
|
+
dispose() {
|
|
18414
|
+
clearTimeout(timer);
|
|
18415
|
+
parent?.removeEventListener("abort", abortFromParent);
|
|
18416
|
+
}
|
|
18417
|
+
};
|
|
18418
|
+
}
|
|
18419
|
+
async function verifyOwnerNodeBinding(options2) {
|
|
18420
|
+
validateSubject(options2.ownerDid);
|
|
18421
|
+
validateSubject(options2.nodeDid);
|
|
18422
|
+
const registryUrl = exactHttpOrigin(options2.registryUrl, "registry URL");
|
|
18423
|
+
const nodeOrigin = exactHttpOrigin(options2.nodeOrigin, "node origin");
|
|
18424
|
+
const fetchFn = options2.fetch ?? globalThis.fetch.bind(globalThis);
|
|
18425
|
+
const lookup = boundedSignal(options2.signal, options2.timeoutMs ?? OWNER_NODE_BINDING_TIMEOUT_MS);
|
|
18426
|
+
try {
|
|
18427
|
+
const record2 = await fetchLocationRecord(registryUrl, options2.ownerDid, fetchFn, lookup.signal);
|
|
18428
|
+
if (record2 === null) {
|
|
18429
|
+
throw new LocationRecordValidationError("owner has no published location record");
|
|
18430
|
+
}
|
|
18431
|
+
if (record2.subject !== options2.ownerDid || !await verifyLocationRecord(record2)) {
|
|
18432
|
+
throw new LocationRecordValidationError("owner location record signature is invalid");
|
|
18433
|
+
}
|
|
18434
|
+
const publishedOrigins = publishedHttpOrigins(record2);
|
|
18435
|
+
if (!publishedOrigins.includes(nodeOrigin)) {
|
|
18436
|
+
throw new LocationRecordValidationError("share target is not in the owner's signed location record");
|
|
18437
|
+
}
|
|
18438
|
+
const response = await fetchFn(new URL("/info", nodeOrigin), {
|
|
18439
|
+
redirect: "error",
|
|
18440
|
+
headers: { accept: "application/json" },
|
|
18441
|
+
signal: lookup.signal
|
|
18442
|
+
});
|
|
18443
|
+
if (!response.ok) {
|
|
18444
|
+
throw new LocationRecordValidationError(`target node /info returned HTTP ${response.status}`);
|
|
18445
|
+
}
|
|
18446
|
+
const text2 = await boundedResponseText(response, "target node /info", 16 * 1024);
|
|
18447
|
+
let body;
|
|
18448
|
+
try {
|
|
18449
|
+
body = JSON.parse(text2);
|
|
18450
|
+
} catch {
|
|
18451
|
+
throw new LocationRecordValidationError("target node /info response is not JSON");
|
|
18452
|
+
}
|
|
18453
|
+
if (body === null || typeof body !== "object" || Array.isArray(body) || body.nodeId !== options2.nodeDid) {
|
|
18454
|
+
throw new LocationRecordValidationError("target node identity does not match the share attestation");
|
|
18455
|
+
}
|
|
18456
|
+
return Object.freeze({ record: record2, nodeOrigin, nodeDid: options2.nodeDid });
|
|
18457
|
+
} finally {
|
|
18458
|
+
lookup.dispose();
|
|
18459
|
+
}
|
|
18460
|
+
}
|
|
17773
18461
|
function httpUrlToMultiaddr(input) {
|
|
17774
18462
|
const url = new URL(input);
|
|
17775
18463
|
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
@@ -17931,7 +18619,7 @@ async function verifyPkhSignature(did, payload, signature) {
|
|
|
17931
18619
|
}
|
|
17932
18620
|
function verifyDidKeySignature(did, payload, signature) {
|
|
17933
18621
|
const publicKey = ed25519PublicKeyFromDidKey2(did);
|
|
17934
|
-
const signatureBytes =
|
|
18622
|
+
const signatureBytes = decodeBase64Url4(signature);
|
|
17935
18623
|
if (signatureBytes.length !== 64) {
|
|
17936
18624
|
throw new LocationRecordValidationError(
|
|
17937
18625
|
"did:key signature must be a base64url Ed25519 signature"
|
|
@@ -17984,7 +18672,7 @@ function base64UrlEncode3(bytes2) {
|
|
|
17984
18672
|
}
|
|
17985
18673
|
return output;
|
|
17986
18674
|
}
|
|
17987
|
-
function
|
|
18675
|
+
function decodeBase64Url4(value) {
|
|
17988
18676
|
const alphabet2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
17989
18677
|
const bytes2 = [];
|
|
17990
18678
|
let buffer = 0;
|