@tinycloud/share-sdk 1.4.2-beta.2 → 1.4.3-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -0
- package/dist/addressed-publish-BPFFqD5s.d.cts +380 -0
- package/dist/addressed-publish-DorqWQJ8.d.ts +380 -0
- package/dist/index.cjs +265 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +254 -26
- package/dist/index.js.map +1 -1
- package/dist/share-root.d.cts +1 -1
- package/dist/share-root.d.ts +1 -1
- package/package.json +2 -2
- package/dist/addressed-publish-BcvisG4a.d.cts +0 -230
- package/dist/addressed-publish-CwTTn9Jn.d.ts +0 -230
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
ADDRESSED_SHARE_TOO_LARGE_MESSAGE: () => ADDRESSED_SHARE_TOO_LARGE_MESSAGE,
|
|
34
|
+
AddressedContentSourceError: () => AddressedContentSourceError,
|
|
34
35
|
AddressedShareOverLimitError: () => AddressedShareOverLimitError,
|
|
35
36
|
ArtifactPinError: () => ArtifactPinError,
|
|
36
37
|
CredentialInvitationError: () => CredentialInvitationError,
|
|
@@ -59,8 +60,10 @@ __export(src_exports, {
|
|
|
59
60
|
artifactBindingView: () => artifactBindingView,
|
|
60
61
|
assertAddressedShareFits: () => assertAddressedShareFits,
|
|
61
62
|
assertArtifactBinding: () => assertArtifactBinding,
|
|
63
|
+
assertPathBoundContentSource: () => assertPathBoundContentSource,
|
|
62
64
|
authorizationMethodForTarget: () => authorizationMethodForTarget,
|
|
63
65
|
authorizeShare: () => authorizeShare,
|
|
66
|
+
bindPathBoundContentSource: () => bindPathBoundContentSource,
|
|
64
67
|
canonicalOwnerSharePolicy: () => canonicalOwnerSharePolicy,
|
|
65
68
|
canonicalShareFilename: () => canonicalShareFilename,
|
|
66
69
|
canonicalize: () => canonicalize,
|
|
@@ -72,7 +75,9 @@ __export(src_exports, {
|
|
|
72
75
|
createNativeShare: () => createNativeShare,
|
|
73
76
|
createPolicyEnforcementDelegation: () => createPolicyEnforcementDelegation,
|
|
74
77
|
createShareV2HolderBindingArtifact: () => createShareV2HolderBindingArtifact,
|
|
78
|
+
defaultEncryptionNetworkForOwner: () => defaultEncryptionNetworkForOwner,
|
|
75
79
|
deliverCredentialInvitation: () => deliverCredentialInvitation,
|
|
80
|
+
encryptAddressedShareContent: () => encryptAddressedShareContent,
|
|
76
81
|
fromBase64Url: () => fromBase64Url,
|
|
77
82
|
hasUnsafeFilenameCodePoint: () => hasUnsafeFilenameCodePoint,
|
|
78
83
|
historyRecordForPublishedShare: () => historyRecordForPublishedShare,
|
|
@@ -83,6 +88,10 @@ __export(src_exports, {
|
|
|
83
88
|
openNativeShare: () => openNativeShare,
|
|
84
89
|
parseAddressedEnvelope: () => parseAddressedEnvelope,
|
|
85
90
|
parseNativeShareUrl: () => parseNativeShareUrl,
|
|
91
|
+
pathBoundKeyPath: () => pathBoundKeyPath,
|
|
92
|
+
pathBoundSessionPermissions: () => pathBoundSessionPermissions,
|
|
93
|
+
pathBoundStoredBytes: () => pathBoundStoredBytes,
|
|
94
|
+
planPathBoundShare: () => planPathBoundShare,
|
|
86
95
|
prepareAddressedShare: () => prepareAddressedShare,
|
|
87
96
|
publishAddressedShare: () => publishAddressedShare,
|
|
88
97
|
publishPolicyShare: () => publishPolicyShare,
|
|
@@ -102,7 +111,8 @@ __export(src_exports, {
|
|
|
102
111
|
toShareErrorInfo: () => toShareErrorInfo,
|
|
103
112
|
validateOwnerSharePolicyRegistration: () => validateOwnerSharePolicyRegistration,
|
|
104
113
|
validateOwnerSharePolicyRegistrationBytes: () => validateOwnerSharePolicyRegistrationBytes,
|
|
105
|
-
verifyArtifactShare: () => verifyArtifactShare
|
|
114
|
+
verifyArtifactShare: () => verifyArtifactShare,
|
|
115
|
+
wrappedKeyDigestHex: () => wrappedKeyDigestHex
|
|
106
116
|
});
|
|
107
117
|
module.exports = __toCommonJS(src_exports);
|
|
108
118
|
|
|
@@ -6521,7 +6531,220 @@ async function parseAddressedEnvelope(value) {
|
|
|
6521
6531
|
}
|
|
6522
6532
|
|
|
6523
6533
|
// src/addressed-publish.ts
|
|
6534
|
+
var import_sha2563 = require("@noble/hashes/sha256");
|
|
6535
|
+
|
|
6536
|
+
// src/addressed-content.ts
|
|
6524
6537
|
var import_sha2562 = require("@noble/hashes/sha256");
|
|
6538
|
+
var import_sha3 = require("@noble/hashes/sha3");
|
|
6539
|
+
var AddressedContentSourceError = class extends TypeError {
|
|
6540
|
+
code;
|
|
6541
|
+
constructor(code3, message) {
|
|
6542
|
+
super(message);
|
|
6543
|
+
this.name = "AddressedContentSourceError";
|
|
6544
|
+
this.code = code3;
|
|
6545
|
+
}
|
|
6546
|
+
};
|
|
6547
|
+
var SHARE_ID = /^[a-z0-9][a-z0-9_-]{15,63}$/;
|
|
6548
|
+
var NETWORK = /^urn:tinycloud:encryption:(.+):default$/;
|
|
6549
|
+
var OWNER_PKH = /^did:pkh:eip155:([1-9][0-9]*):(0x[0-9a-fA-F]{40})$/;
|
|
6550
|
+
var OWNER_KEY = /^did:key:[A-Za-z0-9]+$/;
|
|
6551
|
+
var ENVELOPE_FIELDS = ["v", "networkId", "keyPath", "alg", "keyVersion", "encryptedSymmetricKey", "encryptedSymmetricKeyHash", "ciphertext", "aad", "metadata"];
|
|
6552
|
+
var ENVELOPE_ALG = "x25519-aes256gcm/v1";
|
|
6553
|
+
var HEX_DIGEST = /^[0-9a-f]{64}$/;
|
|
6554
|
+
var BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
6555
|
+
var textEncoder = new TextEncoder();
|
|
6556
|
+
function hex2(bytes3) {
|
|
6557
|
+
return [...bytes3].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
6558
|
+
}
|
|
6559
|
+
function checksumAddress(address) {
|
|
6560
|
+
const lower = address.toLowerCase().slice(2);
|
|
6561
|
+
const hash = (0, import_sha3.keccak_256)(textEncoder.encode(lower));
|
|
6562
|
+
let out = "0x";
|
|
6563
|
+
for (let index = 0; index < lower.length; index++) {
|
|
6564
|
+
const nibble = hash[index >> 1] >> (index % 2 === 0 ? 4 : 0) & 15;
|
|
6565
|
+
out += nibble >= 8 ? lower[index].toUpperCase() : lower[index];
|
|
6566
|
+
}
|
|
6567
|
+
return out;
|
|
6568
|
+
}
|
|
6569
|
+
function pathBoundKeyPath(shareId) {
|
|
6570
|
+
if (typeof shareId !== "string" || !SHARE_ID.test(shareId)) {
|
|
6571
|
+
throw new AddressedContentSourceError("share-id-invalid", "addressed share id is invalid: 16 to 64 characters from lowercase letters, digits, '_' and '-', starting with a letter or digit (a lowercase UUID, hyphens included, is valid)");
|
|
6572
|
+
}
|
|
6573
|
+
return `share/${shareId}`;
|
|
6574
|
+
}
|
|
6575
|
+
function defaultEncryptionNetworkForOwner(owner) {
|
|
6576
|
+
let did = owner;
|
|
6577
|
+
const address = typeof owner === "string" ? /^0x[0-9a-fA-F]{40}$/.test(owner) ? owner : void 0 : owner?.address;
|
|
6578
|
+
if (address !== void 0) {
|
|
6579
|
+
const chainId = typeof owner === "string" ? 1 : owner.chainId ?? 1;
|
|
6580
|
+
if (typeof address !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(address) || !Number.isSafeInteger(chainId) || chainId < 1) {
|
|
6581
|
+
throw new AddressedContentSourceError("network-invalid", "owner address is not an Ethereum address");
|
|
6582
|
+
}
|
|
6583
|
+
did = `did:pkh:eip155:${chainId}:${address}`;
|
|
6584
|
+
}
|
|
6585
|
+
const pkh = typeof did === "string" ? OWNER_PKH.exec(did) : null;
|
|
6586
|
+
const normalized = pkh === null ? did : `did:pkh:eip155:${pkh[1]}:${checksumAddress(pkh[2])}`;
|
|
6587
|
+
const network = `urn:tinycloud:encryption:${String(normalized)}:default`;
|
|
6588
|
+
if (typeof normalized !== "string" || !(pkh !== null || OWNER_KEY.test(normalized))) {
|
|
6589
|
+
throw new AddressedContentSourceError("network-invalid", "owner does not name an encryption network");
|
|
6590
|
+
}
|
|
6591
|
+
return network;
|
|
6592
|
+
}
|
|
6593
|
+
function assertEncryptionNetwork(network) {
|
|
6594
|
+
const match = typeof network === "string" ? NETWORK.exec(network) : null;
|
|
6595
|
+
if (match === null) {
|
|
6596
|
+
throw new AddressedContentSourceError("network-invalid", "encryption network must be the owner's default network: urn:tinycloud:encryption:<did>:default");
|
|
6597
|
+
}
|
|
6598
|
+
const owner = match[1];
|
|
6599
|
+
const pkh = OWNER_PKH.exec(owner);
|
|
6600
|
+
if (pkh !== null) {
|
|
6601
|
+
if (pkh[2] !== checksumAddress(pkh[2])) {
|
|
6602
|
+
throw new AddressedContentSourceError("network-not-checksummed", "encryption network address must be EIP-55 checksummed; use defaultEncryptionNetworkForOwner(ownerDid)");
|
|
6603
|
+
}
|
|
6604
|
+
} else if (!OWNER_KEY.test(owner)) {
|
|
6605
|
+
throw new AddressedContentSourceError("network-invalid", "encryption network owner must be a did:pkh:eip155 address or a did:key");
|
|
6606
|
+
}
|
|
6607
|
+
return network;
|
|
6608
|
+
}
|
|
6609
|
+
var assertNetwork = assertEncryptionNetwork;
|
|
6610
|
+
function decodeWrappedKey(encryptedSymmetricKey) {
|
|
6611
|
+
if (typeof encryptedSymmetricKey !== "string" || !BASE64.test(encryptedSymmetricKey)) {
|
|
6612
|
+
throw new AddressedContentSourceError("envelope-invalid", "wrapped content key is not strict base64");
|
|
6613
|
+
}
|
|
6614
|
+
try {
|
|
6615
|
+
return fromBase64Url(encryptedSymmetricKey.replace(/=+$/, "").replace(/\+/g, "-").replace(/\//g, "_"));
|
|
6616
|
+
} catch {
|
|
6617
|
+
throw new AddressedContentSourceError("envelope-invalid", "wrapped content key is not strict base64");
|
|
6618
|
+
}
|
|
6619
|
+
}
|
|
6620
|
+
function decodeBase64(value) {
|
|
6621
|
+
try {
|
|
6622
|
+
return fromBase64Url(value.replace(/=+$/, "").replace(/\+/g, "-").replace(/\//g, "_"));
|
|
6623
|
+
} catch {
|
|
6624
|
+
throw new AddressedContentSourceError("envelope-invalid", "envelope field is not strict base64");
|
|
6625
|
+
}
|
|
6626
|
+
}
|
|
6627
|
+
function wrappedKeyDigestHex(encryptedSymmetricKey) {
|
|
6628
|
+
return hex2((0, import_sha2562.sha256)(decodeWrappedKey(encryptedSymmetricKey)));
|
|
6629
|
+
}
|
|
6630
|
+
function canonicalKeyHashHex(encryptedSymmetricKey) {
|
|
6631
|
+
return hex2((0, import_sha2562.sha256)(textEncoder.encode(canonicalize(encryptedSymmetricKey))));
|
|
6632
|
+
}
|
|
6633
|
+
function pathBoundSessionPermissions(input) {
|
|
6634
|
+
const encryptionNetwork = assertNetwork(input.encryptionNetwork);
|
|
6635
|
+
return [{ service: "tinycloud.encryption", path: `${encryptionNetwork}/share/`, actions: ["tinycloud.encryption/decrypt", "tinycloud.encryption/key.create"] }];
|
|
6636
|
+
}
|
|
6637
|
+
function planPathBoundShare(input) {
|
|
6638
|
+
const keyPath = pathBoundKeyPath(input.shareId);
|
|
6639
|
+
const encryptionNetwork = assertNetwork(input.encryptionNetwork);
|
|
6640
|
+
const keyResource = `${encryptionNetwork}/${keyPath}`;
|
|
6641
|
+
return {
|
|
6642
|
+
shareId: input.shareId,
|
|
6643
|
+
encryptionNetwork,
|
|
6644
|
+
keyPath,
|
|
6645
|
+
keyResource,
|
|
6646
|
+
requiredPermissions: [{ service: "tinycloud.encryption", path: keyResource, actions: ["tinycloud.encryption/key.create"] }],
|
|
6647
|
+
sessionPermissions: pathBoundSessionPermissions({ encryptionNetwork }),
|
|
6648
|
+
registrationPermissions: [{ service: "tinycloud.encryption", path: `${encryptionNetwork}/share/`, actions: ["tinycloud.encryption/decrypt"] }],
|
|
6649
|
+
encryptOptions: (extra) => ({ networkId: encryptionNetwork, ...extra?.metadata === void 0 ? {} : { metadata: { ...extra.metadata } } })
|
|
6650
|
+
};
|
|
6651
|
+
}
|
|
6652
|
+
function pathBoundStoredBytes(envelope) {
|
|
6653
|
+
return textEncoder.encode(canonicalize(envelope));
|
|
6654
|
+
}
|
|
6655
|
+
function assertEnvelope(plan, envelope) {
|
|
6656
|
+
if (envelope === null || typeof envelope !== "object" || envelope.v !== 2) {
|
|
6657
|
+
throw new AddressedContentSourceError("envelope-invalid", "path-bound content must be a v2 named-key envelope (encrypt with keys.encrypt, not encryptToNetwork)");
|
|
6658
|
+
}
|
|
6659
|
+
if (envelope.networkId !== plan.encryptionNetwork || envelope.keyPath !== plan.keyPath) {
|
|
6660
|
+
throw new AddressedContentSourceError("key-path-mismatch", "envelope network or key path does not match the share's plan");
|
|
6661
|
+
}
|
|
6662
|
+
if (!Number.isSafeInteger(envelope.keyVersion) || envelope.keyVersion < 1) {
|
|
6663
|
+
throw new AddressedContentSourceError("envelope-invalid", "envelope key version is invalid");
|
|
6664
|
+
}
|
|
6665
|
+
wrappedKeyDigestHex(envelope.encryptedSymmetricKey);
|
|
6666
|
+
if (envelope.encryptedSymmetricKeyHash !== canonicalKeyHashHex(envelope.encryptedSymmetricKey)) {
|
|
6667
|
+
throw new AddressedContentSourceError("envelope-invalid", "envelope key hash does not match its wrapped key");
|
|
6668
|
+
}
|
|
6669
|
+
const record = envelope;
|
|
6670
|
+
if (Object.keys(record).some((key) => !ENVELOPE_FIELDS.includes(key)) || record.alg !== ENVELOPE_ALG) {
|
|
6671
|
+
throw new AddressedContentSourceError("envelope-invalid", "envelope has a field or algorithm the recipient does not accept");
|
|
6672
|
+
}
|
|
6673
|
+
if (typeof record.ciphertext !== "string" || !BASE64.test(record.ciphertext) || decodeBase64(record.ciphertext).length < 28) {
|
|
6674
|
+
throw new AddressedContentSourceError("envelope-invalid", "envelope ciphertext is not strict base64 of at least 28 bytes");
|
|
6675
|
+
}
|
|
6676
|
+
if (record.aad !== void 0 && (typeof record.aad !== "string" || !BASE64.test(record.aad))) {
|
|
6677
|
+
throw new AddressedContentSourceError("envelope-invalid", "envelope aad is not strict base64");
|
|
6678
|
+
}
|
|
6679
|
+
if (record.metadata !== void 0 && (record.metadata === null || typeof record.metadata !== "object" || Array.isArray(record.metadata) || Object.values(record.metadata).some((entry) => typeof entry !== "string"))) {
|
|
6680
|
+
throw new AddressedContentSourceError("envelope-invalid", "envelope metadata must be an object of strings");
|
|
6681
|
+
}
|
|
6682
|
+
}
|
|
6683
|
+
function bindPathBoundContentSource(input) {
|
|
6684
|
+
const { plan, envelope } = input;
|
|
6685
|
+
assertEnvelope(plan, envelope);
|
|
6686
|
+
const expected = pathBoundStoredBytes(envelope);
|
|
6687
|
+
if (input.storedBytes.length !== expected.length || input.storedBytes.some((byte, index) => byte !== expected[index])) {
|
|
6688
|
+
throw new AddressedContentSourceError("stored-bytes-mismatch", "uploaded bytes are not the canonical v2 envelope; upload pathBoundStoredBytes(envelope)");
|
|
6689
|
+
}
|
|
6690
|
+
return {
|
|
6691
|
+
shareId: plan.shareId,
|
|
6692
|
+
kvResource: input.kvResource,
|
|
6693
|
+
selector: input.selector,
|
|
6694
|
+
encryptionNetwork: plan.encryptionNetwork,
|
|
6695
|
+
encryptionKeyPath: plan.keyPath,
|
|
6696
|
+
encryptedSymmetricKeyDigestHex: wrappedKeyDigestHex(envelope.encryptedSymmetricKey),
|
|
6697
|
+
keyVersion: envelope.keyVersion,
|
|
6698
|
+
mode: "immutable",
|
|
6699
|
+
initialCiphertextDigestHex: hex2((0, import_sha2562.sha256)(expected))
|
|
6700
|
+
};
|
|
6701
|
+
}
|
|
6702
|
+
function isUnauthorized(error) {
|
|
6703
|
+
const value = error;
|
|
6704
|
+
return value !== null && typeof value === "object" && (value.status === 401 || value.status === 403 || value.code === "PERMISSION_DENIED" || value.code === "AUTH_UNAUTHORIZED");
|
|
6705
|
+
}
|
|
6706
|
+
async function encryptAddressedShareContent(input) {
|
|
6707
|
+
const { plan, keys } = input;
|
|
6708
|
+
const byteLength = input.plaintext.byteLength;
|
|
6709
|
+
let receipt;
|
|
6710
|
+
try {
|
|
6711
|
+
receipt = await keys.createKey(plan.keyPath, { networkId: plan.encryptionNetwork });
|
|
6712
|
+
} catch (error) {
|
|
6713
|
+
if (isUnauthorized(error)) {
|
|
6714
|
+
const status = error.status;
|
|
6715
|
+
throw new AddressedContentSourceError(
|
|
6716
|
+
"key-not-authorized",
|
|
6717
|
+
`the owner session does not hold tinycloud.encryption/key.create on ${plan.keyResource} (the node answers "Unauthorized Action: ${plan.keyResource} / tinycloud.encryption/key.create"); hold it either at sign-in (put plan.sessionPermissions in the manifest: key.create on ${plan.encryptionNetwork}/share/) or per share (requestPermissions(plan.requiredPermissions)) before creating the key` + (status === 403 ? "; if the owner signs in through OpenKey, the request must come from a first-party TinyCloud origin" : "")
|
|
6718
|
+
);
|
|
6719
|
+
}
|
|
6720
|
+
throw error;
|
|
6721
|
+
}
|
|
6722
|
+
if (receipt.networkId !== plan.encryptionNetwork || receipt.keyPath !== plan.keyPath || receipt.state !== void 0 && receipt.state !== "active") {
|
|
6723
|
+
throw new AddressedContentSourceError("key-receipt-mismatch", "the created key does not match this share");
|
|
6724
|
+
}
|
|
6725
|
+
const envelope = await keys.encrypt(plan.keyPath, input.plaintext, plan.encryptOptions(input.metadata === void 0 ? void 0 : { metadata: input.metadata }));
|
|
6726
|
+
assertEnvelope(plan, envelope);
|
|
6727
|
+
if (receipt.keyVersion !== void 0 && envelope.keyVersion !== receipt.keyVersion) {
|
|
6728
|
+
throw new AddressedContentSourceError("key-receipt-mismatch", "the envelope key version does not match the created key");
|
|
6729
|
+
}
|
|
6730
|
+
const storedBytes = pathBoundStoredBytes(envelope);
|
|
6731
|
+
return { envelope, storedBytes, byteLength };
|
|
6732
|
+
}
|
|
6733
|
+
function assertPathBoundContentSource(source, shareId) {
|
|
6734
|
+
if (source === void 0 || source === null || typeof source !== "object") {
|
|
6735
|
+
throw new AddressedContentSourceError("key-path-required", "addressed content source is missing");
|
|
6736
|
+
}
|
|
6737
|
+
if (source.encryptionKeyPath === void 0) {
|
|
6738
|
+
throw new AddressedContentSourceError("key-path-required", "addressed content source must name its key (encryptionKeyPath: share/<shareId>); a network-wide source is not accepted");
|
|
6739
|
+
}
|
|
6740
|
+
if (source.encryptionKeyPath !== pathBoundKeyPath(shareId)) {
|
|
6741
|
+
throw new AddressedContentSourceError("key-path-mismatch", "addressed content source key path must be share/<shareId> of this share");
|
|
6742
|
+
}
|
|
6743
|
+
if (typeof source.encryptedSymmetricKeyDigestHex !== "string" || !HEX_DIGEST.test(source.encryptedSymmetricKeyDigestHex)) {
|
|
6744
|
+
throw new AddressedContentSourceError("digest-invalid", "addressed content source needs encryptedSymmetricKeyDigestHex: 64 lowercase hex characters");
|
|
6745
|
+
}
|
|
6746
|
+
assertEncryptionNetwork(source.encryptionNetwork);
|
|
6747
|
+
}
|
|
6525
6748
|
|
|
6526
6749
|
// src/authorization.ts
|
|
6527
6750
|
async function authorizeShare(input) {
|
|
@@ -7019,7 +7242,7 @@ var POLICY_CAPABILITY_V1_DOMAIN2 = "xyz.tinycloud.policy/PolicyCapability/v1\0";
|
|
|
7019
7242
|
var CONTENT_SOURCE_V1_DOMAIN2 = "xyz.tinycloud.policy/ContentSource/v1\0";
|
|
7020
7243
|
var NATIVE_PROJECTION_V1_DOMAIN2 = "xyz.tinycloud.policy/NativeProjection/v1\0";
|
|
7021
7244
|
var ENVELOPE_V3_DOMAIN = "xyz.tinycloud.share/envelope/v3\0";
|
|
7022
|
-
var
|
|
7245
|
+
var textEncoder2 = new TextEncoder();
|
|
7023
7246
|
function targetMatcher(target) {
|
|
7024
7247
|
if (target.kind === "recipientDid") return { kind: "recipientDid", value: target.did };
|
|
7025
7248
|
if (target.kind === "email") return { kind: "exactEmail", value: target.address };
|
|
@@ -7030,17 +7253,18 @@ function targetKind(target) {
|
|
|
7030
7253
|
}
|
|
7031
7254
|
var OWNER_SHARE_ACTIONS = /* @__PURE__ */ new Set(["tinycloud.kv/get", "tinycloud.kv/list", "tinycloud.kv/metadata", "tinycloud.kv/put"]);
|
|
7032
7255
|
function assertSafeInput(input) {
|
|
7033
|
-
|
|
7256
|
+
pathBoundKeyPath(input.shareId);
|
|
7257
|
+
assertPathBoundContentSource(input.contentSource, input.shareId);
|
|
7034
7258
|
if (!Number.isSafeInteger(input.byteLength) || input.byteLength < 0 || input.byteLength > SHARE_CONTENT_LIMIT) throw new TypeError("addressed content length is invalid");
|
|
7035
7259
|
if (!Number.isFinite(input.expiresAt.getTime()) || input.expiresAt.getTime() <= Date.now()) throw new TypeError("addressed share expiry must be in the future");
|
|
7036
7260
|
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");
|
|
7037
7261
|
if (input.contentSource.shareId !== input.shareId || input.contentSource.selector !== input.resource.kind) throw new TypeError("addressed content source is not bound to the share");
|
|
7038
7262
|
}
|
|
7039
|
-
function
|
|
7263
|
+
function hex3(bytes3) {
|
|
7040
7264
|
return [...bytes3].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
7041
7265
|
}
|
|
7042
7266
|
function digestHex(value, domain) {
|
|
7043
|
-
return
|
|
7267
|
+
return hex3((0, import_sha2563.sha256)(textEncoder2.encode(`${domain}${canonicalize(value)}`)));
|
|
7044
7268
|
}
|
|
7045
7269
|
function sortCanonical(values) {
|
|
7046
7270
|
return [...values].sort((left, right) => canonicalize(left).localeCompare(canonicalize(right)));
|
|
@@ -7084,19 +7308,20 @@ async function createPolicy(options, capabilities) {
|
|
|
7084
7308
|
const unsigned = options.credentialRequirement === void 0 ? { schema: "xyz.tinycloud.policy/policy/v1", ...fields } : { schema: "xyz.tinycloud.policy/policy/v2", ...fields, credentialRequirement: options.credentialRequirement };
|
|
7085
7309
|
const domain = options.credentialRequirement === void 0 ? POLICY_V1_DOMAIN : POLICY_V2_DOMAIN;
|
|
7086
7310
|
const policyDigestHex = digestHex(unsigned, domain);
|
|
7087
|
-
const signature = await options.authority.sign((0,
|
|
7311
|
+
const signature = await options.authority.sign((0, import_sha2563.sha256)(textEncoder2.encode(`${domain}${canonicalize(unsigned)}`)));
|
|
7088
7312
|
if (signature.byteLength !== 64) throw new TypeError("policy signature must be Ed25519");
|
|
7089
7313
|
const policy = {
|
|
7090
7314
|
...unsigned,
|
|
7091
7315
|
policyId: `pol_${base32Lower2(Uint8Array.from(policyDigestHex.match(/../g), (byte) => Number.parseInt(byte, 16)))}`,
|
|
7092
7316
|
signature: { suite: "Ed25519", signerDid: options.authority.ownerDid, value: toBase64Url(signature) }
|
|
7093
7317
|
};
|
|
7094
|
-
return { policy, policyCid: await computeCid(
|
|
7318
|
+
return { policy, policyCid: await computeCid(textEncoder2.encode(canonicalize(policy))), policyDigestHex };
|
|
7095
7319
|
}
|
|
7096
7320
|
function addressedPlan(options) {
|
|
7097
7321
|
const capabilities = sortCanonical([
|
|
7098
7322
|
{ kind: "kv", resource: options.contentSource.kvResource, selector: options.resource.kind, actions: [...options.policyActions] },
|
|
7099
|
-
|
|
7323
|
+
// Only this share's one key: there is no network-scope capability.
|
|
7324
|
+
{ kind: "encryption", resource: `${options.contentSource.encryptionNetwork}/${options.contentSource.encryptionKeyPath}`, action: "tinycloud.encryption/decrypt" }
|
|
7100
7325
|
]);
|
|
7101
7326
|
return {
|
|
7102
7327
|
expiry: rfc3339Seconds(options.expiresAt),
|
|
@@ -7184,7 +7409,7 @@ function mailboxCredentialCommitment(target) {
|
|
|
7184
7409
|
return {
|
|
7185
7410
|
type: "TinyCloudPolicyCredentialRequirement",
|
|
7186
7411
|
version: 1,
|
|
7187
|
-
requirementDigest: toBase64Url((0,
|
|
7412
|
+
requirementDigest: toBase64Url((0, import_sha2563.sha256)(textEncoder2.encode(canonicalize(requirement)))),
|
|
7188
7413
|
descriptorDigest: target.kind === "email" ? EMAIL_DESCRIPTOR_DIGEST : EMAIL_DOMAIN_DESCRIPTOR_DIGEST,
|
|
7189
7414
|
issuerDid: EMAIL_CREDENTIAL_ISSUER_DID,
|
|
7190
7415
|
issuerKid: EMAIL_CREDENTIAL_ISSUER_KID,
|
|
@@ -7279,11 +7504,11 @@ async function publishAddressedShare(input) {
|
|
|
7279
7504
|
expiry
|
|
7280
7505
|
});
|
|
7281
7506
|
unsignedShareEnvelopeV3Schema.parse(unsigned);
|
|
7282
|
-
const envelopeSignature = await options.authority.sign((0,
|
|
7507
|
+
const envelopeSignature = await options.authority.sign((0, import_sha2563.sha256)(textEncoder2.encode(`${ENVELOPE_V3_DOMAIN}${canonicalize(unsigned)}`)));
|
|
7283
7508
|
if (envelopeSignature.byteLength !== 64) throw new TypeError("v3 envelope signature must be Ed25519");
|
|
7284
7509
|
const envelope = signedAddressedEnvelope(unsigned, options.authority.ownerDid, envelopeSignature);
|
|
7285
7510
|
shareEnvelopeV3Schema.parse(envelope);
|
|
7286
|
-
const envelopeBytes =
|
|
7511
|
+
const envelopeBytes = textEncoder2.encode(canonicalize(envelope));
|
|
7287
7512
|
const envelopeKeyBytes = generateKey();
|
|
7288
7513
|
const sealed = await seal(envelopeBytes, envelopeKeyBytes);
|
|
7289
7514
|
const envelopeKey = toBase64Url(envelopeKeyBytes);
|
|
@@ -7363,7 +7588,7 @@ function rootAuthorizationBound(input) {
|
|
|
7363
7588
|
nnc: "A".repeat(36),
|
|
7364
7589
|
prf: []
|
|
7365
7590
|
};
|
|
7366
|
-
return `${toBase64Url(
|
|
7591
|
+
return `${toBase64Url(textEncoder2.encode(canonicalize(header)))}.${toBase64Url(textEncoder2.encode(canonicalize(payload)))}.${"A".repeat(86)}`;
|
|
7367
7592
|
}
|
|
7368
7593
|
async function sealedBodyUpperBound(options) {
|
|
7369
7594
|
const plan = addressedPlan(options);
|
|
@@ -7411,7 +7636,7 @@ async function sealedBodyUpperBound(options) {
|
|
|
7411
7636
|
expiry: plan.expiry
|
|
7412
7637
|
});
|
|
7413
7638
|
const envelope = signedAddressedEnvelope(unsigned, options.ownerDid, signature);
|
|
7414
|
-
return (await seal(
|
|
7639
|
+
return (await seal(textEncoder2.encode(canonicalize(envelope)), generateKey())).blob.length;
|
|
7415
7640
|
}
|
|
7416
7641
|
async function assertSealedBodyFits(options) {
|
|
7417
7642
|
if (await sealedBodyUpperBound(options) > await linkBodyLimit()) throw new SharePublishError("max-bytes-exceeded", ADDRESSED_SHARE_TOO_LARGE_MESSAGE);
|
|
@@ -7421,12 +7646,15 @@ async function addressedShareBodyUpperBound(input) {
|
|
|
7421
7646
|
const deliveryEmail = input.deliveryEmail ?? (target.kind === "email" && isEnvelopeDeliveryEmail(target.address) ? target.address : void 0);
|
|
7422
7647
|
const artifactStorage = "artifactId" in input.storage ? input.storage : void 0;
|
|
7423
7648
|
const path = "path" in input.storage ? input.storage.path : `${ARTIFACT_ROOT}/${input.storage.artifactId}/versions/${Number.MAX_SAFE_INTEGER}/${input.storage.storageName}`;
|
|
7649
|
+
const keyPath = pathBoundKeyPath(input.shareId);
|
|
7650
|
+
assertEncryptionNetwork(input.encryptionNetwork);
|
|
7651
|
+
if (input.encryptionKeyPath !== void 0 && input.encryptionKeyPath !== keyPath) throw new AddressedContentSourceError("key-path-mismatch", "addressed content source key path must be share/<shareId> of this share");
|
|
7424
7652
|
const contentSource = {
|
|
7425
7653
|
shareId: input.shareId,
|
|
7426
7654
|
kvResource: `${input.spaceId}/kv/${path}`,
|
|
7427
7655
|
selector: input.resource.kind,
|
|
7428
7656
|
encryptionNetwork: input.encryptionNetwork,
|
|
7429
|
-
|
|
7657
|
+
encryptionKeyPath: keyPath,
|
|
7430
7658
|
encryptedSymmetricKeyDigestHex: "f".repeat(64),
|
|
7431
7659
|
keyVersion: Number.MAX_SAFE_INTEGER,
|
|
7432
7660
|
mode: "immutable",
|
|
@@ -7466,13 +7694,13 @@ async function assertAddressedShareFits(input) {
|
|
|
7466
7694
|
}
|
|
7467
7695
|
|
|
7468
7696
|
// src/cache.ts
|
|
7469
|
-
var
|
|
7697
|
+
var import_sha2564 = require("@noble/hashes/sha256");
|
|
7470
7698
|
function cacheKey(key) {
|
|
7471
7699
|
if (!key.cid || !key.expiresAt) throw new Error("share cache keys require CID and expiry");
|
|
7472
7700
|
return `${key.cid}\0${key.expiresAt}`;
|
|
7473
7701
|
}
|
|
7474
7702
|
function digest(bytes3) {
|
|
7475
|
-
return (0,
|
|
7703
|
+
return (0, import_sha2564.sha256)(bytes3);
|
|
7476
7704
|
}
|
|
7477
7705
|
function equalBytes(left, right) {
|
|
7478
7706
|
if (left.byteLength !== right.byteLength) return false;
|
|
@@ -7528,7 +7756,7 @@ var MemoryShareCache = class {
|
|
|
7528
7756
|
|
|
7529
7757
|
// src/owner-policy.ts
|
|
7530
7758
|
var import_ed255194 = require("@noble/curves/ed25519");
|
|
7531
|
-
var
|
|
7759
|
+
var import_sha2565 = require("@noble/hashes/sha256");
|
|
7532
7760
|
|
|
7533
7761
|
// ../../node_modules/multiformats/dist/src/bases/base32.js
|
|
7534
7762
|
var base322 = rfc46482({
|
|
@@ -8082,10 +8310,10 @@ function fromB64(value) {
|
|
|
8082
8310
|
throw new Error("base64url decoding is unavailable");
|
|
8083
8311
|
}
|
|
8084
8312
|
function cid(bytesValue) {
|
|
8085
|
-
return CID2.createV1(code2, create2(SHA256_CODE2, (0,
|
|
8313
|
+
return CID2.createV1(code2, create2(SHA256_CODE2, (0, import_sha2565.sha256)(bytesValue))).toString(base322.encoder);
|
|
8086
8314
|
}
|
|
8087
8315
|
function digest2(bytesValue) {
|
|
8088
|
-
return b64((0,
|
|
8316
|
+
return b64((0, import_sha2565.sha256)(bytesValue));
|
|
8089
8317
|
}
|
|
8090
8318
|
function didKeyFromEd25519PublicKey2(publicKey) {
|
|
8091
8319
|
if (publicKey.length !== 32) throw new Error("share key public key must be 32 bytes");
|
|
@@ -8757,7 +8985,7 @@ async function showShare(input) {
|
|
|
8757
8985
|
|
|
8758
8986
|
// src/recipient.ts
|
|
8759
8987
|
var import_ed255195 = require("@noble/curves/ed25519");
|
|
8760
|
-
var
|
|
8988
|
+
var import_sha2566 = require("@noble/hashes/sha256");
|
|
8761
8989
|
|
|
8762
8990
|
// src/protocol.ts
|
|
8763
8991
|
var SHARE_V2_PROTOCOL = Object.freeze({
|
|
@@ -8925,12 +9153,12 @@ function requestedSessionExpiry(expiry) {
|
|
|
8925
9153
|
if (!Number.isFinite(seconds) || seconds <= Math.floor(Date.now() / 1e3) + 60) return void 0;
|
|
8926
9154
|
return new Date(seconds * 1e3).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
8927
9155
|
}
|
|
8928
|
-
function
|
|
9156
|
+
function hex4(value) {
|
|
8929
9157
|
return [...value].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
8930
9158
|
}
|
|
8931
9159
|
function matchesInitialCiphertextDigest(bytes3, expected) {
|
|
8932
9160
|
if (typeof expected !== "string" || !LOWER_SHA256_HEX.test(expected)) return false;
|
|
8933
|
-
const actual = (0,
|
|
9161
|
+
const actual = (0, import_sha2566.sha256)(bytes3);
|
|
8934
9162
|
let mismatch = 0;
|
|
8935
9163
|
for (let index = 0; index < actual.length; index += 1) {
|
|
8936
9164
|
mismatch |= actual[index] ^ Number.parseInt(expected.slice(index * 2, index * 2 + 2), 16);
|
|
@@ -8938,10 +9166,10 @@ function matchesInitialCiphertextDigest(bytes3, expected) {
|
|
|
8938
9166
|
return mismatch === 0;
|
|
8939
9167
|
}
|
|
8940
9168
|
function canonicalHashHex(value) {
|
|
8941
|
-
return
|
|
9169
|
+
return hex4((0, import_sha2566.sha256)(new TextEncoder().encode(canonicalize(value))));
|
|
8942
9170
|
}
|
|
8943
9171
|
function registeredWrappedKeyDigest(encryptedSymmetricKey, pathBound) {
|
|
8944
|
-
const rawDigest =
|
|
9172
|
+
const rawDigest = hex4((0, import_sha2566.sha256)(fromBase64(encryptedSymmetricKey, "v3 encrypted symmetric key")));
|
|
8945
9173
|
return pathBound ? rawDigest : canonicalHashHex(encryptedSymmetricKey);
|
|
8946
9174
|
}
|
|
8947
9175
|
async function aesGcmDecrypt(key, blob, aad) {
|
|
@@ -9110,7 +9338,7 @@ var ShareRecipientClient = class {
|
|
|
9110
9338
|
const receiverPublicKey = toBase64(import_ed255195.x25519.getPublicKey(receiverPrivateKey));
|
|
9111
9339
|
const receiverPublicKeyHash = canonicalHashHex(receiverPublicKey);
|
|
9112
9340
|
const body = { type: "tinycloud.encryption.decrypt/v1", targetNode: this.v3NodeAudience, networkId: encrypted.networkId, ...encrypted.keyPath === void 0 ? {} : { keyPath: encrypted.keyPath }, alg: encrypted.alg, keyVersion: encrypted.keyVersion, encryptedSymmetricKey: encrypted.encryptedSymmetricKey, encryptedSymmetricKeyHash: encrypted.encryptedSymmetricKeyHash, receiverPublicKey, receiverPublicKeyHash };
|
|
9113
|
-
const bodyHash =
|
|
9341
|
+
const bodyHash = hex4((0, import_sha2566.sha256)(new TextEncoder().encode(canonicalize(body))));
|
|
9114
9342
|
const session = verifyCompactUcanAuthorization(this.v3Authorization, this.session.sessionId);
|
|
9115
9343
|
const now = Math.floor(Date.now() / 1e3);
|
|
9116
9344
|
const decryptResource = encrypted.keyPath === void 0 ? encrypted.networkId : `${encrypted.networkId}/${encrypted.keyPath}`;
|
|
@@ -9131,7 +9359,7 @@ var ShareRecipientClient = class {
|
|
|
9131
9359
|
if (!response.ok) throw await recipientHttpError(response, "v3 decrypt invocation rejected");
|
|
9132
9360
|
const value = object3(await response.json(), "v3 decrypt response");
|
|
9133
9361
|
const allowed = ["type", "targetNode", "networkId", "invocationCid", "encryptedSymmetricKeyHash", "receiverPublicKeyHash", "wrappedKey", "alg", "keyVersion", "requestHash", "nodeId", "nodeSignature"];
|
|
9134
|
-
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 !==
|
|
9362
|
+
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 !== hex4((0, import_sha2566.sha256)(new TextEncoder().encode(`${invocation.cid}${bodyHash}`))) || typeof value.wrappedKey !== "string" || typeof value.nodeSignature !== "string") throw new Error("v3 decrypt response binding is invalid");
|
|
9135
9363
|
const unsigned = { ...value };
|
|
9136
9364
|
delete unsigned.nodeSignature;
|
|
9137
9365
|
const signature = fromBase64(value.nodeSignature, "v3 decrypt response signature");
|
|
@@ -9297,6 +9525,7 @@ async function openNativeShare(sharing, link) {
|
|
|
9297
9525
|
// Annotate the CommonJS export names for ESM import in node:
|
|
9298
9526
|
0 && (module.exports = {
|
|
9299
9527
|
ADDRESSED_SHARE_TOO_LARGE_MESSAGE,
|
|
9528
|
+
AddressedContentSourceError,
|
|
9300
9529
|
AddressedShareOverLimitError,
|
|
9301
9530
|
ArtifactPinError,
|
|
9302
9531
|
CredentialInvitationError,
|
|
@@ -9325,8 +9554,10 @@ async function openNativeShare(sharing, link) {
|
|
|
9325
9554
|
artifactBindingView,
|
|
9326
9555
|
assertAddressedShareFits,
|
|
9327
9556
|
assertArtifactBinding,
|
|
9557
|
+
assertPathBoundContentSource,
|
|
9328
9558
|
authorizationMethodForTarget,
|
|
9329
9559
|
authorizeShare,
|
|
9560
|
+
bindPathBoundContentSource,
|
|
9330
9561
|
canonicalOwnerSharePolicy,
|
|
9331
9562
|
canonicalShareFilename,
|
|
9332
9563
|
canonicalize,
|
|
@@ -9338,7 +9569,9 @@ async function openNativeShare(sharing, link) {
|
|
|
9338
9569
|
createNativeShare,
|
|
9339
9570
|
createPolicyEnforcementDelegation,
|
|
9340
9571
|
createShareV2HolderBindingArtifact,
|
|
9572
|
+
defaultEncryptionNetworkForOwner,
|
|
9341
9573
|
deliverCredentialInvitation,
|
|
9574
|
+
encryptAddressedShareContent,
|
|
9342
9575
|
fromBase64Url,
|
|
9343
9576
|
hasUnsafeFilenameCodePoint,
|
|
9344
9577
|
historyRecordForPublishedShare,
|
|
@@ -9349,6 +9582,10 @@ async function openNativeShare(sharing, link) {
|
|
|
9349
9582
|
openNativeShare,
|
|
9350
9583
|
parseAddressedEnvelope,
|
|
9351
9584
|
parseNativeShareUrl,
|
|
9585
|
+
pathBoundKeyPath,
|
|
9586
|
+
pathBoundSessionPermissions,
|
|
9587
|
+
pathBoundStoredBytes,
|
|
9588
|
+
planPathBoundShare,
|
|
9352
9589
|
prepareAddressedShare,
|
|
9353
9590
|
publishAddressedShare,
|
|
9354
9591
|
publishPolicyShare,
|
|
@@ -9368,6 +9605,7 @@ async function openNativeShare(sharing, link) {
|
|
|
9368
9605
|
toShareErrorInfo,
|
|
9369
9606
|
validateOwnerSharePolicyRegistration,
|
|
9370
9607
|
validateOwnerSharePolicyRegistrationBytes,
|
|
9371
|
-
verifyArtifactShare
|
|
9608
|
+
verifyArtifactShare,
|
|
9609
|
+
wrappedKeyDigestHex
|
|
9372
9610
|
});
|
|
9373
9611
|
//# sourceMappingURL=index.cjs.map
|