@tinycloud/sdk-core 3.0.0-beta.8 → 3.0.0-beta.9

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.
@@ -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
  });
@@ -3401,25 +3394,33 @@ 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.search !== "" || url.username !== "" || url.password !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
3405
- if (url.pathname === "/s/inline" && url.hash.startsWith("#v=2&p=")) {
3397
+ if (url.username !== "" || url.password !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
3398
+ if (url.pathname === "/viewer") {
3399
+ const secretPayload = url.search === "" && url.hash.startsWith("#tc2=") ? url.hash.slice("#tc2=".length) : void 0;
3400
+ const publicPayload = url.hash === "" && url.searchParams.size === 1 ? url.searchParams.get("tc2") ?? void 0 : void 0;
3401
+ if (secretPayload === void 0 && publicPayload === void 0) throw new ShareEnvelopeError("invalid-link", "published share link is invalid");
3402
+ if (publicPayload !== void 0 && url.search !== `?tc2=${publicPayload}`) throw new ShareEnvelopeError("invalid-link", "published share link is not canonical");
3406
3403
  let payload;
3407
3404
  try {
3408
- const bytes2 = base64UrlToBytes(url.hash.slice("#v=2&p=".length));
3405
+ const bytes2 = base64UrlToBytes(secretPayload ?? publicPayload);
3409
3406
  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));
3407
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes2);
3408
+ const value = JSON.parse(text);
3411
3409
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("inline payload shape");
3410
+ if (canonicalJson(value) !== text) throw new Error("inline payload is not canonical JSON");
3412
3411
  payload = value;
3413
3412
  } catch (error) {
3414
3413
  throw new ShareEnvelopeError("invalid-link", error instanceof Error ? error.message : "invalid inline payload");
3415
3414
  }
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");
3415
+ const payloadKeys = Object.keys(payload);
3416
+ if (payload.v !== 2 || typeof payload.c !== "string" || typeof payload.cid !== "string" || payload.k !== void 0 && typeof payload.k !== "string" || payloadKeys.some((key3) => !["v", "c", "cid", "k"].includes(key3)) || publicPayload !== void 0 && (payloadKeys.length !== 3 || payload.k !== void 0)) throw new ShareEnvelopeError("invalid-link", "inline payload fields are invalid");
3417
3417
  const sealed = base64UrlToBytes(payload.c);
3418
3418
  if (!verifyPublishedShareCid(payload.cid, sealed)) throw new ShareEnvelopeError("cid-mismatch", "inline payload CID does not match");
3419
3419
  const key2 = payload.k === void 0 ? void 0 : base64UrlToBytes(payload.k);
3420
3420
  if (key2 !== void 0 && key2.length !== 32) throw new ShareEnvelopeError("invalid-link", "inline payload key is invalid");
3421
3421
  return { origin, cid: payload.cid, ...key2 === void 0 ? {} : { key: key2 }, sealed };
3422
3422
  }
3423
+ if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
3423
3424
  const match = /^\/s\/([a-z2-7]+)$/.exec(url.pathname);
3424
3425
  if (match === null || !url.hash.startsWith("#k=")) throw new ShareEnvelopeError("invalid-link", "published share link is invalid");
3425
3426
  const key = base64UrlToBytes(url.hash.slice(3));
@@ -3439,13 +3440,13 @@ function parseShareUrl(link, options = {}) {
3439
3440
  } catch {
3440
3441
  throw new ShareEnvelopeError("invalid-link", "share link must be an absolute URL");
3441
3442
  }
3442
- if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share links must not contain query parameters");
3443
3443
  const origin = normalizeOrigin(url.origin);
3444
3444
  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 === "/s/inline" && url.hash.startsWith("#v=2&p=")) {
3445
+ if (url.pathname === "/viewer" && (url.hash.startsWith("#tc2=") || url.searchParams.has("tc2"))) {
3446
3446
  const published = parsePublishedShareLink(link, options);
3447
3447
  return { kind: "inline", origin, cid: published.cid, key: published.key, url: `${url.origin}${url.pathname}`, protocol: "share-envelope-v2" };
3448
3448
  }
3449
+ if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share links must not contain query parameters");
3449
3450
  const match = url.pathname.match(/^\/s\/(i\/)?([^/]+)$/);
3450
3451
  if (!match) throw new ShareEnvelopeError("invalid-link", "share link path is invalid");
3451
3452
  const fragmentParams = url.hash === "" ? void 0 : new URLSearchParams(url.hash.slice(1));
@@ -3468,7 +3469,8 @@ function parseShareUrl(link, options = {}) {
3468
3469
  }
3469
3470
  function isV2ShareLink(link) {
3470
3471
  try {
3471
- return new URL(link).pathname.startsWith("/s/");
3472
+ const url = new URL(link);
3473
+ return url.pathname.startsWith("/s/") || url.pathname === "/viewer";
3472
3474
  } catch {
3473
3475
  return link.startsWith(INLINE_PREFIX);
3474
3476
  }
@@ -6707,114 +6709,13 @@ function validateOwnerSharePolicyRegistrationBytes(bytesValue, expected) {
6707
6709
  return validateOwnerSharePolicyRegistration(value, expected);
6708
6710
  }
6709
6711
 
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
6712
  // src/delegations/share-delivery-v3.ts
6812
- var import_ed255198 = require("@noble/curves/ed25519");
6713
+ var import_ed255197 = require("@noble/curves/ed25519");
6813
6714
  var import_sha2566 = require("@noble/hashes/sha256");
6814
6715
  var CREDENTIAL_INVITATION_REQUEST_DOMAIN = "xyz.tinycloud.credentials/invitation-request/v1\0";
6815
6716
  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"];
6717
+ var REQUEST_KEYS = ["schema", "policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "audience", "issuedAt", "expiresAt", "nonce"];
6718
+ var ADMISSION_KEYS = ["schema", "policyId", "ownerDid", "recipient", "resource", "actions", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "senderKeyDid", "audience", "issuedAt", "expiresAt", "nonce", "signature"];
6818
6719
  function exactObject(value, keys, label) {
6819
6720
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
6820
6721
  const record = value;
@@ -6858,15 +6759,16 @@ function validateShareDeliveryAuthorizationV3Bytes(bytes2, expected) {
6858
6759
  const contentSource = object(envelope.contentSource, "v3 content source");
6859
6760
  const envelopeSignature = object(envelope.signature, "v3 envelope signature");
6860
6761
  const target = object(envelope.target, "v3 target");
6861
- const fields = ["policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "audience", "issuedAt", "expiresAt", "nonce"];
6862
- 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.audience !== expected.credentialsAudience || 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" || signature.signerDid !== target.nodeAudience) throw new Error("v3 share delivery authorization is not bound to the submitted request");
6762
+ const binding = object(envelope.attestedEnforcerBinding, "v3 attested enforcer binding");
6763
+ const fields = ["policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "audience", "issuedAt", "expiresAt", "nonce"];
6764
+ 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
6765
  const issuedAt = Date.parse(String(request.issuedAt));
6864
6766
  const expiresAt = Date.parse(String(request.expiresAt));
6865
6767
  if (!Number.isFinite(issuedAt) || !Number.isFinite(expiresAt) || expiresAt <= issuedAt || expiresAt - issuedAt > 15 * 6e4) throw new Error("v3 share delivery authorization time is invalid");
6866
6768
  const admissionSignature = decodeBase64Url2(signature.value);
6867
6769
  const { signature: _signature, ...unsignedAdmission } = admission;
6868
6770
  const digest4 = (0, import_sha2566.sha256)(new TextEncoder().encode(`${DELIVERY_ADMISSION_DOMAIN}${canonicalizeSignedObjectUnsigned(unsignedAdmission)}`));
6869
- if (admissionSignature.length !== 64 || !import_ed255198.ed25519.verify(admissionSignature, digest4, didKeyBytes(String(signature.signerDid)))) throw new Error("v3 share delivery admission signature is invalid");
6771
+ 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
6772
  return { request, admission };
6871
6773
  }
6872
6774