@tinycloud/sdk-core 3.0.0-beta.8 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{credential-admission-CFjUChF8.d.cts → credential-admission-BxpkckU0.d.cts} +23 -3
- package/dist/{credential-admission-DIEPgQ0o.d.ts → credential-admission-CO9WMtGs.d.ts} +23 -3
- package/dist/delegations/index.cjs +101 -188
- 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 +102 -193
- package/dist/delegations/index.js.map +1 -1
- package/dist/index.cjs +381 -218
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +74 -7
- package/dist/index.d.ts +74 -7
- package/dist/index.js +382 -223
- package/dist/index.js.map +1 -1
- package/dist/policy/index.cjs +82 -36
- 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 +82 -36
- package/dist/policy/index.js.map +1 -1
- package/dist/{recipient-types.schema-DjmM_yJu.d.cts → recipient-types.schema-D9AI8tjN.d.cts} +42 -105
- package/dist/{recipient-types.schema-DjmM_yJu.d.ts → recipient-types.schema-D9AI8tjN.d.ts} +42 -105
- package/package.json +4 -4
|
@@ -47,7 +47,6 @@ __export(delegations_exports, {
|
|
|
47
47
|
MemorySenderShareRecordStorage: () => MemorySenderShareRecordStorage,
|
|
48
48
|
MemoryShareCache: () => MemoryShareCache,
|
|
49
49
|
ReceiveOptionsSchema: () => ReceiveOptionsSchema,
|
|
50
|
-
SHARE_DELIVERY_AUTHORIZATION_DOMAIN: () => SHARE_DELIVERY_AUTHORIZATION_DOMAIN,
|
|
51
50
|
SenderShareStore: () => SenderShareStore,
|
|
52
51
|
ShareAccessError: () => ShareAccessError,
|
|
53
52
|
ShareActionSchema: () => ShareActionSchema,
|
|
@@ -87,10 +86,8 @@ __export(delegations_exports, {
|
|
|
87
86
|
importPortableDelegation: () => importPortableDelegation,
|
|
88
87
|
inspectShare: () => import_share_sdk2.inspectShare,
|
|
89
88
|
intersectShareCapabilities: () => intersectShareCapabilities,
|
|
90
|
-
isLegacyShareLink: () => import_share_sdk.isLegacyShareLink,
|
|
91
89
|
isV2ShareLink: () => isV2ShareLink,
|
|
92
90
|
listShares: () => import_share_sdk.listShares,
|
|
93
|
-
migrateShare: () => import_share_sdk.migrateShare,
|
|
94
91
|
normalizeShareActions: () => normalizeShareActions,
|
|
95
92
|
normalizeShareRecipientTarget: () => normalizeShareRecipientTarget,
|
|
96
93
|
normalizeShareResource: () => normalizeShareResource,
|
|
@@ -100,14 +97,12 @@ __export(delegations_exports, {
|
|
|
100
97
|
parseShareUrl: () => parseShareUrl,
|
|
101
98
|
publishPolicyShare: () => import_share_sdk.publishPolicyShare,
|
|
102
99
|
publishTargetShare: () => import_share_sdk.publishTargetShare,
|
|
103
|
-
receiveLegacyShare: () => import_share_sdk.receiveLegacyShare,
|
|
104
100
|
receiveShare: () => import_share_sdk2.receiveShare,
|
|
105
101
|
restoreDelegatedShareKey: () => restoreDelegatedShareKey,
|
|
106
102
|
resumeShareAuthorization: () => import_share_sdk.resumeShareAuthorization,
|
|
107
103
|
revokeShare: () => import_share_sdk.revokeShare,
|
|
108
104
|
serializePortableDelegation: () => serializePortableDelegation,
|
|
109
105
|
shareCapabilityAllows: () => shareCapabilityAllows,
|
|
110
|
-
shareDeliveryTrustedKid: () => shareDeliveryTrustedKid,
|
|
111
106
|
showShare: () => import_share_sdk.showShare,
|
|
112
107
|
signShareEnvelope: () => signShareEnvelope,
|
|
113
108
|
toShareErrorInfo: () => import_share_sdk2.toShareErrorInfo,
|
|
@@ -115,10 +110,8 @@ __export(delegations_exports, {
|
|
|
115
110
|
validateOwnerSharePolicyRegistration: () => validateOwnerSharePolicyRegistration,
|
|
116
111
|
validateOwnerSharePolicyRegistrationBytes: () => validateOwnerSharePolicyRegistrationBytes,
|
|
117
112
|
validateReceiveOptions: () => validateReceiveOptions,
|
|
118
|
-
validateShareDeliveryAuthorizationBytes: () => validateShareDeliveryAuthorizationBytes,
|
|
119
113
|
validateShareDeliveryAuthorizationV3Bytes: () => validateShareDeliveryAuthorizationV3Bytes,
|
|
120
114
|
validateSharingServiceConfig: () => validateSharingServiceConfig,
|
|
121
|
-
verifyBearerEnvelope: () => import_share_sdk2.verifyBearerEnvelope,
|
|
122
115
|
verifyShareCid: () => verifyShareCid,
|
|
123
116
|
verifyShareEnvelope: () => verifyShareEnvelope
|
|
124
117
|
});
|
|
@@ -177,6 +170,7 @@ var DelegationErrorSchema = import_zod.z.object({
|
|
|
177
170
|
});
|
|
178
171
|
var DelegationErrorCodes = {
|
|
179
172
|
AUTH_REQUIRED: "AUTH_REQUIRED",
|
|
173
|
+
AUTH_UNAUTHORIZED: "AUTH_UNAUTHORIZED",
|
|
180
174
|
AUTH_EXPIRED: "AUTH_EXPIRED",
|
|
181
175
|
NOT_INITIALIZED: "NOT_INITIALIZED",
|
|
182
176
|
NOT_FOUND: "NOT_FOUND",
|
|
@@ -541,7 +535,7 @@ var CreateDelegationWasmResultSchema = import_zod.z.object({
|
|
|
541
535
|
// src/expiry.ts
|
|
542
536
|
var EPHEMERAL_MS = 60 * 60 * 1e3;
|
|
543
537
|
var SIGNED_READ_URL_MS = 5 * 60 * 1e3;
|
|
544
|
-
var SESSION_MS =
|
|
538
|
+
var SESSION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
545
539
|
var SHARE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
546
540
|
var APP_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
547
541
|
var MAX_MS = 10 * 365 * 24 * 60 * 60 * 1e3;
|
|
@@ -2696,7 +2690,6 @@ var import_ed25519 = require("@noble/curves/ed25519");
|
|
|
2696
2690
|
var import_blake3 = require("@noble/hashes/blake3");
|
|
2697
2691
|
var import_sha256 = require("@noble/hashes/sha256");
|
|
2698
2692
|
var ENVELOPE_SCHEMA = "xyz.tinycloud.share/envelope";
|
|
2699
|
-
var INLINE_PREFIX = "tc2:";
|
|
2700
2693
|
var RAW_CID_CODEC = 85;
|
|
2701
2694
|
var BLAKE3_256_CODE = 30;
|
|
2702
2695
|
var DEFAULT_MAX_INLINE_BYTES = 256 * 1024;
|
|
@@ -3401,25 +3394,8 @@ function parsePublishedShareLink(link, options = {}) {
|
|
|
3401
3394
|
}
|
|
3402
3395
|
const origin = normalizeOrigin(url.origin);
|
|
3403
3396
|
if (options.trustedOrigins !== void 0 && !options.trustedOrigins.map(normalizeOrigin).includes(origin)) throw new ShareEnvelopeError("origin-mismatch", "share link origin is not trusted");
|
|
3404
|
-
if (url.
|
|
3405
|
-
if (url.
|
|
3406
|
-
let payload;
|
|
3407
|
-
try {
|
|
3408
|
-
const bytes2 = base64UrlToBytes(url.hash.slice("#v=2&p=".length));
|
|
3409
|
-
if (bytes2.length > (options.maxInlineBytes ?? DEFAULT_MAX_INLINE_BYTES) * 2) throw new Error("inline payload too large");
|
|
3410
|
-
const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes2));
|
|
3411
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("inline payload shape");
|
|
3412
|
-
payload = value;
|
|
3413
|
-
} catch (error) {
|
|
3414
|
-
throw new ShareEnvelopeError("invalid-link", error instanceof Error ? error.message : "invalid inline payload");
|
|
3415
|
-
}
|
|
3416
|
-
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");
|
|
3417
|
-
const sealed = base64UrlToBytes(payload.c);
|
|
3418
|
-
if (!verifyPublishedShareCid(payload.cid, sealed)) throw new ShareEnvelopeError("cid-mismatch", "inline payload CID does not match");
|
|
3419
|
-
const key2 = payload.k === void 0 ? void 0 : base64UrlToBytes(payload.k);
|
|
3420
|
-
if (key2 !== void 0 && key2.length !== 32) throw new ShareEnvelopeError("invalid-link", "inline payload key is invalid");
|
|
3421
|
-
return { origin, cid: payload.cid, ...key2 === void 0 ? {} : { key: key2 }, sealed };
|
|
3422
|
-
}
|
|
3397
|
+
if (url.username !== "" || url.password !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
|
|
3398
|
+
if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
|
|
3423
3399
|
const match = /^\/s\/([a-z2-7]+)$/.exec(url.pathname);
|
|
3424
3400
|
if (match === null || !url.hash.startsWith("#k=")) throw new ShareEnvelopeError("invalid-link", "published share link is invalid");
|
|
3425
3401
|
const key = base64UrlToBytes(url.hash.slice(3));
|
|
@@ -3439,13 +3415,10 @@ function parseShareUrl(link, options = {}) {
|
|
|
3439
3415
|
} catch {
|
|
3440
3416
|
throw new ShareEnvelopeError("invalid-link", "share link must be an absolute URL");
|
|
3441
3417
|
}
|
|
3442
|
-
if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share links must not contain query parameters");
|
|
3443
3418
|
const origin = normalizeOrigin(url.origin);
|
|
3444
3419
|
if (options.trustedOrigins !== void 0 && !options.trustedOrigins.map(normalizeOrigin).includes(origin)) throw new ShareEnvelopeError("origin-mismatch", "share link origin is not trusted");
|
|
3445
|
-
if (url.pathname === "/
|
|
3446
|
-
|
|
3447
|
-
return { kind: "inline", origin, cid: published.cid, key: published.key, url: `${url.origin}${url.pathname}`, protocol: "share-envelope-v2" };
|
|
3448
|
-
}
|
|
3420
|
+
if (url.pathname === "/viewer") throw new ShareEnvelopeError("invalid-link", "retired viewer links are not accepted");
|
|
3421
|
+
if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share links must not contain query parameters");
|
|
3449
3422
|
const match = url.pathname.match(/^\/s\/(i\/)?([^/]+)$/);
|
|
3450
3423
|
if (!match) throw new ShareEnvelopeError("invalid-link", "share link path is invalid");
|
|
3451
3424
|
const fragmentParams = url.hash === "" ? void 0 : new URLSearchParams(url.hash.slice(1));
|
|
@@ -3468,9 +3441,10 @@ function parseShareUrl(link, options = {}) {
|
|
|
3468
3441
|
}
|
|
3469
3442
|
function isV2ShareLink(link) {
|
|
3470
3443
|
try {
|
|
3471
|
-
|
|
3444
|
+
const url = new URL(link);
|
|
3445
|
+
return url.search === "" && url.pathname.startsWith("/s/");
|
|
3472
3446
|
} catch {
|
|
3473
|
-
return
|
|
3447
|
+
return false;
|
|
3474
3448
|
}
|
|
3475
3449
|
}
|
|
3476
3450
|
async function encryptShareBytes(bytes2, key) {
|
|
@@ -4031,6 +4005,7 @@ var CompactPayloadSchema = import_zod4.z.object({
|
|
|
4031
4005
|
nnc: import_zod4.z.string().min(1),
|
|
4032
4006
|
prf: import_zod4.z.array(import_zod4.z.string().min(1))
|
|
4033
4007
|
}).strict();
|
|
4008
|
+
var MAX_POLICY_ROOT_SECONDS = 31 * 24 * 60 * 60;
|
|
4034
4009
|
function createCompactPolicyInvocation(input) {
|
|
4035
4010
|
const session = parsePolicySessionUcan(input.sessionAuthorization);
|
|
4036
4011
|
if (session.cid !== input.sessionCid || session.aud !== input.recipientDid)
|
|
@@ -4062,7 +4037,7 @@ function createCompactPolicyInvocation(input) {
|
|
|
4062
4037
|
const signature = import_ed255192.ed25519.sign(signingInput, input.privateKey);
|
|
4063
4038
|
return parseCompactUcanAuthorization(`${protectedSegment}.${payloadSegment}.${encodeBase64Url(signature)}`);
|
|
4064
4039
|
}
|
|
4065
|
-
function
|
|
4040
|
+
function compactPolicyDescendantClaims(input) {
|
|
4066
4041
|
const parent = parseCompactUcanAuthorization(input.parentAuthorization, input.parentCid);
|
|
4067
4042
|
if (parent.payload.aud !== input.issuerDid) throw new Error("policy descendant issuer is not the parent audience");
|
|
4068
4043
|
const parentFact = parent.payload.fct[0];
|
|
@@ -4070,23 +4045,33 @@ function createCompactPolicyDescendant(input) {
|
|
|
4070
4045
|
if (!Number.isInteger(depth) || depth <= 0) throw new Error("policy descendant depth is exhausted");
|
|
4071
4046
|
if (!compactAttenuationContains(parent.payload.att, input.attenuation))
|
|
4072
4047
|
throw new Error("policy descendant exceeds the parent attenuation");
|
|
4048
|
+
const now = Math.max(input.now ?? parent.payload.nbf + 1, parent.payload.nbf + 1);
|
|
4049
|
+
const exp = Math.min(input.expiresAt ?? parent.payload.exp - 1, parent.payload.exp - 1);
|
|
4050
|
+
if (exp <= now) throw new Error("policy parent has no strictly narrower validity window");
|
|
4051
|
+
return {
|
|
4052
|
+
parentCid: parent.cid,
|
|
4053
|
+
fact: { ...parentFact, remainingRedelegationDepth: depth - 1 },
|
|
4054
|
+
notBefore: now,
|
|
4055
|
+
expiresAt: exp,
|
|
4056
|
+
nonce: input.nonce ?? encodeBase64Url(crypto.getRandomValues(new Uint8Array(16)))
|
|
4057
|
+
};
|
|
4058
|
+
}
|
|
4059
|
+
function createCompactPolicyDescendant(input) {
|
|
4060
|
+
const claims = compactPolicyDescendantClaims(input);
|
|
4073
4061
|
const principal = input.issuerDid.split("#", 1)[0];
|
|
4074
4062
|
const didMaterial = base58btc.decode(principal.slice("did:key:".length));
|
|
4075
4063
|
const publicKey = import_ed255192.ed25519.getPublicKey(input.privateKey);
|
|
4076
4064
|
if (didMaterial.length !== 34 || !equalBytes2(publicKey, didMaterial.slice(2))) throw new Error("policy descendant signer does not bind issuer DID");
|
|
4077
|
-
const now = Math.max(input.now ?? Math.floor(Date.now() / 1e3), parent.payload.nbf + 1);
|
|
4078
|
-
const exp = Math.min(input.expiresAt ?? now + 60, now + 60, parent.payload.exp - 1);
|
|
4079
|
-
if (exp <= now) throw new Error("policy parent has no strictly narrower validity window");
|
|
4080
4065
|
const header = { alg: "EdDSA", jwk: { alg: "EdDSA", crv: "Ed25519", kty: "OKP", x: encodeBase64Url(publicKey) }, typ: "JWT", ucv: "0.10.0" };
|
|
4081
4066
|
const payload = {
|
|
4082
4067
|
att: input.attenuation,
|
|
4083
4068
|
aud: input.audienceDid,
|
|
4084
|
-
exp,
|
|
4085
|
-
fct: [
|
|
4069
|
+
exp: claims.expiresAt,
|
|
4070
|
+
fct: [claims.fact],
|
|
4086
4071
|
iss: input.issuerDid.includes("#") ? input.issuerDid : `${principal}#${principal.slice("did:key:".length)}`,
|
|
4087
|
-
nbf:
|
|
4088
|
-
nnc:
|
|
4089
|
-
prf: [
|
|
4072
|
+
nbf: claims.notBefore,
|
|
4073
|
+
nnc: claims.nonce,
|
|
4074
|
+
prf: [claims.parentCid]
|
|
4090
4075
|
};
|
|
4091
4076
|
const protectedSegment = encodeBase64Url(new TextEncoder().encode(jcsCanonicalize(header)));
|
|
4092
4077
|
const payloadSegment = encodeBase64Url(new TextEncoder().encode(jcsCanonicalize(payload)));
|
|
@@ -4162,7 +4147,7 @@ function parsePolicySessionUcan(authorization, expectedProofs) {
|
|
|
4162
4147
|
throw new Error("policy session UCAN fact is incomplete");
|
|
4163
4148
|
if (hasV4AuditFactShape && (!LOWER_SHA256_HEX.test(fact.credentialIdAuditDigestHex) || !LOWER_SHA256_HEX.test(fact.presentationJtiAuditDigestHex)))
|
|
4164
4149
|
throw new Error("policy session UCAN v4 audit facts are invalid");
|
|
4165
|
-
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 >
|
|
4150
|
+
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 > MAX_POLICY_ROOT_SECONDS || parsed.payload.iss.split("#", 1)[0] !== fact.nodeAudience || parsed.payload.aud !== fact.recipientDid)
|
|
4166
4151
|
throw new Error("policy session UCAN fact is invalid");
|
|
4167
4152
|
return {
|
|
4168
4153
|
authorization,
|
|
@@ -5383,6 +5368,13 @@ var ShareRecipientClient = class {
|
|
|
5383
5368
|
};
|
|
5384
5369
|
|
|
5385
5370
|
// src/delegations/SharingService.ts
|
|
5371
|
+
function spaceIdsEqual(left, right) {
|
|
5372
|
+
const normalizeOwnerAddress = (space) => {
|
|
5373
|
+
const match = /^(tinycloud:pkh:eip155:\d+:)(0x[0-9a-fA-F]{40})(:.+)$/.exec(space);
|
|
5374
|
+
return match === null ? space : `${match[1]}${match[2].toLowerCase()}${match[3]}`;
|
|
5375
|
+
};
|
|
5376
|
+
return normalizeOwnerAddress(left) === normalizeOwnerAddress(right);
|
|
5377
|
+
}
|
|
5386
5378
|
function inferShortServiceFromActionUrns(actions) {
|
|
5387
5379
|
let short;
|
|
5388
5380
|
for (const action of actions) {
|
|
@@ -5618,11 +5610,11 @@ var SharingService = class {
|
|
|
5618
5610
|
}
|
|
5619
5611
|
return result;
|
|
5620
5612
|
};
|
|
5621
|
-
const canSatisfyFromRegistry = this.
|
|
5613
|
+
const canSatisfyFromRegistry = this.classifyDelegationAuthority(
|
|
5622
5614
|
fullPath,
|
|
5623
5615
|
actions,
|
|
5624
5616
|
requestedExpiry
|
|
5625
|
-
);
|
|
5617
|
+
) === "ok";
|
|
5626
5618
|
if (canSatisfyFromRegistry) {
|
|
5627
5619
|
const delegationResult = await this.createSessionDelegation(plainDID, fullPath, actions, expiry);
|
|
5628
5620
|
const childDelegationActiveError = this.retiredGraphError();
|
|
@@ -5692,49 +5684,58 @@ var SharingService = class {
|
|
|
5692
5684
|
return { ok: true, data: shareLink };
|
|
5693
5685
|
}
|
|
5694
5686
|
/**
|
|
5695
|
-
*
|
|
5696
|
-
*
|
|
5697
|
-
*
|
|
5698
|
-
*
|
|
5699
|
-
*
|
|
5700
|
-
*
|
|
5687
|
+
* Report, without side effects, whether this session's registry authority
|
|
5688
|
+
* can issue the child delegation {@link generate} would create for the same
|
|
5689
|
+
* `path`, `actions` and `expiry` (defaults and `pathPrefix` applied the
|
|
5690
|
+
* same way):
|
|
5691
|
+
*
|
|
5692
|
+
* - `"ok"`: an uncaveated entry covers it.
|
|
5693
|
+
* - `"caveated"`: only caveated entries cover it. SharingService cannot
|
|
5694
|
+
* carry signed ReCap caveats into a child delegation.
|
|
5695
|
+
* - `"not-covered"`: no entry covers it, or there is no session.
|
|
5696
|
+
*
|
|
5697
|
+
* It does not consult `onRootDelegationNeeded`, so a signer-backed session
|
|
5698
|
+
* may still generate when this is not `"ok"`.
|
|
5699
|
+
*/
|
|
5700
|
+
preflightGenerate(params) {
|
|
5701
|
+
if (!this.session || !params.path) return "not-covered";
|
|
5702
|
+
const fullPath = this.pathPrefix ? `${this.pathPrefix}/${params.path}`.replace(/\/+/g, "/") : params.path;
|
|
5703
|
+
return this.classifyDelegationAuthority(
|
|
5704
|
+
fullPath,
|
|
5705
|
+
params.actions ?? DEFAULT_READ_ACTIONS,
|
|
5706
|
+
params.expiry ?? new Date(Date.now() + DEFAULT_EXPIRY_MS)
|
|
5707
|
+
);
|
|
5708
|
+
}
|
|
5709
|
+
/**
|
|
5710
|
+
* Classify the registry's authority for a child delegation. An entry
|
|
5711
|
+
* satisfies it when it is in the session space, valid, lasts at least until
|
|
5712
|
+
* `requestedExpiry`, allows sub-delegation, covers `path` (exact or parent)
|
|
5713
|
+
* and grants every action. Satisfying entries with caveats are unusable.
|
|
5701
5714
|
* @internal
|
|
5702
5715
|
*/
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
for (const key of
|
|
5706
|
-
const
|
|
5707
|
-
|
|
5708
|
-
if (delegation.spaceId !== this.session?.spaceId) {
|
|
5709
|
-
continue;
|
|
5710
|
-
}
|
|
5711
|
-
if ((delegation.caveats?.length ?? 0) > 0) {
|
|
5712
|
-
continue;
|
|
5713
|
-
}
|
|
5714
|
-
if (!this.registry.isDelegationValid(delegation)) {
|
|
5715
|
-
continue;
|
|
5716
|
-
}
|
|
5717
|
-
if (delegation.expiry < requestedExpiry) {
|
|
5718
|
-
continue;
|
|
5719
|
-
}
|
|
5720
|
-
if (delegation.allowSubDelegation === false) {
|
|
5716
|
+
classifyDelegationAuthority(path, actions, requestedExpiry) {
|
|
5717
|
+
let coveredOnlyByCaveated = false;
|
|
5718
|
+
for (const key of this.registry.getAllKeys()) {
|
|
5719
|
+
for (const delegation of this.registry.getDelegationsForKey(key.id)) {
|
|
5720
|
+
if (delegation.spaceId === void 0 || !spaceIdsEqual(delegation.spaceId, this.session?.spaceId ?? "")) {
|
|
5721
5721
|
continue;
|
|
5722
5722
|
}
|
|
5723
|
-
|
|
5724
|
-
if (
|
|
5723
|
+
if (!this.registry.isDelegationValid(delegation)) continue;
|
|
5724
|
+
if (delegation.expiry < requestedExpiry) continue;
|
|
5725
|
+
if (delegation.allowSubDelegation === false) continue;
|
|
5726
|
+
if (!this.pathMatches(delegation.path || "", path)) continue;
|
|
5727
|
+
const delegationActions = delegation.actions || [];
|
|
5728
|
+
if (!actions.every((action) => delegationActions.includes(action) || delegationActions.includes("*"))) {
|
|
5725
5729
|
continue;
|
|
5726
5730
|
}
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
(action) => delegationActions.includes(action) || delegationActions.includes("*")
|
|
5730
|
-
);
|
|
5731
|
-
if (!hasAllActions) {
|
|
5731
|
+
if ((delegation.caveats?.length ?? 0) > 0) {
|
|
5732
|
+
coveredOnlyByCaveated = true;
|
|
5732
5733
|
continue;
|
|
5733
5734
|
}
|
|
5734
|
-
return
|
|
5735
|
+
return "ok";
|
|
5735
5736
|
}
|
|
5736
5737
|
}
|
|
5737
|
-
return
|
|
5738
|
+
return coveredOnlyByCaveated ? "caveated" : "not-covered";
|
|
5738
5739
|
}
|
|
5739
5740
|
retiredGraphError() {
|
|
5740
5741
|
try {
|
|
@@ -5833,11 +5834,23 @@ var SharingService = class {
|
|
|
5833
5834
|
const errorText = await registerRes.text();
|
|
5834
5835
|
const registrationTextActiveError = this.retiredGraphError();
|
|
5835
5836
|
if (registrationTextActiveError) return { ok: false, error: registrationTextActiveError };
|
|
5837
|
+
const message = `Failed to register delegation with server: ${registerRes.status} ${errorText}`;
|
|
5838
|
+
if (registerRes.status === 401 || registerRes.status === 403) {
|
|
5839
|
+
return {
|
|
5840
|
+
ok: false,
|
|
5841
|
+
error: createError2(
|
|
5842
|
+
DelegationErrorCodes.AUTH_UNAUTHORIZED,
|
|
5843
|
+
message,
|
|
5844
|
+
void 0,
|
|
5845
|
+
{ status: registerRes.status }
|
|
5846
|
+
)
|
|
5847
|
+
};
|
|
5848
|
+
}
|
|
5836
5849
|
return {
|
|
5837
5850
|
ok: false,
|
|
5838
5851
|
error: createError2(
|
|
5839
5852
|
DelegationErrorCodes.CREATION_FAILED,
|
|
5840
|
-
|
|
5853
|
+
message
|
|
5841
5854
|
)
|
|
5842
5855
|
};
|
|
5843
5856
|
}
|
|
@@ -6707,114 +6720,13 @@ function validateOwnerSharePolicyRegistrationBytes(bytesValue, expected) {
|
|
|
6707
6720
|
return validateOwnerSharePolicyRegistration(value, expected);
|
|
6708
6721
|
}
|
|
6709
6722
|
|
|
6710
|
-
// src/delegations/share-delivery.ts
|
|
6711
|
-
var import_ed255197 = require("@noble/curves/ed25519");
|
|
6712
|
-
var SHARE_DELIVERY_AUTHORIZATION_DOMAIN = "xyz.tinycloud.share/delivery-authorization/v2\0";
|
|
6713
|
-
function isCanonicalHttpsOrigin(value) {
|
|
6714
|
-
let url;
|
|
6715
|
-
try {
|
|
6716
|
-
url = new URL(value);
|
|
6717
|
-
} catch {
|
|
6718
|
-
return false;
|
|
6719
|
-
}
|
|
6720
|
-
return url.protocol === "https:" && url.origin === value && url.pathname === "/" && url.search === "" && url.hash === "" && url.username === "" && url.password === "";
|
|
6721
|
-
}
|
|
6722
|
-
var AUTHORIZATION_KEYS = [
|
|
6723
|
-
"type",
|
|
6724
|
-
"version",
|
|
6725
|
-
"jti",
|
|
6726
|
-
"shareCid",
|
|
6727
|
-
"shareId",
|
|
6728
|
-
"registrationCid",
|
|
6729
|
-
"delegationCid",
|
|
6730
|
-
"enforcementDelegationCid",
|
|
6731
|
-
"envelopeCid",
|
|
6732
|
-
"policyCid",
|
|
6733
|
-
"nodeAudience",
|
|
6734
|
-
"targetOrigin",
|
|
6735
|
-
"openCredentialsAudience",
|
|
6736
|
-
"holder",
|
|
6737
|
-
"recipientMatcher",
|
|
6738
|
-
"deliveryEmail",
|
|
6739
|
-
"shareUrl",
|
|
6740
|
-
"returnOrigin",
|
|
6741
|
-
"documentName",
|
|
6742
|
-
"senderDid",
|
|
6743
|
-
"senderTrust",
|
|
6744
|
-
"contentSource",
|
|
6745
|
-
"contentSourceDigest",
|
|
6746
|
-
"shareExpiresAt",
|
|
6747
|
-
"issuedAt",
|
|
6748
|
-
"reportAbuseToken",
|
|
6749
|
-
"actions",
|
|
6750
|
-
"resource",
|
|
6751
|
-
"authorityMaterialHandle",
|
|
6752
|
-
"authorityMaterialDigest",
|
|
6753
|
-
"requestBodyDigest",
|
|
6754
|
-
"idempotencyKey",
|
|
6755
|
-
"expiresAt",
|
|
6756
|
-
"dataAuthority"
|
|
6757
|
-
];
|
|
6758
|
-
function assertObject2(value, keys, label) {
|
|
6759
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
|
|
6760
|
-
const record = value;
|
|
6761
|
-
if (Object.keys(record).length !== keys.length || keys.some((key) => !Object.prototype.hasOwnProperty.call(record, key))) throw new Error(`${label} has unknown or missing fields`);
|
|
6762
|
-
return record;
|
|
6763
|
-
}
|
|
6764
|
-
function fromB642(value) {
|
|
6765
|
-
if (!/^[A-Za-z0-9_-]+$/.test(value)) throw new Error("share delivery value is not canonical base64url");
|
|
6766
|
-
if (typeof atob === "function") {
|
|
6767
|
-
const padded = value.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - value.length % 4) % 4);
|
|
6768
|
-
return Uint8Array.from(atob(padded), (character) => character.charCodeAt(0));
|
|
6769
|
-
}
|
|
6770
|
-
if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(value, "base64url"));
|
|
6771
|
-
throw new Error("base64url decoding is unavailable");
|
|
6772
|
-
}
|
|
6773
|
-
function validateShareDeliveryAuthorizationBytes(bytesValue, expected) {
|
|
6774
|
-
let value;
|
|
6775
|
-
try {
|
|
6776
|
-
value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytesValue));
|
|
6777
|
-
} catch {
|
|
6778
|
-
throw new Error("share delivery authorization response is not valid UTF-8 JSON");
|
|
6779
|
-
}
|
|
6780
|
-
const root = assertObject2(value, ["authorization", "proof"], "share delivery authorization response");
|
|
6781
|
-
const authorization = assertObject2(root.authorization, AUTHORIZATION_KEYS, "share delivery authorization");
|
|
6782
|
-
const proof = assertObject2(root.proof, ["alg", "kid", "signature"], "share delivery authorization proof");
|
|
6783
|
-
const request = expected.request;
|
|
6784
|
-
if (authorization.type !== "TinyCloudShareDeliveryAuthorization" || authorization.version !== 2 || authorization.dataAuthority !== false || authorization.jti !== request.jti || authorization.shareCid !== request.shareCid || authorization.shareId !== request.shareId || authorization.registrationCid !== request.registrationCid || authorization.delegationCid !== request.delegationCid || authorization.enforcementDelegationCid !== request.enforcementDelegationCid || authorization.envelopeCid !== request.envelopeCid || authorization.policyCid !== request.policyCid || authorization.deliveryEmail !== request.recipientEmail || authorization.shareUrl !== request.shareUrl || authorization.documentName !== request.documentName || authorization.requestBodyDigest !== request.requestBodyDigest || authorization.expiresAt !== request.expiresAt || authorization.idempotencyKey !== request.idempotencyKey || authorization.reportAbuseToken !== request.jti || authorization.authorityMaterialHandle !== request.registrationCid) {
|
|
6785
|
-
throw new Error("share delivery authorization is not bound to the submitted request");
|
|
6786
|
-
}
|
|
6787
|
-
if (typeof authorization.issuedAt !== "string" || Date.parse(authorization.issuedAt) > Date.now() + 6e4) throw new Error("share delivery authorization issuedAt is invalid");
|
|
6788
|
-
if (new Date(authorization.expiresAt).toISOString() !== authorization.expiresAt) throw new Error("share delivery authorization expiresAt is non-canonical");
|
|
6789
|
-
if (typeof authorization.openCredentialsAudience !== "string" || !isCanonicalHttpsOrigin(authorization.openCredentialsAudience) || authorization.openCredentialsAudience !== expected.credentialsAudience || authorization.openCredentialsAudience === authorization.nodeAudience || authorization.openCredentialsAudience === authorization.returnOrigin) {
|
|
6790
|
-
throw new Error("share delivery authorization openCredentialsAudience is missing or untrusted");
|
|
6791
|
-
}
|
|
6792
|
-
if (proof.alg !== "EdDSA" || typeof proof.kid !== "string" || typeof proof.signature !== "string") throw new Error("share delivery authorization proof is invalid");
|
|
6793
|
-
if (proof.kid !== expected.nodeProof.kid) throw new Error("share delivery authorization proof key is not trusted");
|
|
6794
|
-
const encodedKid = expected.nodeProof.publicKey;
|
|
6795
|
-
const publicKey = encodedKid.length === 34 && encodedKid[0] === 237 && encodedKid[1] === 1 ? encodedKid.slice(2) : encodedKid;
|
|
6796
|
-
if (publicKey.length !== 32) throw new Error("share delivery authorization proof key is invalid");
|
|
6797
|
-
const signatureBytes = fromB642(proof.signature);
|
|
6798
|
-
const signedBytes = new TextEncoder().encode(`${SHARE_DELIVERY_AUTHORIZATION_DOMAIN}${canonicalizeSignedObjectUnsigned(authorization)}`);
|
|
6799
|
-
if (signatureBytes.length !== 64 || !import_ed255197.ed25519.verify(signatureBytes, signedBytes, publicKey)) throw new Error("share delivery authorization proof signature is invalid");
|
|
6800
|
-
return { authorization, proof };
|
|
6801
|
-
}
|
|
6802
|
-
function shareDeliveryTrustedKid(publicKey) {
|
|
6803
|
-
if (publicKey.length !== 32) throw new Error("share delivery node key must be a raw 32-byte Ed25519 public key");
|
|
6804
|
-
const prefixed = new Uint8Array(34);
|
|
6805
|
-
prefixed.set([237, 1]);
|
|
6806
|
-
prefixed.set(publicKey, 2);
|
|
6807
|
-
const did = `did:key:${base58btc.encode(prefixed)}`;
|
|
6808
|
-
return `${did}#${did.slice("did:key:".length)}`;
|
|
6809
|
-
}
|
|
6810
|
-
|
|
6811
6723
|
// src/delegations/share-delivery-v3.ts
|
|
6812
|
-
var
|
|
6724
|
+
var import_ed255197 = require("@noble/curves/ed25519");
|
|
6813
6725
|
var import_sha2566 = require("@noble/hashes/sha256");
|
|
6814
6726
|
var CREDENTIAL_INVITATION_REQUEST_DOMAIN = "xyz.tinycloud.credentials/invitation-request/v1\0";
|
|
6815
6727
|
var DELIVERY_ADMISSION_DOMAIN = "xyz.tinycloud.policy/delivery-admission/v0\0";
|
|
6816
|
-
var REQUEST_KEYS = ["schema", "policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "audience", "issuedAt", "expiresAt", "nonce"];
|
|
6817
|
-
var ADMISSION_KEYS = ["schema", "policyId", "ownerDid", "recipient", "resource", "actions", "credentialType", "returnLink", "envelopeRef", "senderKeyDid", "audience", "issuedAt", "expiresAt", "nonce", "signature"];
|
|
6728
|
+
var REQUEST_KEYS = ["schema", "policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "audience", "issuedAt", "expiresAt", "nonce"];
|
|
6729
|
+
var ADMISSION_KEYS = ["schema", "policyId", "ownerDid", "recipient", "resource", "actions", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "senderKeyDid", "audience", "issuedAt", "expiresAt", "nonce", "signature"];
|
|
6818
6730
|
function exactObject(value, keys, label) {
|
|
6819
6731
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
|
|
6820
6732
|
const record = value;
|
|
@@ -6858,15 +6770,16 @@ function validateShareDeliveryAuthorizationV3Bytes(bytes2, expected) {
|
|
|
6858
6770
|
const contentSource = object(envelope.contentSource, "v3 content source");
|
|
6859
6771
|
const envelopeSignature = object(envelope.signature, "v3 envelope signature");
|
|
6860
6772
|
const target = object(envelope.target, "v3 target");
|
|
6861
|
-
const
|
|
6862
|
-
|
|
6773
|
+
const binding = object(envelope.attestedEnforcerBinding, "v3 attested enforcer binding");
|
|
6774
|
+
const fields = ["policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "audience", "issuedAt", "expiresAt", "nonce"];
|
|
6775
|
+
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");
|
|
6863
6776
|
const issuedAt = Date.parse(String(request.issuedAt));
|
|
6864
6777
|
const expiresAt = Date.parse(String(request.expiresAt));
|
|
6865
6778
|
if (!Number.isFinite(issuedAt) || !Number.isFinite(expiresAt) || expiresAt <= issuedAt || expiresAt - issuedAt > 15 * 6e4) throw new Error("v3 share delivery authorization time is invalid");
|
|
6866
6779
|
const admissionSignature = decodeBase64Url2(signature.value);
|
|
6867
6780
|
const { signature: _signature, ...unsignedAdmission } = admission;
|
|
6868
6781
|
const digest4 = (0, import_sha2566.sha256)(new TextEncoder().encode(`${DELIVERY_ADMISSION_DOMAIN}${canonicalizeSignedObjectUnsigned(unsignedAdmission)}`));
|
|
6869
|
-
if (admissionSignature.length !== 64 || !
|
|
6782
|
+
if (admissionSignature.length !== 64 || !import_ed255197.ed25519.verify(admissionSignature, digest4, didKeyBytes(String(signature.signerDid)))) throw new Error("v3 share delivery admission signature is invalid");
|
|
6870
6783
|
return { request, admission };
|
|
6871
6784
|
}
|
|
6872
6785
|
|