@tinycloud/sdk-core 3.0.0-beta.1 → 3.0.0-beta.11
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 +758 -306
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +67 -8
- package/dist/index.d.ts +67 -8
- package/dist/index.js +760 -311
- package/dist/index.js.map +1 -1
- package/dist/policy/index.cjs +324 -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 +324 -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,7 @@ __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,
|
|
139
143
|
OpenKeyManageKeyError: () => OpenKeyManageKeyError,
|
|
140
144
|
POLICY_CAPABILITY_V1_DOMAIN: () => POLICY_CAPABILITY_V1_DOMAIN,
|
|
141
145
|
POLICY_CAPABILITY_V1_SCHEMA: () => POLICY_CAPABILITY_V1_SCHEMA,
|
|
@@ -149,6 +153,8 @@ __export(index_exports, {
|
|
|
149
153
|
POLICY_ENGINE_RESOLVE_REQUEST_SCHEMA: () => POLICY_ENGINE_RESOLVE_REQUEST_SCHEMA,
|
|
150
154
|
POLICY_PRESENTATION_V3_DOMAIN: () => POLICY_PRESENTATION_V3_DOMAIN,
|
|
151
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,
|
|
152
158
|
POLICY_SCHEMA: () => POLICY_SCHEMA,
|
|
153
159
|
POLICY_SESSION_UCAN_V1_PROFILE: () => POLICY_SESSION_UCAN_V1_PROFILE,
|
|
154
160
|
POLICY_STATUS_SCHEMA: () => POLICY_STATUS_SCHEMA,
|
|
@@ -173,7 +179,6 @@ __export(index_exports, {
|
|
|
173
179
|
SECRET_RECORDS_SCHEMA: () => import_bootstrap4.SECRET_RECORDS_SCHEMA,
|
|
174
180
|
SERVICE_LONG_TO_SHORT: () => SERVICE_LONG_TO_SHORT,
|
|
175
181
|
SERVICE_SHORT_TO_LONG: () => SERVICE_SHORT_TO_LONG,
|
|
176
|
-
SHARE_DELIVERY_AUTHORIZATION_DOMAIN: () => SHARE_DELIVERY_AUTHORIZATION_DOMAIN,
|
|
177
182
|
SPACE: () => import_bootstrap4.SPACE,
|
|
178
183
|
SQL: () => import_bootstrap4.SQL,
|
|
179
184
|
SQLAction: () => import_sdk_services8.SQLAction,
|
|
@@ -226,6 +231,7 @@ __export(index_exports, {
|
|
|
226
231
|
activateSessionWithHost: () => activateSessionWithHost,
|
|
227
232
|
addressStorageKey: () => addressStorageKey,
|
|
228
233
|
admitPolicyCredentialV3: () => admitPolicyCredentialV3,
|
|
234
|
+
admitPolicyCredentialV4: () => admitPolicyCredentialV4,
|
|
229
235
|
applyPrefix: () => applyPrefix,
|
|
230
236
|
bootstrapEncryptionNetworkId: () => import_bootstrap4.bootstrapEncryptionNetworkId,
|
|
231
237
|
bootstrapSpaceId: () => import_bootstrap4.bootstrapSpaceId,
|
|
@@ -236,6 +242,7 @@ __export(index_exports, {
|
|
|
236
242
|
buildDecryptInvocation: () => import_sdk_services8.buildDecryptInvocation,
|
|
237
243
|
buildNetworkId: () => import_sdk_services8.buildNetworkId,
|
|
238
244
|
buildPolicyCredentialPresentationV3: () => buildPolicyCredentialPresentationV3,
|
|
245
|
+
buildPolicyCredentialPresentationV4: () => buildPolicyCredentialPresentationV4,
|
|
239
246
|
buildSpaceUri: () => buildSpaceUri,
|
|
240
247
|
canonicalDigest: () => canonicalDigest,
|
|
241
248
|
canonicalHashHex: () => import_sdk_services8.canonicalHashHex,
|
|
@@ -266,6 +273,7 @@ __export(index_exports, {
|
|
|
266
273
|
createAndSignSignedObject: () => createAndSignSignedObject,
|
|
267
274
|
createCapabilityKeyRegistry: () => createCapabilityKeyRegistry,
|
|
268
275
|
createDelegatedShareKey: () => createDelegatedShareKey,
|
|
276
|
+
createEmailCredentialRequirement: () => createEmailCredentialRequirement,
|
|
269
277
|
createHolderBinding: () => createHolderBinding,
|
|
270
278
|
createInMemoryLocalNodeIdentityStore: () => createInMemoryLocalNodeIdentityStore,
|
|
271
279
|
createLocalStorageLocalNodeIdentityStore: () => createLocalStorageLocalNodeIdentityStore,
|
|
@@ -344,6 +352,7 @@ __export(index_exports, {
|
|
|
344
352
|
isNetworkId: () => import_sdk_services8.isNetworkId,
|
|
345
353
|
isPlausibleOpenKeyActionId: () => isPlausibleOpenKeyActionId,
|
|
346
354
|
isV2ShareLink: () => isV2ShareLink,
|
|
355
|
+
issuedCredentialEnvelopeFromVerified: () => issuedCredentialEnvelopeFromVerified,
|
|
347
356
|
jcsCanonicalize: () => jcsCanonicalize,
|
|
348
357
|
loadManifest: () => loadManifest,
|
|
349
358
|
locationPayloadForRecord: () => locationPayloadForRecord,
|
|
@@ -372,6 +381,7 @@ __export(index_exports, {
|
|
|
372
381
|
parsePolicySessionUcan: () => parsePolicySessionUcan,
|
|
373
382
|
parsePortableDelegation: () => parsePortableDelegation,
|
|
374
383
|
parseRecapCapabilities: () => parseRecapCapabilities,
|
|
384
|
+
parseSecretCatalogKey: () => import_sdk_services8.parseSecretCatalogKey,
|
|
375
385
|
parseShareArtifact: () => parseShareArtifact,
|
|
376
386
|
parseShareUrl: () => parseShareUrl,
|
|
377
387
|
parseSpaceUri: () => parseSpaceUri,
|
|
@@ -384,7 +394,9 @@ __export(index_exports, {
|
|
|
384
394
|
principalDidEquals: () => principalDidEquals,
|
|
385
395
|
projectUnifiedPolicyCapabilities: () => projectUnifiedPolicyCapabilities,
|
|
386
396
|
projectUnifiedPolicyCapability: () => projectUnifiedPolicyCapability,
|
|
397
|
+
publishLocationRecord: () => publishLocationRecord,
|
|
387
398
|
recapCaveatsEqual: () => recapCaveatsEqual,
|
|
399
|
+
registerPolicyV3: () => registerPolicyV3,
|
|
388
400
|
renewPolicyRootStatusV3: () => renewPolicyRootStatusV3,
|
|
389
401
|
requestTinyCloudManageKeyScope: () => requestTinyCloudManageKeyScope,
|
|
390
402
|
resolveCloudLocation: () => resolveCloudLocation,
|
|
@@ -402,10 +414,10 @@ __export(index_exports, {
|
|
|
402
414
|
serviceError: () => import_sdk_services8.serviceError,
|
|
403
415
|
sha256Base64Url: () => sha256Base64Url,
|
|
404
416
|
shareCapabilityAllows: () => shareCapabilityAllows,
|
|
405
|
-
shareDeliveryTrustedKid: () => shareDeliveryTrustedKid,
|
|
406
417
|
signCompactUcanAuthorization: () => signCompactUcanAuthorization,
|
|
407
418
|
signCompactUcanRootAuthorization: () => signCompactUcanRootAuthorization,
|
|
408
419
|
signLocationRecord: () => signLocationRecord,
|
|
420
|
+
signPolicyCredentialPresentationV4: () => signPolicyCredentialPresentationV4,
|
|
409
421
|
signShareEnvelope: () => signShareEnvelope,
|
|
410
422
|
signedObjectIdFor: () => signedObjectIdFor,
|
|
411
423
|
submitHostDelegation: () => submitHostDelegation,
|
|
@@ -438,13 +450,14 @@ __export(index_exports, {
|
|
|
438
450
|
validatePolicyStatusSignedShape: () => validatePolicyStatusSignedShape,
|
|
439
451
|
validatePolicyStatusUnsigned: () => validatePolicyStatusUnsigned,
|
|
440
452
|
validatePolicyUnsigned: () => validatePolicyUnsigned,
|
|
441
|
-
|
|
453
|
+
validateShareDeliveryAuthorizationV3Bytes: () => validateShareDeliveryAuthorizationV3Bytes,
|
|
442
454
|
validateUnifiedPolicyV2: () => validateUnifiedPolicyV2,
|
|
443
455
|
verifyDecryptResponse: () => import_sdk_services8.verifyDecryptResponse,
|
|
444
456
|
verifyDidKeyEd25519Signature: () => verifyDidKeyEd25519Signature,
|
|
445
457
|
verifyEip191MessageSignature: () => verifyEip191MessageSignature,
|
|
446
458
|
verifyIssuedCredential: () => verifyIssuedCredential,
|
|
447
459
|
verifyLocationRecord: () => verifyLocationRecord,
|
|
460
|
+
verifyOwnerNodeBinding: () => verifyOwnerNodeBinding,
|
|
448
461
|
verifyPolicy: () => verifyPolicy,
|
|
449
462
|
verifyPolicyEngineRecord: () => verifyPolicyEngineRecord,
|
|
450
463
|
verifyPolicyRootRevocationV3: () => verifyPolicyRootRevocationV3,
|
|
@@ -755,6 +768,7 @@ var CREDENTIAL_ERROR_CODES = [
|
|
|
755
768
|
"ISSUER_UNREADY",
|
|
756
769
|
"OFFLINE",
|
|
757
770
|
"POPUP_BLOCKED",
|
|
771
|
+
"PROOF_REJECTED",
|
|
758
772
|
"REQUEST_EXPIRED",
|
|
759
773
|
"REQUEST_SUBSTITUTED",
|
|
760
774
|
"SIGNATURE_REJECTED",
|
|
@@ -768,6 +782,7 @@ var RECOVERABLE = /* @__PURE__ */ new Set([
|
|
|
768
782
|
"ISSUER_UNREADY",
|
|
769
783
|
"OFFLINE",
|
|
770
784
|
"POPUP_BLOCKED",
|
|
785
|
+
"PROOF_REJECTED",
|
|
771
786
|
"REQUEST_EXPIRED",
|
|
772
787
|
"SIGNATURE_REJECTED",
|
|
773
788
|
"UNSUPPORTED_PROFILE",
|
|
@@ -1009,6 +1024,17 @@ function isDigest(value) {
|
|
|
1009
1024
|
|
|
1010
1025
|
// src/credentials/requirement.ts
|
|
1011
1026
|
var ID2 = /^[a-z0-9][a-z0-9._-]{0,127}(?:\/v1)?$/;
|
|
1027
|
+
var EMAIL_CREDENTIAL_MAX_AGE_SECONDS = 3600;
|
|
1028
|
+
function createEmailCredentialRequirement(input) {
|
|
1029
|
+
return validateCredentialRequirement({
|
|
1030
|
+
type: "TinyCloudCredentialRequirement",
|
|
1031
|
+
version: 1,
|
|
1032
|
+
profile: input.profile,
|
|
1033
|
+
credentialType: input.credentialType,
|
|
1034
|
+
claims: { email: input.email },
|
|
1035
|
+
maxAgeSeconds: EMAIL_CREDENTIAL_MAX_AGE_SECONDS
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1012
1038
|
function validateCredentialRequirement(value) {
|
|
1013
1039
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new CredentialError("UNSUPPORTED_PROFILE", "Credential requirement is invalid");
|
|
1014
1040
|
const raw = value;
|
|
@@ -2223,16 +2249,16 @@ function policyCapabilityContains(authority, request) {
|
|
|
2223
2249
|
return caveatsContain(auth.caveats, req.caveats);
|
|
2224
2250
|
}
|
|
2225
2251
|
function normalizePolicyCapabilityWithOptions(input, options2) {
|
|
2226
|
-
const
|
|
2227
|
-
assertExactKeys(
|
|
2228
|
-
const service = requiredString(
|
|
2252
|
+
const object4 = expectObject(input, "$");
|
|
2253
|
+
assertExactKeys(object4, ["service", "space", "path", "actions", "caveats"], "$");
|
|
2254
|
+
const service = requiredString(object4, "service", "$", "policy-capability-malformed-service");
|
|
2229
2255
|
if (!CEILING_SERVICES.has(service)) {
|
|
2230
2256
|
throw new PolicyCapabilityError(
|
|
2231
2257
|
"policy-capability-malformed-service",
|
|
2232
2258
|
"$.service is outside the frozen permissionsCeiling vocabulary"
|
|
2233
2259
|
);
|
|
2234
2260
|
}
|
|
2235
|
-
const space = requiredString(
|
|
2261
|
+
const space = requiredString(object4, "space", "$", "policy-capability-malformed-space");
|
|
2236
2262
|
validateConcreteSpace(space);
|
|
2237
2263
|
const canonicalSpace = space.normalize("NFC");
|
|
2238
2264
|
if (options2.requireCanonical && canonicalSpace !== space) {
|
|
@@ -2241,7 +2267,7 @@ function normalizePolicyCapabilityWithOptions(input, options2) {
|
|
|
2241
2267
|
"$.space must already be canonical NFC"
|
|
2242
2268
|
);
|
|
2243
2269
|
}
|
|
2244
|
-
const rawPath = requiredString(
|
|
2270
|
+
const rawPath = requiredString(object4, "path", "$", "policy-capability-malformed-path");
|
|
2245
2271
|
validateRawPath(rawPath, options2);
|
|
2246
2272
|
const path = normalizePath(rawPath);
|
|
2247
2273
|
if (options2.requireCanonical && path !== rawPath) {
|
|
@@ -2252,7 +2278,7 @@ function normalizePolicyCapabilityWithOptions(input, options2) {
|
|
|
2252
2278
|
}
|
|
2253
2279
|
validateRawPath(path, options2);
|
|
2254
2280
|
const actions = normalizeActions(
|
|
2255
|
-
requiredArray(
|
|
2281
|
+
requiredArray(object4, "actions", "$", "policy-capability-malformed-action"),
|
|
2256
2282
|
service,
|
|
2257
2283
|
options2
|
|
2258
2284
|
);
|
|
@@ -2261,9 +2287,9 @@ function normalizePolicyCapabilityWithOptions(input, options2) {
|
|
|
2261
2287
|
output.space = canonicalSpace;
|
|
2262
2288
|
output.path = path;
|
|
2263
2289
|
output.actions = actions;
|
|
2264
|
-
if (hasOwn(
|
|
2290
|
+
if (hasOwn(object4, "caveats")) {
|
|
2265
2291
|
output.caveats = validateCaveats(
|
|
2266
|
-
requiredValue(
|
|
2292
|
+
requiredValue(object4, "caveats", "$", "policy-capability-malformed-caveats"),
|
|
2267
2293
|
output.service
|
|
2268
2294
|
);
|
|
2269
2295
|
}
|
|
@@ -2286,9 +2312,9 @@ function expectObject(input, path) {
|
|
|
2286
2312
|
);
|
|
2287
2313
|
}
|
|
2288
2314
|
}
|
|
2289
|
-
function assertExactKeys(
|
|
2315
|
+
function assertExactKeys(object4, allowed, path) {
|
|
2290
2316
|
const allowedSet = new Set(allowed);
|
|
2291
|
-
for (const key of Object.keys(
|
|
2317
|
+
for (const key of Object.keys(object4)) {
|
|
2292
2318
|
if (!allowedSet.has(key)) {
|
|
2293
2319
|
throw new PolicyCapabilityError(
|
|
2294
2320
|
key === "id" || key === "scope" ? "policy-capability-malformed" : "policy-capability-unknown-key",
|
|
@@ -2297,21 +2323,21 @@ function assertExactKeys(object3, allowed, path) {
|
|
|
2297
2323
|
}
|
|
2298
2324
|
}
|
|
2299
2325
|
}
|
|
2300
|
-
function requiredValue(
|
|
2301
|
-
if (!hasOwn(
|
|
2326
|
+
function requiredValue(object4, key, path, code3) {
|
|
2327
|
+
if (!hasOwn(object4, key)) {
|
|
2302
2328
|
throw new PolicyCapabilityError(code3, `${path}.${key} is required`);
|
|
2303
2329
|
}
|
|
2304
|
-
return
|
|
2330
|
+
return object4[key];
|
|
2305
2331
|
}
|
|
2306
|
-
function requiredString(
|
|
2307
|
-
const value = requiredValue(
|
|
2332
|
+
function requiredString(object4, key, path, code3) {
|
|
2333
|
+
const value = requiredValue(object4, key, path, code3);
|
|
2308
2334
|
if (typeof value !== "string" || value.length === 0) {
|
|
2309
2335
|
throw new PolicyCapabilityError(code3, `${path}.${key} must be a non-empty string`);
|
|
2310
2336
|
}
|
|
2311
2337
|
return value;
|
|
2312
2338
|
}
|
|
2313
|
-
function requiredArray(
|
|
2314
|
-
const value = requiredValue(
|
|
2339
|
+
function requiredArray(object4, key, path, code3) {
|
|
2340
|
+
const value = requiredValue(object4, key, path, code3);
|
|
2315
2341
|
if (!Array.isArray(value)) {
|
|
2316
2342
|
throw new PolicyCapabilityError(code3, `${path}.${key} must be an array`);
|
|
2317
2343
|
}
|
|
@@ -2456,11 +2482,11 @@ function validateSqlStatement(input, path) {
|
|
|
2456
2482
|
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
2457
2483
|
throw new PolicyCapabilityError("policy-capability-malformed-caveats", `${path} must be an object`);
|
|
2458
2484
|
}
|
|
2459
|
-
const
|
|
2460
|
-
assertCaveatKeys(
|
|
2461
|
-
caveatString(
|
|
2462
|
-
caveatString(
|
|
2463
|
-
const fixedParams = requiredCaveat(
|
|
2485
|
+
const object4 = input;
|
|
2486
|
+
assertCaveatKeys(object4, ["name", "sql", "fixedParams"], path);
|
|
2487
|
+
caveatString(object4, "name", path);
|
|
2488
|
+
caveatString(object4, "sql", path);
|
|
2489
|
+
const fixedParams = requiredCaveat(object4, "fixedParams", path);
|
|
2464
2490
|
if (!Array.isArray(fixedParams)) {
|
|
2465
2491
|
throw new PolicyCapabilityError(
|
|
2466
2492
|
"policy-capability-malformed-caveats",
|
|
@@ -2475,20 +2501,20 @@ function validateFixedParam(input, path) {
|
|
|
2475
2501
|
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
2476
2502
|
throw new PolicyCapabilityError("policy-capability-malformed-caveats", `${path} must be an object`);
|
|
2477
2503
|
}
|
|
2478
|
-
const
|
|
2479
|
-
assertCaveatKeys(
|
|
2480
|
-
const index = requiredCaveat(
|
|
2504
|
+
const object4 = input;
|
|
2505
|
+
assertCaveatKeys(object4, ["index", "value"], path);
|
|
2506
|
+
const index = requiredCaveat(object4, "index", path);
|
|
2481
2507
|
if (typeof index !== "number" || !Number.isSafeInteger(index) || index < 0) {
|
|
2482
2508
|
throw new PolicyCapabilityError(
|
|
2483
2509
|
"policy-capability-malformed-caveats",
|
|
2484
2510
|
`${path}.index must be a non-negative integer`
|
|
2485
2511
|
);
|
|
2486
2512
|
}
|
|
2487
|
-
requiredCaveat(
|
|
2513
|
+
requiredCaveat(object4, "value", path);
|
|
2488
2514
|
}
|
|
2489
|
-
function assertCaveatKeys(
|
|
2515
|
+
function assertCaveatKeys(object4, allowed, path) {
|
|
2490
2516
|
const allowedSet = new Set(allowed);
|
|
2491
|
-
for (const key of Object.keys(
|
|
2517
|
+
for (const key of Object.keys(object4)) {
|
|
2492
2518
|
if (!allowedSet.has(key)) {
|
|
2493
2519
|
throw new PolicyCapabilityError(
|
|
2494
2520
|
"policy-capability-malformed-caveats",
|
|
@@ -2497,17 +2523,17 @@ function assertCaveatKeys(object3, allowed, path) {
|
|
|
2497
2523
|
}
|
|
2498
2524
|
}
|
|
2499
2525
|
}
|
|
2500
|
-
function requiredCaveat(
|
|
2501
|
-
if (!hasOwn(
|
|
2526
|
+
function requiredCaveat(object4, key, path) {
|
|
2527
|
+
if (!hasOwn(object4, key)) {
|
|
2502
2528
|
throw new PolicyCapabilityError(
|
|
2503
2529
|
"policy-capability-malformed-caveats",
|
|
2504
2530
|
`${path}.${key} is required`
|
|
2505
2531
|
);
|
|
2506
2532
|
}
|
|
2507
|
-
return
|
|
2533
|
+
return object4[key];
|
|
2508
2534
|
}
|
|
2509
|
-
function caveatString(
|
|
2510
|
-
const value = requiredCaveat(
|
|
2535
|
+
function caveatString(object4, key, path) {
|
|
2536
|
+
const value = requiredCaveat(object4, key, path);
|
|
2511
2537
|
if (typeof value !== "string" || value.length === 0) {
|
|
2512
2538
|
throw new PolicyCapabilityError(
|
|
2513
2539
|
"policy-capability-malformed-caveats",
|
|
@@ -2538,8 +2564,8 @@ function caveatsContain(authority, request) {
|
|
|
2538
2564
|
(statement) => authorityStatements.some((candidate) => jcsCanonicalize(candidate) === jcsCanonicalize(statement))
|
|
2539
2565
|
);
|
|
2540
2566
|
}
|
|
2541
|
-
function hasOwn(
|
|
2542
|
-
return objectHasOwn2(
|
|
2567
|
+
function hasOwn(object4, key) {
|
|
2568
|
+
return objectHasOwn2(object4, key);
|
|
2543
2569
|
}
|
|
2544
2570
|
|
|
2545
2571
|
// src/policy/signed-object.ts
|
|
@@ -2867,17 +2893,17 @@ async function createAndSignSignedObject(input, signer) {
|
|
|
2867
2893
|
}
|
|
2868
2894
|
function createAndSignPolicy(input, signer) {
|
|
2869
2895
|
return createAndSignSignedObject(input, signer).then(
|
|
2870
|
-
(
|
|
2896
|
+
(object4) => validatePolicySignedShape(object4)
|
|
2871
2897
|
);
|
|
2872
2898
|
}
|
|
2873
2899
|
function createAndSignPolicyStatus(input, signer) {
|
|
2874
2900
|
return createAndSignSignedObject(input, signer).then(
|
|
2875
|
-
(
|
|
2901
|
+
(object4) => validatePolicyStatusSignedShape(object4)
|
|
2876
2902
|
);
|
|
2877
2903
|
}
|
|
2878
2904
|
function createAndSignPolicyEngineRecord(input, signer) {
|
|
2879
2905
|
return createAndSignSignedObject(input, signer).then(
|
|
2880
|
-
(
|
|
2906
|
+
(object4) => validatePolicyEngineRecordSignedShape(object4)
|
|
2881
2907
|
);
|
|
2882
2908
|
}
|
|
2883
2909
|
async function verifySignedObject(input) {
|
|
@@ -2981,9 +3007,9 @@ function validateUnsignedForDescriptor(input, descriptor) {
|
|
|
2981
3007
|
}
|
|
2982
3008
|
}
|
|
2983
3009
|
function validatePolicyShape(input, signed) {
|
|
2984
|
-
const
|
|
3010
|
+
const object4 = expectJsonObject(normalizeJson(input), "$");
|
|
2985
3011
|
assertExactKeys2(
|
|
2986
|
-
|
|
3012
|
+
object4,
|
|
2987
3013
|
signed ? [
|
|
2988
3014
|
"schema",
|
|
2989
3015
|
"policyId",
|
|
@@ -3011,30 +3037,30 @@ function validatePolicyShape(input, signed) {
|
|
|
3011
3037
|
],
|
|
3012
3038
|
"$"
|
|
3013
3039
|
);
|
|
3014
|
-
expectConst(requiredString2(
|
|
3040
|
+
expectConst(requiredString2(object4, "schema", "$"), POLICY_SCHEMA, "$.schema");
|
|
3015
3041
|
if (signed) {
|
|
3016
|
-
requiredString2(
|
|
3017
|
-
validateSignature(requiredValue2(
|
|
3042
|
+
requiredString2(object4, "policyId", "$");
|
|
3043
|
+
validateSignature(requiredValue2(object4, "signature", "$"));
|
|
3018
3044
|
}
|
|
3019
|
-
requiredString2(
|
|
3020
|
-
requiredString2(
|
|
3021
|
-
requiredDateString(
|
|
3022
|
-
optionalDateString(
|
|
3023
|
-
validatePolicyResource(requiredValue2(
|
|
3024
|
-
validateExpression(requiredValue2(
|
|
3025
|
-
validateGrant(requiredValue2(
|
|
3026
|
-
if (hasOwn2(
|
|
3027
|
-
validateDisclosure(requiredValue2(
|
|
3045
|
+
requiredString2(object4, "ownerDid", "$");
|
|
3046
|
+
requiredString2(object4, "signingKeyDid", "$");
|
|
3047
|
+
requiredDateString(object4, "createdAt", "$");
|
|
3048
|
+
optionalDateString(object4, "expiresAt", "$");
|
|
3049
|
+
validatePolicyResource(requiredValue2(object4, "resource", "$"), "$.resource");
|
|
3050
|
+
validateExpression(requiredValue2(object4, "when", "$"), "$.when");
|
|
3051
|
+
validateGrant(requiredValue2(object4, "grant", "$"), "$.grant");
|
|
3052
|
+
if (hasOwn2(object4, "disclosure")) {
|
|
3053
|
+
validateDisclosure(requiredValue2(object4, "disclosure", "$"), "$.disclosure");
|
|
3028
3054
|
}
|
|
3029
|
-
if (hasOwn2(
|
|
3030
|
-
validateAudit(requiredValue2(
|
|
3055
|
+
if (hasOwn2(object4, "audit")) {
|
|
3056
|
+
validateAudit(requiredValue2(object4, "audit", "$"), "$.audit");
|
|
3031
3057
|
}
|
|
3032
|
-
return
|
|
3058
|
+
return object4;
|
|
3033
3059
|
}
|
|
3034
3060
|
function validatePolicyStatusShape(input, signed) {
|
|
3035
|
-
const
|
|
3061
|
+
const object4 = expectJsonObject(normalizeJson(input), "$");
|
|
3036
3062
|
assertExactKeys2(
|
|
3037
|
-
|
|
3063
|
+
object4,
|
|
3038
3064
|
signed ? [
|
|
3039
3065
|
"schema",
|
|
3040
3066
|
"statusId",
|
|
@@ -3058,28 +3084,28 @@ function validatePolicyStatusShape(input, signed) {
|
|
|
3058
3084
|
],
|
|
3059
3085
|
"$"
|
|
3060
3086
|
);
|
|
3061
|
-
expectConst(requiredString2(
|
|
3087
|
+
expectConst(requiredString2(object4, "schema", "$"), POLICY_STATUS_SCHEMA, "$.schema");
|
|
3062
3088
|
if (signed) {
|
|
3063
|
-
requiredString2(
|
|
3064
|
-
validateSignature(requiredValue2(
|
|
3089
|
+
requiredString2(object4, "statusId", "$");
|
|
3090
|
+
validateSignature(requiredValue2(object4, "signature", "$"));
|
|
3065
3091
|
}
|
|
3066
|
-
requiredString2(
|
|
3067
|
-
requiredString2(
|
|
3068
|
-
requiredInteger(
|
|
3069
|
-
expectOneOf(requiredString2(
|
|
3092
|
+
requiredString2(object4, "policyId", "$");
|
|
3093
|
+
requiredString2(object4, "ownerDid", "$");
|
|
3094
|
+
requiredInteger(object4, "sequence", "$", 0);
|
|
3095
|
+
expectOneOf(requiredString2(object4, "disposition", "$"), [
|
|
3070
3096
|
"active",
|
|
3071
3097
|
"suspended",
|
|
3072
3098
|
"revoked"
|
|
3073
3099
|
], "$.disposition");
|
|
3074
|
-
requiredDateString(
|
|
3075
|
-
optionalString(
|
|
3076
|
-
requiredString2(
|
|
3077
|
-
return
|
|
3100
|
+
requiredDateString(object4, "effectiveAt", "$");
|
|
3101
|
+
optionalString(object4, "reasonCode", "$");
|
|
3102
|
+
requiredString2(object4, "signingKeyDid", "$");
|
|
3103
|
+
return object4;
|
|
3078
3104
|
}
|
|
3079
3105
|
function validatePolicyEngineRecordShape(input, signed) {
|
|
3080
|
-
const
|
|
3106
|
+
const object4 = expectJsonObject(normalizeJson(input), "$");
|
|
3081
3107
|
assertExactKeys2(
|
|
3082
|
-
|
|
3108
|
+
object4,
|
|
3083
3109
|
signed ? [
|
|
3084
3110
|
"schema",
|
|
3085
3111
|
"engineRecordId",
|
|
@@ -3104,44 +3130,44 @@ function validatePolicyEngineRecordShape(input, signed) {
|
|
|
3104
3130
|
"$"
|
|
3105
3131
|
);
|
|
3106
3132
|
expectConst(
|
|
3107
|
-
requiredString2(
|
|
3133
|
+
requiredString2(object4, "schema", "$"),
|
|
3108
3134
|
POLICY_ENGINE_RECORD_SCHEMA,
|
|
3109
3135
|
"$.schema"
|
|
3110
3136
|
);
|
|
3111
3137
|
if (signed) {
|
|
3112
|
-
requiredString2(
|
|
3113
|
-
validateSignature(requiredValue2(
|
|
3138
|
+
requiredString2(object4, "engineRecordId", "$");
|
|
3139
|
+
validateSignature(requiredValue2(object4, "signature", "$"));
|
|
3114
3140
|
}
|
|
3115
|
-
requiredString2(
|
|
3116
|
-
requiredString2(
|
|
3117
|
-
requiredString2(
|
|
3141
|
+
requiredString2(object4, "ownerDid", "$");
|
|
3142
|
+
requiredString2(object4, "endpoint", "$");
|
|
3143
|
+
requiredString2(object4, "audience", "$");
|
|
3118
3144
|
requiredStringArray(
|
|
3119
|
-
|
|
3145
|
+
object4,
|
|
3120
3146
|
"supportedPolicyVersions",
|
|
3121
3147
|
"$",
|
|
3122
3148
|
(value, path) => expectConst(value, "v0", path)
|
|
3123
3149
|
);
|
|
3124
|
-
requiredStringArray(
|
|
3125
|
-
requiredString2(
|
|
3126
|
-
requiredDateString(
|
|
3127
|
-
return
|
|
3150
|
+
requiredStringArray(object4, "supportedEvidenceVerifiers", "$");
|
|
3151
|
+
requiredString2(object4, "grantIssuerDid", "$");
|
|
3152
|
+
requiredDateString(object4, "expiresAt", "$");
|
|
3153
|
+
return object4;
|
|
3128
3154
|
}
|
|
3129
3155
|
function validateSignature(input) {
|
|
3130
|
-
const
|
|
3131
|
-
assertExactKeys2(
|
|
3132
|
-
const suite = requiredString2(
|
|
3156
|
+
const object4 = expectJsonObject(input, "$.signature");
|
|
3157
|
+
assertExactKeys2(object4, ["suite", "signerDid", "value"], "$.signature");
|
|
3158
|
+
const suite = requiredString2(object4, "suite", "$.signature");
|
|
3133
3159
|
assertSupportedSignatureSuite(suite);
|
|
3134
|
-
const signerDid = requiredString2(
|
|
3135
|
-
const value = requiredString2(
|
|
3160
|
+
const signerDid = requiredString2(object4, "signerDid", "$.signature");
|
|
3161
|
+
const value = requiredString2(object4, "value", "$.signature");
|
|
3136
3162
|
decodeSignatureValue(value, suite);
|
|
3137
3163
|
return { suite, signerDid, value };
|
|
3138
3164
|
}
|
|
3139
3165
|
function validatePolicyResource(input, path) {
|
|
3140
|
-
const
|
|
3141
|
-
assertExactKeys2(
|
|
3142
|
-
requiredString2(
|
|
3143
|
-
requiredString2(
|
|
3144
|
-
const ceiling = requiredArray2(
|
|
3166
|
+
const object4 = expectJsonObject(input, path);
|
|
3167
|
+
assertExactKeys2(object4, ["resourceType", "resourceId", "permissionsCeiling"], path);
|
|
3168
|
+
requiredString2(object4, "resourceType", path);
|
|
3169
|
+
requiredString2(object4, "resourceId", path);
|
|
3170
|
+
const ceiling = requiredArray2(object4, "permissionsCeiling", path, 1);
|
|
3145
3171
|
for (let index = 0; index < ceiling.length; index++) {
|
|
3146
3172
|
validatePolicyCapability(ceiling[index], `${path}.permissionsCeiling[${index}]`);
|
|
3147
3173
|
}
|
|
@@ -3157,10 +3183,10 @@ function validatePolicyPermissionsCeilingForSigning(input) {
|
|
|
3157
3183
|
}
|
|
3158
3184
|
}
|
|
3159
3185
|
function validatePolicyCapability(input, path) {
|
|
3160
|
-
const
|
|
3186
|
+
const object4 = expectJsonObject(input, path);
|
|
3161
3187
|
try {
|
|
3162
|
-
const canonical = canonicalizePolicyCapability(
|
|
3163
|
-
if (jcsCanonicalize(
|
|
3188
|
+
const canonical = canonicalizePolicyCapability(object4);
|
|
3189
|
+
if (jcsCanonicalize(object4) !== jcsCanonicalize(canonical)) {
|
|
3164
3190
|
throw new SignedObjectSchemaError(`${path} must be canonical PolicyCapability JSON`);
|
|
3165
3191
|
}
|
|
3166
3192
|
} catch (error) {
|
|
@@ -3173,10 +3199,10 @@ function validatePolicyCapability(input, path) {
|
|
|
3173
3199
|
}
|
|
3174
3200
|
}
|
|
3175
3201
|
function validatePolicyCapabilityForSigning(input, path) {
|
|
3176
|
-
const
|
|
3202
|
+
const object4 = expectJsonObject(input, path);
|
|
3177
3203
|
try {
|
|
3178
|
-
const canonical = normalizePolicyCapability(
|
|
3179
|
-
if (jcsCanonicalize(
|
|
3204
|
+
const canonical = normalizePolicyCapability(object4);
|
|
3205
|
+
if (jcsCanonicalize(object4) !== jcsCanonicalize(canonical)) {
|
|
3180
3206
|
throw new SignedObjectSchemaError(`${path} must be strict canonical PolicyCapability JSON`);
|
|
3181
3207
|
}
|
|
3182
3208
|
} catch (error) {
|
|
@@ -3189,43 +3215,43 @@ function validatePolicyCapabilityForSigning(input, path) {
|
|
|
3189
3215
|
}
|
|
3190
3216
|
}
|
|
3191
3217
|
function validateExpression(input, path) {
|
|
3192
|
-
const
|
|
3193
|
-
const keys = Object.keys(
|
|
3218
|
+
const object4 = expectJsonObject(input, path);
|
|
3219
|
+
const keys = Object.keys(object4);
|
|
3194
3220
|
if (keys.length !== 1) {
|
|
3195
3221
|
throw new SignedObjectSchemaError(`${path} must have exactly one expression key`);
|
|
3196
3222
|
}
|
|
3197
3223
|
const key = keys[0];
|
|
3198
3224
|
if (key === "allOf" || key === "anyOf") {
|
|
3199
|
-
const values = requiredArray2(
|
|
3225
|
+
const values = requiredArray2(object4, key, path, 1);
|
|
3200
3226
|
for (let index = 0; index < values.length; index++) {
|
|
3201
3227
|
validateExpression(values[index], `${path}.${key}[${index}]`);
|
|
3202
3228
|
}
|
|
3203
3229
|
return;
|
|
3204
3230
|
}
|
|
3205
3231
|
if (key === "subject") {
|
|
3206
|
-
const subject = expectJsonObject(requiredValue2(
|
|
3232
|
+
const subject = expectJsonObject(requiredValue2(object4, "subject", path), `${path}.subject`);
|
|
3207
3233
|
assertExactKeys2(subject, ["did"], `${path}.subject`);
|
|
3208
3234
|
requiredString2(subject, "did", `${path}.subject`);
|
|
3209
3235
|
return;
|
|
3210
3236
|
}
|
|
3211
3237
|
if (key === "evidence") {
|
|
3212
|
-
validateEvidenceRequirement(requiredValue2(
|
|
3238
|
+
validateEvidenceRequirement(requiredValue2(object4, "evidence", path), `${path}.evidence`);
|
|
3213
3239
|
return;
|
|
3214
3240
|
}
|
|
3215
3241
|
throw new SignedObjectSchemaError(`${path} has unknown expression key ${key}`);
|
|
3216
3242
|
}
|
|
3217
3243
|
function validateEvidenceRequirement(input, path) {
|
|
3218
|
-
const
|
|
3244
|
+
const object4 = expectJsonObject(input, path);
|
|
3219
3245
|
assertExactKeys2(
|
|
3220
|
-
|
|
3246
|
+
object4,
|
|
3221
3247
|
["requirementId", "verifier", "requirements", "authority", "freshness"],
|
|
3222
3248
|
path
|
|
3223
3249
|
);
|
|
3224
|
-
requiredString2(
|
|
3225
|
-
requiredString2(
|
|
3226
|
-
requiredValue2(
|
|
3227
|
-
if (hasOwn2(
|
|
3228
|
-
const authority = expectJsonObject(requiredValue2(
|
|
3250
|
+
requiredString2(object4, "requirementId", path);
|
|
3251
|
+
requiredString2(object4, "verifier", path);
|
|
3252
|
+
requiredValue2(object4, "requirements", path);
|
|
3253
|
+
if (hasOwn2(object4, "authority")) {
|
|
3254
|
+
const authority = expectJsonObject(requiredValue2(object4, "authority", path), `${path}.authority`);
|
|
3229
3255
|
assertExactKeys2(
|
|
3230
3256
|
authority,
|
|
3231
3257
|
["profile", "acceptedIssuers", "allowOwnerAuthorizedIssuer"],
|
|
@@ -3237,35 +3263,35 @@ function validateEvidenceRequirement(input, path) {
|
|
|
3237
3263
|
}
|
|
3238
3264
|
optionalBoolean(authority, "allowOwnerAuthorizedIssuer", `${path}.authority`);
|
|
3239
3265
|
}
|
|
3240
|
-
if (hasOwn2(
|
|
3241
|
-
const freshness = expectJsonObject(requiredValue2(
|
|
3266
|
+
if (hasOwn2(object4, "freshness")) {
|
|
3267
|
+
const freshness = expectJsonObject(requiredValue2(object4, "freshness", path), `${path}.freshness`);
|
|
3242
3268
|
assertExactKeys2(freshness, ["maxStatusAgeSeconds"], `${path}.freshness`);
|
|
3243
3269
|
requiredInteger(freshness, "maxStatusAgeSeconds", `${path}.freshness`, 0);
|
|
3244
3270
|
}
|
|
3245
3271
|
}
|
|
3246
3272
|
function validateGrant(input, path) {
|
|
3247
|
-
const
|
|
3248
|
-
assertExactKeys2(
|
|
3249
|
-
expectConst(requiredString2(
|
|
3250
|
-
requiredInteger(
|
|
3251
|
-
expectOneOf(requiredString2(
|
|
3273
|
+
const object4 = expectJsonObject(input, path);
|
|
3274
|
+
assertExactKeys2(object4, ["output", "maxTtlSeconds", "delegationMode", "revocation"], path);
|
|
3275
|
+
expectConst(requiredString2(object4, "output", path), "portable-delegation", `${path}.output`);
|
|
3276
|
+
requiredInteger(object4, "maxTtlSeconds", path, 1);
|
|
3277
|
+
expectOneOf(requiredString2(object4, "delegationMode", path), [
|
|
3252
3278
|
"terminal",
|
|
3253
3279
|
"attenuable"
|
|
3254
3280
|
], `${path}.delegationMode`);
|
|
3255
|
-
expectOneOf(requiredString2(
|
|
3281
|
+
expectOneOf(requiredString2(object4, "revocation", path), [
|
|
3256
3282
|
"refresh_only",
|
|
3257
3283
|
"active_cutoff"
|
|
3258
3284
|
], `${path}.revocation`);
|
|
3259
3285
|
}
|
|
3260
3286
|
function validateDisclosure(input, path) {
|
|
3261
|
-
const
|
|
3262
|
-
assertExactKeys2(
|
|
3263
|
-
expectOneOf(requiredString2(
|
|
3287
|
+
const object4 = expectJsonObject(input, path);
|
|
3288
|
+
assertExactKeys2(object4, ["denial"], path);
|
|
3289
|
+
expectOneOf(requiredString2(object4, "denial", path), ["none", "code", "debug"], `${path}.denial`);
|
|
3264
3290
|
}
|
|
3265
3291
|
function validateAudit(input, path) {
|
|
3266
|
-
const
|
|
3267
|
-
assertExactKeys2(
|
|
3268
|
-
expectOneOf(requiredString2(
|
|
3292
|
+
const object4 = expectJsonObject(input, path);
|
|
3293
|
+
assertExactKeys2(object4, ["issuance"], path);
|
|
3294
|
+
expectOneOf(requiredString2(object4, "issuance", path), ["off", "security", "full"], `${path}.issuance`);
|
|
3269
3295
|
}
|
|
3270
3296
|
function materialForUnsigned(unsigned, descriptor) {
|
|
3271
3297
|
const jcs = jcsCanonicalize(unsigned);
|
|
@@ -3414,9 +3440,9 @@ function encodeSignatureValue(value, suite) {
|
|
|
3414
3440
|
decodeSignatureValue(encoded, suite);
|
|
3415
3441
|
return encoded;
|
|
3416
3442
|
}
|
|
3417
|
-
function stripOwnIdAndSignature(
|
|
3443
|
+
function stripOwnIdAndSignature(object4, descriptor) {
|
|
3418
3444
|
const output = /* @__PURE__ */ Object.create(null);
|
|
3419
|
-
for (const [key, value] of Object.entries(
|
|
3445
|
+
for (const [key, value] of Object.entries(object4)) {
|
|
3420
3446
|
if (key === descriptor.idField || key === "signature") {
|
|
3421
3447
|
continue;
|
|
3422
3448
|
}
|
|
@@ -3425,8 +3451,8 @@ function stripOwnIdAndSignature(object3, descriptor) {
|
|
|
3425
3451
|
return output;
|
|
3426
3452
|
}
|
|
3427
3453
|
function descriptorForUnsigned(input) {
|
|
3428
|
-
const
|
|
3429
|
-
return descriptorForSchema(requiredString2(
|
|
3454
|
+
const object4 = expectJsonObject(normalizeJson(input), "$");
|
|
3455
|
+
return descriptorForSchema(requiredString2(object4, "schema", "$"));
|
|
3430
3456
|
}
|
|
3431
3457
|
function descriptorForSchema(schema) {
|
|
3432
3458
|
if (schema === POLICY_SCHEMA) return DESCRIPTORS.Policy;
|
|
@@ -3440,28 +3466,28 @@ function expectJsonObject(input, path) {
|
|
|
3440
3466
|
}
|
|
3441
3467
|
return input;
|
|
3442
3468
|
}
|
|
3443
|
-
function assertExactKeys2(
|
|
3469
|
+
function assertExactKeys2(object4, allowed, path) {
|
|
3444
3470
|
const allowedSet = new Set(allowed);
|
|
3445
|
-
for (const key of Object.keys(
|
|
3471
|
+
for (const key of Object.keys(object4)) {
|
|
3446
3472
|
if (!allowedSet.has(key)) {
|
|
3447
3473
|
throw new SignedObjectSchemaError(`${path} has unknown field ${key}`);
|
|
3448
3474
|
}
|
|
3449
3475
|
}
|
|
3450
3476
|
}
|
|
3451
|
-
function requiredValue2(
|
|
3452
|
-
if (!hasOwn2(
|
|
3477
|
+
function requiredValue2(object4, key, path) {
|
|
3478
|
+
if (!hasOwn2(object4, key)) {
|
|
3453
3479
|
throw new SignedObjectSchemaError(`${path}.${key} is required`);
|
|
3454
3480
|
}
|
|
3455
|
-
return
|
|
3481
|
+
return object4[key];
|
|
3456
3482
|
}
|
|
3457
|
-
function requiredString2(
|
|
3458
|
-
return requireStringType(requiredValue2(
|
|
3483
|
+
function requiredString2(object4, key, path) {
|
|
3484
|
+
return requireStringType(requiredValue2(object4, key, path), `${path}.${key}`);
|
|
3459
3485
|
}
|
|
3460
|
-
function optionalString(
|
|
3461
|
-
if (!hasOwn2(
|
|
3486
|
+
function optionalString(object4, key, path) {
|
|
3487
|
+
if (!hasOwn2(object4, key)) {
|
|
3462
3488
|
return;
|
|
3463
3489
|
}
|
|
3464
|
-
requireStringType(requiredValue2(
|
|
3490
|
+
requireStringType(requiredValue2(object4, key, path), `${path}.${key}`);
|
|
3465
3491
|
}
|
|
3466
3492
|
function requireStringType(value, path) {
|
|
3467
3493
|
if (typeof value !== "string" || value.length === 0) {
|
|
@@ -3469,16 +3495,16 @@ function requireStringType(value, path) {
|
|
|
3469
3495
|
}
|
|
3470
3496
|
return value;
|
|
3471
3497
|
}
|
|
3472
|
-
function optionalBoolean(
|
|
3473
|
-
if (!hasOwn2(
|
|
3498
|
+
function optionalBoolean(object4, key, path) {
|
|
3499
|
+
if (!hasOwn2(object4, key)) {
|
|
3474
3500
|
return;
|
|
3475
3501
|
}
|
|
3476
|
-
if (typeof requiredValue2(
|
|
3502
|
+
if (typeof requiredValue2(object4, key, path) !== "boolean") {
|
|
3477
3503
|
throw new SignedObjectSchemaError(`${path}.${key} must be a boolean`);
|
|
3478
3504
|
}
|
|
3479
3505
|
}
|
|
3480
|
-
function requiredInteger(
|
|
3481
|
-
const value = requiredValue2(
|
|
3506
|
+
function requiredInteger(object4, key, path, minimum) {
|
|
3507
|
+
const value = requiredValue2(object4, key, path);
|
|
3482
3508
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum) {
|
|
3483
3509
|
throw new SignedObjectSchemaError(
|
|
3484
3510
|
`${path}.${key} must be an integer >= ${minimum}`
|
|
@@ -3486,8 +3512,8 @@ function requiredInteger(object3, key, path, minimum) {
|
|
|
3486
3512
|
}
|
|
3487
3513
|
return value;
|
|
3488
3514
|
}
|
|
3489
|
-
function requiredArray2(
|
|
3490
|
-
const value = requiredValue2(
|
|
3515
|
+
function requiredArray2(object4, key, path, minimum) {
|
|
3516
|
+
const value = requiredValue2(object4, key, path);
|
|
3491
3517
|
if (!Array.isArray(value) || value.length < minimum) {
|
|
3492
3518
|
throw new SignedObjectSchemaError(
|
|
3493
3519
|
`${path}.${key} must be an array with at least ${minimum} item(s)`
|
|
@@ -3495,22 +3521,22 @@ function requiredArray2(object3, key, path, minimum) {
|
|
|
3495
3521
|
}
|
|
3496
3522
|
return value;
|
|
3497
3523
|
}
|
|
3498
|
-
function requiredStringArray(
|
|
3499
|
-
const values = requiredArray2(
|
|
3524
|
+
function requiredStringArray(object4, key, path, check) {
|
|
3525
|
+
const values = requiredArray2(object4, key, path, 0);
|
|
3500
3526
|
for (let index = 0; index < values.length; index++) {
|
|
3501
3527
|
const itemPath = `${path}.${key}[${index}]`;
|
|
3502
3528
|
const value = requireStringType(values[index], itemPath);
|
|
3503
3529
|
check?.(value, itemPath);
|
|
3504
3530
|
}
|
|
3505
3531
|
}
|
|
3506
|
-
function requiredDateString(
|
|
3507
|
-
assertRfc3339(requiredString2(
|
|
3532
|
+
function requiredDateString(object4, key, path) {
|
|
3533
|
+
assertRfc3339(requiredString2(object4, key, path), `${path}.${key}`);
|
|
3508
3534
|
}
|
|
3509
|
-
function optionalDateString(
|
|
3510
|
-
if (!hasOwn2(
|
|
3535
|
+
function optionalDateString(object4, key, path) {
|
|
3536
|
+
if (!hasOwn2(object4, key)) {
|
|
3511
3537
|
return;
|
|
3512
3538
|
}
|
|
3513
|
-
assertRfc3339(requiredString2(
|
|
3539
|
+
assertRfc3339(requiredString2(object4, key, path), `${path}.${key}`);
|
|
3514
3540
|
}
|
|
3515
3541
|
function assertRfc3339(value, path) {
|
|
3516
3542
|
const match = value.match(
|
|
@@ -3542,8 +3568,8 @@ function expectOneOf(actual, allowed, path) {
|
|
|
3542
3568
|
}
|
|
3543
3569
|
throw new SignedObjectSchemaError(`${path} has unsupported value ${actual}`);
|
|
3544
3570
|
}
|
|
3545
|
-
function hasOwn2(
|
|
3546
|
-
return objectHasOwn3(
|
|
3571
|
+
function hasOwn2(object4, key) {
|
|
3572
|
+
return objectHasOwn3(object4, key);
|
|
3547
3573
|
}
|
|
3548
3574
|
function assertSupportedSignatureSuite(suite) {
|
|
3549
3575
|
if (suite !== ED25519_JCS_SIGNATURE_SUITE && suite !== EIP191_JCS_SIGNATURE_SUITE) {
|
|
@@ -3770,9 +3796,9 @@ function expectObject2(input, path, code3) {
|
|
|
3770
3796
|
);
|
|
3771
3797
|
}
|
|
3772
3798
|
}
|
|
3773
|
-
function assertExactKeys3(
|
|
3799
|
+
function assertExactKeys3(object4, allowed, path) {
|
|
3774
3800
|
const allowedSet = new Set(allowed);
|
|
3775
|
-
for (const key of Object.keys(
|
|
3801
|
+
for (const key of Object.keys(object4)) {
|
|
3776
3802
|
if (!allowedSet.has(key)) {
|
|
3777
3803
|
throw new PolicyAuthoringError(
|
|
3778
3804
|
"policy-authoring-unknown-key",
|
|
@@ -3781,17 +3807,17 @@ function assertExactKeys3(object3, allowed, path) {
|
|
|
3781
3807
|
}
|
|
3782
3808
|
}
|
|
3783
3809
|
}
|
|
3784
|
-
function requiredValue3(
|
|
3785
|
-
if (!hasOwn3(
|
|
3810
|
+
function requiredValue3(object4, key, path) {
|
|
3811
|
+
if (!hasOwn3(object4, key)) {
|
|
3786
3812
|
throw new PolicyAuthoringError(
|
|
3787
3813
|
"policy-authoring-malformed",
|
|
3788
3814
|
`${path}.${key} is required`
|
|
3789
3815
|
);
|
|
3790
3816
|
}
|
|
3791
|
-
return
|
|
3817
|
+
return object4[key];
|
|
3792
3818
|
}
|
|
3793
|
-
function requiredString3(
|
|
3794
|
-
const value = requiredValue3(
|
|
3819
|
+
function requiredString3(object4, key, path) {
|
|
3820
|
+
const value = requiredValue3(object4, key, path);
|
|
3795
3821
|
if (typeof value !== "string" || value.length === 0) {
|
|
3796
3822
|
throw new PolicyAuthoringError(
|
|
3797
3823
|
"policy-authoring-malformed",
|
|
@@ -3800,11 +3826,11 @@ function requiredString3(object3, key, path) {
|
|
|
3800
3826
|
}
|
|
3801
3827
|
return value;
|
|
3802
3828
|
}
|
|
3803
|
-
function fieldString(
|
|
3804
|
-
if (!hasOwn3(
|
|
3829
|
+
function fieldString(object4, key, path, code3) {
|
|
3830
|
+
if (!hasOwn3(object4, key)) {
|
|
3805
3831
|
throw new PolicyAuthoringError(code3, `${path}.${key} is required`);
|
|
3806
3832
|
}
|
|
3807
|
-
const value =
|
|
3833
|
+
const value = object4[key];
|
|
3808
3834
|
if (typeof value !== "string" || value.length === 0) {
|
|
3809
3835
|
throw new PolicyAuthoringError(
|
|
3810
3836
|
code3,
|
|
@@ -3842,8 +3868,8 @@ function parseStrictRfc3339(value, path) {
|
|
|
3842
3868
|
var objectHasOwn4 = Object.hasOwn ?? Object.prototype.hasOwnProperty.call.bind(
|
|
3843
3869
|
Object.prototype.hasOwnProperty
|
|
3844
3870
|
);
|
|
3845
|
-
function hasOwn3(
|
|
3846
|
-
return objectHasOwn4(
|
|
3871
|
+
function hasOwn3(object4, key) {
|
|
3872
|
+
return objectHasOwn4(object4, key);
|
|
3847
3873
|
}
|
|
3848
3874
|
|
|
3849
3875
|
// src/policy/unified.ts
|
|
@@ -3870,9 +3896,11 @@ var MAX_LEGACY_ENVELOPE_EXPIRES_AT = "2026-12-29T00:00:00Z";
|
|
|
3870
3896
|
var LAST_V2_READ_AT = "2027-01-05T00:00:00Z";
|
|
3871
3897
|
async function requestPolicyChallengeV3(input) {
|
|
3872
3898
|
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3873
|
-
const
|
|
3899
|
+
const policyRuntimePath = policyRuntimeBasePath(input.policyRuntimePath);
|
|
3900
|
+
const response = await fetchFn(new URL(`${policyRuntimePath}/challenges`, input.nodeOrigin), {
|
|
3874
3901
|
method: "POST",
|
|
3875
3902
|
redirect: "error",
|
|
3903
|
+
signal: input.signal,
|
|
3876
3904
|
headers: { accept: "application/json", "content-type": "application/json" },
|
|
3877
3905
|
body: JSON.stringify({ policyCid: input.policyCid, recipientDid: input.recipientDid, requestedCapabilities: input.requestedCapabilities.map(normalizeUnifiedPolicyCapability) })
|
|
3878
3906
|
});
|
|
@@ -3882,6 +3910,13 @@ async function requestPolicyChallengeV3(input) {
|
|
|
3882
3910
|
throw new Error("policy challenge binding is invalid");
|
|
3883
3911
|
return value;
|
|
3884
3912
|
}
|
|
3913
|
+
function policyRuntimeBasePath(value) {
|
|
3914
|
+
const path = value ?? "/policy/v3";
|
|
3915
|
+
if (!/^\/policy(?:\/[a-z0-9-]+)*$/.test(path)) {
|
|
3916
|
+
throw new Error("policy runtime path is invalid");
|
|
3917
|
+
}
|
|
3918
|
+
return path;
|
|
3919
|
+
}
|
|
3885
3920
|
function compactAttenuationForPolicyCapabilities(input) {
|
|
3886
3921
|
const attenuation = {};
|
|
3887
3922
|
for (const raw of input) {
|
|
@@ -3927,7 +3962,7 @@ function verifyPolicyRootStatusCheckpointV3(input) {
|
|
|
3927
3962
|
}
|
|
3928
3963
|
async function getPolicyRootStatusV3(input) {
|
|
3929
3964
|
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3930
|
-
const response = await fetchFn(new URL(`/
|
|
3965
|
+
const response = await fetchFn(new URL(`/policy/v3/status/${encodeURIComponent(input.rootCid)}`, input.nodeOrigin), {
|
|
3931
3966
|
method: "GET",
|
|
3932
3967
|
redirect: "error",
|
|
3933
3968
|
headers: { accept: "application/json" }
|
|
@@ -3986,7 +4021,7 @@ async function renewPolicyRootStatusV3(input) {
|
|
|
3986
4021
|
};
|
|
3987
4022
|
const signature = await input.sign((0, import_sha256.sha256)(new TextEncoder().encode(ROOT_STATUS_RENEWAL_V1_DOMAIN + jcsCanonicalize(unsigned))));
|
|
3988
4023
|
if (signature.length !== 64) throw new Error("policy root renewal signature must be Ed25519");
|
|
3989
|
-
const response = await fetchFn(new URL("/
|
|
4024
|
+
const response = await fetchFn(new URL("/policy/v3/status", input.nodeOrigin), {
|
|
3990
4025
|
method: "POST",
|
|
3991
4026
|
redirect: "error",
|
|
3992
4027
|
headers: { accept: "application/json", "content-type": "application/json" },
|
|
@@ -4042,6 +4077,11 @@ var POLICY_SESSION_FACT_KEYS = [
|
|
|
4042
4077
|
"issuanceAuditDigestHex",
|
|
4043
4078
|
"remainingRedelegationDepth"
|
|
4044
4079
|
];
|
|
4080
|
+
var POLICY_SESSION_V4_AUDIT_FACT_KEYS = [
|
|
4081
|
+
"credentialIdAuditDigestHex",
|
|
4082
|
+
"presentationJtiAuditDigestHex"
|
|
4083
|
+
];
|
|
4084
|
+
var LOWER_SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
4045
4085
|
var KV_ACTIONS = /* @__PURE__ */ new Set([
|
|
4046
4086
|
"tinycloud.kv/get",
|
|
4047
4087
|
"tinycloud.kv/list",
|
|
@@ -4236,6 +4276,133 @@ function contentSourceDigestHex(input) {
|
|
|
4236
4276
|
)
|
|
4237
4277
|
);
|
|
4238
4278
|
}
|
|
4279
|
+
function exactObject(value, keys, label) {
|
|
4280
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
4281
|
+
throw new Error(`${label} is invalid`);
|
|
4282
|
+
}
|
|
4283
|
+
const record2 = value;
|
|
4284
|
+
if (Object.keys(record2).length !== keys.length || keys.some((key) => !Object.prototype.hasOwnProperty.call(record2, key))) {
|
|
4285
|
+
throw new Error(`${label} is invalid`);
|
|
4286
|
+
}
|
|
4287
|
+
return record2;
|
|
4288
|
+
}
|
|
4289
|
+
function didKeyEd25519PublicKey(value) {
|
|
4290
|
+
const principal = value.split("#", 1)[0];
|
|
4291
|
+
if (!principal.startsWith("did:key:")) {
|
|
4292
|
+
throw new Error("policy enforcer binding signer is invalid");
|
|
4293
|
+
}
|
|
4294
|
+
const material = base58btc.decode(principal.slice("did:key:".length));
|
|
4295
|
+
if (material.length !== 34 || material[0] !== 237 || material[1] !== 1) {
|
|
4296
|
+
throw new Error("policy enforcer binding signer is invalid");
|
|
4297
|
+
}
|
|
4298
|
+
return material.slice(2);
|
|
4299
|
+
}
|
|
4300
|
+
async function registerPolicyV3(input) {
|
|
4301
|
+
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
4302
|
+
const rootExpiresAt = new Date(input.rootExpiresAt);
|
|
4303
|
+
const canonicalRootExpiresAt = new Date(
|
|
4304
|
+
Math.floor(rootExpiresAt.getTime() / 1e3) * 1e3
|
|
4305
|
+
).toISOString().replace(/\.000Z$/, "Z");
|
|
4306
|
+
if (!Number.isFinite(rootExpiresAt.getTime()) || canonicalRootExpiresAt !== input.rootExpiresAt || rootExpiresAt.getTime() <= Date.now()) {
|
|
4307
|
+
throw new Error("policy root expiry is invalid");
|
|
4308
|
+
}
|
|
4309
|
+
const bindingResponse = await fetchFn(
|
|
4310
|
+
new URL("/policy/v3/enforcer-bindings", input.nodeOrigin),
|
|
4311
|
+
{
|
|
4312
|
+
method: "POST",
|
|
4313
|
+
redirect: "error",
|
|
4314
|
+
signal: input.signal,
|
|
4315
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
4316
|
+
body: JSON.stringify({
|
|
4317
|
+
rootExpiresAt: input.rootExpiresAt,
|
|
4318
|
+
enforcerDid: input.enforcerDid
|
|
4319
|
+
})
|
|
4320
|
+
}
|
|
4321
|
+
);
|
|
4322
|
+
if (!bindingResponse.ok) {
|
|
4323
|
+
throw new Error(`policy enforcer binding rejected (${bindingResponse.status})`);
|
|
4324
|
+
}
|
|
4325
|
+
const binding = exactObject(
|
|
4326
|
+
await bindingResponse.json(),
|
|
4327
|
+
[
|
|
4328
|
+
"schema",
|
|
4329
|
+
"enforcerDid",
|
|
4330
|
+
"nodeAudience",
|
|
4331
|
+
"attestationBindingDigestHex",
|
|
4332
|
+
"issuedAt",
|
|
4333
|
+
"expiresAt",
|
|
4334
|
+
"signature"
|
|
4335
|
+
],
|
|
4336
|
+
"policy enforcer binding"
|
|
4337
|
+
);
|
|
4338
|
+
const signature = exactObject(
|
|
4339
|
+
binding.signature,
|
|
4340
|
+
["suite", "signerDid", "value"],
|
|
4341
|
+
"policy enforcer binding signature"
|
|
4342
|
+
);
|
|
4343
|
+
const expectedBindingDigestHex = hex(
|
|
4344
|
+
(0, import_sha256.sha256)(
|
|
4345
|
+
new TextEncoder().encode(
|
|
4346
|
+
jcsCanonicalize({
|
|
4347
|
+
enforcerDid: input.enforcerDid,
|
|
4348
|
+
nodeAudience: input.expectedNodeAudience
|
|
4349
|
+
})
|
|
4350
|
+
)
|
|
4351
|
+
)
|
|
4352
|
+
);
|
|
4353
|
+
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") {
|
|
4354
|
+
throw new Error("policy enforcer binding is invalid");
|
|
4355
|
+
}
|
|
4356
|
+
const { signature: _signature, ...unsignedBinding } = binding;
|
|
4357
|
+
const signedBinding = (0, import_sha256.sha256)(
|
|
4358
|
+
new TextEncoder().encode(
|
|
4359
|
+
`xyz.tinycloud.policy/AttestedEnforcerBinding/v2\0${jcsCanonicalize(unsignedBinding)}`
|
|
4360
|
+
)
|
|
4361
|
+
);
|
|
4362
|
+
if (!import_ed255193.ed25519.verify(
|
|
4363
|
+
decodeBase64Url2(signature.value),
|
|
4364
|
+
signedBinding,
|
|
4365
|
+
didKeyEd25519PublicKey(input.expectedNodeAudience),
|
|
4366
|
+
{ zip215: false }
|
|
4367
|
+
)) {
|
|
4368
|
+
throw new Error("policy enforcer binding signature is invalid");
|
|
4369
|
+
}
|
|
4370
|
+
const response = await fetchFn(
|
|
4371
|
+
new URL("/policy/v3/policies", input.nodeOrigin),
|
|
4372
|
+
{
|
|
4373
|
+
method: "POST",
|
|
4374
|
+
redirect: "error",
|
|
4375
|
+
signal: input.signal,
|
|
4376
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
4377
|
+
body: JSON.stringify({
|
|
4378
|
+
policyCid: input.policyCid,
|
|
4379
|
+
policy: input.policy,
|
|
4380
|
+
policyRoot: input.policyRoot.authorization.replace(/^Bearer\s+/i, ""),
|
|
4381
|
+
enforcementRoot: input.enforcementRoot.authorization.replace(/^Bearer\s+/i, ""),
|
|
4382
|
+
contentSourceDigestHex: input.contentSourceDigestHex,
|
|
4383
|
+
nativeProjectionHashHex: input.nativeProjectionHashHex,
|
|
4384
|
+
attestedEnforcerBinding: binding
|
|
4385
|
+
})
|
|
4386
|
+
}
|
|
4387
|
+
);
|
|
4388
|
+
if (!response.ok) {
|
|
4389
|
+
throw new Error(`policy registration rejected (${response.status})`);
|
|
4390
|
+
}
|
|
4391
|
+
const receipt = exactObject(
|
|
4392
|
+
await response.json(),
|
|
4393
|
+
["policyCid", "policyRootCid", "enforcementRootCid"],
|
|
4394
|
+
"policy registration response"
|
|
4395
|
+
);
|
|
4396
|
+
if (receipt.policyCid !== input.policyCid || receipt.policyRootCid !== input.policyRoot.cid || receipt.enforcementRootCid !== input.enforcementRoot.cid) {
|
|
4397
|
+
throw new Error("policy registration response binding is invalid");
|
|
4398
|
+
}
|
|
4399
|
+
return {
|
|
4400
|
+
policyCid: input.policyCid,
|
|
4401
|
+
policyRootCid: input.policyRoot.cid,
|
|
4402
|
+
enforcementRootCid: input.enforcementRoot.cid,
|
|
4403
|
+
attestedEnforcerBinding: binding
|
|
4404
|
+
};
|
|
4405
|
+
}
|
|
4239
4406
|
function policyDigestHex(policy) {
|
|
4240
4407
|
const canonicalPolicy = {
|
|
4241
4408
|
...policy,
|
|
@@ -4434,9 +4601,13 @@ function parsePolicySessionUcan(authorization, expectedProofs) {
|
|
|
4434
4601
|
if (expectedProofs !== void 0 && (parsed.payload.prf[0] !== expectedProofs[0] || parsed.payload.prf[1] !== expectedProofs[1]))
|
|
4435
4602
|
throw new Error("policy session UCAN ordered proofs do not match");
|
|
4436
4603
|
const factKeys = Object.keys(fact);
|
|
4437
|
-
|
|
4604
|
+
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);
|
|
4605
|
+
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);
|
|
4606
|
+
if (!hasLegacyFactShape && !hasV4AuditFactShape)
|
|
4438
4607
|
throw new Error("policy session UCAN fact is incomplete");
|
|
4439
|
-
if (
|
|
4608
|
+
if (hasV4AuditFactShape && (!LOWER_SHA256_HEX.test(fact.credentialIdAuditDigestHex) || !LOWER_SHA256_HEX.test(fact.presentationJtiAuditDigestHex)))
|
|
4609
|
+
throw new Error("policy session UCAN v4 audit facts are invalid");
|
|
4610
|
+
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)
|
|
4440
4611
|
throw new Error("policy session UCAN fact is invalid");
|
|
4441
4612
|
return {
|
|
4442
4613
|
authorization,
|
|
@@ -4495,6 +4666,8 @@ var POLICY_V2_DOMAIN = "xyz.tinycloud.policy/policy/v2\0";
|
|
|
4495
4666
|
var POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE = "TinyCloudPolicyCredentialRequirement";
|
|
4496
4667
|
var POLICY_PRESENTATION_V3_SCHEMA = "xyz.tinycloud.policy/presentation/v3";
|
|
4497
4668
|
var POLICY_PRESENTATION_V3_DOMAIN = "xyz.tinycloud.policy/Presentation/v3\0";
|
|
4669
|
+
var POLICY_PRESENTATION_V4_SCHEMA = "xyz.tinycloud.policy/presentation/v4";
|
|
4670
|
+
var POLICY_PRESENTATION_V4_DOMAIN = "xyz.tinycloud.policy/Presentation/v4\0";
|
|
4498
4671
|
function record(value, label) {
|
|
4499
4672
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
4500
4673
|
throw new Error(`${label} must be an object`);
|
|
@@ -4669,6 +4842,97 @@ function envelopeFromVerified(verified) {
|
|
|
4669
4842
|
} = verified;
|
|
4670
4843
|
return envelope;
|
|
4671
4844
|
}
|
|
4845
|
+
function issuedCredentialEnvelopeFromVerified(verified) {
|
|
4846
|
+
return envelopeFromVerified(verified);
|
|
4847
|
+
}
|
|
4848
|
+
async function signPolicyCredentialPresentationV4(value, sign) {
|
|
4849
|
+
const item = record(value, "Policy presentation v4");
|
|
4850
|
+
exactKeys2(item, [
|
|
4851
|
+
"schema",
|
|
4852
|
+
"jti",
|
|
4853
|
+
"challengeId",
|
|
4854
|
+
"nonce",
|
|
4855
|
+
"policyCid",
|
|
4856
|
+
"nodeAudience",
|
|
4857
|
+
"holderDid",
|
|
4858
|
+
"subjectDid",
|
|
4859
|
+
"credentialDigest",
|
|
4860
|
+
"requirementDigest",
|
|
4861
|
+
"descriptorDigest",
|
|
4862
|
+
"requestedCapabilities",
|
|
4863
|
+
"issuedAt",
|
|
4864
|
+
"expiresAt"
|
|
4865
|
+
], "Policy presentation v4");
|
|
4866
|
+
if (item.schema !== POLICY_PRESENTATION_V4_SCHEMA || item.holderDid !== item.subjectDid || !Array.isArray(item.requestedCapabilities)) throw new Error("Policy presentation v4 is invalid");
|
|
4867
|
+
const holderDid = nonEmpty(item.holderDid, "Policy presentation v4 holder DID");
|
|
4868
|
+
didKeyBytes(holderDid);
|
|
4869
|
+
const unsigned = Object.freeze({
|
|
4870
|
+
schema: POLICY_PRESENTATION_V4_SCHEMA,
|
|
4871
|
+
jti: nonEmpty(item.jti, "Policy presentation v4 jti"),
|
|
4872
|
+
challengeId: nonEmpty(item.challengeId, "Policy presentation v4 challenge"),
|
|
4873
|
+
nonce: nonEmpty(item.nonce, "Policy presentation v4 nonce"),
|
|
4874
|
+
policyCid: nonEmpty(item.policyCid, "Policy presentation v4 policy CID"),
|
|
4875
|
+
nodeAudience: nonEmpty(item.nodeAudience, "Policy presentation v4 audience"),
|
|
4876
|
+
holderDid,
|
|
4877
|
+
subjectDid: holderDid,
|
|
4878
|
+
credentialDigest: nonEmpty(item.credentialDigest, "Policy presentation v4 credential digest"),
|
|
4879
|
+
requirementDigest: nonEmpty(item.requirementDigest, "Policy presentation v4 requirement digest"),
|
|
4880
|
+
descriptorDigest: nonEmpty(item.descriptorDigest, "Policy presentation v4 descriptor digest"),
|
|
4881
|
+
// The low-level signer preserves the frozen wire bytes exactly. The
|
|
4882
|
+
// policy-aware builder performs capability normalization and containment.
|
|
4883
|
+
requestedCapabilities: Object.freeze(item.requestedCapabilities),
|
|
4884
|
+
issuedAt: date(item.issuedAt, "Policy presentation v4 issuedAt"),
|
|
4885
|
+
expiresAt: date(item.expiresAt, "Policy presentation v4 expiresAt")
|
|
4886
|
+
});
|
|
4887
|
+
const digest4 = (0, import_sha2562.sha256)(new TextEncoder().encode(
|
|
4888
|
+
POLICY_PRESENTATION_V4_DOMAIN + jcsCanonicalize(unsigned)
|
|
4889
|
+
));
|
|
4890
|
+
const signature = await sign(digest4);
|
|
4891
|
+
if (signature.length !== 64 || !import_ed255194.ed25519.verify(signature, digest4, didKeyBytes(holderDid))) throw new Error("policy presentation signature is invalid");
|
|
4892
|
+
return Object.freeze({
|
|
4893
|
+
...unsigned,
|
|
4894
|
+
signature: Object.freeze({
|
|
4895
|
+
suite: "Ed25519",
|
|
4896
|
+
signerDid: holderDid,
|
|
4897
|
+
value: encodeBase64Url(signature)
|
|
4898
|
+
})
|
|
4899
|
+
});
|
|
4900
|
+
}
|
|
4901
|
+
async function buildPolicyCredentialPresentationV4(input) {
|
|
4902
|
+
const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
|
|
4903
|
+
const requirement = validateCredentialRequirement(input.requirement);
|
|
4904
|
+
const commitment = policy.credentialRequirement;
|
|
4905
|
+
const requirementDigest = await credentialRequirementDigest(requirement);
|
|
4906
|
+
const credentialDigest = await sha256Base64Url(input.credential.credential);
|
|
4907
|
+
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");
|
|
4908
|
+
const requestedCapabilities = input.requestedCapabilities.map(normalizeUnifiedPolicyCapability);
|
|
4909
|
+
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");
|
|
4910
|
+
const holderDid = input.credential.holderDid;
|
|
4911
|
+
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");
|
|
4912
|
+
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
4913
|
+
const expiresAtMs = Math.min(
|
|
4914
|
+
now.getTime() + 6e4,
|
|
4915
|
+
Date.parse(input.challenge.expiresAt),
|
|
4916
|
+
Date.parse(input.credential.expiresAt)
|
|
4917
|
+
);
|
|
4918
|
+
if (expiresAtMs <= now.getTime()) throw new Error("policy challenge is expired");
|
|
4919
|
+
return signPolicyCredentialPresentationV4(Object.freeze({
|
|
4920
|
+
schema: POLICY_PRESENTATION_V4_SCHEMA,
|
|
4921
|
+
jti: input.jti ?? encodeBase64Url(crypto.getRandomValues(new Uint8Array(16))),
|
|
4922
|
+
challengeId: input.challenge.challengeId,
|
|
4923
|
+
nonce: input.challenge.nonce,
|
|
4924
|
+
policyCid: input.policyCid,
|
|
4925
|
+
nodeAudience: input.challenge.nodeAudience,
|
|
4926
|
+
holderDid,
|
|
4927
|
+
subjectDid: holderDid,
|
|
4928
|
+
credentialDigest,
|
|
4929
|
+
requirementDigest,
|
|
4930
|
+
descriptorDigest: input.credential.descriptorDigest,
|
|
4931
|
+
requestedCapabilities: Object.freeze(requestedCapabilities),
|
|
4932
|
+
issuedAt: rfc3339Seconds(now),
|
|
4933
|
+
expiresAt: rfc3339Seconds(new Date(expiresAtMs))
|
|
4934
|
+
}), input.sign);
|
|
4935
|
+
}
|
|
4672
4936
|
async function buildPolicyCredentialPresentationV3(input) {
|
|
4673
4937
|
const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
|
|
4674
4938
|
const requirement = validateCredentialRequirement(input.requirement);
|
|
@@ -4755,10 +5019,12 @@ async function admitPolicyCredentialV3(input) {
|
|
|
4755
5019
|
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
4756
5020
|
const challenge = await requestPolicyChallengeV3({
|
|
4757
5021
|
nodeOrigin,
|
|
5022
|
+
policyRuntimePath: input.policyRuntimePath,
|
|
4758
5023
|
policyCid: input.policyCid,
|
|
4759
5024
|
recipientDid: input.credential.holderDid,
|
|
4760
5025
|
requestedCapabilities: input.requestedCapabilities,
|
|
4761
|
-
fetch: fetchFn
|
|
5026
|
+
fetch: fetchFn,
|
|
5027
|
+
signal: input.signal
|
|
4762
5028
|
});
|
|
4763
5029
|
const presentation = await buildPolicyCredentialPresentationV3({
|
|
4764
5030
|
...input,
|
|
@@ -4766,10 +5032,11 @@ async function admitPolicyCredentialV3(input) {
|
|
|
4766
5032
|
challenge
|
|
4767
5033
|
});
|
|
4768
5034
|
const response = await fetchFn(
|
|
4769
|
-
new URL(
|
|
5035
|
+
new URL(`${policyRuntimeBasePath2(input.policyRuntimePath)}/delegations`, nodeOrigin),
|
|
4770
5036
|
{
|
|
4771
5037
|
method: "POST",
|
|
4772
5038
|
redirect: "error",
|
|
5039
|
+
signal: input.signal,
|
|
4773
5040
|
headers: { accept: "application/json", "content-type": "application/json" },
|
|
4774
5041
|
body: JSON.stringify({
|
|
4775
5042
|
policyCid: input.policyCid,
|
|
@@ -4798,8 +5065,74 @@ async function admitPolicyCredentialV3(input) {
|
|
|
4798
5065
|
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)) {
|
|
4799
5066
|
throw new Error("policy delegation signed binding is invalid");
|
|
4800
5067
|
}
|
|
5068
|
+
input.signal?.throwIfAborted();
|
|
4801
5069
|
return Object.freeze({ challenge, presentation, session });
|
|
4802
5070
|
}
|
|
5071
|
+
function validatePolicyCredentialAdmissionV4Authority(session, expected) {
|
|
5072
|
+
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");
|
|
5073
|
+
}
|
|
5074
|
+
async function admitPolicyCredentialV4(input) {
|
|
5075
|
+
const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
|
|
5076
|
+
const nodeOrigin = policyNodeOrigin(input.nodeOrigin);
|
|
5077
|
+
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
5078
|
+
const challenge = await requestPolicyChallengeV3({
|
|
5079
|
+
nodeOrigin,
|
|
5080
|
+
policyRuntimePath: input.policyRuntimePath,
|
|
5081
|
+
policyCid: input.policyCid,
|
|
5082
|
+
recipientDid: input.credential.holderDid,
|
|
5083
|
+
requestedCapabilities: input.requestedCapabilities,
|
|
5084
|
+
fetch: fetchFn,
|
|
5085
|
+
signal: input.signal
|
|
5086
|
+
});
|
|
5087
|
+
const presentation = await buildPolicyCredentialPresentationV4({
|
|
5088
|
+
...input,
|
|
5089
|
+
policy,
|
|
5090
|
+
challenge
|
|
5091
|
+
});
|
|
5092
|
+
const response = await fetchFn(new URL(`${policyRuntimeBasePath2(input.policyRuntimePath)}/delegations`, nodeOrigin), {
|
|
5093
|
+
method: "POST",
|
|
5094
|
+
redirect: "error",
|
|
5095
|
+
signal: input.signal,
|
|
5096
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
5097
|
+
body: JSON.stringify({
|
|
5098
|
+
policyCid: input.policyCid,
|
|
5099
|
+
challengeId: challenge.challengeId,
|
|
5100
|
+
nonce: challenge.nonce,
|
|
5101
|
+
requirement: validateCredentialRequirement(input.requirement),
|
|
5102
|
+
credential: envelopeFromVerified(input.credential),
|
|
5103
|
+
presentation
|
|
5104
|
+
})
|
|
5105
|
+
});
|
|
5106
|
+
if (!response.ok) throw new Error(`policy delegation rejected (${response.status})`);
|
|
5107
|
+
const value = record(await response.json(), "policy delegation");
|
|
5108
|
+
if (value.admitted !== true || typeof value.sessionCid !== "string" || typeof value.authorization !== "string") throw new Error("policy delegation response is invalid");
|
|
5109
|
+
const session = parsePolicySessionUcan(value.authorization, [
|
|
5110
|
+
input.policyRootCid,
|
|
5111
|
+
input.enforcementRootCid
|
|
5112
|
+
]);
|
|
5113
|
+
const expectedAttenuation = compactAttenuationForPolicyCapabilities(input.requestedCapabilities);
|
|
5114
|
+
validatePolicyCredentialAdmissionV4Authority(session, {
|
|
5115
|
+
nodeAudience: input.expectedNodeAudience,
|
|
5116
|
+
enforcerDid: input.expectedEnforcerDid
|
|
5117
|
+
});
|
|
5118
|
+
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");
|
|
5119
|
+
const importResponse = await fetchFn(new URL("/delegate", nodeOrigin), {
|
|
5120
|
+
method: "POST",
|
|
5121
|
+
redirect: "error",
|
|
5122
|
+
signal: input.signal,
|
|
5123
|
+
headers: { Authorization: session.authorization }
|
|
5124
|
+
});
|
|
5125
|
+
if (!importResponse.ok) throw new Error(`delegation import rejected (${importResponse.status})`);
|
|
5126
|
+
input.signal?.throwIfAborted();
|
|
5127
|
+
return Object.freeze({ challenge, presentation, session, delegationImported: true });
|
|
5128
|
+
}
|
|
5129
|
+
function policyRuntimeBasePath2(value) {
|
|
5130
|
+
const path = value ?? "/policy/v3";
|
|
5131
|
+
if (!/^\/policy(?:\/[a-z0-9-]+)*$/.test(path)) {
|
|
5132
|
+
throw new Error("policy runtime path is invalid");
|
|
5133
|
+
}
|
|
5134
|
+
return path;
|
|
5135
|
+
}
|
|
4803
5136
|
|
|
4804
5137
|
// src/requester/index.ts
|
|
4805
5138
|
var REQUESTER_NEAR_EXPIRY_SECONDS = 30;
|
|
@@ -8206,13 +8539,13 @@ function canonicalizeRecapCaveats(caveats) {
|
|
|
8206
8539
|
}
|
|
8207
8540
|
if (Array.isArray(value)) return `[${value.map(canonicalize).join(",")}]`;
|
|
8208
8541
|
if (typeof value === "object") {
|
|
8209
|
-
const
|
|
8210
|
-
const prototype = Object.getPrototypeOf(
|
|
8542
|
+
const object4 = value;
|
|
8543
|
+
const prototype = Object.getPrototypeOf(object4);
|
|
8211
8544
|
if (prototype !== Object.prototype && prototype !== null) {
|
|
8212
8545
|
throw new Error("ReCap caveats must contain only JSON values.");
|
|
8213
8546
|
}
|
|
8214
|
-
return `{${Object.keys(
|
|
8215
|
-
(key) => `${JSON.stringify(key)}:${canonicalize(
|
|
8547
|
+
return `{${Object.keys(object4).sort().map(
|
|
8548
|
+
(key) => `${JSON.stringify(key)}:${canonicalize(object4[key])}`
|
|
8216
8549
|
).join(",")}}`;
|
|
8217
8550
|
}
|
|
8218
8551
|
throw new Error("ReCap caveats must contain only JSON values.");
|
|
@@ -8296,7 +8629,6 @@ var import_ed255195 = require("@noble/curves/ed25519");
|
|
|
8296
8629
|
var import_blake33 = require("@noble/hashes/blake3");
|
|
8297
8630
|
var import_sha2563 = require("@noble/hashes/sha256");
|
|
8298
8631
|
var ENVELOPE_SCHEMA = "xyz.tinycloud.share/envelope";
|
|
8299
|
-
var INLINE_PREFIX = "tc2:";
|
|
8300
8632
|
var RAW_CID_CODEC = 85;
|
|
8301
8633
|
var BLAKE3_256_CODE = 30;
|
|
8302
8634
|
var DEFAULT_MAX_INLINE_BYTES = 256 * 1024;
|
|
@@ -8343,8 +8675,8 @@ function canonicalJson(value) {
|
|
|
8343
8675
|
}
|
|
8344
8676
|
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
8345
8677
|
if (typeof value === "object") {
|
|
8346
|
-
const
|
|
8347
|
-
return `{${Object.keys(
|
|
8678
|
+
const object4 = value;
|
|
8679
|
+
return `{${Object.keys(object4).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(object4[key])}`).join(",")}}`;
|
|
8348
8680
|
}
|
|
8349
8681
|
throw new ShareEnvelopeError("invalid-envelope", "unsupported JSON value");
|
|
8350
8682
|
}
|
|
@@ -9001,25 +9333,8 @@ function parsePublishedShareLink(link, options2 = {}) {
|
|
|
9001
9333
|
}
|
|
9002
9334
|
const origin2 = normalizeOrigin(url.origin);
|
|
9003
9335
|
if (options2.trustedOrigins !== void 0 && !options2.trustedOrigins.map(normalizeOrigin).includes(origin2)) throw new ShareEnvelopeError("origin-mismatch", "share link origin is not trusted");
|
|
9004
|
-
if (url.
|
|
9005
|
-
if (url.
|
|
9006
|
-
let payload;
|
|
9007
|
-
try {
|
|
9008
|
-
const bytes2 = base64UrlToBytes(url.hash.slice("#v=2&p=".length));
|
|
9009
|
-
if (bytes2.length > (options2.maxInlineBytes ?? DEFAULT_MAX_INLINE_BYTES) * 2) throw new Error("inline payload too large");
|
|
9010
|
-
const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes2));
|
|
9011
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("inline payload shape");
|
|
9012
|
-
payload = value;
|
|
9013
|
-
} catch (error) {
|
|
9014
|
-
throw new ShareEnvelopeError("invalid-link", error instanceof Error ? error.message : "invalid inline payload");
|
|
9015
|
-
}
|
|
9016
|
-
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");
|
|
9017
|
-
const sealed = base64UrlToBytes(payload.c);
|
|
9018
|
-
if (!verifyPublishedShareCid(payload.cid, sealed)) throw new ShareEnvelopeError("cid-mismatch", "inline payload CID does not match");
|
|
9019
|
-
const key2 = payload.k === void 0 ? void 0 : base64UrlToBytes(payload.k);
|
|
9020
|
-
if (key2 !== void 0 && key2.length !== 32) throw new ShareEnvelopeError("invalid-link", "inline payload key is invalid");
|
|
9021
|
-
return { origin: origin2, cid: payload.cid, ...key2 === void 0 ? {} : { key: key2 }, sealed };
|
|
9022
|
-
}
|
|
9336
|
+
if (url.username !== "" || url.password !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
|
|
9337
|
+
if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
|
|
9023
9338
|
const match = /^\/s\/([a-z2-7]+)$/.exec(url.pathname);
|
|
9024
9339
|
if (match === null || !url.hash.startsWith("#k=")) throw new ShareEnvelopeError("invalid-link", "published share link is invalid");
|
|
9025
9340
|
const key = base64UrlToBytes(url.hash.slice(3));
|
|
@@ -9039,13 +9354,10 @@ function parseShareUrl(link, options2 = {}) {
|
|
|
9039
9354
|
} catch {
|
|
9040
9355
|
throw new ShareEnvelopeError("invalid-link", "share link must be an absolute URL");
|
|
9041
9356
|
}
|
|
9042
|
-
if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share links must not contain query parameters");
|
|
9043
9357
|
const origin2 = normalizeOrigin(url.origin);
|
|
9044
9358
|
if (options2.trustedOrigins !== void 0 && !options2.trustedOrigins.map(normalizeOrigin).includes(origin2)) throw new ShareEnvelopeError("origin-mismatch", "share link origin is not trusted");
|
|
9045
|
-
if (url.pathname === "/
|
|
9046
|
-
|
|
9047
|
-
return { kind: "inline", origin: origin2, cid: published.cid, key: published.key, url: `${url.origin}${url.pathname}`, protocol: "share-envelope-v2" };
|
|
9048
|
-
}
|
|
9359
|
+
if (url.pathname === "/viewer") throw new ShareEnvelopeError("invalid-link", "retired viewer links are not accepted");
|
|
9360
|
+
if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share links must not contain query parameters");
|
|
9049
9361
|
const match = url.pathname.match(/^\/s\/(i\/)?([^/]+)$/);
|
|
9050
9362
|
if (!match) throw new ShareEnvelopeError("invalid-link", "share link path is invalid");
|
|
9051
9363
|
const fragmentParams = url.hash === "" ? void 0 : new URLSearchParams(url.hash.slice(1));
|
|
@@ -9068,9 +9380,10 @@ function parseShareUrl(link, options2 = {}) {
|
|
|
9068
9380
|
}
|
|
9069
9381
|
function isV2ShareLink(link) {
|
|
9070
9382
|
try {
|
|
9071
|
-
|
|
9383
|
+
const url = new URL(link);
|
|
9384
|
+
return url.search === "" && url.pathname.startsWith("/s/");
|
|
9072
9385
|
} catch {
|
|
9073
|
-
return
|
|
9386
|
+
return false;
|
|
9074
9387
|
}
|
|
9075
9388
|
}
|
|
9076
9389
|
async function encryptShareBytes(bytes2, key) {
|
|
@@ -9526,8 +9839,8 @@ function canonicalJsonForProof(value) {
|
|
|
9526
9839
|
}
|
|
9527
9840
|
if (Array.isArray(value)) return `[${value.map(canonicalJsonForProof).join(",")}]`;
|
|
9528
9841
|
if (typeof value === "object") {
|
|
9529
|
-
const
|
|
9530
|
-
return `{${Object.keys(
|
|
9842
|
+
const object4 = value;
|
|
9843
|
+
return `{${Object.keys(object4).sort().map((key) => `${JSON.stringify(key)}:${canonicalJsonForProof(object4[key])}`).join(",")}}`;
|
|
9531
9844
|
}
|
|
9532
9845
|
throw new ShareAccessError("SHARE_SESSION_INVALID");
|
|
9533
9846
|
}
|
|
@@ -10236,12 +10549,12 @@ var ShareRecipientClient = class {
|
|
|
10236
10549
|
this.assertDetachedProof(request.proof);
|
|
10237
10550
|
}
|
|
10238
10551
|
parseSessionCapability(value) {
|
|
10239
|
-
const
|
|
10240
|
-
if (typeof
|
|
10241
|
-
if (!Array.isArray(
|
|
10242
|
-
const resource = this.requireObject(
|
|
10552
|
+
const object4 = this.requireObject(value, "session capability");
|
|
10553
|
+
if (typeof object4.spaceId !== "string") throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability space is invalid");
|
|
10554
|
+
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");
|
|
10555
|
+
const resource = this.requireObject(object4.resource, "session capability resource");
|
|
10243
10556
|
if (resource.kind !== "exact" && resource.kind !== "prefix" || typeof resource.path !== "string") throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability resource is invalid");
|
|
10244
|
-
return { spaceId:
|
|
10557
|
+
return { spaceId: object4.spaceId, actions: [...new Set(object4.actions)], resource: { kind: resource.kind, path: resource.path } };
|
|
10245
10558
|
}
|
|
10246
10559
|
assertProvidedPolicySession(session, envelope) {
|
|
10247
10560
|
const sessionExpiry = dateValue(session.expiresAt);
|
|
@@ -11881,62 +12194,21 @@ function validateOwnerSharePolicyRegistrationBytes(bytesValue, expected) {
|
|
|
11881
12194
|
return validateOwnerSharePolicyRegistration(value, expected);
|
|
11882
12195
|
}
|
|
11883
12196
|
|
|
11884
|
-
// src/delegations/share-delivery.ts
|
|
12197
|
+
// src/delegations/share-delivery-v3.ts
|
|
11885
12198
|
var import_ed255199 = require("@noble/curves/ed25519");
|
|
11886
|
-
var
|
|
11887
|
-
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
|
|
11891
|
-
|
|
11892
|
-
return false;
|
|
11893
|
-
}
|
|
11894
|
-
return url.protocol === "https:" && url.origin === value && url.pathname === "/" && url.search === "" && url.hash === "" && url.username === "" && url.password === "";
|
|
11895
|
-
}
|
|
11896
|
-
var AUTHORIZATION_KEYS = [
|
|
11897
|
-
"type",
|
|
11898
|
-
"version",
|
|
11899
|
-
"jti",
|
|
11900
|
-
"shareCid",
|
|
11901
|
-
"shareId",
|
|
11902
|
-
"registrationCid",
|
|
11903
|
-
"delegationCid",
|
|
11904
|
-
"enforcementDelegationCid",
|
|
11905
|
-
"envelopeCid",
|
|
11906
|
-
"policyCid",
|
|
11907
|
-
"nodeAudience",
|
|
11908
|
-
"targetOrigin",
|
|
11909
|
-
"openCredentialsAudience",
|
|
11910
|
-
"holder",
|
|
11911
|
-
"recipientMatcher",
|
|
11912
|
-
"deliveryEmail",
|
|
11913
|
-
"shareUrl",
|
|
11914
|
-
"returnOrigin",
|
|
11915
|
-
"documentName",
|
|
11916
|
-
"senderDid",
|
|
11917
|
-
"senderTrust",
|
|
11918
|
-
"contentSource",
|
|
11919
|
-
"contentSourceDigest",
|
|
11920
|
-
"shareExpiresAt",
|
|
11921
|
-
"issuedAt",
|
|
11922
|
-
"reportAbuseToken",
|
|
11923
|
-
"actions",
|
|
11924
|
-
"resource",
|
|
11925
|
-
"authorityMaterialHandle",
|
|
11926
|
-
"authorityMaterialDigest",
|
|
11927
|
-
"requestBodyDigest",
|
|
11928
|
-
"idempotencyKey",
|
|
11929
|
-
"expiresAt",
|
|
11930
|
-
"dataAuthority"
|
|
11931
|
-
];
|
|
11932
|
-
function assertObject2(value, keys, label) {
|
|
12199
|
+
var import_sha2567 = require("@noble/hashes/sha256");
|
|
12200
|
+
var CREDENTIAL_INVITATION_REQUEST_DOMAIN = "xyz.tinycloud.credentials/invitation-request/v1\0";
|
|
12201
|
+
var DELIVERY_ADMISSION_DOMAIN = "xyz.tinycloud.policy/delivery-admission/v0\0";
|
|
12202
|
+
var REQUEST_KEYS = ["schema", "policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "audience", "issuedAt", "expiresAt", "nonce"];
|
|
12203
|
+
var ADMISSION_KEYS = ["schema", "policyId", "ownerDid", "recipient", "resource", "actions", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "senderKeyDid", "audience", "issuedAt", "expiresAt", "nonce", "signature"];
|
|
12204
|
+
function exactObject2(value, keys, label) {
|
|
11933
12205
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
|
|
11934
12206
|
const record2 = value;
|
|
11935
12207
|
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`);
|
|
11936
12208
|
return record2;
|
|
11937
12209
|
}
|
|
11938
|
-
function
|
|
11939
|
-
if (!/^[A-Za-z0-9_-]+$/.test(value)) throw new Error("share delivery value is not canonical base64url");
|
|
12210
|
+
function decodeBase64Url3(value) {
|
|
12211
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9_-]+$/.test(value)) throw new Error("share delivery value is not canonical base64url");
|
|
11940
12212
|
if (typeof atob === "function") {
|
|
11941
12213
|
const padded = value.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - value.length % 4) % 4);
|
|
11942
12214
|
return Uint8Array.from(atob(padded), (character) => character.charCodeAt(0));
|
|
@@ -11944,42 +12216,45 @@ function fromB642(value) {
|
|
|
11944
12216
|
if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(value, "base64url"));
|
|
11945
12217
|
throw new Error("base64url decoding is unavailable");
|
|
11946
12218
|
}
|
|
11947
|
-
function
|
|
11948
|
-
|
|
12219
|
+
function object3(value, label) {
|
|
12220
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
|
|
12221
|
+
return value;
|
|
12222
|
+
}
|
|
12223
|
+
function didKeyBytes2(did) {
|
|
12224
|
+
if (!did.startsWith("did:key:z") || /[#/?]/.test(did.slice("did:key:".length))) throw new Error("v3 delivery admission signer is invalid");
|
|
12225
|
+
const decoded = base58btc.decode(did.slice("did:key:".length));
|
|
12226
|
+
if (decoded.length !== 34 || decoded[0] !== 237 || decoded[1] !== 1) throw new Error("v3 delivery admission signer is invalid");
|
|
12227
|
+
return decoded.slice(2);
|
|
12228
|
+
}
|
|
12229
|
+
function validateShareDeliveryAuthorizationV3Bytes(bytes2, expected) {
|
|
12230
|
+
let parsed;
|
|
11949
12231
|
try {
|
|
11950
|
-
|
|
12232
|
+
parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes2));
|
|
11951
12233
|
} catch {
|
|
11952
|
-
throw new Error("share delivery
|
|
11953
|
-
}
|
|
11954
|
-
const root =
|
|
11955
|
-
const
|
|
11956
|
-
const
|
|
11957
|
-
const
|
|
11958
|
-
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
|
|
11963
|
-
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
if (
|
|
11968
|
-
const
|
|
11969
|
-
const
|
|
11970
|
-
if (
|
|
11971
|
-
const
|
|
11972
|
-
const
|
|
11973
|
-
|
|
11974
|
-
|
|
11975
|
-
}
|
|
11976
|
-
function shareDeliveryTrustedKid(publicKey) {
|
|
11977
|
-
if (publicKey.length !== 32) throw new Error("share delivery node key must be a raw 32-byte Ed25519 public key");
|
|
11978
|
-
const prefixed = new Uint8Array(34);
|
|
11979
|
-
prefixed.set([237, 1]);
|
|
11980
|
-
prefixed.set(publicKey, 2);
|
|
11981
|
-
const did = `did:key:${base58btc.encode(prefixed)}`;
|
|
11982
|
-
return `${did}#${did.slice("did:key:".length)}`;
|
|
12234
|
+
throw new Error("v3 share delivery response is not valid UTF-8 JSON");
|
|
12235
|
+
}
|
|
12236
|
+
const root = exactObject2(parsed, ["request", "admission"], "v3 share delivery response");
|
|
12237
|
+
const request = exactObject2(root.request, REQUEST_KEYS, "credential invitation request");
|
|
12238
|
+
const admission = exactObject2(root.admission, ADMISSION_KEYS, "delivery admission");
|
|
12239
|
+
const signature = exactObject2(admission.signature, ["suite", "signerDid", "value"], "delivery admission signature");
|
|
12240
|
+
const envelope = object3(expected.request.envelope, "v3 share envelope");
|
|
12241
|
+
const policy = object3(envelope.policy, "v3 share policy");
|
|
12242
|
+
const requirement = object3(policy.credentialRequirement, "v3 credential requirement");
|
|
12243
|
+
const credentialType = object3(requirement.credentialType, "v3 credential type");
|
|
12244
|
+
const contentSource = object3(envelope.contentSource, "v3 content source");
|
|
12245
|
+
const envelopeSignature = object3(envelope.signature, "v3 envelope signature");
|
|
12246
|
+
const target = object3(envelope.target, "v3 target");
|
|
12247
|
+
const binding = object3(envelope.attestedEnforcerBinding, "v3 attested enforcer binding");
|
|
12248
|
+
const fields = ["policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "audience", "issuedAt", "expiresAt", "nonce"];
|
|
12249
|
+
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");
|
|
12250
|
+
const issuedAt = Date.parse(String(request.issuedAt));
|
|
12251
|
+
const expiresAt = Date.parse(String(request.expiresAt));
|
|
12252
|
+
if (!Number.isFinite(issuedAt) || !Number.isFinite(expiresAt) || expiresAt <= issuedAt || expiresAt - issuedAt > 15 * 6e4) throw new Error("v3 share delivery authorization time is invalid");
|
|
12253
|
+
const admissionSignature = decodeBase64Url3(signature.value);
|
|
12254
|
+
const { signature: _signature, ...unsignedAdmission } = admission;
|
|
12255
|
+
const digest4 = (0, import_sha2567.sha256)(new TextEncoder().encode(`${DELIVERY_ADMISSION_DOMAIN}${canonicalizeSignedObjectUnsigned(unsignedAdmission)}`));
|
|
12256
|
+
if (admissionSignature.length !== 64 || !import_ed255199.ed25519.verify(admissionSignature, digest4, didKeyBytes2(String(signature.signerDid)))) throw new Error("v3 share delivery admission signature is invalid");
|
|
12257
|
+
return { request, admission };
|
|
11983
12258
|
}
|
|
11984
12259
|
|
|
11985
12260
|
// src/delegations/sender-share-store.ts
|
|
@@ -14702,8 +14977,8 @@ function stableJson(value) {
|
|
|
14702
14977
|
if (Array.isArray(value)) {
|
|
14703
14978
|
return `[${value.map(stableJson).join(",")}]`;
|
|
14704
14979
|
}
|
|
14705
|
-
const
|
|
14706
|
-
return `{${Object.keys(
|
|
14980
|
+
const object4 = value;
|
|
14981
|
+
return `{${Object.keys(object4).sort().map((key) => `${JSON.stringify(key)}:${stableJson(object4[key])}`).join(",")}}`;
|
|
14707
14982
|
}
|
|
14708
14983
|
function indexedDelegationFromRow(row) {
|
|
14709
14984
|
const [
|
|
@@ -17611,6 +17886,7 @@ var DEFAULT_LOCAL_NODE_URL = "http://127.0.0.1:8000";
|
|
|
17611
17886
|
var LOCAL_LOOPBACK_PROBE_TIMEOUT_MS = 250;
|
|
17612
17887
|
var LOCAL_LINK_PROBE_TIMEOUT_MS = 750;
|
|
17613
17888
|
var LOCAL_LINK_HOST_SUFFIX = ".local.tinycloud.link";
|
|
17889
|
+
var OWNER_NODE_BINDING_TIMEOUT_MS = 5e3;
|
|
17614
17890
|
function createInMemoryLocalNodeIdentityStore() {
|
|
17615
17891
|
const pins = /* @__PURE__ */ new Map();
|
|
17616
17892
|
return {
|
|
@@ -17743,21 +18019,80 @@ async function verifyLocationRecord(input) {
|
|
|
17743
18019
|
}
|
|
17744
18020
|
return false;
|
|
17745
18021
|
}
|
|
17746
|
-
async function fetchLocationRecord(registryUrl, subject, fetchFn = globalThis.fetch) {
|
|
18022
|
+
async function fetchLocationRecord(registryUrl, subject, fetchFn = globalThis.fetch, signal) {
|
|
17747
18023
|
const url = `${registryUrl.replace(/\/$/, "")}/v1/locations/${encodeURIComponent(subject)}`;
|
|
17748
|
-
const response = await fetchFn(url
|
|
18024
|
+
const response = await fetchFn(url, {
|
|
18025
|
+
redirect: "error",
|
|
18026
|
+
headers: { accept: "application/json" },
|
|
18027
|
+
...signal === void 0 ? {} : { signal }
|
|
18028
|
+
});
|
|
17749
18029
|
if (response.status === 404) {
|
|
17750
18030
|
return null;
|
|
17751
18031
|
}
|
|
17752
18032
|
if (!response.ok) {
|
|
17753
18033
|
throw new Error(`location registry returned HTTP ${response.status}`);
|
|
17754
18034
|
}
|
|
17755
|
-
const
|
|
18035
|
+
const text2 = await boundedResponseText(response, "location registry", 64 * 1024);
|
|
18036
|
+
let body;
|
|
18037
|
+
try {
|
|
18038
|
+
body = JSON.parse(text2);
|
|
18039
|
+
} catch {
|
|
18040
|
+
throw new LocationRecordValidationError("registry response is not JSON");
|
|
18041
|
+
}
|
|
17756
18042
|
if (body.record === void 0) {
|
|
17757
18043
|
throw new LocationRecordValidationError("registry response missing record");
|
|
17758
18044
|
}
|
|
17759
18045
|
return validateLocationRecord(body.record);
|
|
17760
18046
|
}
|
|
18047
|
+
async function publishLocationRecord(options2) {
|
|
18048
|
+
validateSubject(options2.subject);
|
|
18049
|
+
const registryUrl = exactHttpOrigin(options2.registryUrl, "registry URL");
|
|
18050
|
+
const nodeOrigin = exactHttpOrigin(options2.nodeOrigin, "node origin");
|
|
18051
|
+
const fetchFn = options2.fetch ?? globalThis.fetch.bind(globalThis);
|
|
18052
|
+
const current = await fetchLocationRecord(registryUrl, options2.subject, fetchFn);
|
|
18053
|
+
if (current !== null) {
|
|
18054
|
+
if (current.subject !== options2.subject || !await verifyLocationRecord(current)) {
|
|
18055
|
+
throw new LocationRecordValidationError("existing location record signature is invalid");
|
|
18056
|
+
}
|
|
18057
|
+
if (publishedHttpOrigins(current).includes(nodeOrigin)) {
|
|
18058
|
+
return current;
|
|
18059
|
+
}
|
|
18060
|
+
}
|
|
18061
|
+
const record2 = await signLocationRecord({
|
|
18062
|
+
version: 1,
|
|
18063
|
+
subject: options2.subject,
|
|
18064
|
+
multiaddrs: [...current?.multiaddrs ?? [], httpUrlToMultiaddr(nodeOrigin)],
|
|
18065
|
+
updated_at: (options2.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
18066
|
+
sequence: (current?.sequence ?? -1) + 1
|
|
18067
|
+
}, options2.signer);
|
|
18068
|
+
const response = await fetchFn(
|
|
18069
|
+
`${registryUrl}/v1/locations/${encodeURIComponent(options2.subject)}`,
|
|
18070
|
+
{
|
|
18071
|
+
method: "PUT",
|
|
18072
|
+
redirect: "error",
|
|
18073
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
18074
|
+
body: JSON.stringify(record2)
|
|
18075
|
+
}
|
|
18076
|
+
);
|
|
18077
|
+
if (!response.ok) {
|
|
18078
|
+
throw new Error(`location registry publish returned HTTP ${response.status}`);
|
|
18079
|
+
}
|
|
18080
|
+
const text2 = await boundedResponseText(response, "location registry", 64 * 1024);
|
|
18081
|
+
let body;
|
|
18082
|
+
try {
|
|
18083
|
+
body = JSON.parse(text2);
|
|
18084
|
+
} catch {
|
|
18085
|
+
throw new LocationRecordValidationError("registry publish response is not JSON");
|
|
18086
|
+
}
|
|
18087
|
+
if (body.record === void 0) {
|
|
18088
|
+
throw new LocationRecordValidationError("registry publish response missing record");
|
|
18089
|
+
}
|
|
18090
|
+
const published = validateLocationRecord(body.record);
|
|
18091
|
+
if (canonicalLocationPayload(locationPayloadForRecord(published)) !== canonicalLocationPayload(locationPayloadForRecord(record2)) || published.signature !== record2.signature || !await verifyLocationRecord(published)) {
|
|
18092
|
+
throw new LocationRecordValidationError("registry publish response does not match the signed record");
|
|
18093
|
+
}
|
|
18094
|
+
return published;
|
|
18095
|
+
}
|
|
17761
18096
|
async function resolveCloudLocation(subject, options2 = {}) {
|
|
17762
18097
|
validateSubject(subject);
|
|
17763
18098
|
const verifyRecords = options2.verifyRecords ?? true;
|
|
@@ -18008,6 +18343,123 @@ function multiaddrToHttpUrl(input) {
|
|
|
18008
18343
|
}
|
|
18009
18344
|
return uri;
|
|
18010
18345
|
}
|
|
18346
|
+
function exactHttpOrigin(value, label) {
|
|
18347
|
+
let url;
|
|
18348
|
+
try {
|
|
18349
|
+
url = new URL(value);
|
|
18350
|
+
} catch {
|
|
18351
|
+
throw new LocationRecordValidationError(`${label} must be an absolute URL origin`);
|
|
18352
|
+
}
|
|
18353
|
+
const loopback = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
|
|
18354
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) {
|
|
18355
|
+
throw new LocationRecordValidationError(`${label} must use HTTPS (or loopback HTTP)`);
|
|
18356
|
+
}
|
|
18357
|
+
if (url.username !== "" || url.password !== "" || url.pathname !== "/" || url.search !== "" || url.hash !== "") {
|
|
18358
|
+
throw new LocationRecordValidationError(`${label} must be an origin without credentials, path, query, or fragment`);
|
|
18359
|
+
}
|
|
18360
|
+
return url.origin;
|
|
18361
|
+
}
|
|
18362
|
+
function publishedHttpOrigins(record2) {
|
|
18363
|
+
const origins = [];
|
|
18364
|
+
for (const address of record2.multiaddrs) {
|
|
18365
|
+
try {
|
|
18366
|
+
origins.push(exactHttpOrigin(multiaddrToHttpUrl(address), "published node URL"));
|
|
18367
|
+
} catch {
|
|
18368
|
+
}
|
|
18369
|
+
}
|
|
18370
|
+
return origins;
|
|
18371
|
+
}
|
|
18372
|
+
async function boundedResponseText(response, label, maxBytes) {
|
|
18373
|
+
const declaredLength = response.headers.get("content-length");
|
|
18374
|
+
if (declaredLength !== null && (!/^\d+$/.test(declaredLength) || Number(declaredLength) > maxBytes)) {
|
|
18375
|
+
throw new LocationRecordValidationError(`${label} response is too large`);
|
|
18376
|
+
}
|
|
18377
|
+
if (response.body === null) return "";
|
|
18378
|
+
const reader = response.body.getReader();
|
|
18379
|
+
const chunks = [];
|
|
18380
|
+
let total = 0;
|
|
18381
|
+
try {
|
|
18382
|
+
while (true) {
|
|
18383
|
+
const { done, value } = await reader.read();
|
|
18384
|
+
if (done) break;
|
|
18385
|
+
if (value === void 0) continue;
|
|
18386
|
+
total += value.byteLength;
|
|
18387
|
+
if (total > maxBytes) {
|
|
18388
|
+
await reader.cancel().catch(() => void 0);
|
|
18389
|
+
throw new LocationRecordValidationError(`${label} response is too large`);
|
|
18390
|
+
}
|
|
18391
|
+
chunks.push(value);
|
|
18392
|
+
}
|
|
18393
|
+
} finally {
|
|
18394
|
+
reader.releaseLock();
|
|
18395
|
+
}
|
|
18396
|
+
const bytes2 = new Uint8Array(total);
|
|
18397
|
+
let offset = 0;
|
|
18398
|
+
for (const chunk of chunks) {
|
|
18399
|
+
bytes2.set(chunk, offset);
|
|
18400
|
+
offset += chunk.byteLength;
|
|
18401
|
+
}
|
|
18402
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes2);
|
|
18403
|
+
}
|
|
18404
|
+
function boundedSignal(parent, timeoutMs) {
|
|
18405
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
18406
|
+
throw new LocationRecordValidationError("owner-node binding timeout must be positive");
|
|
18407
|
+
}
|
|
18408
|
+
const controller = new AbortController();
|
|
18409
|
+
const abortFromParent = () => controller.abort(parent?.reason);
|
|
18410
|
+
if (parent?.aborted) abortFromParent();
|
|
18411
|
+
else parent?.addEventListener("abort", abortFromParent, { once: true });
|
|
18412
|
+
const timer = setTimeout(() => controller.abort(new DOMException("owner-node binding timed out", "TimeoutError")), timeoutMs);
|
|
18413
|
+
return {
|
|
18414
|
+
signal: controller.signal,
|
|
18415
|
+
dispose() {
|
|
18416
|
+
clearTimeout(timer);
|
|
18417
|
+
parent?.removeEventListener("abort", abortFromParent);
|
|
18418
|
+
}
|
|
18419
|
+
};
|
|
18420
|
+
}
|
|
18421
|
+
async function verifyOwnerNodeBinding(options2) {
|
|
18422
|
+
validateSubject(options2.ownerDid);
|
|
18423
|
+
validateSubject(options2.nodeDid);
|
|
18424
|
+
const registryUrl = exactHttpOrigin(options2.registryUrl, "registry URL");
|
|
18425
|
+
const nodeOrigin = exactHttpOrigin(options2.nodeOrigin, "node origin");
|
|
18426
|
+
const fetchFn = options2.fetch ?? globalThis.fetch.bind(globalThis);
|
|
18427
|
+
const lookup = boundedSignal(options2.signal, options2.timeoutMs ?? OWNER_NODE_BINDING_TIMEOUT_MS);
|
|
18428
|
+
try {
|
|
18429
|
+
const record2 = await fetchLocationRecord(registryUrl, options2.ownerDid, fetchFn, lookup.signal);
|
|
18430
|
+
if (record2 === null) {
|
|
18431
|
+
throw new LocationRecordValidationError("owner has no published location record");
|
|
18432
|
+
}
|
|
18433
|
+
if (record2.subject !== options2.ownerDid || !await verifyLocationRecord(record2)) {
|
|
18434
|
+
throw new LocationRecordValidationError("owner location record signature is invalid");
|
|
18435
|
+
}
|
|
18436
|
+
const publishedOrigins = publishedHttpOrigins(record2);
|
|
18437
|
+
if (!publishedOrigins.includes(nodeOrigin)) {
|
|
18438
|
+
throw new LocationRecordValidationError("share target is not in the owner's signed location record");
|
|
18439
|
+
}
|
|
18440
|
+
const response = await fetchFn(new URL("/info", nodeOrigin), {
|
|
18441
|
+
redirect: "error",
|
|
18442
|
+
headers: { accept: "application/json" },
|
|
18443
|
+
signal: lookup.signal
|
|
18444
|
+
});
|
|
18445
|
+
if (!response.ok) {
|
|
18446
|
+
throw new LocationRecordValidationError(`target node /info returned HTTP ${response.status}`);
|
|
18447
|
+
}
|
|
18448
|
+
const text2 = await boundedResponseText(response, "target node /info", 16 * 1024);
|
|
18449
|
+
let body;
|
|
18450
|
+
try {
|
|
18451
|
+
body = JSON.parse(text2);
|
|
18452
|
+
} catch {
|
|
18453
|
+
throw new LocationRecordValidationError("target node /info response is not JSON");
|
|
18454
|
+
}
|
|
18455
|
+
if (body === null || typeof body !== "object" || Array.isArray(body) || body.nodeId !== options2.nodeDid) {
|
|
18456
|
+
throw new LocationRecordValidationError("target node identity does not match the share attestation");
|
|
18457
|
+
}
|
|
18458
|
+
return Object.freeze({ record: record2, nodeOrigin, nodeDid: options2.nodeDid });
|
|
18459
|
+
} finally {
|
|
18460
|
+
lookup.dispose();
|
|
18461
|
+
}
|
|
18462
|
+
}
|
|
18011
18463
|
function httpUrlToMultiaddr(input) {
|
|
18012
18464
|
const url = new URL(input);
|
|
18013
18465
|
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
@@ -18169,7 +18621,7 @@ async function verifyPkhSignature(did, payload, signature) {
|
|
|
18169
18621
|
}
|
|
18170
18622
|
function verifyDidKeySignature(did, payload, signature) {
|
|
18171
18623
|
const publicKey = ed25519PublicKeyFromDidKey2(did);
|
|
18172
|
-
const signatureBytes =
|
|
18624
|
+
const signatureBytes = decodeBase64Url4(signature);
|
|
18173
18625
|
if (signatureBytes.length !== 64) {
|
|
18174
18626
|
throw new LocationRecordValidationError(
|
|
18175
18627
|
"did:key signature must be a base64url Ed25519 signature"
|
|
@@ -18222,7 +18674,7 @@ function base64UrlEncode3(bytes2) {
|
|
|
18222
18674
|
}
|
|
18223
18675
|
return output;
|
|
18224
18676
|
}
|
|
18225
|
-
function
|
|
18677
|
+
function decodeBase64Url4(value) {
|
|
18226
18678
|
const alphabet2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
18227
18679
|
const bytes2 = [];
|
|
18228
18680
|
let buffer = 0;
|