@tinycloud/share-sdk 1.4.1-beta.1 → 1.4.1-beta.2

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.
@@ -1,3 +1,3 @@
1
- export { f as ShareMetadata, r as ShareRecipientClient, u as inspectShare } from './web-recipient-entry-Cy1fXl_5.cjs';
1
+ export { f as ShareMetadata, r as ShareRecipientClient, u as inspectShare } from './web-recipient-entry-Bn3yRB7g.cjs';
2
2
  import '@tinycloud/share-envelope';
3
3
  import './authorization-CmP1skO8.cjs';
@@ -1,3 +1,3 @@
1
- export { f as ShareMetadata, r as ShareRecipientClient, u as inspectShare } from './web-recipient-entry-CPntAP0H.js';
1
+ export { f as ShareMetadata, r as ShareRecipientClient, u as inspectShare } from './web-recipient-entry-CTFoIH8l.js';
2
2
  import '@tinycloud/share-envelope';
3
3
  import './authorization-CmP1skO8.js';
@@ -6096,41 +6096,18 @@ function object2(value, label) {
6096
6096
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
6097
6097
  return value;
6098
6098
  }
6099
- var BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
6100
6099
  function toBase64(bytes) {
6101
- let out = "";
6102
- for (let i = 0; i < bytes.length; i += 3) {
6103
- const b0 = bytes[i];
6104
- const b1 = i + 1 < bytes.length ? bytes[i + 1] : 0;
6105
- const b2 = i + 2 < bytes.length ? bytes[i + 2] : 0;
6106
- out += BASE64_ALPHABET[b0 >> 2 & 63];
6107
- out += BASE64_ALPHABET[(b0 << 4 | b1 >> 4) & 63];
6108
- out += i + 1 < bytes.length ? BASE64_ALPHABET[(b1 << 2 | b2 >> 6) & 63] : "=";
6109
- out += i + 2 < bytes.length ? BASE64_ALPHABET[b2 & 63] : "=";
6110
- }
6111
- return out;
6100
+ return toBase64Url(bytes).replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(bytes.length / 3) * 4, "=");
6112
6101
  }
6113
6102
  function fromBase64(value, label) {
6114
6103
  if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)) {
6115
6104
  throw new Error(`${label} is invalid`);
6116
6105
  }
6117
- const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
6118
- const out = new Uint8Array(value.length / 4 * 3 - padding);
6119
- let outIdx = 0;
6120
- for (let i = 0; i < value.length; i += 4) {
6121
- const v0 = BASE64_ALPHABET.indexOf(value[i]);
6122
- const v1 = BASE64_ALPHABET.indexOf(value[i + 1]);
6123
- const v2 = value[i + 2] === "=" ? 0 : BASE64_ALPHABET.indexOf(value[i + 2]);
6124
- const v3 = value[i + 3] === "=" ? 0 : BASE64_ALPHABET.indexOf(value[i + 3]);
6125
- const b0 = v0 << 2 | v1 >> 4;
6126
- const b1 = (v1 & 15) << 4 | v2 >> 2;
6127
- const b2 = (v2 & 3) << 6 | v3;
6128
- if (outIdx < out.length) out[outIdx++] = b0;
6129
- if (outIdx < out.length) out[outIdx++] = b1;
6130
- if (outIdx < out.length) out[outIdx++] = b2;
6131
- }
6132
- if (toBase64(out) !== value) throw new Error(`${label} is invalid`);
6133
- return out;
6106
+ try {
6107
+ return fromBase64Url(value.replace(/=+$/, "").replace(/\+/g, "-").replace(/\//g, "_"));
6108
+ } catch {
6109
+ throw new Error(`${label} is invalid`);
6110
+ }
6134
6111
  }
6135
6112
  async function digestBytes(value) {
6136
6113
  return toBase64Url(new Uint8Array(await crypto.subtle.digest("SHA-256", value)));
@@ -6241,16 +6218,16 @@ function matchesInitialCiphertextDigest(bytes, expected) {
6241
6218
  function canonicalHashHex(value) {
6242
6219
  return hex2(sha2563(new TextEncoder().encode(canonicalize(value))));
6243
6220
  }
6244
- async function aesGcmDecrypt(key, blob) {
6221
+ async function aesGcmDecrypt(key, blob, aad) {
6245
6222
  if (key.length !== 32 || blob.length < 28) throw new Error("encrypted content is malformed");
6246
6223
  const cryptoKey = await crypto.subtle.importKey("raw", key, "AES-GCM", false, ["decrypt"]);
6247
- return new Uint8Array(await crypto.subtle.decrypt({ name: "AES-GCM", iv: blob.slice(0, 12) }, cryptoKey, blob.slice(12)));
6224
+ return new Uint8Array(await crypto.subtle.decrypt({ name: "AES-GCM", iv: blob.slice(0, 12), ...aad && { additionalData: aad } }, cryptoKey, blob.slice(12)));
6248
6225
  }
6249
- async function aesGcmEncrypt(key, plaintext) {
6226
+ async function aesGcmEncrypt(key, plaintext, aad) {
6250
6227
  if (key.length !== 32) throw new Error("encrypted content key is malformed");
6251
6228
  const nonce = crypto.getRandomValues(new Uint8Array(12));
6252
6229
  const cryptoKey = await crypto.subtle.importKey("raw", key, "AES-GCM", false, ["encrypt"]);
6253
- const ciphertext = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv: nonce }, cryptoKey, plaintext));
6230
+ const ciphertext = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv: nonce, ...aad && { additionalData: aad } }, cryptoKey, plaintext));
6254
6231
  const output = new Uint8Array(nonce.length + ciphertext.length);
6255
6232
  output.set(nonce);
6256
6233
  output.set(ciphertext, nonce.length);
@@ -6265,8 +6242,9 @@ function parseV3InlineEncryptedEnvelope(bytes, expected) {
6265
6242
  }
6266
6243
  if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("encrypted content envelope is malformed");
6267
6244
  const record = value;
6268
- const allowed = ["v", "networkId", "keyPath", "alg", "keyVersion", "encryptedSymmetricKey", "encryptedSymmetricKeyHash", "ciphertext", "metadata"];
6269
- if (Object.keys(record).some((key) => !allowed.includes(key)) || record.v !== 1 || record.networkId !== expected.encryptionNetwork || record.keyPath !== expected.contentSource.encryptionKeyPath || record.keyPath !== void 0 && record.keyPath !== `share/${expected.shareId}` || record.alg !== "x25519-aes256gcm/v1" || record.keyVersion !== expected.contentSource.keyVersion || typeof record.encryptedSymmetricKey !== "string" || typeof record.encryptedSymmetricKeyHash !== "string" || record.encryptedSymmetricKeyHash !== expected.contentSource.encryptedSymmetricKeyDigestHex || record.encryptedSymmetricKeyHash !== canonicalHashHex(record.encryptedSymmetricKey) || typeof record.ciphertext !== "string" || record.metadata !== void 0 && (record.metadata === null || typeof record.metadata !== "object" || Array.isArray(record.metadata) || Object.values(record.metadata).some((entry) => typeof entry !== "string"))) {
6245
+ const allowed = ["v", "networkId", "keyPath", "alg", "keyVersion", "encryptedSymmetricKey", "encryptedSymmetricKeyHash", "ciphertext", "aad", "metadata"];
6246
+ const source = expected.contentSource;
6247
+ if (Object.keys(record).some((key) => !allowed.includes(key)) || record.v !== (source.encryptionKeyPath === void 0 ? 1 : 2) || record.networkId !== source.encryptionNetwork || record.networkId !== expected.encryptionNetwork || record.keyPath !== source.encryptionKeyPath || record.keyPath !== void 0 && record.keyPath !== `share/${expected.shareId}` || record.alg !== "x25519-aes256gcm/v1" || record.keyVersion !== source.keyVersion || typeof record.encryptedSymmetricKey !== "string" || typeof record.encryptedSymmetricKeyHash !== "string" || hex2(sha2563(fromBase64(record.encryptedSymmetricKey, "v3 encrypted symmetric key"))) !== source.encryptedSymmetricKeyDigestHex || record.encryptedSymmetricKeyHash !== canonicalHashHex(record.encryptedSymmetricKey) || typeof record.ciphertext !== "string" || record.aad !== void 0 && typeof record.aad !== "string" || record.metadata !== void 0 && (record.metadata === null || typeof record.metadata !== "object" || Array.isArray(record.metadata) || Object.values(record.metadata).some((entry) => typeof entry !== "string"))) {
6270
6248
  throw new Error("encrypted content envelope binding is invalid");
6271
6249
  }
6272
6250
  return record;
@@ -6387,7 +6365,7 @@ var ShareRecipientClient = class {
6387
6365
  this.session = { sessionId: delegation.sessionCid, expiresAt: new Date(compact.payload.exp * 1e3).toISOString(), actions: envelope.actions, resource: envelope.resource };
6388
6366
  return this.session;
6389
6367
  }
6390
- /** Decrypt a v3 KV value through a fresh recipient-signed decrypt invocation. */
6368
+ /** Decrypt unscoped v1 or path-bound v2 content, independently checking canonical and registered raw-key digests. */
6391
6369
  async decryptV3Content(bytes) {
6392
6370
  const envelope = this.options.envelope;
6393
6371
  const signer = this.nativeSigner ?? this.signer;
@@ -6395,6 +6373,9 @@ var ShareRecipientClient = class {
6395
6373
  assertArtifactBinding(artifactBindingView(envelope));
6396
6374
  if (!matchesInitialCiphertextDigest(bytes, envelope.contentSource.initialCiphertextDigestHex)) throw new Error("v3 owner KV ciphertext digest mismatch");
6397
6375
  const encrypted = parseV3InlineEncryptedEnvelope(bytes, envelope);
6376
+ const ciphertext = fromBase64(encrypted.ciphertext, "v3 encrypted content");
6377
+ if (ciphertext.length < 28) throw new Error("encrypted content is malformed");
6378
+ const aad = encrypted.aad === void 0 ? void 0 : fromBase64(encrypted.aad, "v3 encrypted content AAD");
6398
6379
  const receiverPrivateKey = crypto.getRandomValues(new Uint8Array(32));
6399
6380
  const receiverPublicKey = toBase64(x25519.getPublicKey(receiverPrivateKey));
6400
6381
  const receiverPublicKeyHash = canonicalHashHex(receiverPublicKey);
@@ -6431,7 +6412,7 @@ var ShareRecipientClient = class {
6431
6412
  const symmetricKey = await aesGcmDecrypt(shared, wrapped.slice(33));
6432
6413
  shared.fill(0);
6433
6414
  if (symmetricKey.length !== 32) throw new Error("v3 content key is malformed");
6434
- const plaintext = await aesGcmDecrypt(symmetricKey, fromBase64(encrypted.ciphertext, "v3 encrypted content"));
6415
+ const plaintext = await aesGcmDecrypt(symmetricKey, ciphertext, aad);
6435
6416
  this.v3ContentKey?.fill(0);
6436
6417
  this.v3ContentKey = symmetricKey;
6437
6418
  this.v3ContentEnvelope = encrypted;
@@ -6443,7 +6424,9 @@ var ShareRecipientClient = class {
6443
6424
  /** Re-encrypt edited v3 content with the admitted content key. */
6444
6425
  async encryptV3Content(bytes, mediaType) {
6445
6426
  if (this.options.envelope.version !== 3 || this.v3ContentKey === void 0 || this.v3ContentEnvelope === void 0) throw new Error("v3 content must be decrypted before it can be saved");
6446
- return new TextEncoder().encode(canonicalize({ ...this.v3ContentEnvelope, ciphertext: toBase64Url(await aesGcmEncrypt(this.v3ContentKey, bytes)), metadata: { ...this.v3ContentEnvelope.metadata ?? {}, contentType: mediaType } }));
6427
+ const envelope = this.v3ContentEnvelope;
6428
+ const aad = envelope.aad === void 0 ? void 0 : fromBase64(envelope.aad, "v3 encrypted content AAD");
6429
+ return new TextEncoder().encode(canonicalize({ ...envelope, ciphertext: toBase64(await aesGcmEncrypt(this.v3ContentKey, bytes, aad)), metadata: { ...envelope.metadata ?? {}, contentType: mediaType } }));
6447
6430
  }
6448
6431
  async resumeWithProof(envelope, resumeToken, proof) {
6449
6432
  void envelope;