@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
|
@@ -32,6 +32,8 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
32
32
|
// src/delegations/index.ts
|
|
33
33
|
var delegations_exports = {};
|
|
34
34
|
__export(delegations_exports, {
|
|
35
|
+
CREDENTIAL_INVITATION_REQUEST_DOMAIN: () => CREDENTIAL_INVITATION_REQUEST_DOMAIN,
|
|
36
|
+
DELIVERY_ADMISSION_DOMAIN: () => DELIVERY_ADMISSION_DOMAIN,
|
|
35
37
|
DelegationErrorCodes: () => DelegationErrorCodes,
|
|
36
38
|
DelegationManager: () => DelegationManager,
|
|
37
39
|
EncodedShareDataSchema: () => EncodedShareDataSchema,
|
|
@@ -45,7 +47,6 @@ __export(delegations_exports, {
|
|
|
45
47
|
MemorySenderShareRecordStorage: () => MemorySenderShareRecordStorage,
|
|
46
48
|
MemoryShareCache: () => MemoryShareCache,
|
|
47
49
|
ReceiveOptionsSchema: () => ReceiveOptionsSchema,
|
|
48
|
-
SHARE_DELIVERY_AUTHORIZATION_DOMAIN: () => SHARE_DELIVERY_AUTHORIZATION_DOMAIN,
|
|
49
50
|
SenderShareStore: () => SenderShareStore,
|
|
50
51
|
ShareAccessError: () => ShareAccessError,
|
|
51
52
|
ShareActionSchema: () => ShareActionSchema,
|
|
@@ -85,10 +86,8 @@ __export(delegations_exports, {
|
|
|
85
86
|
importPortableDelegation: () => importPortableDelegation,
|
|
86
87
|
inspectShare: () => import_share_sdk2.inspectShare,
|
|
87
88
|
intersectShareCapabilities: () => intersectShareCapabilities,
|
|
88
|
-
isLegacyShareLink: () => import_share_sdk.isLegacyShareLink,
|
|
89
89
|
isV2ShareLink: () => isV2ShareLink,
|
|
90
90
|
listShares: () => import_share_sdk.listShares,
|
|
91
|
-
migrateShare: () => import_share_sdk.migrateShare,
|
|
92
91
|
normalizeShareActions: () => normalizeShareActions,
|
|
93
92
|
normalizeShareRecipientTarget: () => normalizeShareRecipientTarget,
|
|
94
93
|
normalizeShareResource: () => normalizeShareResource,
|
|
@@ -98,14 +97,12 @@ __export(delegations_exports, {
|
|
|
98
97
|
parseShareUrl: () => parseShareUrl,
|
|
99
98
|
publishPolicyShare: () => import_share_sdk.publishPolicyShare,
|
|
100
99
|
publishTargetShare: () => import_share_sdk.publishTargetShare,
|
|
101
|
-
receiveLegacyShare: () => import_share_sdk.receiveLegacyShare,
|
|
102
100
|
receiveShare: () => import_share_sdk2.receiveShare,
|
|
103
101
|
restoreDelegatedShareKey: () => restoreDelegatedShareKey,
|
|
104
102
|
resumeShareAuthorization: () => import_share_sdk.resumeShareAuthorization,
|
|
105
103
|
revokeShare: () => import_share_sdk.revokeShare,
|
|
106
104
|
serializePortableDelegation: () => serializePortableDelegation,
|
|
107
105
|
shareCapabilityAllows: () => shareCapabilityAllows,
|
|
108
|
-
shareDeliveryTrustedKid: () => shareDeliveryTrustedKid,
|
|
109
106
|
showShare: () => import_share_sdk.showShare,
|
|
110
107
|
signShareEnvelope: () => signShareEnvelope,
|
|
111
108
|
toShareErrorInfo: () => import_share_sdk2.toShareErrorInfo,
|
|
@@ -113,9 +110,8 @@ __export(delegations_exports, {
|
|
|
113
110
|
validateOwnerSharePolicyRegistration: () => validateOwnerSharePolicyRegistration,
|
|
114
111
|
validateOwnerSharePolicyRegistrationBytes: () => validateOwnerSharePolicyRegistrationBytes,
|
|
115
112
|
validateReceiveOptions: () => validateReceiveOptions,
|
|
116
|
-
|
|
113
|
+
validateShareDeliveryAuthorizationV3Bytes: () => validateShareDeliveryAuthorizationV3Bytes,
|
|
117
114
|
validateSharingServiceConfig: () => validateSharingServiceConfig,
|
|
118
|
-
verifyBearerEnvelope: () => import_share_sdk2.verifyBearerEnvelope,
|
|
119
115
|
verifyShareCid: () => verifyShareCid,
|
|
120
116
|
verifyShareEnvelope: () => verifyShareEnvelope
|
|
121
117
|
});
|
|
@@ -1493,13 +1489,13 @@ function canonicalizeRecapCaveats(caveats) {
|
|
|
1493
1489
|
}
|
|
1494
1490
|
if (Array.isArray(value)) return `[${value.map(canonicalize).join(",")}]`;
|
|
1495
1491
|
if (typeof value === "object") {
|
|
1496
|
-
const
|
|
1497
|
-
const prototype = Object.getPrototypeOf(
|
|
1492
|
+
const object2 = value;
|
|
1493
|
+
const prototype = Object.getPrototypeOf(object2);
|
|
1498
1494
|
if (prototype !== Object.prototype && prototype !== null) {
|
|
1499
1495
|
throw new Error("ReCap caveats must contain only JSON values.");
|
|
1500
1496
|
}
|
|
1501
|
-
return `{${Object.keys(
|
|
1502
|
-
(key) => `${JSON.stringify(key)}:${canonicalize(
|
|
1497
|
+
return `{${Object.keys(object2).sort().map(
|
|
1498
|
+
(key) => `${JSON.stringify(key)}:${canonicalize(object2[key])}`
|
|
1503
1499
|
).join(",")}}`;
|
|
1504
1500
|
}
|
|
1505
1501
|
throw new Error("ReCap caveats must contain only JSON values.");
|
|
@@ -2693,7 +2689,6 @@ var import_ed25519 = require("@noble/curves/ed25519");
|
|
|
2693
2689
|
var import_blake3 = require("@noble/hashes/blake3");
|
|
2694
2690
|
var import_sha256 = require("@noble/hashes/sha256");
|
|
2695
2691
|
var ENVELOPE_SCHEMA = "xyz.tinycloud.share/envelope";
|
|
2696
|
-
var INLINE_PREFIX = "tc2:";
|
|
2697
2692
|
var RAW_CID_CODEC = 85;
|
|
2698
2693
|
var BLAKE3_256_CODE = 30;
|
|
2699
2694
|
var DEFAULT_MAX_INLINE_BYTES = 256 * 1024;
|
|
@@ -2740,8 +2735,8 @@ function canonicalJson(value) {
|
|
|
2740
2735
|
}
|
|
2741
2736
|
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
2742
2737
|
if (typeof value === "object") {
|
|
2743
|
-
const
|
|
2744
|
-
return `{${Object.keys(
|
|
2738
|
+
const object2 = value;
|
|
2739
|
+
return `{${Object.keys(object2).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(object2[key])}`).join(",")}}`;
|
|
2745
2740
|
}
|
|
2746
2741
|
throw new ShareEnvelopeError("invalid-envelope", "unsupported JSON value");
|
|
2747
2742
|
}
|
|
@@ -3398,25 +3393,8 @@ function parsePublishedShareLink(link, options = {}) {
|
|
|
3398
3393
|
}
|
|
3399
3394
|
const origin = normalizeOrigin(url.origin);
|
|
3400
3395
|
if (options.trustedOrigins !== void 0 && !options.trustedOrigins.map(normalizeOrigin).includes(origin)) throw new ShareEnvelopeError("origin-mismatch", "share link origin is not trusted");
|
|
3401
|
-
if (url.
|
|
3402
|
-
if (url.
|
|
3403
|
-
let payload;
|
|
3404
|
-
try {
|
|
3405
|
-
const bytes2 = base64UrlToBytes(url.hash.slice("#v=2&p=".length));
|
|
3406
|
-
if (bytes2.length > (options.maxInlineBytes ?? DEFAULT_MAX_INLINE_BYTES) * 2) throw new Error("inline payload too large");
|
|
3407
|
-
const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes2));
|
|
3408
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("inline payload shape");
|
|
3409
|
-
payload = value;
|
|
3410
|
-
} catch (error) {
|
|
3411
|
-
throw new ShareEnvelopeError("invalid-link", error instanceof Error ? error.message : "invalid inline payload");
|
|
3412
|
-
}
|
|
3413
|
-
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");
|
|
3414
|
-
const sealed = base64UrlToBytes(payload.c);
|
|
3415
|
-
if (!verifyPublishedShareCid(payload.cid, sealed)) throw new ShareEnvelopeError("cid-mismatch", "inline payload CID does not match");
|
|
3416
|
-
const key2 = payload.k === void 0 ? void 0 : base64UrlToBytes(payload.k);
|
|
3417
|
-
if (key2 !== void 0 && key2.length !== 32) throw new ShareEnvelopeError("invalid-link", "inline payload key is invalid");
|
|
3418
|
-
return { origin, cid: payload.cid, ...key2 === void 0 ? {} : { key: key2 }, sealed };
|
|
3419
|
-
}
|
|
3396
|
+
if (url.username !== "" || url.password !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
|
|
3397
|
+
if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
|
|
3420
3398
|
const match = /^\/s\/([a-z2-7]+)$/.exec(url.pathname);
|
|
3421
3399
|
if (match === null || !url.hash.startsWith("#k=")) throw new ShareEnvelopeError("invalid-link", "published share link is invalid");
|
|
3422
3400
|
const key = base64UrlToBytes(url.hash.slice(3));
|
|
@@ -3436,13 +3414,10 @@ function parseShareUrl(link, options = {}) {
|
|
|
3436
3414
|
} catch {
|
|
3437
3415
|
throw new ShareEnvelopeError("invalid-link", "share link must be an absolute URL");
|
|
3438
3416
|
}
|
|
3439
|
-
if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share links must not contain query parameters");
|
|
3440
3417
|
const origin = normalizeOrigin(url.origin);
|
|
3441
3418
|
if (options.trustedOrigins !== void 0 && !options.trustedOrigins.map(normalizeOrigin).includes(origin)) throw new ShareEnvelopeError("origin-mismatch", "share link origin is not trusted");
|
|
3442
|
-
if (url.pathname === "/
|
|
3443
|
-
|
|
3444
|
-
return { kind: "inline", origin, cid: published.cid, key: published.key, url: `${url.origin}${url.pathname}`, protocol: "share-envelope-v2" };
|
|
3445
|
-
}
|
|
3419
|
+
if (url.pathname === "/viewer") throw new ShareEnvelopeError("invalid-link", "retired viewer links are not accepted");
|
|
3420
|
+
if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share links must not contain query parameters");
|
|
3446
3421
|
const match = url.pathname.match(/^\/s\/(i\/)?([^/]+)$/);
|
|
3447
3422
|
if (!match) throw new ShareEnvelopeError("invalid-link", "share link path is invalid");
|
|
3448
3423
|
const fragmentParams = url.hash === "" ? void 0 : new URLSearchParams(url.hash.slice(1));
|
|
@@ -3465,9 +3440,10 @@ function parseShareUrl(link, options = {}) {
|
|
|
3465
3440
|
}
|
|
3466
3441
|
function isV2ShareLink(link) {
|
|
3467
3442
|
try {
|
|
3468
|
-
|
|
3443
|
+
const url = new URL(link);
|
|
3444
|
+
return url.search === "" && url.pathname.startsWith("/s/");
|
|
3469
3445
|
} catch {
|
|
3470
|
-
return
|
|
3446
|
+
return false;
|
|
3471
3447
|
}
|
|
3472
3448
|
}
|
|
3473
3449
|
async function encryptShareBytes(bytes2, key) {
|
|
@@ -4007,6 +3983,11 @@ var POLICY_SESSION_FACT_KEYS = [
|
|
|
4007
3983
|
"issuanceAuditDigestHex",
|
|
4008
3984
|
"remainingRedelegationDepth"
|
|
4009
3985
|
];
|
|
3986
|
+
var POLICY_SESSION_V4_AUDIT_FACT_KEYS = [
|
|
3987
|
+
"credentialIdAuditDigestHex",
|
|
3988
|
+
"presentationJtiAuditDigestHex"
|
|
3989
|
+
];
|
|
3990
|
+
var LOWER_SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
4010
3991
|
var CompactHeaderSchema = import_zod4.z.object({
|
|
4011
3992
|
alg: import_zod4.z.literal("EdDSA"),
|
|
4012
3993
|
jwk: import_zod4.z.object({ alg: import_zod4.z.literal("EdDSA"), crv: import_zod4.z.literal("Ed25519"), kty: import_zod4.z.literal("OKP"), x: import_zod4.z.string().min(1) }).strict(),
|
|
@@ -4148,9 +4129,13 @@ function parsePolicySessionUcan(authorization, expectedProofs) {
|
|
|
4148
4129
|
if (expectedProofs !== void 0 && (parsed.payload.prf[0] !== expectedProofs[0] || parsed.payload.prf[1] !== expectedProofs[1]))
|
|
4149
4130
|
throw new Error("policy session UCAN ordered proofs do not match");
|
|
4150
4131
|
const factKeys = Object.keys(fact);
|
|
4151
|
-
|
|
4132
|
+
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);
|
|
4133
|
+
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);
|
|
4134
|
+
if (!hasLegacyFactShape && !hasV4AuditFactShape)
|
|
4152
4135
|
throw new Error("policy session UCAN fact is incomplete");
|
|
4153
|
-
if (
|
|
4136
|
+
if (hasV4AuditFactShape && (!LOWER_SHA256_HEX.test(fact.credentialIdAuditDigestHex) || !LOWER_SHA256_HEX.test(fact.presentationJtiAuditDigestHex)))
|
|
4137
|
+
throw new Error("policy session UCAN v4 audit facts are invalid");
|
|
4138
|
+
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)
|
|
4154
4139
|
throw new Error("policy session UCAN fact is invalid");
|
|
4155
4140
|
return {
|
|
4156
4141
|
authorization,
|
|
@@ -4327,8 +4312,8 @@ function canonicalJsonForProof(value) {
|
|
|
4327
4312
|
}
|
|
4328
4313
|
if (Array.isArray(value)) return `[${value.map(canonicalJsonForProof).join(",")}]`;
|
|
4329
4314
|
if (typeof value === "object") {
|
|
4330
|
-
const
|
|
4331
|
-
return `{${Object.keys(
|
|
4315
|
+
const object2 = value;
|
|
4316
|
+
return `{${Object.keys(object2).sort().map((key) => `${JSON.stringify(key)}:${canonicalJsonForProof(object2[key])}`).join(",")}}`;
|
|
4332
4317
|
}
|
|
4333
4318
|
throw new ShareAccessError("SHARE_SESSION_INVALID");
|
|
4334
4319
|
}
|
|
@@ -5037,12 +5022,12 @@ var ShareRecipientClient = class {
|
|
|
5037
5022
|
this.assertDetachedProof(request.proof);
|
|
5038
5023
|
}
|
|
5039
5024
|
parseSessionCapability(value) {
|
|
5040
|
-
const
|
|
5041
|
-
if (typeof
|
|
5042
|
-
if (!Array.isArray(
|
|
5043
|
-
const resource = this.requireObject(
|
|
5025
|
+
const object2 = this.requireObject(value, "session capability");
|
|
5026
|
+
if (typeof object2.spaceId !== "string") throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability space is invalid");
|
|
5027
|
+
if (!Array.isArray(object2.actions) || object2.actions.some((action) => !["read", "list", "edit"].includes(action))) throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability actions are invalid");
|
|
5028
|
+
const resource = this.requireObject(object2.resource, "session capability resource");
|
|
5044
5029
|
if (resource.kind !== "exact" && resource.kind !== "prefix" || typeof resource.path !== "string") throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability resource is invalid");
|
|
5045
|
-
return { spaceId:
|
|
5030
|
+
return { spaceId: object2.spaceId, actions: [...new Set(object2.actions)], resource: { kind: resource.kind, path: resource.path } };
|
|
5046
5031
|
}
|
|
5047
5032
|
assertProvidedPolicySession(session, envelope) {
|
|
5048
5033
|
const sessionExpiry = dateValue(session.expiresAt);
|
|
@@ -6695,62 +6680,21 @@ function validateOwnerSharePolicyRegistrationBytes(bytesValue, expected) {
|
|
|
6695
6680
|
return validateOwnerSharePolicyRegistration(value, expected);
|
|
6696
6681
|
}
|
|
6697
6682
|
|
|
6698
|
-
// src/delegations/share-delivery.ts
|
|
6683
|
+
// src/delegations/share-delivery-v3.ts
|
|
6699
6684
|
var import_ed255197 = require("@noble/curves/ed25519");
|
|
6700
|
-
var
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
return false;
|
|
6707
|
-
}
|
|
6708
|
-
return url.protocol === "https:" && url.origin === value && url.pathname === "/" && url.search === "" && url.hash === "" && url.username === "" && url.password === "";
|
|
6709
|
-
}
|
|
6710
|
-
var AUTHORIZATION_KEYS = [
|
|
6711
|
-
"type",
|
|
6712
|
-
"version",
|
|
6713
|
-
"jti",
|
|
6714
|
-
"shareCid",
|
|
6715
|
-
"shareId",
|
|
6716
|
-
"registrationCid",
|
|
6717
|
-
"delegationCid",
|
|
6718
|
-
"enforcementDelegationCid",
|
|
6719
|
-
"envelopeCid",
|
|
6720
|
-
"policyCid",
|
|
6721
|
-
"nodeAudience",
|
|
6722
|
-
"targetOrigin",
|
|
6723
|
-
"openCredentialsAudience",
|
|
6724
|
-
"holder",
|
|
6725
|
-
"recipientMatcher",
|
|
6726
|
-
"deliveryEmail",
|
|
6727
|
-
"shareUrl",
|
|
6728
|
-
"returnOrigin",
|
|
6729
|
-
"documentName",
|
|
6730
|
-
"senderDid",
|
|
6731
|
-
"senderTrust",
|
|
6732
|
-
"contentSource",
|
|
6733
|
-
"contentSourceDigest",
|
|
6734
|
-
"shareExpiresAt",
|
|
6735
|
-
"issuedAt",
|
|
6736
|
-
"reportAbuseToken",
|
|
6737
|
-
"actions",
|
|
6738
|
-
"resource",
|
|
6739
|
-
"authorityMaterialHandle",
|
|
6740
|
-
"authorityMaterialDigest",
|
|
6741
|
-
"requestBodyDigest",
|
|
6742
|
-
"idempotencyKey",
|
|
6743
|
-
"expiresAt",
|
|
6744
|
-
"dataAuthority"
|
|
6745
|
-
];
|
|
6746
|
-
function assertObject2(value, keys, label) {
|
|
6685
|
+
var import_sha2566 = require("@noble/hashes/sha256");
|
|
6686
|
+
var CREDENTIAL_INVITATION_REQUEST_DOMAIN = "xyz.tinycloud.credentials/invitation-request/v1\0";
|
|
6687
|
+
var DELIVERY_ADMISSION_DOMAIN = "xyz.tinycloud.policy/delivery-admission/v0\0";
|
|
6688
|
+
var REQUEST_KEYS = ["schema", "policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "audience", "issuedAt", "expiresAt", "nonce"];
|
|
6689
|
+
var ADMISSION_KEYS = ["schema", "policyId", "ownerDid", "recipient", "resource", "actions", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "senderKeyDid", "audience", "issuedAt", "expiresAt", "nonce", "signature"];
|
|
6690
|
+
function exactObject(value, keys, label) {
|
|
6747
6691
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
|
|
6748
6692
|
const record = value;
|
|
6749
6693
|
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`);
|
|
6750
6694
|
return record;
|
|
6751
6695
|
}
|
|
6752
|
-
function
|
|
6753
|
-
if (!/^[A-Za-z0-9_-]+$/.test(value)) throw new Error("share delivery value is not canonical base64url");
|
|
6696
|
+
function decodeBase64Url2(value) {
|
|
6697
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9_-]+$/.test(value)) throw new Error("share delivery value is not canonical base64url");
|
|
6754
6698
|
if (typeof atob === "function") {
|
|
6755
6699
|
const padded = value.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - value.length % 4) % 4);
|
|
6756
6700
|
return Uint8Array.from(atob(padded), (character) => character.charCodeAt(0));
|
|
@@ -6758,42 +6702,45 @@ function fromB642(value) {
|
|
|
6758
6702
|
if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(value, "base64url"));
|
|
6759
6703
|
throw new Error("base64url decoding is unavailable");
|
|
6760
6704
|
}
|
|
6761
|
-
function
|
|
6762
|
-
|
|
6705
|
+
function object(value, label) {
|
|
6706
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
|
|
6707
|
+
return value;
|
|
6708
|
+
}
|
|
6709
|
+
function didKeyBytes(did) {
|
|
6710
|
+
if (!did.startsWith("did:key:z") || /[#/?]/.test(did.slice("did:key:".length))) throw new Error("v3 delivery admission signer is invalid");
|
|
6711
|
+
const decoded = base58btc.decode(did.slice("did:key:".length));
|
|
6712
|
+
if (decoded.length !== 34 || decoded[0] !== 237 || decoded[1] !== 1) throw new Error("v3 delivery admission signer is invalid");
|
|
6713
|
+
return decoded.slice(2);
|
|
6714
|
+
}
|
|
6715
|
+
function validateShareDeliveryAuthorizationV3Bytes(bytes2, expected) {
|
|
6716
|
+
let parsed;
|
|
6763
6717
|
try {
|
|
6764
|
-
|
|
6718
|
+
parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes2));
|
|
6765
6719
|
} catch {
|
|
6766
|
-
throw new Error("share delivery
|
|
6767
|
-
}
|
|
6768
|
-
const root =
|
|
6769
|
-
const
|
|
6770
|
-
const
|
|
6771
|
-
const
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
if (
|
|
6782
|
-
const
|
|
6783
|
-
const
|
|
6784
|
-
if (
|
|
6785
|
-
const
|
|
6786
|
-
const
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
}
|
|
6790
|
-
function shareDeliveryTrustedKid(publicKey) {
|
|
6791
|
-
if (publicKey.length !== 32) throw new Error("share delivery node key must be a raw 32-byte Ed25519 public key");
|
|
6792
|
-
const prefixed = new Uint8Array(34);
|
|
6793
|
-
prefixed.set([237, 1]);
|
|
6794
|
-
prefixed.set(publicKey, 2);
|
|
6795
|
-
const did = `did:key:${base58btc.encode(prefixed)}`;
|
|
6796
|
-
return `${did}#${did.slice("did:key:".length)}`;
|
|
6720
|
+
throw new Error("v3 share delivery response is not valid UTF-8 JSON");
|
|
6721
|
+
}
|
|
6722
|
+
const root = exactObject(parsed, ["request", "admission"], "v3 share delivery response");
|
|
6723
|
+
const request = exactObject(root.request, REQUEST_KEYS, "credential invitation request");
|
|
6724
|
+
const admission = exactObject(root.admission, ADMISSION_KEYS, "delivery admission");
|
|
6725
|
+
const signature = exactObject(admission.signature, ["suite", "signerDid", "value"], "delivery admission signature");
|
|
6726
|
+
const envelope = object(expected.request.envelope, "v3 share envelope");
|
|
6727
|
+
const policy = object(envelope.policy, "v3 share policy");
|
|
6728
|
+
const requirement = object(policy.credentialRequirement, "v3 credential requirement");
|
|
6729
|
+
const credentialType = object(requirement.credentialType, "v3 credential type");
|
|
6730
|
+
const contentSource = object(envelope.contentSource, "v3 content source");
|
|
6731
|
+
const envelopeSignature = object(envelope.signature, "v3 envelope signature");
|
|
6732
|
+
const target = object(envelope.target, "v3 target");
|
|
6733
|
+
const binding = object(envelope.attestedEnforcerBinding, "v3 attested enforcer binding");
|
|
6734
|
+
const fields = ["policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "audience", "issuedAt", "expiresAt", "nonce"];
|
|
6735
|
+
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");
|
|
6736
|
+
const issuedAt = Date.parse(String(request.issuedAt));
|
|
6737
|
+
const expiresAt = Date.parse(String(request.expiresAt));
|
|
6738
|
+
if (!Number.isFinite(issuedAt) || !Number.isFinite(expiresAt) || expiresAt <= issuedAt || expiresAt - issuedAt > 15 * 6e4) throw new Error("v3 share delivery authorization time is invalid");
|
|
6739
|
+
const admissionSignature = decodeBase64Url2(signature.value);
|
|
6740
|
+
const { signature: _signature, ...unsignedAdmission } = admission;
|
|
6741
|
+
const digest4 = (0, import_sha2566.sha256)(new TextEncoder().encode(`${DELIVERY_ADMISSION_DOMAIN}${canonicalizeSignedObjectUnsigned(unsignedAdmission)}`));
|
|
6742
|
+
if (admissionSignature.length !== 64 || !import_ed255197.ed25519.verify(admissionSignature, digest4, didKeyBytes(String(signature.signerDid)))) throw new Error("v3 share delivery admission signature is invalid");
|
|
6743
|
+
return { request, admission };
|
|
6797
6744
|
}
|
|
6798
6745
|
|
|
6799
6746
|
// src/delegations/sender-share-store.ts
|