@tinycloud/share-sdk 0.3.0-beta.0 → 0.3.0-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.
- package/dist/index.cjs +455 -605
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +133 -53
- package/dist/index.d.ts +133 -53
- package/dist/index.js +452 -602
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -4531,25 +4531,25 @@ function encodeTo(int, target, offset = 0) {
|
|
|
4531
4531
|
function encodingLength(int) {
|
|
4532
4532
|
return varint_default.encodingLength(int);
|
|
4533
4533
|
}
|
|
4534
|
-
function create(code22,
|
|
4535
|
-
const size =
|
|
4534
|
+
function create(code22, digest3) {
|
|
4535
|
+
const size = digest3.byteLength;
|
|
4536
4536
|
const sizeOffset = encodingLength(code22);
|
|
4537
4537
|
const digestOffset = sizeOffset + encodingLength(size);
|
|
4538
4538
|
const bytes3 = new Uint8Array(digestOffset + size);
|
|
4539
4539
|
encodeTo(code22, bytes3, 0);
|
|
4540
4540
|
encodeTo(size, bytes3, sizeOffset);
|
|
4541
|
-
bytes3.set(
|
|
4542
|
-
return new Digest(code22, size,
|
|
4541
|
+
bytes3.set(digest3, digestOffset);
|
|
4542
|
+
return new Digest(code22, size, digest3, bytes3);
|
|
4543
4543
|
}
|
|
4544
4544
|
function decode4(multihash) {
|
|
4545
4545
|
const bytes3 = coerce2(multihash);
|
|
4546
4546
|
const [code22, sizeOffset] = decode3(bytes3);
|
|
4547
4547
|
const [size, digestOffset] = decode3(bytes3.subarray(sizeOffset));
|
|
4548
|
-
const
|
|
4549
|
-
if (
|
|
4548
|
+
const digest3 = bytes3.subarray(sizeOffset + digestOffset);
|
|
4549
|
+
if (digest3.byteLength !== size) {
|
|
4550
4550
|
throw new Error("Incorrect length");
|
|
4551
4551
|
}
|
|
4552
|
-
return new Digest(code22, size,
|
|
4552
|
+
return new Digest(code22, size, digest3, bytes3);
|
|
4553
4553
|
}
|
|
4554
4554
|
function equals2(a, b) {
|
|
4555
4555
|
if (a === b) {
|
|
@@ -4567,10 +4567,10 @@ var Digest = class {
|
|
|
4567
4567
|
/**
|
|
4568
4568
|
* Creates a multihash digest.
|
|
4569
4569
|
*/
|
|
4570
|
-
constructor(code22, size,
|
|
4570
|
+
constructor(code22, size, digest3, bytes3) {
|
|
4571
4571
|
this.code = code22;
|
|
4572
4572
|
this.size = size;
|
|
4573
|
-
this.digest =
|
|
4573
|
+
this.digest = digest3;
|
|
4574
4574
|
this.bytes = bytes3;
|
|
4575
4575
|
}
|
|
4576
4576
|
};
|
|
@@ -4651,8 +4651,8 @@ var CID = class _CID {
|
|
|
4651
4651
|
toV1() {
|
|
4652
4652
|
switch (this.version) {
|
|
4653
4653
|
case 0: {
|
|
4654
|
-
const { code: code22, digest:
|
|
4655
|
-
const multihash = create(code22,
|
|
4654
|
+
const { code: code22, digest: digest3 } = this.multihash;
|
|
4655
|
+
const multihash = create(code22, digest3);
|
|
4656
4656
|
return _CID.createV1(this.code, multihash);
|
|
4657
4657
|
}
|
|
4658
4658
|
case 1: {
|
|
@@ -4706,8 +4706,8 @@ var CID = class _CID {
|
|
|
4706
4706
|
return new _CID(version, code22, multihash, bytes3 ?? encodeCID(version, code22, multihash.bytes));
|
|
4707
4707
|
} else if (value[cidSymbol] === true) {
|
|
4708
4708
|
const { version, multihash, code: code22 } = value;
|
|
4709
|
-
const
|
|
4710
|
-
return _CID.create(version, code22,
|
|
4709
|
+
const digest3 = decode4(multihash);
|
|
4710
|
+
return _CID.create(version, code22, digest3);
|
|
4711
4711
|
} else {
|
|
4712
4712
|
return null;
|
|
4713
4713
|
}
|
|
@@ -4717,11 +4717,11 @@ var CID = class _CID {
|
|
|
4717
4717
|
* @param code - Code of the codec content is encoded in, see https://github.com/multiformats/multicodec/blob/master/table.csv
|
|
4718
4718
|
* @param digest - (Multi)hash of the of the content.
|
|
4719
4719
|
*/
|
|
4720
|
-
static create(version, code22,
|
|
4720
|
+
static create(version, code22, digest3) {
|
|
4721
4721
|
if (typeof code22 !== "number") {
|
|
4722
4722
|
throw new Error("String codecs are no longer supported");
|
|
4723
4723
|
}
|
|
4724
|
-
if (!(
|
|
4724
|
+
if (!(digest3.bytes instanceof Uint8Array)) {
|
|
4725
4725
|
throw new Error("Invalid digest");
|
|
4726
4726
|
}
|
|
4727
4727
|
switch (version) {
|
|
@@ -4729,12 +4729,12 @@ var CID = class _CID {
|
|
|
4729
4729
|
if (code22 !== DAG_PB_CODE) {
|
|
4730
4730
|
throw new Error(`Version 0 CID must use dag-pb (code: ${DAG_PB_CODE}) block encoding`);
|
|
4731
4731
|
} else {
|
|
4732
|
-
return new _CID(version, code22,
|
|
4732
|
+
return new _CID(version, code22, digest3, digest3.bytes);
|
|
4733
4733
|
}
|
|
4734
4734
|
}
|
|
4735
4735
|
case 1: {
|
|
4736
|
-
const bytes3 = encodeCID(version, code22,
|
|
4737
|
-
return new _CID(version, code22,
|
|
4736
|
+
const bytes3 = encodeCID(version, code22, digest3.bytes);
|
|
4737
|
+
return new _CID(version, code22, digest3, bytes3);
|
|
4738
4738
|
}
|
|
4739
4739
|
default: {
|
|
4740
4740
|
throw new Error("Invalid version");
|
|
@@ -4744,8 +4744,8 @@ var CID = class _CID {
|
|
|
4744
4744
|
/**
|
|
4745
4745
|
* Simplified version of `create` for CIDv0.
|
|
4746
4746
|
*/
|
|
4747
|
-
static createV0(
|
|
4748
|
-
return _CID.create(0, DAG_PB_CODE,
|
|
4747
|
+
static createV0(digest3) {
|
|
4748
|
+
return _CID.create(0, DAG_PB_CODE, digest3);
|
|
4749
4749
|
}
|
|
4750
4750
|
/**
|
|
4751
4751
|
* Simplified version of `create` for CIDv1.
|
|
@@ -4753,8 +4753,8 @@ var CID = class _CID {
|
|
|
4753
4753
|
* @param code - Content encoding format code.
|
|
4754
4754
|
* @param digest - Multihash of the content.
|
|
4755
4755
|
*/
|
|
4756
|
-
static createV1(code22,
|
|
4757
|
-
return _CID.create(1, code22,
|
|
4756
|
+
static createV1(code22, digest3) {
|
|
4757
|
+
return _CID.create(1, code22, digest3);
|
|
4758
4758
|
}
|
|
4759
4759
|
/**
|
|
4760
4760
|
* Decoded a CID from its binary representation. The byte array must contain
|
|
@@ -4787,8 +4787,8 @@ var CID = class _CID {
|
|
|
4787
4787
|
throw new Error("Incorrect length");
|
|
4788
4788
|
}
|
|
4789
4789
|
const digestBytes3 = multihashBytes.subarray(specs.multihashSize - specs.digestSize);
|
|
4790
|
-
const
|
|
4791
|
-
const cid2 = specs.version === 0 ? _CID.createV0(
|
|
4790
|
+
const digest3 = new Digest(specs.multihashCode, specs.digestSize, digestBytes3, multihashBytes);
|
|
4791
|
+
const cid2 = specs.version === 0 ? _CID.createV0(digest3) : _CID.createV1(specs.codec, digest3);
|
|
4792
4792
|
return [cid2, bytes3.subarray(specs.size)];
|
|
4793
4793
|
}
|
|
4794
4794
|
/**
|
|
@@ -4911,8 +4911,8 @@ var cidSymbol = /* @__PURE__ */ Symbol.for("@ipld/js-cid/CID");
|
|
|
4911
4911
|
var code = 85;
|
|
4912
4912
|
var SHA256_CODE = 18;
|
|
4913
4913
|
async function computeCid(bytes3) {
|
|
4914
|
-
const
|
|
4915
|
-
return CID.create(1, code,
|
|
4914
|
+
const digest3 = create(SHA256_CODE, sha256(new Uint8Array(bytes3)));
|
|
4915
|
+
return CID.create(1, code, digest3).toString();
|
|
4916
4916
|
}
|
|
4917
4917
|
function isCanonicalRawCid(cidString) {
|
|
4918
4918
|
let cid2;
|
|
@@ -5723,7 +5723,7 @@ async function signCompactUcanAuthorization(input) {
|
|
|
5723
5723
|
const publicKey = ed25519PublicKeyFromDidKey(principal);
|
|
5724
5724
|
const header = {
|
|
5725
5725
|
alg: "EdDSA",
|
|
5726
|
-
jwk: { alg: "EdDSA", crv: "Ed25519", kty: "OKP", x:
|
|
5726
|
+
jwk: { alg: "EdDSA", crv: "Ed25519", kty: "OKP", x: toBase64Url(publicKey) },
|
|
5727
5727
|
typ: "JWT",
|
|
5728
5728
|
ucv: "0.10.0"
|
|
5729
5729
|
};
|
|
@@ -5737,13 +5737,13 @@ async function signCompactUcanAuthorization(input) {
|
|
|
5737
5737
|
nnc: input.nonce,
|
|
5738
5738
|
prf: input.proofs
|
|
5739
5739
|
};
|
|
5740
|
-
const protectedSegment =
|
|
5741
|
-
const payloadSegment =
|
|
5740
|
+
const protectedSegment = toBase64Url(new TextEncoder().encode(canonicalize(header)));
|
|
5741
|
+
const payloadSegment = toBase64Url(new TextEncoder().encode(canonicalize(payload)));
|
|
5742
5742
|
const signingInput = new TextEncoder().encode(`${protectedSegment}.${payloadSegment}`);
|
|
5743
5743
|
const signature = await input.sign(signingInput);
|
|
5744
5744
|
if (signature.length !== 64) throw new TypeError("compact Authorization signature must be Ed25519");
|
|
5745
5745
|
return verifyCompactUcanAuthorization(
|
|
5746
|
-
`${protectedSegment}.${payloadSegment}.${
|
|
5746
|
+
`${protectedSegment}.${payloadSegment}.${toBase64Url(signature)}`
|
|
5747
5747
|
);
|
|
5748
5748
|
}
|
|
5749
5749
|
function verifyCompactUcanAuthorization(authorization, expectedCid) {
|
|
@@ -5751,9 +5751,9 @@ function verifyCompactUcanAuthorization(authorization, expectedCid) {
|
|
|
5751
5751
|
const segments = authorization.split(".");
|
|
5752
5752
|
if (segments.length !== 3 || segments.some((segment) => segment.length === 0)) throw new TypeError("compact Authorization must contain three segments");
|
|
5753
5753
|
const [headerSegment, payloadSegment, signatureSegment] = segments;
|
|
5754
|
-
const headerBytes =
|
|
5755
|
-
const payloadBytes =
|
|
5756
|
-
const signature =
|
|
5754
|
+
const headerBytes = fromBase64Url(headerSegment);
|
|
5755
|
+
const payloadBytes = fromBase64Url(payloadSegment);
|
|
5756
|
+
const signature = fromBase64Url(signatureSegment);
|
|
5757
5757
|
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
5758
5758
|
const header = JSON.parse(decoder.decode(headerBytes));
|
|
5759
5759
|
const payload = JSON.parse(decoder.decode(payloadBytes));
|
|
@@ -5766,7 +5766,7 @@ function verifyCompactUcanAuthorization(authorization, expectedCid) {
|
|
|
5766
5766
|
if (typeof payload.iss !== "string" || typeof payload.aud !== "string" || typeof payload.nnc !== "string" || !Number.isInteger(payload.nbf) || !Number.isInteger(payload.exp) || payload.nbf >= payload.exp || !Array.isArray(payload.prf) || payload.prf.some((proof) => typeof proof !== "string") || !Array.isArray(payload.fct) || payload.fct.length !== 1) throw new TypeError("compact Authorization payload is invalid");
|
|
5767
5767
|
const principal = payload.iss.split("#", 1)[0];
|
|
5768
5768
|
const publicKey = ed25519PublicKeyFromDidKey(principal);
|
|
5769
|
-
if (!equal(publicKey,
|
|
5769
|
+
if (!equal(publicKey, fromBase64Url(jwk.x))) throw new TypeError("compact Authorization JWK does not bind issuer");
|
|
5770
5770
|
if (!ed255192.verify(signature, new TextEncoder().encode(`${headerSegment}.${payloadSegment}`), publicKey, { zip215: false })) throw new TypeError("compact Authorization signature is invalid");
|
|
5771
5771
|
const cid2 = CID.createV1(85, create(30, blake3(new TextEncoder().encode(authorization)))).toString();
|
|
5772
5772
|
if (expectedCid !== void 0 && cid2 !== expectedCid) throw new TypeError("compact Authorization CID mismatch");
|
|
@@ -5779,16 +5779,6 @@ function object(value, label) {
|
|
|
5779
5779
|
function assertExactKeys(value, keys, label) {
|
|
5780
5780
|
if (Object.keys(value).length !== keys.length || Object.keys(value).some((key) => !keys.includes(key))) throw new TypeError(`${label} has unknown or missing fields`);
|
|
5781
5781
|
}
|
|
5782
|
-
function decodeBase64Url(value) {
|
|
5783
|
-
if (!/^[A-Za-z0-9_-]+$/.test(value) || value.length % 4 === 1) throw new TypeError("compact Authorization segment is not base64url");
|
|
5784
|
-
const bytes3 = typeof Buffer !== "undefined" ? new Uint8Array(Buffer.from(value, "base64url")) : Uint8Array.from(atob(value.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(value.length / 4) * 4, "=")), (character) => character.charCodeAt(0));
|
|
5785
|
-
const encoded = typeof Buffer !== "undefined" ? Buffer.from(bytes3).toString("base64url") : btoa(String.fromCharCode(...bytes3)).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
5786
|
-
if (encoded !== value) throw new TypeError("compact Authorization segment is not canonical");
|
|
5787
|
-
return bytes3;
|
|
5788
|
-
}
|
|
5789
|
-
function encodeBase64Url(value) {
|
|
5790
|
-
return typeof Buffer !== "undefined" ? Buffer.from(value).toString("base64url") : btoa(String.fromCharCode(...value)).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
5791
|
-
}
|
|
5792
5782
|
function equal(left, right) {
|
|
5793
5783
|
return left.length === right.length && left.every((byte, index) => byte === right[index]);
|
|
5794
5784
|
}
|
|
@@ -5906,8 +5896,8 @@ async function verifyEnvelopeV3(envelope, options) {
|
|
|
5906
5896
|
if (hex(sourceDigest) !== parsed.contentSourceDigestHex) return false;
|
|
5907
5897
|
const sortedCapabilities = [...policy.capabilityCeiling].sort((left, right) => canonicalize(left).localeCompare(canonicalize(right)));
|
|
5908
5898
|
const capabilityCeilingHashHex = hex(sha2562(new TextEncoder().encode(`${POLICY_CAPABILITY_V1_DOMAIN}${canonicalize(sortedCapabilities)}`)));
|
|
5909
|
-
const
|
|
5910
|
-
const nativeProjectionHashHex = hex(sha2562(new TextEncoder().encode(`${NATIVE_PROJECTION_V1_DOMAIN}${canonicalize(
|
|
5899
|
+
const nativeProjection2 = sortedCapabilities.map((capability) => capability.kind === "encryption" ? { service: "tinycloud.encryption", space: capability.resource, path: capability.resource, actions: [capability.action] } : { service: "tinycloud.kv", space: capability.resource.slice(0, capability.resource.indexOf("/kv/")), path: capability.resource.split("/kv/")[1], actions: [...capability.actions], caveat: { type: "xyz.tinycloud.resource/selector", kind: capability.selector, value: capability.resource } }).sort((left, right) => canonicalize(left).localeCompare(canonicalize(right)));
|
|
5900
|
+
const nativeProjectionHashHex = hex(sha2562(new TextEncoder().encode(`${NATIVE_PROJECTION_V1_DOMAIN}${canonicalize(nativeProjection2)}`)));
|
|
5911
5901
|
const expectedAttenuation = Object.fromEntries(sortedCapabilities.map((capability) => capability.kind === "encryption" ? [capability.resource, { [capability.action]: [{}] }] : [capability.resource, Object.fromEntries(capability.actions.map((action) => [action, [{ kind: capability.selector, type: "xyz.tinycloud.resource/selector", value: capability.resource }]]))]));
|
|
5912
5902
|
const kv = policy.capabilityCeiling.find((capability) => capability.kind === "kv");
|
|
5913
5903
|
const expectedKvActions = parsed.actions.flatMap((action) => action === "read" ? ["tinycloud.kv/get", "tinycloud.kv/metadata"] : action === "list" ? ["tinycloud.kv/list"] : ["tinycloud.kv/put"]);
|
|
@@ -5920,8 +5910,8 @@ async function verifyEnvelopeV3(envelope, options) {
|
|
|
5920
5910
|
const expectedBindingDigestHex = hex(sha2562(new TextEncoder().encode(canonicalize({ enforcerDid: binding.enforcerDid, nodeAudience: binding.nodeAudience }))));
|
|
5921
5911
|
if (binding.enforcerDid !== parsed.target.nodeAudience || binding.attestationBindingDigestHex !== expectedBindingDigestHex || bindingSignature.signerDid !== binding.nodeAudience || bindingSignature.suite !== "Ed25519" || Date.parse(binding.issuedAt) > Date.now() || Date.parse(binding.expiresAt) <= Date.now() || Date.parse(binding.expiresAt) < Date.parse(parsed.expiry)) return false;
|
|
5922
5912
|
try {
|
|
5923
|
-
const
|
|
5924
|
-
if (!ed255193.verify(fromBase64Url(bindingSignature.value),
|
|
5913
|
+
const digest3 = sha2562(new TextEncoder().encode(`${ATTESTED_ENFORCER_V2_DOMAIN}${canonicalize(unsignedBinding)}`));
|
|
5914
|
+
if (!ed255193.verify(fromBase64Url(bindingSignature.value), digest3, ed25519PublicKeyFromDidKey(binding.nodeAudience), ED25519_VERIFY_OPTS2)) return false;
|
|
5925
5915
|
} catch {
|
|
5926
5916
|
return false;
|
|
5927
5917
|
}
|
|
@@ -6704,10 +6694,31 @@ async function parseAddressedEnvelope(value) {
|
|
|
6704
6694
|
return { envelope, policy: decoded, policyCid: envelope.authorizationTarget.policyCid };
|
|
6705
6695
|
}
|
|
6706
6696
|
|
|
6707
|
-
// src/
|
|
6708
|
-
import { ed25519 as ed255195 } from "@noble/curves/ed25519";
|
|
6697
|
+
// src/addressed-publish.ts
|
|
6709
6698
|
import { sha256 as sha2563 } from "@noble/hashes/sha256";
|
|
6710
6699
|
|
|
6700
|
+
// src/authorization.ts
|
|
6701
|
+
async function authorizeShare(input) {
|
|
6702
|
+
if (input.resumeToken !== void 0) {
|
|
6703
|
+
if (input.resumeToken.length < 16 || input.resumeToken.length > 512) {
|
|
6704
|
+
return { state: "denied", reason: "rejected" };
|
|
6705
|
+
}
|
|
6706
|
+
return input.adapter.resume({
|
|
6707
|
+
envelope: input.envelope,
|
|
6708
|
+
method: input.method,
|
|
6709
|
+
resumeToken: input.resumeToken,
|
|
6710
|
+
...input.proof === void 0 ? {} : { proof: input.proof }
|
|
6711
|
+
});
|
|
6712
|
+
}
|
|
6713
|
+
return input.adapter.begin({ envelope: input.envelope, method: input.method });
|
|
6714
|
+
}
|
|
6715
|
+
function authorizationMethodForTarget(target) {
|
|
6716
|
+
if (target.kind === "recipientDid") return "openkey-device";
|
|
6717
|
+
if (target.kind === "email") return "email-claim";
|
|
6718
|
+
if (target.kind === "emailDomain") return "email-claim";
|
|
6719
|
+
return void 0;
|
|
6720
|
+
}
|
|
6721
|
+
|
|
6711
6722
|
// ../../node_modules/multiformats/dist/src/bytes.js
|
|
6712
6723
|
var empty2 = new Uint8Array(0);
|
|
6713
6724
|
function equals3(aa, bb) {
|
|
@@ -7049,6 +7060,347 @@ function rfc46482({ name, prefix, bitsPerChar, alphabet }) {
|
|
|
7049
7060
|
});
|
|
7050
7061
|
}
|
|
7051
7062
|
|
|
7063
|
+
// ../../node_modules/multiformats/dist/src/bases/base58.js
|
|
7064
|
+
var base58btc2 = baseX2({
|
|
7065
|
+
name: "base58btc",
|
|
7066
|
+
prefix: "z",
|
|
7067
|
+
alphabet: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
|
7068
|
+
});
|
|
7069
|
+
var base58flickr2 = baseX2({
|
|
7070
|
+
name: "base58flickr",
|
|
7071
|
+
prefix: "Z",
|
|
7072
|
+
alphabet: "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
|
|
7073
|
+
});
|
|
7074
|
+
|
|
7075
|
+
// src/targets.ts
|
|
7076
|
+
function validEmail(value) {
|
|
7077
|
+
return /^[^@\s]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i.test(value);
|
|
7078
|
+
}
|
|
7079
|
+
function validDomain(value) {
|
|
7080
|
+
return /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(?:\.(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?))+$/i.test(value);
|
|
7081
|
+
}
|
|
7082
|
+
function validRecipientDid(value) {
|
|
7083
|
+
if (value.length === 0 || value.length > 2048 || /[\u0000-\u0020\u007f]/.test(value)) return false;
|
|
7084
|
+
const parts = value.split(":");
|
|
7085
|
+
if (parts.length < 3 || parts[0] !== "did" || !/^[a-z0-9]+$/.test(parts[1] ?? "")) return false;
|
|
7086
|
+
const identifier = parts.slice(2);
|
|
7087
|
+
if (identifier.some((part) => part.length === 0)) return false;
|
|
7088
|
+
if (parts[1] === "web") {
|
|
7089
|
+
const host = identifier[0] ?? "";
|
|
7090
|
+
if (host.length > 253 || host.split(".").some((label) => !label || label.length > 63 || !/^[A-Za-z0-9-]+$/.test(label) || label.startsWith("-") || label.endsWith("-"))) return false;
|
|
7091
|
+
return identifier.slice(1).every((part) => /^[A-Za-z0-9._%-]+$/.test(part));
|
|
7092
|
+
}
|
|
7093
|
+
if (parts[1] === "pkh") return identifier.length >= 3 && identifier.every((part) => /^[A-Za-z0-9._%-]+$/.test(part));
|
|
7094
|
+
if (parts[1] === "key") {
|
|
7095
|
+
try {
|
|
7096
|
+
const bytes3 = base58btc2.decode(identifier.join(":"));
|
|
7097
|
+
return bytes3.length === 34 && bytes3[0] === 237 && bytes3[1] === 1;
|
|
7098
|
+
} catch {
|
|
7099
|
+
return false;
|
|
7100
|
+
}
|
|
7101
|
+
}
|
|
7102
|
+
return false;
|
|
7103
|
+
}
|
|
7104
|
+
function normalizeShareTarget(target) {
|
|
7105
|
+
if (target.kind === "bearer") return target;
|
|
7106
|
+
if (target.kind === "recipientDid") {
|
|
7107
|
+
if (!validRecipientDid(target.did)) throw new TypeError("recipient DID is invalid");
|
|
7108
|
+
return { kind: target.kind, did: target.did };
|
|
7109
|
+
}
|
|
7110
|
+
if (target.kind === "email") {
|
|
7111
|
+
if (!validEmail(target.address)) throw new TypeError("recipient email is invalid");
|
|
7112
|
+
const at = target.address.lastIndexOf("@");
|
|
7113
|
+
return { kind: target.kind, address: `${target.address.slice(0, at)}@${target.address.slice(at + 1).toLowerCase()}` };
|
|
7114
|
+
}
|
|
7115
|
+
if (!validDomain(target.domain)) throw new TypeError("recipient email domain is invalid");
|
|
7116
|
+
return { kind: target.kind, domain: target.domain.toLowerCase() };
|
|
7117
|
+
}
|
|
7118
|
+
function targetAuthorizationMethod(target) {
|
|
7119
|
+
return authorizationMethodForTarget(target);
|
|
7120
|
+
}
|
|
7121
|
+
async function publishTargetShare(input) {
|
|
7122
|
+
const target = normalizeShareTarget(input.target);
|
|
7123
|
+
if (target.kind === "bearer") return publishShare(input);
|
|
7124
|
+
if (input.targetAdapter === void 0) {
|
|
7125
|
+
return {
|
|
7126
|
+
state: "authorization-required",
|
|
7127
|
+
method: targetAuthorizationMethod(target)
|
|
7128
|
+
};
|
|
7129
|
+
}
|
|
7130
|
+
const source = input.source instanceof Uint8Array ? input.source.slice() : await (async () => {
|
|
7131
|
+
const chunks = [];
|
|
7132
|
+
let size = 0;
|
|
7133
|
+
for await (const chunk of input.source) {
|
|
7134
|
+
if (!(chunk instanceof Uint8Array)) throw new TypeError("addressed publication source yielded invalid bytes");
|
|
7135
|
+
size += chunk.byteLength;
|
|
7136
|
+
if (size > (input.maxBytes ?? 100 * 1024 * 1024)) throw new TypeError("addressed publication exceeds maxBytes");
|
|
7137
|
+
chunks.push(chunk.slice());
|
|
7138
|
+
}
|
|
7139
|
+
const bytes3 = new Uint8Array(size);
|
|
7140
|
+
let offset = 0;
|
|
7141
|
+
for (const chunk of chunks) {
|
|
7142
|
+
bytes3.set(chunk, offset);
|
|
7143
|
+
offset += chunk.byteLength;
|
|
7144
|
+
}
|
|
7145
|
+
return bytes3;
|
|
7146
|
+
})();
|
|
7147
|
+
const files = input.files === void 0 || input.files.length === 0 ? [{ bytes: source }] : input.files;
|
|
7148
|
+
const limit = input.maxBytes ?? SHARE_CONTENT_LIMIT;
|
|
7149
|
+
let totalBytes = 0;
|
|
7150
|
+
for (const file of files) {
|
|
7151
|
+
totalBytes += file.bytes.byteLength;
|
|
7152
|
+
if (!Number.isSafeInteger(totalBytes) || totalBytes > limit) {
|
|
7153
|
+
throw new SharePublishError("max-bytes-exceeded", "addressed publication exceeds the combined byte limit");
|
|
7154
|
+
}
|
|
7155
|
+
}
|
|
7156
|
+
return input.targetAdapter.publish({
|
|
7157
|
+
source,
|
|
7158
|
+
filename: input.filename,
|
|
7159
|
+
...input.files === void 0 ? {} : { files: input.files },
|
|
7160
|
+
...input.mediaType === void 0 ? {} : { mediaType: input.mediaType },
|
|
7161
|
+
...input.resourceKind === void 0 ? {} : { resourceKind: input.resourceKind },
|
|
7162
|
+
...input.actions === void 0 ? {} : { actions: input.actions },
|
|
7163
|
+
target,
|
|
7164
|
+
expiresAt: input.expiresAt ?? new Date((input.now?.() ?? Date.now()) + 7 * 24 * 60 * 60 * 1e3),
|
|
7165
|
+
origin: input.origin,
|
|
7166
|
+
...input.inline === void 0 ? {} : { inline: input.inline },
|
|
7167
|
+
upload: {
|
|
7168
|
+
...input.registryBaseUrl === void 0 ? {} : { registryBaseUrl: input.registryBaseUrl },
|
|
7169
|
+
...input.fetchFn === void 0 ? {} : { fetchFn: input.fetchFn },
|
|
7170
|
+
...input.authorizeUpload === void 0 ? {} : { authorizeUpload: input.authorizeUpload },
|
|
7171
|
+
...input.authorizationOrigin === void 0 ? {} : { authorizationOrigin: input.authorizationOrigin },
|
|
7172
|
+
...input.credentials === void 0 ? {} : { credentials: input.credentials },
|
|
7173
|
+
...input.allowInsecureRegistry === void 0 ? {} : { allowInsecureRegistry: input.allowInsecureRegistry },
|
|
7174
|
+
...input.uploadBlob === void 0 ? {} : { uploadBlob: input.uploadBlob }
|
|
7175
|
+
},
|
|
7176
|
+
...input.notify === void 0 ? {} : { notify: input.notify }
|
|
7177
|
+
});
|
|
7178
|
+
}
|
|
7179
|
+
|
|
7180
|
+
// src/addressed-publish.ts
|
|
7181
|
+
var POLICY_V1_DOMAIN = "xyz.tinycloud.policy/policy/v1\0";
|
|
7182
|
+
var POLICY_V2_DOMAIN = "xyz.tinycloud.policy/policy/v2\0";
|
|
7183
|
+
var POLICY_CAPABILITY_V1_DOMAIN2 = "xyz.tinycloud.policy/PolicyCapability/v1\0";
|
|
7184
|
+
var CONTENT_SOURCE_V1_DOMAIN2 = "xyz.tinycloud.policy/ContentSource/v1\0";
|
|
7185
|
+
var NATIVE_PROJECTION_V1_DOMAIN2 = "xyz.tinycloud.policy/NativeProjection/v1\0";
|
|
7186
|
+
var ENVELOPE_V3_DOMAIN = "xyz.tinycloud.share/envelope/v3\0";
|
|
7187
|
+
var textEncoder = new TextEncoder();
|
|
7188
|
+
function targetMatcher(target) {
|
|
7189
|
+
if (target.kind === "recipientDid") return { kind: "recipientDid", value: target.did };
|
|
7190
|
+
if (target.kind === "email") return { kind: "exactEmail", value: target.address };
|
|
7191
|
+
return { kind: "emailDomain", value: target.domain };
|
|
7192
|
+
}
|
|
7193
|
+
function targetKind(target) {
|
|
7194
|
+
return target.kind;
|
|
7195
|
+
}
|
|
7196
|
+
function assertSafeInput(input) {
|
|
7197
|
+
if (!/^[A-Za-z0-9_-]{16,128}$/.test(input.shareId)) throw new TypeError("addressed share id is invalid");
|
|
7198
|
+
if (input.filename.length === 0 || input.filename === "." || input.filename === ".." || /[/\\\u0000-\u001f\u007f]/.test(input.filename)) throw new TypeError("addressed filename is invalid");
|
|
7199
|
+
if (!Number.isSafeInteger(input.byteLength) || input.byteLength < 0 || input.byteLength > SHARE_CONTENT_LIMIT) throw new TypeError("addressed content length is invalid");
|
|
7200
|
+
if (input.actions.length === 0 || input.policyActions.length === 0) throw new TypeError("addressed share actions are empty");
|
|
7201
|
+
if (!Number.isFinite(input.expiresAt.getTime()) || input.expiresAt.getTime() <= Date.now()) throw new TypeError("addressed share expiry must be in the future");
|
|
7202
|
+
if (input.artifact === "html" && (input.resource.kind !== "prefix" || !input.actions.includes("read") || !input.actions.includes("list"))) throw new TypeError("html artifacts require a readable prefix");
|
|
7203
|
+
if (input.contentSource.shareId !== input.shareId || input.contentSource.selector !== input.resource.kind) throw new TypeError("addressed content source is not bound to the share");
|
|
7204
|
+
}
|
|
7205
|
+
function hex2(bytes3) {
|
|
7206
|
+
return [...bytes3].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
7207
|
+
}
|
|
7208
|
+
function digestHex(value, domain) {
|
|
7209
|
+
return hex2(sha2563(textEncoder.encode(`${domain}${canonicalize(value)}`)));
|
|
7210
|
+
}
|
|
7211
|
+
function sortCanonical(values) {
|
|
7212
|
+
return [...values].sort((left, right) => canonicalize(left).localeCompare(canonicalize(right)));
|
|
7213
|
+
}
|
|
7214
|
+
function base32Lower2(bytes3) {
|
|
7215
|
+
const alphabet = "abcdefghijklmnopqrstuvwxyz234567";
|
|
7216
|
+
let output = "";
|
|
7217
|
+
let buffer = 0;
|
|
7218
|
+
let bits = 0;
|
|
7219
|
+
for (const byte of bytes3) {
|
|
7220
|
+
buffer = buffer << 8 | byte;
|
|
7221
|
+
bits += 8;
|
|
7222
|
+
while (bits >= 5) {
|
|
7223
|
+
output += alphabet[buffer >>> bits - 5 & 31];
|
|
7224
|
+
bits -= 5;
|
|
7225
|
+
}
|
|
7226
|
+
}
|
|
7227
|
+
if (bits > 0) output += alphabet[buffer << 5 - bits & 31];
|
|
7228
|
+
return output;
|
|
7229
|
+
}
|
|
7230
|
+
function rfc3339Seconds(value) {
|
|
7231
|
+
return new Date(Math.floor(value.getTime() / 1e3) * 1e3).toISOString().replace(".000Z", "Z");
|
|
7232
|
+
}
|
|
7233
|
+
function nativeProjection(capabilities) {
|
|
7234
|
+
return sortCanonical(capabilities.map((capability) => capability.kind === "encryption" ? { service: "tinycloud.encryption", space: capability.resource, path: capability.resource, actions: [capability.action] } : {
|
|
7235
|
+
service: "tinycloud.kv",
|
|
7236
|
+
space: capability.resource.slice(0, capability.resource.indexOf("/kv/")),
|
|
7237
|
+
path: capability.resource.slice(capability.resource.indexOf("/kv/") + 4),
|
|
7238
|
+
actions: [...capability.actions],
|
|
7239
|
+
caveat: { type: "xyz.tinycloud.resource/selector", kind: capability.selector, value: capability.resource }
|
|
7240
|
+
}));
|
|
7241
|
+
}
|
|
7242
|
+
async function createPolicy(options, capabilities) {
|
|
7243
|
+
const fields = {
|
|
7244
|
+
ownerDid: options.authority.ownerDid,
|
|
7245
|
+
createdAt: rfc3339Seconds(/* @__PURE__ */ new Date()),
|
|
7246
|
+
expiresAt: rfc3339Seconds(options.expiresAt),
|
|
7247
|
+
contentSource: options.contentSource,
|
|
7248
|
+
capabilityCeiling: [...capabilities]
|
|
7249
|
+
};
|
|
7250
|
+
const unsigned = options.credentialRequirement === void 0 ? { schema: "xyz.tinycloud.policy/policy/v1", ...fields } : { schema: "xyz.tinycloud.policy/policy/v2", ...fields, credentialRequirement: options.credentialRequirement };
|
|
7251
|
+
const domain = options.credentialRequirement === void 0 ? POLICY_V1_DOMAIN : POLICY_V2_DOMAIN;
|
|
7252
|
+
const policyDigestHex = digestHex(unsigned, domain);
|
|
7253
|
+
const signature = await options.authority.sign(sha2563(textEncoder.encode(`${domain}${canonicalize(unsigned)}`)));
|
|
7254
|
+
if (signature.byteLength !== 64) throw new TypeError("policy signature must be Ed25519");
|
|
7255
|
+
const policy = {
|
|
7256
|
+
...unsigned,
|
|
7257
|
+
policyId: `pol_${base32Lower2(Uint8Array.from(policyDigestHex.match(/../g), (byte) => Number.parseInt(byte, 16)))}`,
|
|
7258
|
+
signature: { suite: "Ed25519", signerDid: options.authority.ownerDid, value: toBase64Url(signature) }
|
|
7259
|
+
};
|
|
7260
|
+
return { policy, policyCid: await computeCid(textEncoder.encode(canonicalize(policy))), policyDigestHex };
|
|
7261
|
+
}
|
|
7262
|
+
function publicationResult(input) {
|
|
7263
|
+
const result = {
|
|
7264
|
+
protocol: "tinycloud-share",
|
|
7265
|
+
version: SHARE_PUBLISH_RESULT_VERSION,
|
|
7266
|
+
url: input.url,
|
|
7267
|
+
link: { kind: input.options.inline === true ? "inline" : "compact", cid: input.envelopeCid },
|
|
7268
|
+
metadata: {
|
|
7269
|
+
protocol: "tinycloud-share",
|
|
7270
|
+
version: 1,
|
|
7271
|
+
shareId: input.options.shareId,
|
|
7272
|
+
origin: input.options.shareOrigin,
|
|
7273
|
+
target: { kind: targetKind(input.options.target), origin: input.options.nodeOrigin, nodeAudience: input.enforcerDid, spaceId: input.options.spaceId },
|
|
7274
|
+
resource: { ...input.options.resource },
|
|
7275
|
+
actions: [...input.options.actions],
|
|
7276
|
+
expiresAt: input.expiry,
|
|
7277
|
+
display: { filename: input.options.filename },
|
|
7278
|
+
recipientMatcher: { ...input.matcher },
|
|
7279
|
+
policyCid: input.policyCid,
|
|
7280
|
+
ownerDelegationCid: input.policyRootCid,
|
|
7281
|
+
enforcementDelegationCid: input.enforcementRootCid,
|
|
7282
|
+
ownerDid: input.options.authority.ownerDid,
|
|
7283
|
+
enforcerDid: input.enforcerDid,
|
|
7284
|
+
envelopeCid: input.envelopeCid,
|
|
7285
|
+
shareCid: input.envelopeCid
|
|
7286
|
+
},
|
|
7287
|
+
registryDeleteAfter: input.retention
|
|
7288
|
+
};
|
|
7289
|
+
Object.defineProperty(result, "toJSON", { enumerable: false, value: () => redactPublishedShare(result) });
|
|
7290
|
+
Object.defineProperty(result, "url", { enumerable: false, value: input.url });
|
|
7291
|
+
Object.defineProperty(result, "deliveryMaterial", { enumerable: false, value: input.deliveryMaterial });
|
|
7292
|
+
return result;
|
|
7293
|
+
}
|
|
7294
|
+
async function publishAddressedShare(options) {
|
|
7295
|
+
assertSafeInput(options);
|
|
7296
|
+
const target = normalizeShareTarget(options.target);
|
|
7297
|
+
if (target.kind === "bearer") throw new TypeError("addressed target is required");
|
|
7298
|
+
const expiry = rfc3339Seconds(options.expiresAt);
|
|
7299
|
+
const matcher = targetMatcher(target);
|
|
7300
|
+
const capabilities = sortCanonical([
|
|
7301
|
+
{ kind: "kv", resource: options.contentSource.kvResource, selector: options.resource.kind, actions: [...options.policyActions] },
|
|
7302
|
+
{ kind: "encryption", resource: options.contentSource.encryptionNetwork, action: "tinycloud.encryption/decrypt" }
|
|
7303
|
+
]);
|
|
7304
|
+
const created = await createPolicy(options, capabilities);
|
|
7305
|
+
const contentSourceDigestHex = digestHex(options.contentSource, CONTENT_SOURCE_V1_DOMAIN2);
|
|
7306
|
+
const capabilityCeilingHashHex = digestHex(capabilities, POLICY_CAPABILITY_V1_DOMAIN2);
|
|
7307
|
+
const nativeProjectionHashHex = digestHex(nativeProjection(capabilities), NATIVE_PROJECTION_V1_DOMAIN2);
|
|
7308
|
+
const commonRoot = {
|
|
7309
|
+
ownerDid: options.authority.ownerDid,
|
|
7310
|
+
policyId: created.policy.policyId,
|
|
7311
|
+
policyDigestHex: created.policyDigestHex,
|
|
7312
|
+
policyCid: created.policyCid,
|
|
7313
|
+
contentSourceDigestHex,
|
|
7314
|
+
capabilityCeilingHashHex,
|
|
7315
|
+
nativeProjectionHashHex,
|
|
7316
|
+
notBefore: new Date(created.policy.createdAt),
|
|
7317
|
+
expiresAt: new Date(expiry),
|
|
7318
|
+
nodeAudience: options.nodeAudience,
|
|
7319
|
+
capabilities
|
|
7320
|
+
};
|
|
7321
|
+
const policyRootReceipt = await options.authority.createOwnerRoot({ ...commonRoot, role: "policy-authority", audienceDid: `did:tinycloud:policy:${created.policyDigestHex}` });
|
|
7322
|
+
const enforcementRootReceipt = await options.authority.createOwnerRoot({ ...commonRoot, role: "policy-enforcement", audienceDid: options.enforcerDid });
|
|
7323
|
+
const policyRoot = { cid: policyRootReceipt.cid, authorization: policyRootReceipt.delegationHeader.Authorization.replace(/^Bearer\s+/i, ""), role: "policy-authority" };
|
|
7324
|
+
const enforcementRoot = { cid: enforcementRootReceipt.cid, authorization: enforcementRootReceipt.delegationHeader.Authorization.replace(/^Bearer\s+/i, ""), role: "policy-enforcement" };
|
|
7325
|
+
const registration = await options.authority.registerPolicy({
|
|
7326
|
+
policyCid: created.policyCid,
|
|
7327
|
+
policy: created.policy,
|
|
7328
|
+
policyRoot,
|
|
7329
|
+
enforcementRoot,
|
|
7330
|
+
contentSourceDigestHex,
|
|
7331
|
+
nativeProjectionHashHex,
|
|
7332
|
+
rootExpiresAt: expiry,
|
|
7333
|
+
enforcerDid: options.enforcerDid,
|
|
7334
|
+
expectedNodeAudience: options.nodeAudience
|
|
7335
|
+
});
|
|
7336
|
+
if (registration.policyCid !== created.policyCid || registration.policyRootCid !== policyRoot.cid || registration.enforcementRootCid !== enforcementRoot.cid) throw new Error("Policy/v3 registration receipt is not bound to the published roots");
|
|
7337
|
+
const unsigned = {
|
|
7338
|
+
version: 3,
|
|
7339
|
+
shareId: options.shareId,
|
|
7340
|
+
recipientMatcher: matcher,
|
|
7341
|
+
...options.deliveryEmail === void 0 ? {} : { deliveryEmail: options.deliveryEmail },
|
|
7342
|
+
actions: [...options.actions],
|
|
7343
|
+
resource: { ...options.resource },
|
|
7344
|
+
target: { origin: options.nodeOrigin, nodeAudience: registration.attestedEnforcerBinding.enforcerDid, spaceId: options.spaceId },
|
|
7345
|
+
policy: created.policy,
|
|
7346
|
+
policyCid: created.policyCid,
|
|
7347
|
+
policyRoot,
|
|
7348
|
+
enforcementRoot,
|
|
7349
|
+
attestedEnforcerBinding: registration.attestedEnforcerBinding,
|
|
7350
|
+
contentSource: options.contentSource,
|
|
7351
|
+
contentSourceDigestHex,
|
|
7352
|
+
encryptionNetwork: options.contentSource.encryptionNetwork,
|
|
7353
|
+
expiry,
|
|
7354
|
+
display: { filename: options.filename },
|
|
7355
|
+
encrypted: true,
|
|
7356
|
+
metadata: {
|
|
7357
|
+
mediaType: options.mediaType,
|
|
7358
|
+
byteLength: options.byteLength,
|
|
7359
|
+
filename: options.filename,
|
|
7360
|
+
...options.mediaType.startsWith("text/") ? { encoding: "utf-8" } : {},
|
|
7361
|
+
...options.artifact === void 0 ? {} : { artifact: options.artifact }
|
|
7362
|
+
}
|
|
7363
|
+
};
|
|
7364
|
+
unsignedShareEnvelopeV3Schema.parse(unsigned);
|
|
7365
|
+
const envelopeSignature = await options.authority.sign(sha2563(textEncoder.encode(`${ENVELOPE_V3_DOMAIN}${canonicalize(unsigned)}`)));
|
|
7366
|
+
if (envelopeSignature.byteLength !== 64) throw new TypeError("v3 envelope signature must be Ed25519");
|
|
7367
|
+
const envelope = { ...unsigned, signature: { signerDid: options.authority.ownerDid, algorithm: "Ed25519", value: toBase64Url(envelopeSignature) } };
|
|
7368
|
+
shareEnvelopeV3Schema.parse(envelope);
|
|
7369
|
+
const envelopeKey = generateKey();
|
|
7370
|
+
try {
|
|
7371
|
+
const sealed = await seal(textEncoder.encode(canonicalize(envelope)), envelopeKey);
|
|
7372
|
+
let url;
|
|
7373
|
+
let retention = options.expiresAt.toISOString();
|
|
7374
|
+
if (options.inline === true) url = await encodeInlineShareUrl({ origin: options.shareOrigin, ciphertext: sealed.blob, key32: envelopeKey });
|
|
7375
|
+
else {
|
|
7376
|
+
const uploaded = await uploadShareBlob({ source: new Uint8Array([1]), filename: options.filename, origin: options.shareOrigin, ...options.upload }, { blob: sealed.blob, cid: sealed.cid, deleteAfter: retention, contentLength: sealed.blob.byteLength });
|
|
7377
|
+
retention = uploaded.deleteAfter;
|
|
7378
|
+
url = encodeShareUrl({ origin: options.shareOrigin, ciphertextCid: uploaded.cid, key32: envelopeKey });
|
|
7379
|
+
}
|
|
7380
|
+
await options.publishBinding?.({
|
|
7381
|
+
version: 3,
|
|
7382
|
+
shareCid: sealed.cid,
|
|
7383
|
+
shareId: options.shareId,
|
|
7384
|
+
policyCid: created.policyCid,
|
|
7385
|
+
policyRootCid: policyRoot.cid,
|
|
7386
|
+
enforcementRootCid: enforcementRoot.cid,
|
|
7387
|
+
contentSourceDigestHex
|
|
7388
|
+
});
|
|
7389
|
+
const deliveryMaterial = { envelope, sealedEnvelope: toBase64Url(sealed.blob), envelopeKey: toBase64Url(envelopeKey), shareCid: sealed.cid };
|
|
7390
|
+
options.onDeliveryMaterial?.(deliveryMaterial);
|
|
7391
|
+
return publicationResult({ options, url, envelopeCid: sealed.cid, matcher, policyCid: created.policyCid, policyRootCid: policyRoot.cid, enforcementRootCid: enforcementRoot.cid, enforcerDid: registration.attestedEnforcerBinding.enforcerDid, expiry, retention, deliveryMaterial });
|
|
7392
|
+
} finally {
|
|
7393
|
+
envelopeKey.fill(0);
|
|
7394
|
+
}
|
|
7395
|
+
}
|
|
7396
|
+
|
|
7397
|
+
// src/policy-authority.ts
|
|
7398
|
+
import { ed25519 as ed255196 } from "@noble/curves/ed25519";
|
|
7399
|
+
|
|
7400
|
+
// src/owner-policy.ts
|
|
7401
|
+
import { ed25519 as ed255195 } from "@noble/curves/ed25519";
|
|
7402
|
+
import { sha256 as sha2564 } from "@noble/hashes/sha256";
|
|
7403
|
+
|
|
7052
7404
|
// ../../node_modules/multiformats/dist/src/bases/base32.js
|
|
7053
7405
|
var base322 = rfc46482({
|
|
7054
7406
|
prefix: "b",
|
|
@@ -7117,18 +7469,6 @@ var base36upper2 = baseX2({
|
|
|
7117
7469
|
alphabet: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
7118
7470
|
});
|
|
7119
7471
|
|
|
7120
|
-
// ../../node_modules/multiformats/dist/src/bases/base58.js
|
|
7121
|
-
var base58btc2 = baseX2({
|
|
7122
|
-
name: "base58btc",
|
|
7123
|
-
prefix: "z",
|
|
7124
|
-
alphabet: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
|
7125
|
-
});
|
|
7126
|
-
var base58flickr2 = baseX2({
|
|
7127
|
-
name: "base58flickr",
|
|
7128
|
-
prefix: "Z",
|
|
7129
|
-
alphabet: "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
|
|
7130
|
-
});
|
|
7131
|
-
|
|
7132
7472
|
// ../../node_modules/multiformats/dist/src/vendor/varint.js
|
|
7133
7473
|
var encode_12 = encode4;
|
|
7134
7474
|
var MSB2 = 128;
|
|
@@ -7202,25 +7542,25 @@ function encodingLength2(int) {
|
|
|
7202
7542
|
}
|
|
7203
7543
|
|
|
7204
7544
|
// ../../node_modules/multiformats/dist/src/hashes/digest.js
|
|
7205
|
-
function create2(code3,
|
|
7206
|
-
const size =
|
|
7545
|
+
function create2(code3, digest3) {
|
|
7546
|
+
const size = digest3.byteLength;
|
|
7207
7547
|
const sizeOffset = encodingLength2(code3);
|
|
7208
7548
|
const digestOffset = sizeOffset + encodingLength2(size);
|
|
7209
7549
|
const bytes3 = new Uint8Array(digestOffset + size);
|
|
7210
7550
|
encodeTo2(code3, bytes3, 0);
|
|
7211
7551
|
encodeTo2(size, bytes3, sizeOffset);
|
|
7212
|
-
bytes3.set(
|
|
7213
|
-
return new Digest2(code3, size,
|
|
7552
|
+
bytes3.set(digest3, digestOffset);
|
|
7553
|
+
return new Digest2(code3, size, digest3, bytes3);
|
|
7214
7554
|
}
|
|
7215
7555
|
function decode8(multihash) {
|
|
7216
7556
|
const bytes3 = coerce3(multihash);
|
|
7217
7557
|
const [code3, sizeOffset] = decode7(bytes3);
|
|
7218
7558
|
const [size, digestOffset] = decode7(bytes3.subarray(sizeOffset));
|
|
7219
|
-
const
|
|
7220
|
-
if (
|
|
7559
|
+
const digest3 = bytes3.subarray(sizeOffset + digestOffset);
|
|
7560
|
+
if (digest3.byteLength !== size) {
|
|
7221
7561
|
throw new Error("Incorrect length");
|
|
7222
7562
|
}
|
|
7223
|
-
return new Digest2(code3, size,
|
|
7563
|
+
return new Digest2(code3, size, digest3, bytes3);
|
|
7224
7564
|
}
|
|
7225
7565
|
function equals4(a, b) {
|
|
7226
7566
|
if (a === b) {
|
|
@@ -7238,10 +7578,10 @@ var Digest2 = class {
|
|
|
7238
7578
|
/**
|
|
7239
7579
|
* Creates a multihash digest.
|
|
7240
7580
|
*/
|
|
7241
|
-
constructor(code3, size,
|
|
7581
|
+
constructor(code3, size, digest3, bytes3) {
|
|
7242
7582
|
this.code = code3;
|
|
7243
7583
|
this.size = size;
|
|
7244
|
-
this.digest =
|
|
7584
|
+
this.digest = digest3;
|
|
7245
7585
|
this.bytes = bytes3;
|
|
7246
7586
|
}
|
|
7247
7587
|
};
|
|
@@ -7324,8 +7664,8 @@ var CID2 = class _CID2 {
|
|
|
7324
7664
|
toV1() {
|
|
7325
7665
|
switch (this.version) {
|
|
7326
7666
|
case 0: {
|
|
7327
|
-
const { code: code3, digest:
|
|
7328
|
-
const multihash = create2(code3,
|
|
7667
|
+
const { code: code3, digest: digest3 } = this.multihash;
|
|
7668
|
+
const multihash = create2(code3, digest3);
|
|
7329
7669
|
return _CID2.createV1(this.code, multihash);
|
|
7330
7670
|
}
|
|
7331
7671
|
case 1: {
|
|
@@ -7379,8 +7719,8 @@ var CID2 = class _CID2 {
|
|
|
7379
7719
|
return new _CID2(version, code3, multihash, bytes3 ?? encodeCID2(version, code3, multihash.bytes));
|
|
7380
7720
|
} else if (value[cidSymbol2] === true) {
|
|
7381
7721
|
const { version, multihash, code: code3 } = value;
|
|
7382
|
-
const
|
|
7383
|
-
return _CID2.create(version, code3,
|
|
7722
|
+
const digest3 = decode8(multihash);
|
|
7723
|
+
return _CID2.create(version, code3, digest3);
|
|
7384
7724
|
} else {
|
|
7385
7725
|
return null;
|
|
7386
7726
|
}
|
|
@@ -7390,11 +7730,11 @@ var CID2 = class _CID2 {
|
|
|
7390
7730
|
* @param code - Code of the codec content is encoded in, see https://github.com/multiformats/multicodec/blob/master/table.csv
|
|
7391
7731
|
* @param digest - (Multi)hash of the of the content.
|
|
7392
7732
|
*/
|
|
7393
|
-
static create(version, code3,
|
|
7733
|
+
static create(version, code3, digest3) {
|
|
7394
7734
|
if (typeof code3 !== "number") {
|
|
7395
7735
|
throw new Error("String codecs are no longer supported");
|
|
7396
7736
|
}
|
|
7397
|
-
if (!(
|
|
7737
|
+
if (!(digest3.bytes instanceof Uint8Array)) {
|
|
7398
7738
|
throw new Error("Invalid digest");
|
|
7399
7739
|
}
|
|
7400
7740
|
switch (version) {
|
|
@@ -7402,12 +7742,12 @@ var CID2 = class _CID2 {
|
|
|
7402
7742
|
if (code3 !== DAG_PB_CODE2) {
|
|
7403
7743
|
throw new Error(`Version 0 CID must use dag-pb (code: ${DAG_PB_CODE2}) block encoding`);
|
|
7404
7744
|
} else {
|
|
7405
|
-
return new _CID2(version, code3,
|
|
7745
|
+
return new _CID2(version, code3, digest3, digest3.bytes);
|
|
7406
7746
|
}
|
|
7407
7747
|
}
|
|
7408
7748
|
case 1: {
|
|
7409
|
-
const bytes3 = encodeCID2(version, code3,
|
|
7410
|
-
return new _CID2(version, code3,
|
|
7749
|
+
const bytes3 = encodeCID2(version, code3, digest3.bytes);
|
|
7750
|
+
return new _CID2(version, code3, digest3, bytes3);
|
|
7411
7751
|
}
|
|
7412
7752
|
default: {
|
|
7413
7753
|
throw new Error("Invalid version");
|
|
@@ -7417,8 +7757,8 @@ var CID2 = class _CID2 {
|
|
|
7417
7757
|
/**
|
|
7418
7758
|
* Simplified version of `create` for CIDv0.
|
|
7419
7759
|
*/
|
|
7420
|
-
static createV0(
|
|
7421
|
-
return _CID2.create(0, DAG_PB_CODE2,
|
|
7760
|
+
static createV0(digest3) {
|
|
7761
|
+
return _CID2.create(0, DAG_PB_CODE2, digest3);
|
|
7422
7762
|
}
|
|
7423
7763
|
/**
|
|
7424
7764
|
* Simplified version of `create` for CIDv1.
|
|
@@ -7426,8 +7766,8 @@ var CID2 = class _CID2 {
|
|
|
7426
7766
|
* @param code - Content encoding format code.
|
|
7427
7767
|
* @param digest - Multihash of the content.
|
|
7428
7768
|
*/
|
|
7429
|
-
static createV1(code3,
|
|
7430
|
-
return _CID2.create(1, code3,
|
|
7769
|
+
static createV1(code3, digest3) {
|
|
7770
|
+
return _CID2.create(1, code3, digest3);
|
|
7431
7771
|
}
|
|
7432
7772
|
/**
|
|
7433
7773
|
* Decoded a CID from its binary representation. The byte array must contain
|
|
@@ -7460,8 +7800,8 @@ var CID2 = class _CID2 {
|
|
|
7460
7800
|
throw new Error("Incorrect length");
|
|
7461
7801
|
}
|
|
7462
7802
|
const digestBytes3 = multihashBytes.subarray(specs.multihashSize - specs.digestSize);
|
|
7463
|
-
const
|
|
7464
|
-
const cid2 = specs.version === 0 ? _CID2.createV0(
|
|
7803
|
+
const digest3 = new Digest2(specs.multihashCode, specs.digestSize, digestBytes3, multihashBytes);
|
|
7804
|
+
const cid2 = specs.version === 0 ? _CID2.createV0(digest3) : _CID2.createV1(specs.codec, digest3);
|
|
7465
7805
|
return [cid2, bytes3.subarray(specs.size)];
|
|
7466
7806
|
}
|
|
7467
7807
|
/**
|
|
@@ -7613,10 +7953,10 @@ function fromB64(value) {
|
|
|
7613
7953
|
throw new Error("base64url decoding is unavailable");
|
|
7614
7954
|
}
|
|
7615
7955
|
function cid(bytesValue) {
|
|
7616
|
-
return CID2.createV1(code2, create2(SHA256_CODE2,
|
|
7956
|
+
return CID2.createV1(code2, create2(SHA256_CODE2, sha2564(bytesValue))).toString(base322.encoder);
|
|
7617
7957
|
}
|
|
7618
7958
|
function digest(bytesValue) {
|
|
7619
|
-
return b64(
|
|
7959
|
+
return b64(sha2564(bytesValue));
|
|
7620
7960
|
}
|
|
7621
7961
|
function didKeyFromEd25519PublicKey2(publicKey) {
|
|
7622
7962
|
if (publicKey.length !== 32) throw new Error("share key public key must be 32 bytes");
|
|
@@ -7807,368 +8147,8 @@ function validateOwnerSharePolicyRegistrationBytes(bytesValue, expected) {
|
|
|
7807
8147
|
return validateOwnerSharePolicyRegistration(value, expected);
|
|
7808
8148
|
}
|
|
7809
8149
|
|
|
7810
|
-
// src/authorization.ts
|
|
7811
|
-
async function authorizeShare(input) {
|
|
7812
|
-
if (input.resumeToken !== void 0) {
|
|
7813
|
-
if (input.resumeToken.length < 16 || input.resumeToken.length > 512) {
|
|
7814
|
-
return { state: "denied", reason: "rejected" };
|
|
7815
|
-
}
|
|
7816
|
-
return input.adapter.resume({
|
|
7817
|
-
envelope: input.envelope,
|
|
7818
|
-
method: input.method,
|
|
7819
|
-
resumeToken: input.resumeToken,
|
|
7820
|
-
...input.proof === void 0 ? {} : { proof: input.proof }
|
|
7821
|
-
});
|
|
7822
|
-
}
|
|
7823
|
-
return input.adapter.begin({ envelope: input.envelope, method: input.method });
|
|
7824
|
-
}
|
|
7825
|
-
function authorizationMethodForTarget(target) {
|
|
7826
|
-
if (target.kind === "recipientDid") return "openkey-device";
|
|
7827
|
-
if (target.kind === "email") return "email-claim";
|
|
7828
|
-
if (target.kind === "emailDomain") return "email-claim";
|
|
7829
|
-
return void 0;
|
|
7830
|
-
}
|
|
7831
|
-
|
|
7832
|
-
// src/targets.ts
|
|
7833
|
-
function validEmail(value) {
|
|
7834
|
-
return /^[^@\s]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i.test(value);
|
|
7835
|
-
}
|
|
7836
|
-
function validDomain(value) {
|
|
7837
|
-
return /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(?:\.(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?))+$/i.test(value);
|
|
7838
|
-
}
|
|
7839
|
-
function validRecipientDid(value) {
|
|
7840
|
-
if (value.length === 0 || value.length > 2048 || /[\u0000-\u0020\u007f]/.test(value)) return false;
|
|
7841
|
-
const parts = value.split(":");
|
|
7842
|
-
if (parts.length < 3 || parts[0] !== "did" || !/^[a-z0-9]+$/.test(parts[1] ?? "")) return false;
|
|
7843
|
-
const identifier = parts.slice(2);
|
|
7844
|
-
if (identifier.some((part) => part.length === 0)) return false;
|
|
7845
|
-
if (parts[1] === "web") {
|
|
7846
|
-
const host = identifier[0] ?? "";
|
|
7847
|
-
if (host.length > 253 || host.split(".").some((label) => !label || label.length > 63 || !/^[A-Za-z0-9-]+$/.test(label) || label.startsWith("-") || label.endsWith("-"))) return false;
|
|
7848
|
-
return identifier.slice(1).every((part) => /^[A-Za-z0-9._%-]+$/.test(part));
|
|
7849
|
-
}
|
|
7850
|
-
if (parts[1] === "pkh") return identifier.length >= 3 && identifier.every((part) => /^[A-Za-z0-9._%-]+$/.test(part));
|
|
7851
|
-
if (parts[1] === "key") {
|
|
7852
|
-
try {
|
|
7853
|
-
const bytes3 = base58btc2.decode(identifier.join(":"));
|
|
7854
|
-
return bytes3.length === 34 && bytes3[0] === 237 && bytes3[1] === 1;
|
|
7855
|
-
} catch {
|
|
7856
|
-
return false;
|
|
7857
|
-
}
|
|
7858
|
-
}
|
|
7859
|
-
return false;
|
|
7860
|
-
}
|
|
7861
|
-
function normalizeShareTarget(target) {
|
|
7862
|
-
if (target.kind === "bearer") return target;
|
|
7863
|
-
if (target.kind === "recipientDid") {
|
|
7864
|
-
if (!validRecipientDid(target.did)) throw new TypeError("recipient DID is invalid");
|
|
7865
|
-
return { kind: target.kind, did: target.did };
|
|
7866
|
-
}
|
|
7867
|
-
if (target.kind === "email") {
|
|
7868
|
-
if (!validEmail(target.address)) throw new TypeError("recipient email is invalid");
|
|
7869
|
-
const at = target.address.lastIndexOf("@");
|
|
7870
|
-
return { kind: target.kind, address: `${target.address.slice(0, at)}@${target.address.slice(at + 1).toLowerCase()}` };
|
|
7871
|
-
}
|
|
7872
|
-
if (!validDomain(target.domain)) throw new TypeError("recipient email domain is invalid");
|
|
7873
|
-
return { kind: target.kind, domain: target.domain.toLowerCase() };
|
|
7874
|
-
}
|
|
7875
|
-
function targetAuthorizationMethod(target) {
|
|
7876
|
-
return authorizationMethodForTarget(target);
|
|
7877
|
-
}
|
|
7878
|
-
async function publishTargetShare(input) {
|
|
7879
|
-
const target = normalizeShareTarget(input.target);
|
|
7880
|
-
if (target.kind === "bearer") return publishShare(input);
|
|
7881
|
-
if (input.targetAdapter === void 0) {
|
|
7882
|
-
return {
|
|
7883
|
-
state: "authorization-required",
|
|
7884
|
-
method: targetAuthorizationMethod(target)
|
|
7885
|
-
};
|
|
7886
|
-
}
|
|
7887
|
-
const source = input.source instanceof Uint8Array ? input.source.slice() : await (async () => {
|
|
7888
|
-
const chunks = [];
|
|
7889
|
-
let size = 0;
|
|
7890
|
-
for await (const chunk of input.source) {
|
|
7891
|
-
if (!(chunk instanceof Uint8Array)) throw new TypeError("addressed publication source yielded invalid bytes");
|
|
7892
|
-
size += chunk.byteLength;
|
|
7893
|
-
if (size > (input.maxBytes ?? 100 * 1024 * 1024)) throw new TypeError("addressed publication exceeds maxBytes");
|
|
7894
|
-
chunks.push(chunk.slice());
|
|
7895
|
-
}
|
|
7896
|
-
const bytes3 = new Uint8Array(size);
|
|
7897
|
-
let offset = 0;
|
|
7898
|
-
for (const chunk of chunks) {
|
|
7899
|
-
bytes3.set(chunk, offset);
|
|
7900
|
-
offset += chunk.byteLength;
|
|
7901
|
-
}
|
|
7902
|
-
return bytes3;
|
|
7903
|
-
})();
|
|
7904
|
-
const files = input.files === void 0 || input.files.length === 0 ? [{ bytes: source }] : input.files;
|
|
7905
|
-
const limit = input.maxBytes ?? SHARE_CONTENT_LIMIT;
|
|
7906
|
-
let totalBytes = 0;
|
|
7907
|
-
for (const file of files) {
|
|
7908
|
-
totalBytes += file.bytes.byteLength;
|
|
7909
|
-
if (!Number.isSafeInteger(totalBytes) || totalBytes > limit) {
|
|
7910
|
-
throw new SharePublishError("max-bytes-exceeded", "addressed publication exceeds the combined byte limit");
|
|
7911
|
-
}
|
|
7912
|
-
}
|
|
7913
|
-
return input.targetAdapter.publish({
|
|
7914
|
-
source,
|
|
7915
|
-
filename: input.filename,
|
|
7916
|
-
...input.files === void 0 ? {} : { files: input.files },
|
|
7917
|
-
...input.mediaType === void 0 ? {} : { mediaType: input.mediaType },
|
|
7918
|
-
...input.resourceKind === void 0 ? {} : { resourceKind: input.resourceKind },
|
|
7919
|
-
...input.actions === void 0 ? {} : { actions: input.actions },
|
|
7920
|
-
target,
|
|
7921
|
-
expiresAt: input.expiresAt ?? new Date((input.now?.() ?? Date.now()) + 7 * 24 * 60 * 60 * 1e3),
|
|
7922
|
-
origin: input.origin,
|
|
7923
|
-
...input.inline === void 0 ? {} : { inline: input.inline },
|
|
7924
|
-
upload: {
|
|
7925
|
-
...input.registryBaseUrl === void 0 ? {} : { registryBaseUrl: input.registryBaseUrl },
|
|
7926
|
-
...input.fetchFn === void 0 ? {} : { fetchFn: input.fetchFn },
|
|
7927
|
-
...input.authorizeUpload === void 0 ? {} : { authorizeUpload: input.authorizeUpload },
|
|
7928
|
-
...input.authorizationOrigin === void 0 ? {} : { authorizationOrigin: input.authorizationOrigin },
|
|
7929
|
-
...input.credentials === void 0 ? {} : { credentials: input.credentials },
|
|
7930
|
-
...input.allowInsecureRegistry === void 0 ? {} : { allowInsecureRegistry: input.allowInsecureRegistry },
|
|
7931
|
-
...input.uploadBlob === void 0 ? {} : { uploadBlob: input.uploadBlob }
|
|
7932
|
-
},
|
|
7933
|
-
...input.notify === void 0 ? {} : { notify: input.notify }
|
|
7934
|
-
});
|
|
7935
|
-
}
|
|
7936
|
-
|
|
7937
|
-
// src/addressed-publish.ts
|
|
7938
|
-
var ENVELOPE_DOMAIN = "xyz.tinycloud.share/envelope/v2\0";
|
|
7939
|
-
function targetMatcher(target) {
|
|
7940
|
-
if (target.kind === "recipientDid") return { kind: "recipientDid", value: target.did };
|
|
7941
|
-
if (target.kind === "email") return { kind: "exactEmail", value: target.address };
|
|
7942
|
-
return { kind: "emailDomain", value: target.domain };
|
|
7943
|
-
}
|
|
7944
|
-
function targetKind(target) {
|
|
7945
|
-
return target.kind;
|
|
7946
|
-
}
|
|
7947
|
-
function assertSafeInput(input) {
|
|
7948
|
-
if (!/^[A-Za-z0-9_-]{16,128}$/.test(input.shareId)) throw new TypeError("addressed share id is invalid");
|
|
7949
|
-
if (input.filename.length === 0 || input.filename === "." || input.filename === ".." || /[/\\\u0000-\u001f\u007f]/.test(input.filename)) throw new TypeError("addressed filename is invalid");
|
|
7950
|
-
if (!Number.isSafeInteger(input.byteLength) || input.byteLength < 0 || input.byteLength > SHARE_CONTENT_LIMIT) throw new TypeError("addressed content length is invalid");
|
|
7951
|
-
if (input.actions.length === 0 || input.policyActions.length === 0) throw new TypeError("addressed share actions are empty");
|
|
7952
|
-
const expiry = input.expiresAt.getTime();
|
|
7953
|
-
if (!Number.isFinite(expiry) || expiry <= Date.now()) throw new TypeError("addressed share expiry must be in the future");
|
|
7954
|
-
if (input.artifact === "html" && (input.resource.kind !== "prefix" || !input.actions.includes("read") || !input.actions.includes("list"))) throw new TypeError("html artifacts require a readable prefix");
|
|
7955
|
-
if (input.contentSource.kind !== "kv") throw new TypeError("addressed policy publication requires a KV content source");
|
|
7956
|
-
}
|
|
7957
|
-
async function sha2564(value) {
|
|
7958
|
-
return toBase64Url(new Uint8Array(await crypto.subtle.digest("SHA-256", value)));
|
|
7959
|
-
}
|
|
7960
|
-
function publicationResult(input) {
|
|
7961
|
-
const result = {
|
|
7962
|
-
protocol: "tinycloud-share",
|
|
7963
|
-
version: SHARE_PUBLISH_RESULT_VERSION,
|
|
7964
|
-
url: input.url,
|
|
7965
|
-
link: { kind: input.options.inline === true ? "inline" : "compact", cid: input.envelopeCid },
|
|
7966
|
-
metadata: {
|
|
7967
|
-
protocol: "tinycloud-share",
|
|
7968
|
-
version: 1,
|
|
7969
|
-
shareId: input.options.shareId,
|
|
7970
|
-
origin: input.options.shareOrigin,
|
|
7971
|
-
target: {
|
|
7972
|
-
kind: targetKind(input.options.target),
|
|
7973
|
-
origin: input.options.nodeOrigin,
|
|
7974
|
-
nodeAudience: input.options.nodeAudience,
|
|
7975
|
-
spaceId: input.options.spaceId
|
|
7976
|
-
},
|
|
7977
|
-
resource: { ...input.options.resource },
|
|
7978
|
-
actions: [...input.options.actions],
|
|
7979
|
-
expiresAt: input.options.expiresAt.toISOString(),
|
|
7980
|
-
display: { filename: input.options.filename },
|
|
7981
|
-
recipientMatcher: { ...input.matcher },
|
|
7982
|
-
registrationCid: input.registration.registrationCid,
|
|
7983
|
-
policyCid: input.policyCid,
|
|
7984
|
-
ownerDelegationCid: input.registration.ownerDelegationCid,
|
|
7985
|
-
enforcementDelegationCid: input.enforcementDelegationCid,
|
|
7986
|
-
ownerDid: input.registration.ownerDid,
|
|
7987
|
-
shareKeyDid: input.shareKeyDid,
|
|
7988
|
-
enforcerDid: input.registration.enforcerDid,
|
|
7989
|
-
envelopeCid: input.envelopeCid,
|
|
7990
|
-
shareCid: input.shareCid
|
|
7991
|
-
},
|
|
7992
|
-
registryDeleteAfter: input.retention
|
|
7993
|
-
};
|
|
7994
|
-
Object.defineProperty(result, "toJSON", { enumerable: false, value: () => redactPublishedShare(result) });
|
|
7995
|
-
Object.defineProperty(result, "url", { enumerable: false, value: input.url });
|
|
7996
|
-
return result;
|
|
7997
|
-
}
|
|
7998
|
-
async function publishAddressedShare(options) {
|
|
7999
|
-
assertSafeInput(options);
|
|
8000
|
-
const target = normalizeShareTarget(options.target);
|
|
8001
|
-
if (target.kind === "bearer") throw new TypeError("addressed target is required");
|
|
8002
|
-
if (options.contentSource.kind !== "kv") throw new TypeError("addressed policy publication requires a KV content source");
|
|
8003
|
-
const contentSource = options.contentSource;
|
|
8004
|
-
const matcher = targetMatcher(target);
|
|
8005
|
-
const expiresAt = options.expiresAt.toISOString();
|
|
8006
|
-
const contentSourceDigest = options.contentSourceDigest ?? await sha2564(new TextEncoder().encode(canonicalize(contentSource)));
|
|
8007
|
-
const shareKey = await createDelegatedShareKey({ extractable: false });
|
|
8008
|
-
let envelopeKey;
|
|
8009
|
-
try {
|
|
8010
|
-
const permissions = [
|
|
8011
|
-
{
|
|
8012
|
-
service: "tinycloud.kv",
|
|
8013
|
-
path: options.resource.kind === "prefix" ? `${options.resource.path.replace(/\/+$/, "")}/` : options.resource.path,
|
|
8014
|
-
actions: [...options.policyActions]
|
|
8015
|
-
},
|
|
8016
|
-
...options.decryption === void 0 ? [] : [{
|
|
8017
|
-
service: "tinycloud.encryption",
|
|
8018
|
-
path: options.decryption.networkId,
|
|
8019
|
-
actions: [options.decryption.action]
|
|
8020
|
-
}]
|
|
8021
|
-
];
|
|
8022
|
-
const ownerDelegation = await options.authority.createOwnerDelegation({
|
|
8023
|
-
delegateDid: shareKey.did,
|
|
8024
|
-
spaceId: options.spaceId,
|
|
8025
|
-
permissions,
|
|
8026
|
-
expiresAt: options.expiresAt
|
|
8027
|
-
});
|
|
8028
|
-
const policy = {
|
|
8029
|
-
type: "TinyCloudSharePolicy",
|
|
8030
|
-
version: 2,
|
|
8031
|
-
shareId: options.shareId,
|
|
8032
|
-
ownerDid: options.authority.ownerDid,
|
|
8033
|
-
shareKeyDid: shareKey.did,
|
|
8034
|
-
recipientMatcher: matcher,
|
|
8035
|
-
target: {
|
|
8036
|
-
origin: options.nodeOrigin,
|
|
8037
|
-
nodeAudience: options.nodeAudience,
|
|
8038
|
-
enforcerDid: options.enforcerDid,
|
|
8039
|
-
spaceId: options.spaceId
|
|
8040
|
-
},
|
|
8041
|
-
resource: { ...options.resource },
|
|
8042
|
-
actions: [...options.policyActions],
|
|
8043
|
-
...options.decryption === void 0 ? {} : { decryption: options.decryption },
|
|
8044
|
-
contentSource,
|
|
8045
|
-
contentSourceDigest,
|
|
8046
|
-
ownerDelegationCid: ownerDelegation.delegationCid,
|
|
8047
|
-
expiresAt
|
|
8048
|
-
};
|
|
8049
|
-
const canonicalPolicy = await canonicalOwnerSharePolicy(policy);
|
|
8050
|
-
const policyProof = toBase64Url(await shareKey.sign(canonicalPolicy.bytes));
|
|
8051
|
-
const enforcementDelegation = await createPolicyEnforcementDelegation({
|
|
8052
|
-
ownerDelegation,
|
|
8053
|
-
shareKey,
|
|
8054
|
-
enforcerDid: options.enforcerDid,
|
|
8055
|
-
policyCid: canonicalPolicy.cid,
|
|
8056
|
-
shareId: options.shareId,
|
|
8057
|
-
spaceId: options.spaceId,
|
|
8058
|
-
nodeAudience: options.nodeAudience,
|
|
8059
|
-
path: options.resource.path,
|
|
8060
|
-
actions: options.policyActions,
|
|
8061
|
-
contentSourceDigest,
|
|
8062
|
-
expiresAt
|
|
8063
|
-
});
|
|
8064
|
-
const registration = await options.authority.registerOwnerSharePolicy({
|
|
8065
|
-
policy: { bytes: canonicalPolicy.bytes, cid: canonicalPolicy.cid, proof: policyProof },
|
|
8066
|
-
ownerDelegation,
|
|
8067
|
-
enforcementDelegation,
|
|
8068
|
-
contentSourceDigest
|
|
8069
|
-
});
|
|
8070
|
-
const authorityMaterialDigest = await sha2564(fromBase64Url(enforcementDelegation.dagCbor));
|
|
8071
|
-
const authorityTarget = {
|
|
8072
|
-
origin: options.nodeOrigin,
|
|
8073
|
-
nodeAudience: options.nodeAudience,
|
|
8074
|
-
enforcerDid: options.enforcerDid,
|
|
8075
|
-
spaceId: options.spaceId
|
|
8076
|
-
};
|
|
8077
|
-
const envelopeIdentity = {
|
|
8078
|
-
schema: "xyz.tinycloud.share/envelope/v2",
|
|
8079
|
-
version: 2,
|
|
8080
|
-
shareId: options.shareId,
|
|
8081
|
-
delegationCid: ownerDelegation.delegationCid,
|
|
8082
|
-
policyCid: canonicalPolicy.cid,
|
|
8083
|
-
target: authorityTarget,
|
|
8084
|
-
resource: options.resource,
|
|
8085
|
-
actions: options.policyActions,
|
|
8086
|
-
...options.decryption === void 0 ? {} : { decryption: options.decryption },
|
|
8087
|
-
contentSource,
|
|
8088
|
-
contentSourceDigest,
|
|
8089
|
-
expiresAt
|
|
8090
|
-
};
|
|
8091
|
-
const envelopeCid = await computeCid(new TextEncoder().encode(canonicalize(envelopeIdentity)));
|
|
8092
|
-
const shareCid = await computeCid(new TextEncoder().encode(canonicalize({ version: 2, shareId: options.shareId, policyCid: canonicalPolicy.cid, envelopeCid })));
|
|
8093
|
-
const outerUnsigned = { ...envelopeIdentity, envelopeCid, shareCid };
|
|
8094
|
-
const outerSignature = toBase64Url(await shareKey.sign(new TextEncoder().encode(`${ENVELOPE_DOMAIN}${canonicalize(outerUnsigned)}`)));
|
|
8095
|
-
const unsigned = {
|
|
8096
|
-
version: 2,
|
|
8097
|
-
shareId: options.shareId,
|
|
8098
|
-
recipientMatcher: matcher,
|
|
8099
|
-
...options.deliveryEmail === void 0 ? {} : { deliveryEmail: options.deliveryEmail },
|
|
8100
|
-
actions: [...options.actions],
|
|
8101
|
-
resource: { ...options.resource },
|
|
8102
|
-
target: { origin: options.nodeOrigin, nodeAudience: options.nodeAudience, spaceId: options.spaceId },
|
|
8103
|
-
delegationCid: ownerDelegation.delegationCid,
|
|
8104
|
-
authorityMaterialHandle: registration.registration.registrationCid,
|
|
8105
|
-
authorityMaterialDigest,
|
|
8106
|
-
contentSource,
|
|
8107
|
-
contentSourceDigest,
|
|
8108
|
-
authorizationTarget: { kind: "policy", policyCid: canonicalPolicy.cid, policyBytes: toBase64Url(canonicalPolicy.bytes) },
|
|
8109
|
-
display: { filename: options.filename },
|
|
8110
|
-
expiry: expiresAt,
|
|
8111
|
-
encrypted: true,
|
|
8112
|
-
metadata: {
|
|
8113
|
-
mediaType: options.mediaType,
|
|
8114
|
-
byteLength: options.byteLength,
|
|
8115
|
-
filename: options.filename,
|
|
8116
|
-
...options.mediaType.startsWith("text/") ? { encoding: "utf-8" } : {},
|
|
8117
|
-
...options.artifact === void 0 ? {} : { artifact: options.artifact }
|
|
8118
|
-
},
|
|
8119
|
-
ownerAuthority: {
|
|
8120
|
-
registrationCid: registration.registration.registrationCid,
|
|
8121
|
-
shareCid,
|
|
8122
|
-
envelopeCid,
|
|
8123
|
-
enforcementDelegation,
|
|
8124
|
-
registrationReceipt: registration,
|
|
8125
|
-
outerEnvelope: {
|
|
8126
|
-
...outerUnsigned,
|
|
8127
|
-
signature: { signerDid: shareKey.did, algorithm: "Ed25519", value: outerSignature }
|
|
8128
|
-
}
|
|
8129
|
-
}
|
|
8130
|
-
};
|
|
8131
|
-
unsignedShareEnvelopeV2Schema.parse(unsigned);
|
|
8132
|
-
const envelopeSignature = toBase64Url(await shareKey.sign(new TextEncoder().encode(`${ENVELOPE_DOMAIN}${canonicalize(unsigned)}`)));
|
|
8133
|
-
const envelope = { ...unsigned, signature: { signerDid: shareKey.did, algorithm: "Ed25519", value: envelopeSignature } };
|
|
8134
|
-
shareEnvelopeV2Schema.parse(envelope);
|
|
8135
|
-
envelopeKey = generateKey();
|
|
8136
|
-
const sealed = await seal(new TextEncoder().encode(canonicalize(envelope)), envelopeKey);
|
|
8137
|
-
let url;
|
|
8138
|
-
let retention = expiresAt;
|
|
8139
|
-
if (options.inline === true) {
|
|
8140
|
-
url = await encodeInlineShareUrl({ origin: options.shareOrigin, ciphertext: sealed.blob, key32: envelopeKey });
|
|
8141
|
-
} else {
|
|
8142
|
-
const uploaded = await uploadShareBlob({
|
|
8143
|
-
source: new Uint8Array([1]),
|
|
8144
|
-
filename: options.filename,
|
|
8145
|
-
origin: options.shareOrigin,
|
|
8146
|
-
...options.upload
|
|
8147
|
-
}, { blob: sealed.blob, cid: sealed.cid, deleteAfter: expiresAt, contentLength: sealed.blob.byteLength });
|
|
8148
|
-
retention = uploaded.deleteAfter;
|
|
8149
|
-
url = encodeShareUrl({ origin: options.shareOrigin, ciphertextCid: uploaded.cid, key32: envelopeKey });
|
|
8150
|
-
}
|
|
8151
|
-
return publicationResult({
|
|
8152
|
-
options,
|
|
8153
|
-
url,
|
|
8154
|
-
envelopeCid: sealed.cid,
|
|
8155
|
-
shareCid,
|
|
8156
|
-
matcher,
|
|
8157
|
-
registration: registration.registration,
|
|
8158
|
-
policyCid: canonicalPolicy.cid,
|
|
8159
|
-
enforcementDelegationCid: enforcementDelegation.cid,
|
|
8160
|
-
shareKeyDid: shareKey.did,
|
|
8161
|
-
retention
|
|
8162
|
-
});
|
|
8163
|
-
} finally {
|
|
8164
|
-
shareKey.clear();
|
|
8165
|
-
envelopeKey?.fill(0);
|
|
8166
|
-
}
|
|
8167
|
-
}
|
|
8168
|
-
|
|
8169
8150
|
// src/policy-authority.ts
|
|
8170
|
-
|
|
8171
|
-
var ENVELOPE_DOMAIN2 = "xyz.tinycloud.share/envelope/v2\0";
|
|
8151
|
+
var ENVELOPE_DOMAIN = "xyz.tinycloud.share/envelope/v2\0";
|
|
8172
8152
|
function record(value, keys, label) {
|
|
8173
8153
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
|
|
8174
8154
|
const output = value;
|
|
@@ -8249,7 +8229,7 @@ async function verifyOuterEnvelope(value, envelope, policyCid, expectedEnforcerD
|
|
|
8249
8229
|
const signatureBytes = fromBase64Url(requiredString(signature.value, "outer signature"));
|
|
8250
8230
|
if (signatureBytes.length !== 64 || !ed255196.verify(
|
|
8251
8231
|
signatureBytes,
|
|
8252
|
-
new TextEncoder().encode(`${
|
|
8232
|
+
new TextEncoder().encode(`${ENVELOPE_DOMAIN}${canonicalize(unsigned)}`),
|
|
8253
8233
|
ed25519PublicKeyFromDidKey(envelope.signature.signerDid),
|
|
8254
8234
|
{ zip215: false }
|
|
8255
8235
|
)) return void 0;
|
|
@@ -8522,6 +8502,7 @@ function historyRecordForPublishedShare(result, now = /* @__PURE__ */ new Date()
|
|
|
8522
8502
|
registeredAt: now.toISOString(),
|
|
8523
8503
|
expiresAt: result.metadata.expiresAt,
|
|
8524
8504
|
link: result.url,
|
|
8505
|
+
...result.deliveryMaterial === void 0 ? {} : { deliveryMaterial: result.deliveryMaterial },
|
|
8525
8506
|
...result.metadata.display.filename === void 0 ? {} : { filename: result.metadata.display.filename }
|
|
8526
8507
|
};
|
|
8527
8508
|
}
|
|
@@ -8706,11 +8687,11 @@ async function notifyShare(input) {
|
|
|
8706
8687
|
}
|
|
8707
8688
|
async function defaultIdempotencyKey(shareId, recipient) {
|
|
8708
8689
|
const canonicalRecipient = canonicalize(recipient.trim().toLowerCase());
|
|
8709
|
-
const
|
|
8690
|
+
const digest3 = new Uint8Array(await crypto.subtle.digest(
|
|
8710
8691
|
"SHA-256",
|
|
8711
8692
|
new TextEncoder().encode(canonicalRecipient)
|
|
8712
8693
|
));
|
|
8713
|
-
return `tinycloud-share:${shareId}:${toBase64Url(
|
|
8694
|
+
return `tinycloud-share:${shareId}:${toBase64Url(digest3)}`;
|
|
8714
8695
|
}
|
|
8715
8696
|
|
|
8716
8697
|
// src/lifecycle.ts
|
|
@@ -8858,12 +8839,6 @@ function fromBase64(value, label) {
|
|
|
8858
8839
|
if (toBase64(out) !== value) throw new Error(`${label} is invalid`);
|
|
8859
8840
|
return out;
|
|
8860
8841
|
}
|
|
8861
|
-
async function digest3(value) {
|
|
8862
|
-
return toBase64Url(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(canonicalize(value)))));
|
|
8863
|
-
}
|
|
8864
|
-
async function digestText2(value) {
|
|
8865
|
-
return toBase64Url(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value))));
|
|
8866
|
-
}
|
|
8867
8842
|
async function digestBytes2(value) {
|
|
8868
8843
|
return toBase64Url(new Uint8Array(await crypto.subtle.digest("SHA-256", value)));
|
|
8869
8844
|
}
|
|
@@ -8873,24 +8848,9 @@ function trustedPublicKey(value) {
|
|
|
8873
8848
|
}
|
|
8874
8849
|
return value.invitationPublicKey;
|
|
8875
8850
|
}
|
|
8876
|
-
async function verifyWrapped(value, key, domain, trust) {
|
|
8877
|
-
const wrapper = object2(value, `${key} response`);
|
|
8878
|
-
if (Object.keys(wrapper).length !== 2 || !Object.hasOwn(wrapper, key) || !Object.hasOwn(wrapper, "proof")) throw new Error(`${key} response is invalid`);
|
|
8879
|
-
const artifact = object2(wrapper[key], `${key} artifact`);
|
|
8880
|
-
const proof = object2(wrapper.proof, `${key} proof`);
|
|
8881
|
-
if (proof.alg !== "EdDSA" || proof.kid !== trust.invitationKid) throw new Error(`${key} proof is invalid`);
|
|
8882
|
-
if (!ed255198.verify(bytes2(proof.signature, `${key} signature`), new TextEncoder().encode(`${domain}${canonicalize(artifact)}`), trustedPublicKey(trust))) throw new Error(`${key} proof is invalid`);
|
|
8883
|
-
return artifact;
|
|
8884
|
-
}
|
|
8885
8851
|
function nativeAction(action) {
|
|
8886
8852
|
return action === "list" ? "tinycloud.kv/list" : action === "edit" ? "tinycloud.kv/put" : "tinycloud.kv/get";
|
|
8887
8853
|
}
|
|
8888
|
-
function uiAction(action) {
|
|
8889
|
-
return action === "tinycloud.kv/list" ? "list" : action === "tinycloud.kv/put" ? "edit" : "read";
|
|
8890
|
-
}
|
|
8891
|
-
function selectedAction(envelope) {
|
|
8892
|
-
return envelope.actions.includes("list") ? "tinycloud.kv/list" : envelope.actions.includes("edit") ? "tinycloud.kv/put" : "tinycloud.kv/get";
|
|
8893
|
-
}
|
|
8894
8854
|
function policyAttenuationForV3(envelope) {
|
|
8895
8855
|
const policy = object2(envelope.policy, "v3 policy");
|
|
8896
8856
|
if (!Array.isArray(policy.capabilityCeiling)) throw new Error("v3 policy capability ceiling is invalid");
|
|
@@ -8960,11 +8920,11 @@ function verifyV3PolicyAuthorization(input) {
|
|
|
8960
8920
|
if (!legacyShape && !v4Shape || POLICY_SESSION_DIGEST_FACT_KEYS.some((key) => !LOWER_SHA256_HEX.test(fact[key])) || v4Shape && (!LOWER_SHA256_HEX.test(fact.credentialIdAuditDigestHex) || !LOWER_SHA256_HEX.test(fact.presentationJtiAuditDigestHex)) || compact.payload.aud !== input.holderDid || compact.payload.iss.split("#", 1)[0] !== fact.nodeAudience || fact.profile !== "policy-session-ucan/v1" || fact.ownerDid !== policy.ownerDid || fact.policyId !== policy.policyId || fact.policyCid !== input.envelope.policyCid || fact.contentSourceDigestHex !== input.envelope.contentSourceDigestHex || fact.enforcerDid !== binding.enforcerDid || fact.nodeAudience !== binding.nodeAudience || fact.recipientDid !== input.holderDid || fact.policyDelegationCid !== input.envelope.policyRoot.cid || fact.enforcementDelegationCid !== input.envelope.enforcementRoot.cid || typeof fact.remainingRedelegationDepth !== "number" || !Number.isInteger(fact.remainingRedelegationDepth) || fact.remainingRedelegationDepth < 0 || fact.remainingRedelegationDepth > 8 || compact.payload.prf.length !== 2 || compact.payload.prf[0] !== input.envelope.policyRoot.cid || compact.payload.prf[1] !== input.envelope.enforcementRoot.cid || compact.payload.nbf > now || compact.payload.exp <= now || compact.payload.exp - compact.payload.nbf > 60 || canonicalize(compact.payload.att) !== canonicalize(policyAttenuationForV3(input.envelope))) throw new Error("v3 policy delegation signed binding mismatch");
|
|
8961
8921
|
return compact;
|
|
8962
8922
|
}
|
|
8963
|
-
function
|
|
8923
|
+
function hex3(value) {
|
|
8964
8924
|
return [...value].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
8965
8925
|
}
|
|
8966
8926
|
function canonicalHashHex(value) {
|
|
8967
|
-
return
|
|
8927
|
+
return hex3(sha2566(new TextEncoder().encode(canonicalize(value))));
|
|
8968
8928
|
}
|
|
8969
8929
|
async function aesGcmDecrypt(key, blob) {
|
|
8970
8930
|
if (key.length !== 32 || blob.length < 28) throw new Error("encrypted content is malformed");
|
|
@@ -8996,25 +8956,6 @@ function parseV3InlineEncryptedEnvelope(bytes3, expected) {
|
|
|
8996
8956
|
}
|
|
8997
8957
|
return record2;
|
|
8998
8958
|
}
|
|
8999
|
-
async function verifyDetachedResponse(response, trust) {
|
|
9000
|
-
let value;
|
|
9001
|
-
try {
|
|
9002
|
-
value = await response.clone().json();
|
|
9003
|
-
} catch {
|
|
9004
|
-
throw new Error("share read response is invalid");
|
|
9005
|
-
}
|
|
9006
|
-
const record2 = object2(value, "share read response");
|
|
9007
|
-
const proof = object2(record2.proof, "share read detached proof");
|
|
9008
|
-
if (proof.alg !== "EdDSA" || proof.kid !== trust.invitationKid) throw new Error("share read detached proof is invalid");
|
|
9009
|
-
const unsigned = { ...record2 };
|
|
9010
|
-
delete unsigned.proof;
|
|
9011
|
-
if (!ed255198.verify(bytes2(proof.signature, "share read signature"), new TextEncoder().encode(`${SHARE_V2_PROTOCOL.readResponseDomain}${canonicalize(unsigned)}`), trustedPublicKey(trust))) throw new Error("share read detached proof is invalid");
|
|
9012
|
-
}
|
|
9013
|
-
async function post(fetchFn, origin, path, body) {
|
|
9014
|
-
const response = await fetchFn(new URL(path, origin), { method: "POST", redirect: "error", headers: { accept: "application/json", "content-type": "application/json" }, body: JSON.stringify(body) });
|
|
9015
|
-
if (!response.ok) throw new Error("share authority rejected the request");
|
|
9016
|
-
return response.json();
|
|
9017
|
-
}
|
|
9018
8959
|
var ShareRecipientClient = class {
|
|
9019
8960
|
constructor(options) {
|
|
9020
8961
|
this.options = options;
|
|
@@ -9045,72 +8986,12 @@ var ShareRecipientClient = class {
|
|
|
9045
8986
|
v3ContentKey;
|
|
9046
8987
|
v3ContentEnvelope;
|
|
9047
8988
|
async beginChallenge(envelope) {
|
|
9048
|
-
|
|
9049
|
-
|
|
9050
|
-
const outer = object2(authority.outerEnvelope, "owner authority outer envelope");
|
|
9051
|
-
const enforcement = object2(authority.enforcementDelegation, "owner authority enforcement delegation");
|
|
9052
|
-
const target = object2(outer.target, "owner authority target");
|
|
9053
|
-
const outerResource = object2(outer.resource, "owner authority resource");
|
|
9054
|
-
const source = object2(outer.contentSource, "owner authority content source");
|
|
9055
|
-
if (!Array.isArray(outer.actions) || outer.actions.length === 0 || outer.actions.some((value) => typeof value !== "string")) throw new Error("owner authority outer envelope actions are invalid");
|
|
9056
|
-
const actions = [...outer.actions];
|
|
9057
|
-
const action = selectedAction(envelope);
|
|
9058
|
-
const challengeBody = { envelopeCid: authority.envelopeCid, shareCid: authority.shareCid, shareId: envelope.shareId, registrationCid: authority.registrationCid, delegationCid: envelope.delegationCid, policyCid: envelope.authorizationTarget.kind === "policy" ? envelope.authorizationTarget.policyCid : "", enforcementDelegationCid: String(enforcement.cid), enforcementDelegation: enforcement, outerEnvelope: outer, contentSource: source, contentSourceDigest: String(outer.contentSourceDigest), holderDid: this.options.holderDid, targetOrigin: String(target.origin), nodeAudience: String(target.nodeAudience), action, actions, resource: String(outerResource.path) };
|
|
9059
|
-
const requestBodyDigest = await digest3(challengeBody);
|
|
9060
|
-
const challenge = await verifyWrapped(await post(this.fetchFn, this.options.nodeOrigin, "/share/v2/policy/challenges", { ...challengeBody, requestBodyDigest }), "challenge", DOMAIN, this.options.trustedNode);
|
|
9061
|
-
if (challenge.type !== "TinyCloudSharePolicyChallenge" || challenge.version !== 2 || challenge.challengeId.length < 16 || challenge.nonce.length < 16 || challenge.shareCid !== authority.shareCid || challenge.shareId !== envelope.shareId || challenge.registrationCid !== authority.registrationCid || challenge.envelopeCid !== authority.envelopeCid || challenge.policyCid !== challengeBody.policyCid || challenge.delegationCid !== envelope.delegationCid || challenge.enforcementDelegationCid !== enforcement.cid || canonicalize(challenge.contentSource) !== canonicalize(source) || challenge.contentSourceDigest !== challengeBody.contentSourceDigest || challenge.requestBodyDigest !== requestBodyDigest || challenge.holderDid !== this.options.holderDid || challenge.targetOrigin !== challengeBody.targetOrigin || challenge.nodeAudience !== challengeBody.nodeAudience || challenge.action !== action || canonicalize(challenge.actions) !== canonicalize(actions) || challenge.resource !== challengeBody.resource || !Number.isFinite(Date.parse(challenge.expiresAt)) || Date.parse(challenge.expiresAt) <= Date.now()) throw new Error("share authority returned an unbound challenge");
|
|
9062
|
-
return challenge;
|
|
8989
|
+
void envelope;
|
|
8990
|
+
throw new Error("v2 Share recipient transport has been removed; use the embedded policy v3 envelope");
|
|
9063
8991
|
}
|
|
9064
8992
|
async establish(envelope) {
|
|
9065
|
-
|
|
9066
|
-
|
|
9067
|
-
const outer = object2(authority.outerEnvelope, "owner authority outer envelope");
|
|
9068
|
-
const enforcement = object2(authority.enforcementDelegation, "owner authority enforcement delegation");
|
|
9069
|
-
const target = object2(outer.target, "owner authority target");
|
|
9070
|
-
const outerResource = object2(outer.resource, "owner authority resource");
|
|
9071
|
-
const source = object2(outer.contentSource, "owner authority content source");
|
|
9072
|
-
if (!Array.isArray(outer.actions) || outer.actions.length === 0 || outer.actions.some((value) => typeof value !== "string")) throw new Error("owner authority outer envelope actions are invalid");
|
|
9073
|
-
const actions = [...outer.actions];
|
|
9074
|
-
const action = selectedAction(envelope);
|
|
9075
|
-
const challengeBody = { envelopeCid: authority.envelopeCid, shareCid: authority.shareCid, shareId: envelope.shareId, registrationCid: authority.registrationCid, delegationCid: envelope.delegationCid, policyCid: envelope.authorizationTarget.kind === "policy" ? envelope.authorizationTarget.policyCid : "", enforcementDelegationCid: String(enforcement.cid), enforcementDelegation: enforcement, outerEnvelope: outer, contentSource: source, contentSourceDigest: String(outer.contentSourceDigest), holderDid: this.options.holderDid, targetOrigin: String(target.origin), nodeAudience: String(target.nodeAudience), action, actions, resource: String(outerResource.path) };
|
|
9076
|
-
const requestBodyDigest = await digest3(challengeBody);
|
|
9077
|
-
const challenge = await verifyWrapped(await post(this.fetchFn, this.options.nodeOrigin, "/share/v2/policy/challenges", { ...challengeBody, requestBodyDigest }), "challenge", DOMAIN, this.options.trustedNode);
|
|
9078
|
-
if (challenge.type !== "TinyCloudSharePolicyChallenge" || challenge.version !== 2 || challenge.challengeId === void 0 || challenge.nonce === void 0 || challenge.shareCid !== authority.shareCid || challenge.shareId !== envelope.shareId || challenge.registrationCid !== authority.registrationCid || challenge.envelopeCid !== authority.envelopeCid || challenge.policyCid !== challengeBody.policyCid || challenge.delegationCid !== envelope.delegationCid || challenge.enforcementDelegationCid !== enforcement.cid || challenge.requestBodyDigest !== requestBodyDigest || canonicalize(challenge.contentSource) !== canonicalize(source) || challenge.contentSourceDigest !== challengeBody.contentSourceDigest || challenge.holderDid !== this.options.holderDid || challenge.targetOrigin !== challengeBody.targetOrigin || challenge.nodeAudience !== challengeBody.nodeAudience || challenge.action !== action || canonicalize(challenge.actions) !== canonicalize(actions) || challenge.resource !== challengeBody.resource || !Number.isFinite(Date.parse(challenge.expiresAt)) || Date.parse(challenge.expiresAt) <= Date.now()) throw new Error("share authority returned an unbound challenge");
|
|
9079
|
-
if (this.options.buildPresentation === void 0) throw new Error("share presentation builder is required");
|
|
9080
|
-
const material = await this.options.buildPresentation({ challenge, envelope, policy: {} });
|
|
9081
|
-
this.holderProof = material.proof;
|
|
9082
|
-
this.signer = material.sign;
|
|
9083
|
-
this.nativeSigner = material.sign;
|
|
9084
|
-
if (this.signer === void 0) throw new Error("share holder signer is required");
|
|
9085
|
-
const presentation = {
|
|
9086
|
-
type: "TinyCloudSharePolicyPresentation",
|
|
9087
|
-
version: 2,
|
|
9088
|
-
challengeId: challenge.challengeId,
|
|
9089
|
-
nonce: challenge.nonce,
|
|
9090
|
-
shareCid: authority.shareCid,
|
|
9091
|
-
shareId: envelope.shareId,
|
|
9092
|
-
delegationCid: envelope.delegationCid,
|
|
9093
|
-
policyCid: challengeBody.policyCid,
|
|
9094
|
-
contentSource: source,
|
|
9095
|
-
contentSourceDigest: challengeBody.contentSourceDigest,
|
|
9096
|
-
holderDid: material.holderDid,
|
|
9097
|
-
targetOrigin: challengeBody.targetOrigin,
|
|
9098
|
-
nodeAudience: challengeBody.nodeAudience,
|
|
9099
|
-
...challenge.enforcerDid === void 0 ? {} : { enforcerDid: challenge.enforcerDid },
|
|
9100
|
-
credentialDigest: material.credentialDigest ?? await digestText2(material.credential),
|
|
9101
|
-
action,
|
|
9102
|
-
actions,
|
|
9103
|
-
resource: challengeBody.resource,
|
|
9104
|
-
requestBodyDigest,
|
|
9105
|
-
issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9106
|
-
expiresAt: challenge.expiresAt,
|
|
9107
|
-
jti: toBase64Url(crypto.getRandomValues(new Uint8Array(16)))
|
|
9108
|
-
};
|
|
9109
|
-
const presentationProof = { alg: "EdDSA", kid: `${material.holderDid}#${material.holderDid.slice("did:key:".length)}`, signature: toBase64Url(await this.signer(new TextEncoder().encode(`${PRESENTATION_DOMAIN}${canonicalize(presentation)}`))) };
|
|
9110
|
-
const session = await verifyWrapped(await post(this.fetchFn, this.options.nodeOrigin, "/share/v2/policy/session", { challengeId: challenge.challengeId, nonce: challenge.nonce, presentation, credential: material.credential, proof: presentationProof, holderBinding: material.holderBinding, readSignerDid: material.holderDid }), "session", SESSION_DOMAIN, this.options.trustedNode);
|
|
9111
|
-
if (session.type !== "TinyCloudSharePolicySession" || session.version !== 2 || typeof session.sessionId !== "string" || session.shareCid !== authority.shareCid || session.shareId !== envelope.shareId || session.registrationCid !== authority.registrationCid || session.envelopeCid !== authority.envelopeCid || session.policyCid !== challengeBody.policyCid || session.delegationCid !== envelope.delegationCid || session.holderDid !== this.options.holderDid || session.targetOrigin !== challengeBody.targetOrigin || session.nodeAudience !== challengeBody.nodeAudience || session.action !== action || canonicalize(session.actions) !== canonicalize(actions) || canonicalize(session.contentSource) !== canonicalize(source) || session.contentSourceDigest !== challengeBody.contentSourceDigest || session.resource !== challengeBody.resource || typeof session.expiresAt !== "string" || !Number.isFinite(Date.parse(session.expiresAt)) || Date.parse(session.expiresAt) <= Date.now()) throw new Error("share authority returned an unbound session");
|
|
9112
|
-
this.session = { sessionId: session.sessionId, expiresAt: session.expiresAt, actions: actions.map(uiAction), resource: { kind: envelope.resource.kind, path: String(session.resource) } };
|
|
9113
|
-
return this.session;
|
|
8993
|
+
void envelope;
|
|
8994
|
+
throw new Error("v2 Share recipient transport has been removed; use the embedded policy v3 envelope");
|
|
9114
8995
|
}
|
|
9115
8996
|
async authorize(envelope) {
|
|
9116
8997
|
if (this.session === void 0) await this.establish(envelope);
|
|
@@ -9138,7 +9019,7 @@ var ShareRecipientClient = class {
|
|
|
9138
9019
|
} catch {
|
|
9139
9020
|
throw new Error("v3 attested Node audience is invalid");
|
|
9140
9021
|
}
|
|
9141
|
-
const challengeResponse = await this.fetchFn(new URL("/
|
|
9022
|
+
const challengeResponse = await this.fetchFn(new URL("/policy/v3/challenges", this.options.nodeOrigin), {
|
|
9142
9023
|
method: "POST",
|
|
9143
9024
|
redirect: "error",
|
|
9144
9025
|
headers: { accept: "application/json", "content-type": "application/json" },
|
|
@@ -9159,7 +9040,7 @@ var ShareRecipientClient = class {
|
|
|
9159
9040
|
throw new Error(accountless ? "v4 ceremony requires a verified credential and requirement" : "v3 ceremony requires a claim");
|
|
9160
9041
|
}
|
|
9161
9042
|
this.options.onStage?.("policy-admission");
|
|
9162
|
-
const delegationResponse = await this.fetchFn(new URL("/
|
|
9043
|
+
const delegationResponse = await this.fetchFn(new URL("/policy/v3/delegations", this.options.nodeOrigin), {
|
|
9163
9044
|
method: "POST",
|
|
9164
9045
|
redirect: "error",
|
|
9165
9046
|
headers: { accept: "application/json", "content-type": "application/json" },
|
|
@@ -9187,10 +9068,10 @@ var ShareRecipientClient = class {
|
|
|
9187
9068
|
if (envelope.version !== 3 || this.session === void 0 || this.v3Authorization === void 0 || this.v3NodeAudience === void 0 || signer === void 0) throw new Error("v3 policy session signer is required");
|
|
9188
9069
|
const encrypted = parseV3InlineEncryptedEnvelope(bytes3, envelope);
|
|
9189
9070
|
const receiverPrivateKey = crypto.getRandomValues(new Uint8Array(32));
|
|
9190
|
-
const receiverPublicKey =
|
|
9071
|
+
const receiverPublicKey = toBase64(x25519.getPublicKey(receiverPrivateKey));
|
|
9191
9072
|
const receiverPublicKeyHash = canonicalHashHex(receiverPublicKey);
|
|
9192
9073
|
const body = { type: "tinycloud.encryption.decrypt/v1", targetNode: this.v3NodeAudience, networkId: encrypted.networkId, alg: encrypted.alg, keyVersion: encrypted.keyVersion, encryptedSymmetricKey: encrypted.encryptedSymmetricKey, encryptedSymmetricKeyHash: encrypted.encryptedSymmetricKeyHash, receiverPublicKey, receiverPublicKeyHash };
|
|
9193
|
-
const bodyHash =
|
|
9074
|
+
const bodyHash = hex3(sha2566(new TextEncoder().encode(canonicalize(body))));
|
|
9194
9075
|
const session = verifyCompactUcanAuthorization(this.v3Authorization, this.session.sessionId);
|
|
9195
9076
|
const now = Math.floor(Date.now() / 1e3);
|
|
9196
9077
|
const invocation = await signCompactUcanAuthorization({
|
|
@@ -9210,18 +9091,18 @@ var ShareRecipientClient = class {
|
|
|
9210
9091
|
if (!response.ok) throw new Error(`v3 decrypt invocation rejected (${response.status})`);
|
|
9211
9092
|
const value = object2(await response.json(), "v3 decrypt response");
|
|
9212
9093
|
const allowed = ["type", "targetNode", "networkId", "invocationCid", "encryptedSymmetricKeyHash", "receiverPublicKeyHash", "wrappedKey", "alg", "keyVersion", "requestHash", "nodeId", "nodeSignature"];
|
|
9213
|
-
if (Object.keys(value).length !== allowed.length || Object.keys(value).some((key) => !allowed.includes(key)) || value.type !== "tinycloud.encryption.decrypt-result/v1" || value.targetNode !== body.targetNode || value.nodeId !== body.targetNode || value.networkId !== body.networkId || value.invocationCid !== invocation.cid || value.encryptedSymmetricKeyHash !== body.encryptedSymmetricKeyHash || value.receiverPublicKeyHash !== receiverPublicKeyHash || value.alg !== body.alg || value.keyVersion !== body.keyVersion || value.requestHash !==
|
|
9094
|
+
if (Object.keys(value).length !== allowed.length || Object.keys(value).some((key) => !allowed.includes(key)) || value.type !== "tinycloud.encryption.decrypt-result/v1" || value.targetNode !== body.targetNode || value.nodeId !== body.targetNode || value.networkId !== body.networkId || value.invocationCid !== invocation.cid || value.encryptedSymmetricKeyHash !== body.encryptedSymmetricKeyHash || value.receiverPublicKeyHash !== receiverPublicKeyHash || value.alg !== body.alg || value.keyVersion !== body.keyVersion || value.requestHash !== hex3(sha2566(new TextEncoder().encode(`${invocation.cid}${bodyHash}`))) || typeof value.wrappedKey !== "string" || typeof value.nodeSignature !== "string") throw new Error("v3 decrypt response binding is invalid");
|
|
9214
9095
|
const unsigned = { ...value };
|
|
9215
9096
|
delete unsigned.nodeSignature;
|
|
9216
9097
|
const signature = fromBase64(value.nodeSignature, "v3 decrypt response signature");
|
|
9217
9098
|
if (signature.length !== 64 || !ed255198.verify(signature, new TextEncoder().encode(canonicalize(unsigned)), ed25519PublicKeyFromDidKey(body.targetNode), { zip215: false })) throw new Error("v3 decrypt response signature is invalid");
|
|
9218
9099
|
const wrapped = fromBase64(value.wrappedKey, "v3 wrapped content key");
|
|
9219
|
-
if (wrapped.length <
|
|
9100
|
+
if (wrapped.length < 61 || wrapped[32] !== 1) throw new Error("v3 wrapped content key is malformed");
|
|
9220
9101
|
const shared = x25519.getSharedSecret(receiverPrivateKey, wrapped.slice(0, 32));
|
|
9221
|
-
const symmetricKey = await aesGcmDecrypt(shared, wrapped.slice(
|
|
9102
|
+
const symmetricKey = await aesGcmDecrypt(shared, wrapped.slice(33));
|
|
9222
9103
|
shared.fill(0);
|
|
9223
9104
|
if (symmetricKey.length !== 32) throw new Error("v3 content key is malformed");
|
|
9224
|
-
const plaintext = await aesGcmDecrypt(symmetricKey,
|
|
9105
|
+
const plaintext = await aesGcmDecrypt(symmetricKey, fromBase64(encrypted.ciphertext, "v3 encrypted content"));
|
|
9225
9106
|
this.v3ContentKey?.fill(0);
|
|
9226
9107
|
this.v3ContentKey = symmetricKey;
|
|
9227
9108
|
this.v3ContentEnvelope = encrypted;
|
|
@@ -9236,47 +9117,16 @@ var ShareRecipientClient = class {
|
|
|
9236
9117
|
return new TextEncoder().encode(canonicalize({ ...this.v3ContentEnvelope, ciphertext: toBase64Url(await aesGcmEncrypt(this.v3ContentKey, bytes3)), metadata: { ...this.v3ContentEnvelope.metadata ?? {}, contentType: mediaType } }));
|
|
9237
9118
|
}
|
|
9238
9119
|
async resumeWithProof(envelope, resumeToken, proof) {
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
const credential = material.credential;
|
|
9244
|
-
const holderDid = material.holderDid;
|
|
9245
|
-
const holderBinding = material.holderBinding;
|
|
9246
|
-
if (typeof nonce !== "string" || typeof credential !== "string" || typeof holderDid !== "string" || holderDid !== this.options.holderDid || typeof holderBinding !== "object" || holderBinding === null || presentationProof.alg !== "EdDSA" || typeof presentationProof.signature !== "string") throw new Error("share authorization proof is incomplete");
|
|
9247
|
-
const value = object2(await post(this.fetchFn, this.options.nodeOrigin, "/share/v2/policy/session", { challengeId: resumeToken, nonce, presentation, credential, proof: presentationProof, holderBinding, readSignerDid: holderDid }), "share policy session");
|
|
9248
|
-
const session = await verifyWrapped(value, "session", SESSION_DOMAIN, this.options.trustedNode);
|
|
9249
|
-
const authority = envelope.ownerAuthority;
|
|
9250
|
-
if (authority === void 0 || session.type !== "TinyCloudSharePolicySession" || session.version !== 2 || typeof session.sessionId !== "string" || session.shareCid !== authority.shareCid || session.shareId !== envelope.shareId || session.registrationCid !== authority.registrationCid || session.envelopeCid !== authority.envelopeCid || session.delegationCid !== envelope.delegationCid || typeof session.resource !== "string" || typeof session.expiresAt !== "string") throw new Error("share authority returned an unbound session");
|
|
9251
|
-
const policyCid = envelope.authorizationTarget.kind === "policy" ? envelope.authorizationTarget.policyCid : "";
|
|
9252
|
-
const actions = [...new Set(envelope.actions.map(nativeAction))].sort();
|
|
9253
|
-
if (session.policyCid !== policyCid || session.holderDid !== this.options.holderDid || session.targetOrigin !== envelope.target.origin || session.nodeAudience !== envelope.target.nodeAudience || session.action !== selectedAction(envelope) || canonicalize(session.actions) !== canonicalize(actions) || canonicalize(session.contentSource) !== canonicalize(envelope.contentSource) || session.contentSourceDigest !== envelope.contentSourceDigest || !Number.isFinite(Date.parse(session.expiresAt)) || Date.parse(session.expiresAt) <= Date.now()) throw new Error("share authority returned an unbound session");
|
|
9254
|
-
this.session = { sessionId: session.sessionId, expiresAt: session.expiresAt, actions: actions.map(uiAction), resource: { kind: envelope.resource.kind, path: String(session.resource) } };
|
|
9255
|
-
this.holderProof = presentationProof;
|
|
9256
|
-
this.signer ??= this.options.sign;
|
|
9257
|
-
return this.authorize(envelope);
|
|
9120
|
+
void envelope;
|
|
9121
|
+
void resumeToken;
|
|
9122
|
+
void proof;
|
|
9123
|
+
throw new Error("v2 Share recipient transport has been removed; use the embedded policy v3 envelope");
|
|
9258
9124
|
}
|
|
9259
9125
|
async nativeInvoke(request) {
|
|
9260
9126
|
if (this.session === void 0) throw new Error("share policy session is required");
|
|
9261
9127
|
const envelope = this.options.envelope;
|
|
9262
9128
|
if (envelope.version === 3) return this.nativeInvokeV3(request, envelope);
|
|
9263
|
-
|
|
9264
|
-
const authority = envelope.ownerAuthority;
|
|
9265
|
-
const action = request.action === "list" ? "tinycloud.kv/list" : request.action === "put" ? "tinycloud.kv/put" : request.action === "metadata" ? "tinycloud.kv/metadata" : "tinycloud.kv/get";
|
|
9266
|
-
const resource = typeof request.resource?.path === "string" ? request.resource.path : this.session.resource.path;
|
|
9267
|
-
const actions = [...new Set(this.session.actions.map(nativeAction).concat(action === "tinycloud.kv/metadata" ? [action] : []))].sort();
|
|
9268
|
-
const bodyBytes = request.body === void 0 ? void 0 : Uint8Array.from(request.body);
|
|
9269
|
-
const bodyDigest = bodyBytes === void 0 ? void 0 : toBase64Url(new Uint8Array(await crypto.subtle.digest("SHA-256", bodyBytes)));
|
|
9270
|
-
const outer = object2(authority.outerEnvelope, "owner authority outer envelope");
|
|
9271
|
-
const enforcement = object2(authority.enforcementDelegation, "owner authority enforcement delegation");
|
|
9272
|
-
const invocationBase = { type: "TinyCloudShareReadInvocation", version: 2, sessionId: this.session.sessionId, envelopeCid: authority.envelopeCid, shareCid: authority.shareCid, shareId: envelope.shareId, registrationCid: authority.registrationCid, delegationCid: envelope.delegationCid, policyCid: envelope.authorizationTarget.kind === "policy" ? envelope.authorizationTarget.policyCid : "", enforcementDelegationCid: String(enforcement.cid), contentSource: outer.contentSource, contentSourceDigest: String(outer.contentSourceDigest), holderDid: this.options.holderDid, targetOrigin: String(object2(outer.target, "owner authority target").origin), nodeAudience: String(object2(outer.target, "owner authority target").nodeAudience), action, actions, resource, ...action === "tinycloud.kv/list" ? { limit: 100 } : {}, ...bodyDigest === void 0 ? {} : { bodyDigest, ifMatch: request.ifMatch, contentType: request.contentType }, issuedAt: (/* @__PURE__ */ new Date()).toISOString(), expiresAt: new Date(Math.min(Date.now() + 6e4, Date.parse(this.session.expiresAt))).toISOString(), jti: toBase64Url(crypto.getRandomValues(new Uint8Array(16))) };
|
|
9273
|
-
const requestBodyDigest = await digest3(invocationBase);
|
|
9274
|
-
const invocation = { ...invocationBase, requestBodyDigest };
|
|
9275
|
-
const proof = { ...this.holderProof, signature: toBase64Url(await this.signer(new TextEncoder().encode(`${INVOCATION_DOMAIN}${canonicalize(invocation)}`))) };
|
|
9276
|
-
const signedRequest = { sessionId: this.session.sessionId, envelopeCid: authority.envelopeCid, shareCid: authority.shareCid, shareId: envelope.shareId, registrationCid: authority.registrationCid, delegationCid: envelope.delegationCid, policyCid: envelope.authorizationTarget.kind === "policy" ? envelope.authorizationTarget.policyCid : "", enforcementDelegationCid: String(enforcement.cid), contentSource: outer.contentSource, contentSourceDigest: String(outer.contentSourceDigest), holderDid: this.options.holderDid, nodeAudience: String(object2(outer.target, "owner authority target").nodeAudience), action, actions, resource, requestBodyDigest, invocation, proof };
|
|
9277
|
-
const response = await this.fetchFn(new URL("/share/v2/invoke", this.options.nodeOrigin), { method: "POST", redirect: "error", headers: { accept: "application/vnd.tinycloud.share+json", "content-type": "application/vnd.tinycloud.share+json" }, body: JSON.stringify({ request: signedRequest, ...action === "tinycloud.kv/list" ? { limit: 100 } : {}, ...bodyBytes === void 0 ? {} : { body: toBase64Url(bodyBytes), bodyDigest, ifMatch: request.ifMatch, contentType: request.contentType } }) });
|
|
9278
|
-
if (response.ok) await verifyDetachedResponse(response, this.options.trustedNode);
|
|
9279
|
-
return response;
|
|
9129
|
+
throw new Error("v2 Share invocation transport has been removed; use the embedded policy v3 envelope");
|
|
9280
9130
|
}
|
|
9281
9131
|
async nativeInvokeV3(request, envelope) {
|
|
9282
9132
|
const signer = this.nativeSigner ?? this.signer;
|