@tinycloud/share-sdk 0.2.0-beta.0 → 0.2.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 +468 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -4
- package/dist/index.d.ts +27 -4
- package/dist/index.js +468 -62
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -235,10 +235,10 @@ var util;
|
|
|
235
235
|
return obj[e];
|
|
236
236
|
});
|
|
237
237
|
};
|
|
238
|
-
util2.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (
|
|
238
|
+
util2.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (object3) => {
|
|
239
239
|
const keys = [];
|
|
240
|
-
for (const key in
|
|
241
|
-
if (Object.prototype.hasOwnProperty.call(
|
|
240
|
+
for (const key in object3) {
|
|
241
|
+
if (Object.prototype.hasOwnProperty.call(object3, key)) {
|
|
242
242
|
keys.push(key);
|
|
243
243
|
}
|
|
244
244
|
}
|
|
@@ -4134,6 +4134,9 @@ var NEVER = INVALID;
|
|
|
4134
4134
|
|
|
4135
4135
|
// ../share-envelope/dist/index.js
|
|
4136
4136
|
var import_ed255192 = require("@noble/curves/ed25519");
|
|
4137
|
+
var import_blake3 = require("@noble/hashes/blake3");
|
|
4138
|
+
var import_ed255193 = require("@noble/curves/ed25519");
|
|
4139
|
+
var import_sha256 = require("@noble/hashes/sha256");
|
|
4137
4140
|
var empty = new Uint8Array(0);
|
|
4138
4141
|
function equals(aa, bb) {
|
|
4139
4142
|
if (aa === bb) {
|
|
@@ -5306,6 +5309,128 @@ function validateV2Invariants(value, ctx) {
|
|
|
5306
5309
|
}
|
|
5307
5310
|
var unsignedShareEnvelopeV2Schema = unsignedShareEnvelopeV2BaseSchema.superRefine(validateV2Invariants);
|
|
5308
5311
|
var shareEnvelopeV2Schema = unsignedShareEnvelopeV2BaseSchema.extend({ signature: signatureSchema }).strict().superRefine(validateV2Invariants);
|
|
5312
|
+
var unifiedResourceSchema = external_exports.string().refine((value) => {
|
|
5313
|
+
const match = /^tinycloud:\/\/([^/]+)\/kv\/(.+)$/.exec(value);
|
|
5314
|
+
if (match === null || /[:?#%]/.test(match[1])) return false;
|
|
5315
|
+
const path = match[2];
|
|
5316
|
+
return !path.startsWith("/") && !path.endsWith("/") && !path.includes("//") && path.split("/").every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
|
|
5317
|
+
}, { message: "expected canonical TinyCloud KV resource" });
|
|
5318
|
+
var unifiedEncryptionNetworkSchema = external_exports.string().refine((value) => {
|
|
5319
|
+
if (!value.startsWith("urn:tinycloud:encryption:")) return false;
|
|
5320
|
+
const rest = value.slice("urn:tinycloud:encryption:".length);
|
|
5321
|
+
const separator = rest.lastIndexOf(":");
|
|
5322
|
+
const owner = separator < 0 ? "" : rest.slice(0, separator);
|
|
5323
|
+
const network = separator < 0 ? "" : rest.slice(separator + 1);
|
|
5324
|
+
return owner.startsWith("did:") && owner.length > 4 && network.length > 0 && !/[:/%?#\s]/.test(network);
|
|
5325
|
+
}, { message: "expected canonical TinyCloud encryption network" });
|
|
5326
|
+
var unifiedKvCapabilitySchema = external_exports.object({
|
|
5327
|
+
kind: external_exports.literal("kv"),
|
|
5328
|
+
resource: unifiedResourceSchema,
|
|
5329
|
+
selector: external_exports.union([external_exports.literal("exact"), external_exports.literal("prefix")]),
|
|
5330
|
+
actions: external_exports.array(external_exports.union([
|
|
5331
|
+
external_exports.literal("tinycloud.kv/get"),
|
|
5332
|
+
external_exports.literal("tinycloud.kv/list"),
|
|
5333
|
+
external_exports.literal("tinycloud.kv/metadata"),
|
|
5334
|
+
external_exports.literal("tinycloud.kv/put")
|
|
5335
|
+
])).min(1)
|
|
5336
|
+
}).strict();
|
|
5337
|
+
var unifiedEncryptionCapabilitySchema = external_exports.object({
|
|
5338
|
+
kind: external_exports.literal("encryption"),
|
|
5339
|
+
resource: unifiedEncryptionNetworkSchema,
|
|
5340
|
+
action: external_exports.literal("tinycloud.encryption/decrypt")
|
|
5341
|
+
}).strict();
|
|
5342
|
+
var unifiedCapabilitySchema = external_exports.union([unifiedKvCapabilitySchema, unifiedEncryptionCapabilitySchema]);
|
|
5343
|
+
var unifiedContentSourceSchema = external_exports.object({
|
|
5344
|
+
shareId: external_exports.string().min(1),
|
|
5345
|
+
kvResource: unifiedResourceSchema,
|
|
5346
|
+
selector: external_exports.union([external_exports.literal("exact"), external_exports.literal("prefix")]),
|
|
5347
|
+
encryptionNetwork: unifiedEncryptionNetworkSchema,
|
|
5348
|
+
encryptedSymmetricKeyDigestHex: external_exports.string().regex(/^[0-9a-f]{64}$/),
|
|
5349
|
+
keyVersion: external_exports.number().int().positive(),
|
|
5350
|
+
mode: external_exports.union([external_exports.literal("mutable"), external_exports.literal("immutable")]),
|
|
5351
|
+
initialCiphertextDigestHex: external_exports.string().regex(/^[0-9a-f]{64}$/).optional()
|
|
5352
|
+
}).strict();
|
|
5353
|
+
var unifiedPolicySchema = external_exports.object({
|
|
5354
|
+
schema: external_exports.literal("xyz.tinycloud.policy/policy/v1"),
|
|
5355
|
+
policyId: external_exports.string().regex(/^pol_[a-z2-7]+$/),
|
|
5356
|
+
ownerDid: external_exports.string().min(1),
|
|
5357
|
+
createdAt: external_exports.string().datetime({ offset: true }),
|
|
5358
|
+
expiresAt: external_exports.string().datetime({ offset: true }).optional(),
|
|
5359
|
+
contentSource: unifiedContentSourceSchema,
|
|
5360
|
+
capabilityCeiling: external_exports.array(unifiedCapabilitySchema).min(2),
|
|
5361
|
+
signature: external_exports.object({ suite: external_exports.string().min(1), signerDid: external_exports.string().min(1), value: external_exports.string().min(1) }).strict()
|
|
5362
|
+
}).strict();
|
|
5363
|
+
var unifiedRootSchema = external_exports.object({
|
|
5364
|
+
cid: external_exports.string().min(1),
|
|
5365
|
+
authorization: external_exports.string().regex(/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/),
|
|
5366
|
+
role: external_exports.union([external_exports.literal("policy-authority"), external_exports.literal("policy-enforcement")])
|
|
5367
|
+
}).strict();
|
|
5368
|
+
var attestedEnforcerBindingV2Schema = external_exports.object({
|
|
5369
|
+
schema: external_exports.literal("xyz.tinycloud.policy/attested-enforcer/v2"),
|
|
5370
|
+
enforcerDid: external_exports.string().regex(/^did:key:z[1-9A-HJ-NP-Za-km-z]+$/),
|
|
5371
|
+
nodeAudience: external_exports.string().min(1),
|
|
5372
|
+
attestationBindingDigestHex: external_exports.string().regex(/^[0-9a-f]{64}$/),
|
|
5373
|
+
issuedAt: external_exports.string().datetime({ offset: true }),
|
|
5374
|
+
expiresAt: external_exports.string().datetime({ offset: true }),
|
|
5375
|
+
signature: external_exports.object({ suite: external_exports.literal("Ed25519"), signerDid: external_exports.string().min(1), value: base64UrlString() }).strict()
|
|
5376
|
+
}).strict();
|
|
5377
|
+
var v3TargetSchema = external_exports.object({
|
|
5378
|
+
origin: external_exports.string().refine(isCanonicalHttpsOrigin, { message: "expected a canonical https origin" }),
|
|
5379
|
+
nodeAudience: external_exports.string().min(1),
|
|
5380
|
+
spaceId: external_exports.string().refine(isCanonicalPathSegment, { message: "expected a canonical space id" })
|
|
5381
|
+
}).strict();
|
|
5382
|
+
var unsignedShareEnvelopeV3BaseSchema = external_exports.object({
|
|
5383
|
+
version: external_exports.literal(3),
|
|
5384
|
+
shareId: external_exports.string().min(1),
|
|
5385
|
+
recipientMatcher: recipientMatcherSchema,
|
|
5386
|
+
deliveryEmail: external_exports.string().email().optional(),
|
|
5387
|
+
actions: external_exports.array(shareActionSchema).min(1).max(3),
|
|
5388
|
+
resource: resourceSelectorSchema,
|
|
5389
|
+
target: v3TargetSchema,
|
|
5390
|
+
policy: unifiedPolicySchema,
|
|
5391
|
+
policyCid: external_exports.string().min(1),
|
|
5392
|
+
policyRoot: unifiedRootSchema,
|
|
5393
|
+
enforcementRoot: unifiedRootSchema,
|
|
5394
|
+
attestedEnforcerBinding: attestedEnforcerBindingV2Schema,
|
|
5395
|
+
contentSource: unifiedContentSourceSchema,
|
|
5396
|
+
contentSourceDigestHex: external_exports.string().regex(/^[0-9a-f]{64}$/),
|
|
5397
|
+
encryptionNetwork: unifiedEncryptionNetworkSchema,
|
|
5398
|
+
expiry: external_exports.string().datetime({ offset: true }),
|
|
5399
|
+
display: displaySchema,
|
|
5400
|
+
encrypted: external_exports.literal(true),
|
|
5401
|
+
metadata: contentMetadataSchema
|
|
5402
|
+
}).strict();
|
|
5403
|
+
function validateV3Invariants(value, ctx) {
|
|
5404
|
+
const actions = [...value.actions];
|
|
5405
|
+
if (new Set(actions).size !== actions.length || actions.some((action, index) => action !== ["read", "list", "edit"].filter((candidate) => actions.includes(candidate))[index])) {
|
|
5406
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["actions"], message: "actions must be unique and canonically ordered" });
|
|
5407
|
+
}
|
|
5408
|
+
if (value.policyRoot.role !== "policy-authority" || value.enforcementRoot.role !== "policy-enforcement") {
|
|
5409
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policyRoot", "role"], message: "root roles are fixed" });
|
|
5410
|
+
}
|
|
5411
|
+
if (value.policyCid.length === 0 || value.policyCid === value.policyRoot.cid || value.policyCid === value.enforcementRoot.cid) {
|
|
5412
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policyCid"], message: "policy CID must be distinct from both roots" });
|
|
5413
|
+
}
|
|
5414
|
+
if (value.contentSource.encryptionNetwork !== value.encryptionNetwork) {
|
|
5415
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["encryptionNetwork"], message: "encryption network is not bound to the source" });
|
|
5416
|
+
}
|
|
5417
|
+
if (value.attestedEnforcerBinding.enforcerDid !== value.target.nodeAudience || value.attestedEnforcerBinding.nodeAudience !== value.target.nodeAudience || value.attestedEnforcerBinding.signature.signerDid !== value.target.nodeAudience || Date.parse(value.attestedEnforcerBinding.expiresAt) < Date.parse(value.expiry)) {
|
|
5418
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["attestedEnforcerBinding"], message: "enforcer binding does not cover the target and share lifetime" });
|
|
5419
|
+
}
|
|
5420
|
+
if (value.policy.contentSource.shareId !== value.contentSource.shareId || value.policy.contentSource.kvResource !== value.contentSource.kvResource || value.policy.contentSource.selector !== value.contentSource.selector || value.policy.contentSource.encryptionNetwork !== value.encryptionNetwork || value.policy.contentSource.encryptedSymmetricKeyDigestHex !== value.contentSource.encryptedSymmetricKeyDigestHex || value.policy.contentSource.keyVersion !== value.contentSource.keyVersion || value.policy.contentSource.mode !== value.contentSource.mode || value.policy.contentSource.initialCiphertextDigestHex !== value.contentSource.initialCiphertextDigestHex) {
|
|
5421
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["contentSource"], message: "content source is not bound to policy" });
|
|
5422
|
+
}
|
|
5423
|
+
if (value.policy.signature.suite !== "Ed25519" || value.policy.signature.signerDid !== value.policy.ownerDid) {
|
|
5424
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policy", "signature"], message: "policy must be signed by its owner" });
|
|
5425
|
+
}
|
|
5426
|
+
const kvCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "kv");
|
|
5427
|
+
const decryptCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "encryption" && capability.resource === value.encryptionNetwork && capability.action === "tinycloud.encryption/decrypt");
|
|
5428
|
+
if (value.policy.capabilityCeiling.length !== 2 || kvCapabilities.length !== 1 || decryptCapabilities.length !== 1 || kvCapabilities[0]?.resource !== value.contentSource.kvResource || kvCapabilities[0]?.selector !== value.contentSource.selector) {
|
|
5429
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain exact decrypt network" });
|
|
5430
|
+
}
|
|
5431
|
+
}
|
|
5432
|
+
var unsignedShareEnvelopeV3Schema = unsignedShareEnvelopeV3BaseSchema.superRefine(validateV3Invariants);
|
|
5433
|
+
var shareEnvelopeV3Schema = unsignedShareEnvelopeV3BaseSchema.extend({ signature: signatureSchema }).strict().superRefine(validateV3Invariants);
|
|
5309
5434
|
var BEARER_READ_ABILITY = "kv/get";
|
|
5310
5435
|
var READ_ABILITIES = /* @__PURE__ */ new Set([BEARER_READ_ABILITY]);
|
|
5311
5436
|
var ED25519_VERIFY_OPTS = { zip215: false };
|
|
@@ -5645,6 +5770,83 @@ function serialize(value) {
|
|
|
5645
5770
|
function canonicalize(value) {
|
|
5646
5771
|
return serialize(value);
|
|
5647
5772
|
}
|
|
5773
|
+
async function signCompactUcanAuthorization(input) {
|
|
5774
|
+
if (input.notBefore >= input.expiresAt || input.expiresAt - input.notBefore > 60) {
|
|
5775
|
+
throw new TypeError("compact invocation lifetime must be between one and 60 seconds");
|
|
5776
|
+
}
|
|
5777
|
+
const principal = input.issuerDid.split("#", 1)[0];
|
|
5778
|
+
const publicKey = ed25519PublicKeyFromDidKey(principal);
|
|
5779
|
+
const header = {
|
|
5780
|
+
alg: "EdDSA",
|
|
5781
|
+
jwk: { alg: "EdDSA", crv: "Ed25519", kty: "OKP", x: encodeBase64Url(publicKey) },
|
|
5782
|
+
typ: "JWT",
|
|
5783
|
+
ucv: "0.10.0"
|
|
5784
|
+
};
|
|
5785
|
+
const payload = {
|
|
5786
|
+
att: input.attenuation,
|
|
5787
|
+
aud: input.audienceDid,
|
|
5788
|
+
exp: input.expiresAt,
|
|
5789
|
+
fct: input.facts,
|
|
5790
|
+
iss: input.issuerDid.includes("#") ? input.issuerDid : `${principal}#${principal.slice("did:key:".length)}`,
|
|
5791
|
+
nbf: input.notBefore,
|
|
5792
|
+
nnc: input.nonce,
|
|
5793
|
+
prf: input.proofs
|
|
5794
|
+
};
|
|
5795
|
+
const protectedSegment = encodeBase64Url(new TextEncoder().encode(canonicalize(header)));
|
|
5796
|
+
const payloadSegment = encodeBase64Url(new TextEncoder().encode(canonicalize(payload)));
|
|
5797
|
+
const signingInput = new TextEncoder().encode(`${protectedSegment}.${payloadSegment}`);
|
|
5798
|
+
const signature = await input.sign(signingInput);
|
|
5799
|
+
if (signature.length !== 64) throw new TypeError("compact Authorization signature must be Ed25519");
|
|
5800
|
+
return verifyCompactUcanAuthorization(
|
|
5801
|
+
`${protectedSegment}.${payloadSegment}.${encodeBase64Url(signature)}`
|
|
5802
|
+
);
|
|
5803
|
+
}
|
|
5804
|
+
function verifyCompactUcanAuthorization(authorization, expectedCid) {
|
|
5805
|
+
if (/\s/.test(authorization)) throw new TypeError("compact Authorization contains whitespace");
|
|
5806
|
+
const segments = authorization.split(".");
|
|
5807
|
+
if (segments.length !== 3 || segments.some((segment) => segment.length === 0)) throw new TypeError("compact Authorization must contain three segments");
|
|
5808
|
+
const [headerSegment, payloadSegment, signatureSegment] = segments;
|
|
5809
|
+
const headerBytes = decodeBase64Url(headerSegment);
|
|
5810
|
+
const payloadBytes = decodeBase64Url(payloadSegment);
|
|
5811
|
+
const signature = decodeBase64Url(signatureSegment);
|
|
5812
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
5813
|
+
const header = JSON.parse(decoder.decode(headerBytes));
|
|
5814
|
+
const payload = JSON.parse(decoder.decode(payloadBytes));
|
|
5815
|
+
if (canonicalize(header) !== decoder.decode(headerBytes) || canonicalize(payload) !== decoder.decode(payloadBytes)) throw new TypeError("compact Authorization JSON is not canonical");
|
|
5816
|
+
assertExactKeys(header, ["alg", "jwk", "typ", "ucv"], "protected header");
|
|
5817
|
+
const jwk = object(header.jwk, "protected JWK");
|
|
5818
|
+
assertExactKeys(jwk, ["alg", "crv", "kty", "x"], "protected JWK");
|
|
5819
|
+
if (header.alg !== "EdDSA" || header.typ !== "JWT" || header.ucv !== "0.10.0" || jwk.alg !== "EdDSA" || jwk.crv !== "Ed25519" || jwk.kty !== "OKP" || typeof jwk.x !== "string") throw new TypeError("compact Authorization header is invalid");
|
|
5820
|
+
assertExactKeys(payload, ["att", "aud", "exp", "fct", "iss", "nbf", "nnc", "prf"], "UCAN payload");
|
|
5821
|
+
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");
|
|
5822
|
+
const principal = payload.iss.split("#", 1)[0];
|
|
5823
|
+
const publicKey = ed25519PublicKeyFromDidKey(principal);
|
|
5824
|
+
if (!equal(publicKey, decodeBase64Url(jwk.x))) throw new TypeError("compact Authorization JWK does not bind issuer");
|
|
5825
|
+
if (!import_ed255192.ed25519.verify(signature, new TextEncoder().encode(`${headerSegment}.${payloadSegment}`), publicKey, { zip215: false })) throw new TypeError("compact Authorization signature is invalid");
|
|
5826
|
+
const cid2 = CID.createV1(85, create(30, (0, import_blake3.blake3)(new TextEncoder().encode(authorization)))).toString();
|
|
5827
|
+
if (expectedCid !== void 0 && cid2 !== expectedCid) throw new TypeError("compact Authorization CID mismatch");
|
|
5828
|
+
return { authorization, cid: cid2, header, payload };
|
|
5829
|
+
}
|
|
5830
|
+
function object(value, label) {
|
|
5831
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new TypeError(`${label} must be an object`);
|
|
5832
|
+
return value;
|
|
5833
|
+
}
|
|
5834
|
+
function assertExactKeys(value, keys, label) {
|
|
5835
|
+
if (Object.keys(value).length !== keys.length || Object.keys(value).some((key) => !keys.includes(key))) throw new TypeError(`${label} has unknown or missing fields`);
|
|
5836
|
+
}
|
|
5837
|
+
function decodeBase64Url(value) {
|
|
5838
|
+
if (!/^[A-Za-z0-9_-]+$/.test(value) || value.length % 4 === 1) throw new TypeError("compact Authorization segment is not base64url");
|
|
5839
|
+
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));
|
|
5840
|
+
const encoded = typeof Buffer !== "undefined" ? Buffer.from(bytes3).toString("base64url") : btoa(String.fromCharCode(...bytes3)).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
5841
|
+
if (encoded !== value) throw new TypeError("compact Authorization segment is not canonical");
|
|
5842
|
+
return bytes3;
|
|
5843
|
+
}
|
|
5844
|
+
function encodeBase64Url(value) {
|
|
5845
|
+
return typeof Buffer !== "undefined" ? Buffer.from(value).toString("base64url") : btoa(String.fromCharCode(...value)).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
5846
|
+
}
|
|
5847
|
+
function equal(left, right) {
|
|
5848
|
+
return left.length === right.length && left.every((byte, index) => byte === right[index]);
|
|
5849
|
+
}
|
|
5648
5850
|
var ED25519_VERIFY_OPTS2 = { zip215: false };
|
|
5649
5851
|
var ENVELOPE_SIGNATURE_DOMAIN = "xyz.tinycloud.share/envelope/v1\0";
|
|
5650
5852
|
var ENVELOPE_V2_SIGNATURE_DOMAIN = "xyz.tinycloud.share/envelope/v2\0";
|
|
@@ -5666,8 +5868,8 @@ function signingBytesV2(unsigned) {
|
|
|
5666
5868
|
}
|
|
5667
5869
|
function signEnvelope(envelopeWithoutSig, ed25519PrivKey) {
|
|
5668
5870
|
const unsigned = unsignedShareEnvelopeSchema.parse(envelopeWithoutSig);
|
|
5669
|
-
const publicKey =
|
|
5670
|
-
const signature =
|
|
5871
|
+
const publicKey = import_ed255193.ed25519.getPublicKey(ed25519PrivKey);
|
|
5872
|
+
const signature = import_ed255193.ed25519.sign(signingBytes(unsigned), ed25519PrivKey);
|
|
5671
5873
|
return {
|
|
5672
5874
|
...unsigned,
|
|
5673
5875
|
signature: {
|
|
@@ -5681,7 +5883,7 @@ function verifyEnvelopeSignatureOnly(envelope) {
|
|
|
5681
5883
|
const parsed = shareEnvelopeSchema.parse(envelope);
|
|
5682
5884
|
const { signature, ...unsigned } = parsed;
|
|
5683
5885
|
const publicKey = ed25519PublicKeyFromDidKey(signature.signerDid);
|
|
5684
|
-
return
|
|
5886
|
+
return import_ed255193.ed25519.verify(
|
|
5685
5887
|
fromBase64Url(signature.value),
|
|
5686
5888
|
signingBytes(unsigned),
|
|
5687
5889
|
publicKey,
|
|
@@ -5703,7 +5905,7 @@ async function verifyEnvelope(envelope, options) {
|
|
|
5703
5905
|
function verifyEnvelopeV2SignatureOnly(envelope) {
|
|
5704
5906
|
const parsed = shareEnvelopeV2Schema.parse(envelope);
|
|
5705
5907
|
const { signature, ...unsigned } = parsed;
|
|
5706
|
-
return
|
|
5908
|
+
return import_ed255193.ed25519.verify(fromBase64Url(signature.value), signingBytesV2(unsigned), ed25519PublicKeyFromDidKey(signature.signerDid), ED25519_VERIFY_OPTS2);
|
|
5707
5909
|
}
|
|
5708
5910
|
async function verifyEnvelopeV2(envelope, options) {
|
|
5709
5911
|
const parsed = shareEnvelopeV2Schema.parse(envelope);
|
|
@@ -6141,7 +6343,7 @@ async function receiveShare(link, options = {}) {
|
|
|
6141
6343
|
}
|
|
6142
6344
|
|
|
6143
6345
|
// src/publish.ts
|
|
6144
|
-
var
|
|
6346
|
+
var import_ed255194 = require("@noble/curves/ed25519");
|
|
6145
6347
|
var SHARE_CONTENT_LIMIT = 100 * 1024 * 1024;
|
|
6146
6348
|
var SHARE_SEALED_OVERHEAD = 1 + 12 + 16;
|
|
6147
6349
|
var SHARE_PUBLISH_RESULT_VERSION = 1;
|
|
@@ -6326,12 +6528,12 @@ async function publishShare(options) {
|
|
|
6326
6528
|
const spaceId = options.spaceId ?? "bearer";
|
|
6327
6529
|
const shareId = toBase64Url(globalThis.crypto.getRandomValues(new Uint8Array(16)));
|
|
6328
6530
|
const path = `shares/${shareId}/${options.filename}`;
|
|
6329
|
-
const sessionPrivateKey =
|
|
6330
|
-
const senderPrivateKey =
|
|
6531
|
+
const sessionPrivateKey = import_ed255194.ed25519.utils.randomPrivateKey();
|
|
6532
|
+
const senderPrivateKey = import_ed255194.ed25519.utils.randomPrivateKey();
|
|
6331
6533
|
let contentKey;
|
|
6332
6534
|
let envelopeKey;
|
|
6333
6535
|
try {
|
|
6334
|
-
const sessionPublicKey =
|
|
6536
|
+
const sessionPublicKey = import_ed255194.ed25519.getPublicKey(sessionPrivateKey);
|
|
6335
6537
|
const sessionDid = didKeyFromEd25519PublicKey(sessionPublicKey);
|
|
6336
6538
|
const delegation = mintBearerDelegation({
|
|
6337
6539
|
issuerPrivateKey: senderPrivateKey,
|
|
@@ -6423,8 +6625,8 @@ async function parseAddressedEnvelope(value) {
|
|
|
6423
6625
|
}
|
|
6424
6626
|
|
|
6425
6627
|
// src/owner-policy.ts
|
|
6426
|
-
var
|
|
6427
|
-
var
|
|
6628
|
+
var import_ed255195 = require("@noble/curves/ed25519");
|
|
6629
|
+
var import_sha2562 = require("@noble/hashes/sha256");
|
|
6428
6630
|
|
|
6429
6631
|
// ../../node_modules/multiformats/dist/src/bytes.js
|
|
6430
6632
|
var empty2 = new Uint8Array(0);
|
|
@@ -7331,10 +7533,10 @@ function fromB64(value) {
|
|
|
7331
7533
|
throw new Error("base64url decoding is unavailable");
|
|
7332
7534
|
}
|
|
7333
7535
|
function cid(bytesValue) {
|
|
7334
|
-
return CID2.createV1(code2, create2(SHA256_CODE2, (0,
|
|
7536
|
+
return CID2.createV1(code2, create2(SHA256_CODE2, (0, import_sha2562.sha256)(bytesValue))).toString(base322.encoder);
|
|
7335
7537
|
}
|
|
7336
7538
|
function digest(bytesValue) {
|
|
7337
|
-
return b64((0,
|
|
7539
|
+
return b64((0, import_sha2562.sha256)(bytesValue));
|
|
7338
7540
|
}
|
|
7339
7541
|
function didKeyFromEd25519PublicKey2(publicKey) {
|
|
7340
7542
|
if (publicKey.length !== 32) throw new Error("share key public key must be 32 bytes");
|
|
@@ -7512,7 +7714,7 @@ function validateOwnerSharePolicyRegistration(value, expected) {
|
|
|
7512
7714
|
const publicKey = encodedKid.length === 34 ? encodedKid.slice(2) : encodedKid;
|
|
7513
7715
|
const signatureBytes = fromB64(proof.signature);
|
|
7514
7716
|
const signedBytes = new TextEncoder().encode(`${OWNER_SHARE_REGISTRATION_DOMAIN}${canonicalize(registrationCore)}`);
|
|
7515
|
-
if (signatureBytes.length !== 64 || !
|
|
7717
|
+
if (signatureBytes.length !== 64 || !import_ed255195.ed25519.verify(signatureBytes, signedBytes, publicKey)) throw new Error("owner-share registration proof signature is invalid");
|
|
7516
7718
|
return { registration, proof };
|
|
7517
7719
|
}
|
|
7518
7720
|
function validateOwnerSharePolicyRegistrationBytes(bytesValue, expected) {
|
|
@@ -7672,7 +7874,7 @@ function assertSafeInput(input) {
|
|
|
7672
7874
|
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");
|
|
7673
7875
|
if (input.contentSource.kind !== "kv") throw new TypeError("addressed policy publication requires a KV content source");
|
|
7674
7876
|
}
|
|
7675
|
-
async function
|
|
7877
|
+
async function sha2564(value) {
|
|
7676
7878
|
return toBase64Url(new Uint8Array(await crypto.subtle.digest("SHA-256", value)));
|
|
7677
7879
|
}
|
|
7678
7880
|
function publicationResult(input) {
|
|
@@ -7721,7 +7923,7 @@ async function publishAddressedShare(options) {
|
|
|
7721
7923
|
const contentSource = options.contentSource;
|
|
7722
7924
|
const matcher = targetMatcher(target);
|
|
7723
7925
|
const expiresAt = options.expiresAt.toISOString();
|
|
7724
|
-
const contentSourceDigest = options.contentSourceDigest ?? await
|
|
7926
|
+
const contentSourceDigest = options.contentSourceDigest ?? await sha2564(new TextEncoder().encode(canonicalize(contentSource)));
|
|
7725
7927
|
const shareKey = await createDelegatedShareKey({ extractable: false });
|
|
7726
7928
|
let envelopeKey;
|
|
7727
7929
|
try {
|
|
@@ -7785,7 +7987,7 @@ async function publishAddressedShare(options) {
|
|
|
7785
7987
|
enforcementDelegation,
|
|
7786
7988
|
contentSourceDigest
|
|
7787
7989
|
});
|
|
7788
|
-
const authorityMaterialDigest = await
|
|
7990
|
+
const authorityMaterialDigest = await sha2564(fromBase64Url(enforcementDelegation.dagCbor));
|
|
7789
7991
|
const authorityTarget = {
|
|
7790
7992
|
origin: options.nodeOrigin,
|
|
7791
7993
|
nodeAudience: options.nodeAudience,
|
|
@@ -7885,7 +8087,7 @@ async function publishAddressedShare(options) {
|
|
|
7885
8087
|
}
|
|
7886
8088
|
|
|
7887
8089
|
// src/policy-authority.ts
|
|
7888
|
-
var
|
|
8090
|
+
var import_ed255196 = require("@noble/curves/ed25519");
|
|
7889
8091
|
var ENVELOPE_DOMAIN2 = "xyz.tinycloud.share/envelope/v2\0";
|
|
7890
8092
|
function record(value, keys, label) {
|
|
7891
8093
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
|
|
@@ -7941,7 +8143,7 @@ async function verifyEnforcement(delegation) {
|
|
|
7941
8143
|
});
|
|
7942
8144
|
if (expectedDagCbor.length !== dagCbor.length || expectedDagCbor.some((byte, index) => byte !== dagCbor[index])) return false;
|
|
7943
8145
|
const signature = fromBase64Url(delegation.signature);
|
|
7944
|
-
return signature.length === 64 &&
|
|
8146
|
+
return signature.length === 64 && import_ed255196.ed25519.verify(
|
|
7945
8147
|
signature,
|
|
7946
8148
|
dagCbor,
|
|
7947
8149
|
ed25519PublicKeyFromDidKey(delegation.issuerDid),
|
|
@@ -7965,7 +8167,7 @@ async function verifyOuterEnvelope(value, envelope, policyCid, expectedEnforcerD
|
|
|
7965
8167
|
if (shareCid !== await computeCid(new TextEncoder().encode(canonicalize({ version: 2, shareId: envelope.shareId, policyCid, envelopeCid })))) return void 0;
|
|
7966
8168
|
const unsigned = { ...identity, envelopeCid, shareCid };
|
|
7967
8169
|
const signatureBytes = fromBase64Url(requiredString(signature.value, "outer signature"));
|
|
7968
|
-
if (signatureBytes.length !== 64 || !
|
|
8170
|
+
if (signatureBytes.length !== 64 || !import_ed255196.ed25519.verify(
|
|
7969
8171
|
signatureBytes,
|
|
7970
8172
|
new TextEncoder().encode(`${ENVELOPE_DOMAIN2}${canonicalize(unsigned)}`),
|
|
7971
8173
|
ed25519PublicKeyFromDidKey(envelope.signature.signerDid),
|
|
@@ -8032,13 +8234,13 @@ function createRegisteredPolicyAuthority(options) {
|
|
|
8032
8234
|
}
|
|
8033
8235
|
|
|
8034
8236
|
// src/cache.ts
|
|
8035
|
-
var
|
|
8237
|
+
var import_sha2563 = require("@noble/hashes/sha256");
|
|
8036
8238
|
function cacheKey(key) {
|
|
8037
8239
|
if (!key.cid || !key.expiresAt) throw new Error("share cache keys require CID and expiry");
|
|
8038
8240
|
return `${key.cid}\0${key.expiresAt}`;
|
|
8039
8241
|
}
|
|
8040
8242
|
function digest2(bytes3) {
|
|
8041
|
-
return (0,
|
|
8243
|
+
return (0, import_sha2563.sha256)(bytes3);
|
|
8042
8244
|
}
|
|
8043
8245
|
function equalBytes(left, right) {
|
|
8044
8246
|
if (left.byteLength !== right.byteLength) return false;
|
|
@@ -8093,7 +8295,7 @@ var MemoryShareCache = class {
|
|
|
8093
8295
|
};
|
|
8094
8296
|
|
|
8095
8297
|
// src/delivery.ts
|
|
8096
|
-
var
|
|
8298
|
+
var import_ed255197 = require("@noble/curves/ed25519");
|
|
8097
8299
|
var SHARE_DELIVERY_AUTHORIZATION_DOMAIN = "xyz.tinycloud.share/delivery-authorization/v2\0";
|
|
8098
8300
|
function isCanonicalHttpsOrigin2(value) {
|
|
8099
8301
|
let url;
|
|
@@ -8185,7 +8387,7 @@ function validateShareDeliveryAuthorizationBytes(bytesValue, expected) {
|
|
|
8185
8387
|
if (publicKey.length !== 32) throw new Error("share delivery authorization proof key is invalid");
|
|
8186
8388
|
const signatureBytes = fromB642(proof.signature);
|
|
8187
8389
|
const signedBytes = new TextEncoder().encode(`${SHARE_DELIVERY_AUTHORIZATION_DOMAIN}${canonicalize(authorization)}`);
|
|
8188
|
-
if (signatureBytes.length !== 64 || !
|
|
8390
|
+
if (signatureBytes.length !== 64 || !import_ed255197.ed25519.verify(signatureBytes, signedBytes, publicKey)) throw new Error("share delivery authorization proof signature is invalid");
|
|
8189
8391
|
return { authorization, proof };
|
|
8190
8392
|
}
|
|
8191
8393
|
function shareDeliveryTrustedKid(publicKey) {
|
|
@@ -8484,7 +8686,8 @@ async function migrateShare(input) {
|
|
|
8484
8686
|
}
|
|
8485
8687
|
|
|
8486
8688
|
// src/recipient.ts
|
|
8487
|
-
var
|
|
8689
|
+
var import_ed255198 = require("@noble/curves/ed25519");
|
|
8690
|
+
var import_sha2564 = require("@noble/hashes/sha256");
|
|
8488
8691
|
|
|
8489
8692
|
// src/protocol.ts
|
|
8490
8693
|
var SHARE_V2_PROTOCOL = Object.freeze({
|
|
@@ -8529,7 +8732,7 @@ var DOMAIN = SHARE_V2_PROTOCOL.challengeDomain;
|
|
|
8529
8732
|
var PRESENTATION_DOMAIN = SHARE_V2_PROTOCOL.sessionDomain;
|
|
8530
8733
|
var SESSION_DOMAIN = SHARE_V2_PROTOCOL.sessionDomain;
|
|
8531
8734
|
var INVOCATION_DOMAIN = SHARE_V2_PROTOCOL.invocationDomain;
|
|
8532
|
-
function
|
|
8735
|
+
function object2(value, label) {
|
|
8533
8736
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
|
|
8534
8737
|
return value;
|
|
8535
8738
|
}
|
|
@@ -8539,6 +8742,42 @@ function bytes2(value, label) {
|
|
|
8539
8742
|
if (decoded.length !== 64 || toBase64Url(decoded) !== value) throw new Error(`${label} is invalid`);
|
|
8540
8743
|
return decoded;
|
|
8541
8744
|
}
|
|
8745
|
+
var BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
8746
|
+
function toBase64(bytes3) {
|
|
8747
|
+
let out = "";
|
|
8748
|
+
for (let i = 0; i < bytes3.length; i += 3) {
|
|
8749
|
+
const b0 = bytes3[i];
|
|
8750
|
+
const b1 = i + 1 < bytes3.length ? bytes3[i + 1] : 0;
|
|
8751
|
+
const b2 = i + 2 < bytes3.length ? bytes3[i + 2] : 0;
|
|
8752
|
+
out += BASE64_ALPHABET[b0 >> 2 & 63];
|
|
8753
|
+
out += BASE64_ALPHABET[(b0 << 4 | b1 >> 4) & 63];
|
|
8754
|
+
out += i + 1 < bytes3.length ? BASE64_ALPHABET[(b1 << 2 | b2 >> 6) & 63] : "=";
|
|
8755
|
+
out += i + 2 < bytes3.length ? BASE64_ALPHABET[b2 & 63] : "=";
|
|
8756
|
+
}
|
|
8757
|
+
return out;
|
|
8758
|
+
}
|
|
8759
|
+
function fromBase64(value, label) {
|
|
8760
|
+
if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)) {
|
|
8761
|
+
throw new Error(`${label} is invalid`);
|
|
8762
|
+
}
|
|
8763
|
+
const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
|
|
8764
|
+
const out = new Uint8Array(value.length / 4 * 3 - padding);
|
|
8765
|
+
let outIdx = 0;
|
|
8766
|
+
for (let i = 0; i < value.length; i += 4) {
|
|
8767
|
+
const v0 = BASE64_ALPHABET.indexOf(value[i]);
|
|
8768
|
+
const v1 = BASE64_ALPHABET.indexOf(value[i + 1]);
|
|
8769
|
+
const v2 = value[i + 2] === "=" ? 0 : BASE64_ALPHABET.indexOf(value[i + 2]);
|
|
8770
|
+
const v3 = value[i + 3] === "=" ? 0 : BASE64_ALPHABET.indexOf(value[i + 3]);
|
|
8771
|
+
const b0 = v0 << 2 | v1 >> 4;
|
|
8772
|
+
const b1 = (v1 & 15) << 4 | v2 >> 2;
|
|
8773
|
+
const b2 = (v2 & 3) << 6 | v3;
|
|
8774
|
+
if (outIdx < out.length) out[outIdx++] = b0;
|
|
8775
|
+
if (outIdx < out.length) out[outIdx++] = b1;
|
|
8776
|
+
if (outIdx < out.length) out[outIdx++] = b2;
|
|
8777
|
+
}
|
|
8778
|
+
if (toBase64(out) !== value) throw new Error(`${label} is invalid`);
|
|
8779
|
+
return out;
|
|
8780
|
+
}
|
|
8542
8781
|
async function digest3(value) {
|
|
8543
8782
|
return toBase64Url(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(canonicalize(value)))));
|
|
8544
8783
|
}
|
|
@@ -8555,12 +8794,12 @@ function trustedPublicKey(value) {
|
|
|
8555
8794
|
return value.invitationPublicKey;
|
|
8556
8795
|
}
|
|
8557
8796
|
async function verifyWrapped(value, key, domain, trust) {
|
|
8558
|
-
const wrapper =
|
|
8797
|
+
const wrapper = object2(value, `${key} response`);
|
|
8559
8798
|
if (Object.keys(wrapper).length !== 2 || !Object.hasOwn(wrapper, key) || !Object.hasOwn(wrapper, "proof")) throw new Error(`${key} response is invalid`);
|
|
8560
|
-
const artifact =
|
|
8561
|
-
const proof =
|
|
8799
|
+
const artifact = object2(wrapper[key], `${key} artifact`);
|
|
8800
|
+
const proof = object2(wrapper.proof, `${key} proof`);
|
|
8562
8801
|
if (proof.alg !== "EdDSA" || proof.kid !== trust.invitationKid) throw new Error(`${key} proof is invalid`);
|
|
8563
|
-
if (!
|
|
8802
|
+
if (!import_ed255198.ed25519.verify(bytes2(proof.signature, `${key} signature`), new TextEncoder().encode(`${domain}${canonicalize(artifact)}`), trustedPublicKey(trust))) throw new Error(`${key} proof is invalid`);
|
|
8564
8803
|
return artifact;
|
|
8565
8804
|
}
|
|
8566
8805
|
function nativeAction(action) {
|
|
@@ -8572,6 +8811,42 @@ function uiAction(action) {
|
|
|
8572
8811
|
function selectedAction(envelope) {
|
|
8573
8812
|
return envelope.actions.includes("list") ? "tinycloud.kv/list" : envelope.actions.includes("edit") ? "tinycloud.kv/put" : "tinycloud.kv/get";
|
|
8574
8813
|
}
|
|
8814
|
+
function hex(value) {
|
|
8815
|
+
return [...value].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
8816
|
+
}
|
|
8817
|
+
function canonicalHashHex(value) {
|
|
8818
|
+
return hex((0, import_sha2564.sha256)(new TextEncoder().encode(canonicalize(value))));
|
|
8819
|
+
}
|
|
8820
|
+
async function aesGcmDecrypt(key, blob) {
|
|
8821
|
+
if (key.length !== 32 || blob.length < 28) throw new Error("encrypted content is malformed");
|
|
8822
|
+
const cryptoKey = await crypto.subtle.importKey("raw", key, "AES-GCM", false, ["decrypt"]);
|
|
8823
|
+
return new Uint8Array(await crypto.subtle.decrypt({ name: "AES-GCM", iv: blob.slice(0, 12) }, cryptoKey, blob.slice(12)));
|
|
8824
|
+
}
|
|
8825
|
+
async function aesGcmEncrypt(key, plaintext) {
|
|
8826
|
+
if (key.length !== 32) throw new Error("encrypted content key is malformed");
|
|
8827
|
+
const nonce = crypto.getRandomValues(new Uint8Array(12));
|
|
8828
|
+
const cryptoKey = await crypto.subtle.importKey("raw", key, "AES-GCM", false, ["encrypt"]);
|
|
8829
|
+
const ciphertext = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv: nonce }, cryptoKey, plaintext));
|
|
8830
|
+
const output = new Uint8Array(nonce.length + ciphertext.length);
|
|
8831
|
+
output.set(nonce);
|
|
8832
|
+
output.set(ciphertext, nonce.length);
|
|
8833
|
+
return output;
|
|
8834
|
+
}
|
|
8835
|
+
function parseV3InlineEncryptedEnvelope(bytes3, expected) {
|
|
8836
|
+
let value;
|
|
8837
|
+
try {
|
|
8838
|
+
value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes3));
|
|
8839
|
+
} catch {
|
|
8840
|
+
throw new Error("encrypted content envelope is malformed");
|
|
8841
|
+
}
|
|
8842
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("encrypted content envelope is malformed");
|
|
8843
|
+
const record2 = value;
|
|
8844
|
+
const allowed = ["v", "networkId", "alg", "keyVersion", "encryptedSymmetricKey", "encryptedSymmetricKeyHash", "ciphertext", "metadata"];
|
|
8845
|
+
if (Object.keys(record2).some((key) => !allowed.includes(key)) || record2.v !== 1 || record2.networkId !== expected.encryptionNetwork || record2.alg !== "x25519-aes256gcm/v1" || record2.keyVersion !== expected.contentSource.keyVersion || typeof record2.encryptedSymmetricKey !== "string" || typeof record2.encryptedSymmetricKeyHash !== "string" || record2.encryptedSymmetricKeyHash !== expected.contentSource.encryptedSymmetricKeyDigestHex || record2.encryptedSymmetricKeyHash !== canonicalHashHex(record2.encryptedSymmetricKey) || typeof record2.ciphertext !== "string" || record2.metadata !== void 0 && (record2.metadata === null || typeof record2.metadata !== "object" || Array.isArray(record2.metadata) || Object.values(record2.metadata).some((entry) => typeof entry !== "string"))) {
|
|
8846
|
+
throw new Error("encrypted content envelope binding is invalid");
|
|
8847
|
+
}
|
|
8848
|
+
return record2;
|
|
8849
|
+
}
|
|
8575
8850
|
async function verifyDetachedResponse(response, trust) {
|
|
8576
8851
|
let value;
|
|
8577
8852
|
try {
|
|
@@ -8579,12 +8854,12 @@ async function verifyDetachedResponse(response, trust) {
|
|
|
8579
8854
|
} catch {
|
|
8580
8855
|
throw new Error("share read response is invalid");
|
|
8581
8856
|
}
|
|
8582
|
-
const record2 =
|
|
8583
|
-
const proof =
|
|
8857
|
+
const record2 = object2(value, "share read response");
|
|
8858
|
+
const proof = object2(record2.proof, "share read detached proof");
|
|
8584
8859
|
if (proof.alg !== "EdDSA" || proof.kid !== trust.invitationKid) throw new Error("share read detached proof is invalid");
|
|
8585
8860
|
const unsigned = { ...record2 };
|
|
8586
8861
|
delete unsigned.proof;
|
|
8587
|
-
if (!
|
|
8862
|
+
if (!import_ed255198.ed25519.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");
|
|
8588
8863
|
}
|
|
8589
8864
|
async function post(fetchFn, origin, path, body) {
|
|
8590
8865
|
const response = await fetchFn(new URL(path, origin), { method: "POST", redirect: "error", headers: { accept: "application/json", "content-type": "application/json" }, body: JSON.stringify(body) });
|
|
@@ -8600,15 +8875,19 @@ var ShareRecipientClient = class {
|
|
|
8600
8875
|
fetchFn;
|
|
8601
8876
|
session;
|
|
8602
8877
|
signer;
|
|
8878
|
+
nativeSigner;
|
|
8603
8879
|
holderProof;
|
|
8880
|
+
v3Authorization;
|
|
8881
|
+
v3ContentKey;
|
|
8882
|
+
v3ContentEnvelope;
|
|
8604
8883
|
async beginChallenge(envelope) {
|
|
8605
8884
|
const authority = envelope.ownerAuthority;
|
|
8606
8885
|
if (authority === void 0) throw new Error("addressed owner authority is required");
|
|
8607
|
-
const outer =
|
|
8608
|
-
const enforcement =
|
|
8609
|
-
const target =
|
|
8610
|
-
const outerResource =
|
|
8611
|
-
const source =
|
|
8886
|
+
const outer = object2(authority.outerEnvelope, "owner authority outer envelope");
|
|
8887
|
+
const enforcement = object2(authority.enforcementDelegation, "owner authority enforcement delegation");
|
|
8888
|
+
const target = object2(outer.target, "owner authority target");
|
|
8889
|
+
const outerResource = object2(outer.resource, "owner authority resource");
|
|
8890
|
+
const source = object2(outer.contentSource, "owner authority content source");
|
|
8612
8891
|
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");
|
|
8613
8892
|
const actions = [...outer.actions];
|
|
8614
8893
|
const action = selectedAction(envelope);
|
|
@@ -8621,11 +8900,11 @@ var ShareRecipientClient = class {
|
|
|
8621
8900
|
async establish(envelope) {
|
|
8622
8901
|
const authority = envelope.ownerAuthority;
|
|
8623
8902
|
if (authority === void 0) throw new Error("addressed owner authority is required");
|
|
8624
|
-
const outer =
|
|
8625
|
-
const enforcement =
|
|
8626
|
-
const target =
|
|
8627
|
-
const outerResource =
|
|
8628
|
-
const source =
|
|
8903
|
+
const outer = object2(authority.outerEnvelope, "owner authority outer envelope");
|
|
8904
|
+
const enforcement = object2(authority.enforcementDelegation, "owner authority enforcement delegation");
|
|
8905
|
+
const target = object2(outer.target, "owner authority target");
|
|
8906
|
+
const outerResource = object2(outer.resource, "owner authority resource");
|
|
8907
|
+
const source = object2(outer.contentSource, "owner authority content source");
|
|
8629
8908
|
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");
|
|
8630
8909
|
const actions = [...outer.actions];
|
|
8631
8910
|
const action = selectedAction(envelope);
|
|
@@ -8637,6 +8916,7 @@ var ShareRecipientClient = class {
|
|
|
8637
8916
|
const material = await this.options.buildPresentation({ challenge, envelope, policy: {} });
|
|
8638
8917
|
this.holderProof = material.proof;
|
|
8639
8918
|
this.signer = material.sign;
|
|
8919
|
+
this.nativeSigner = material.sign;
|
|
8640
8920
|
if (this.signer === void 0) throw new Error("share holder signer is required");
|
|
8641
8921
|
const presentation = {
|
|
8642
8922
|
type: "TinyCloudSharePolicyPresentation",
|
|
@@ -8672,7 +8952,7 @@ var ShareRecipientClient = class {
|
|
|
8672
8952
|
if (this.session === void 0) await this.establish(envelope);
|
|
8673
8953
|
const response = await this.nativeInvoke({ action: "get", resource: envelope.resource });
|
|
8674
8954
|
if (!response.ok) throw new Error("share recipient read was rejected");
|
|
8675
|
-
const value =
|
|
8955
|
+
const value = object2(await response.json(), "share read response");
|
|
8676
8956
|
const expectedAction = nativeAction("read");
|
|
8677
8957
|
if (value.type !== "TinyCloudShareInvokeResponse" || value.version !== 2 || value.action !== expectedAction || value.resource !== envelope.resource.path || typeof value.content !== "string" || typeof value.bodyDigest !== "string" || !/^[A-Za-z0-9_-]{43}$/.test(value.bodyDigest) || !Object.hasOwn(value, "proof")) throw new Error("share read response is invalid");
|
|
8678
8958
|
const content = fromBase64Url(value.content);
|
|
@@ -8681,18 +8961,110 @@ var ShareRecipientClient = class {
|
|
|
8681
8961
|
}
|
|
8682
8962
|
async establishPolicySession() {
|
|
8683
8963
|
if (this.session !== void 0) return this.session;
|
|
8684
|
-
|
|
8964
|
+
const envelope = this.options.envelope;
|
|
8965
|
+
if (envelope.version === 3) return this.establishV3(envelope);
|
|
8966
|
+
return this.establish(envelope);
|
|
8967
|
+
}
|
|
8968
|
+
async establishV3(envelope) {
|
|
8969
|
+
if (this.options.buildPresentation === void 0) throw new Error("share presentation builder is required");
|
|
8970
|
+
const challengeResponse = await this.fetchFn(new URL("/share/v3/policy/challenges", this.options.nodeOrigin), {
|
|
8971
|
+
method: "POST",
|
|
8972
|
+
redirect: "error",
|
|
8973
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
8974
|
+
body: JSON.stringify({ policyCid: envelope.policyCid, recipientDid: this.options.holderDid, requestedCapabilities: envelope.policy.capabilityCeiling })
|
|
8975
|
+
});
|
|
8976
|
+
if (!challengeResponse.ok) throw new Error(`v3 policy challenge rejected (${challengeResponse.status})`);
|
|
8977
|
+
const challenge = object2(await challengeResponse.json(), "v3 policy challenge");
|
|
8978
|
+
if (typeof challenge.challengeId !== "string" || typeof challenge.nonce !== "string" || challenge.policyCid !== envelope.policyCid || challenge.recipientDid !== this.options.holderDid) throw new Error("v3 policy challenge binding mismatch");
|
|
8979
|
+
const material = await this.options.buildPresentation({
|
|
8980
|
+
challenge,
|
|
8981
|
+
envelope,
|
|
8982
|
+
policy: envelope.policy
|
|
8983
|
+
});
|
|
8984
|
+
if (material.sign === void 0 || material.claim === void 0 || material.presentation === void 0) throw new Error("v3 ceremony requires a recipient signer, claim, and presentation");
|
|
8985
|
+
const delegationResponse = await this.fetchFn(new URL("/share/v3/policy/delegations", this.options.nodeOrigin), {
|
|
8986
|
+
method: "POST",
|
|
8987
|
+
redirect: "error",
|
|
8988
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
8989
|
+
body: JSON.stringify({ policyCid: envelope.policyCid, challengeId: challenge.challengeId, nonce: challenge.nonce, claim: material.claim, presentation: material.presentation })
|
|
8990
|
+
});
|
|
8991
|
+
if (!delegationResponse.ok) throw new Error(`v3 policy delegation rejected (${delegationResponse.status})`);
|
|
8992
|
+
const delegation = object2(await delegationResponse.json(), "v3 policy delegation");
|
|
8993
|
+
if (delegation.admitted !== true || typeof delegation.sessionCid !== "string" || typeof delegation.authorization !== "string") throw new Error("v3 policy delegation response is not admitted");
|
|
8994
|
+
const compact = verifyCompactUcanAuthorization(delegation.authorization, delegation.sessionCid);
|
|
8995
|
+
const fact = compact.payload.fct[0];
|
|
8996
|
+
if (compact.payload.aud !== this.options.holderDid || fact.profile !== "policy-session-ucan/v1" || fact.policyCid !== envelope.policyCid || fact.recipientDid !== this.options.holderDid || fact.policyDelegationCid !== envelope.policyRoot.cid || fact.enforcementDelegationCid !== envelope.enforcementRoot.cid || compact.payload.prf.length !== 2 || compact.payload.prf[0] !== envelope.policyRoot.cid || compact.payload.prf[1] !== envelope.enforcementRoot.cid || compact.payload.exp - compact.payload.nbf > 60) throw new Error("v3 policy delegation signed binding mismatch");
|
|
8997
|
+
const imported = await this.fetchFn(new URL("/delegate", this.options.nodeOrigin), { method: "POST", redirect: "error", headers: { Authorization: delegation.authorization } });
|
|
8998
|
+
if (!imported.ok) throw new Error(`ordinary delegation import rejected (${imported.status})`);
|
|
8999
|
+
this.signer = material.sign;
|
|
9000
|
+
this.v3Authorization = delegation.authorization;
|
|
9001
|
+
this.session = { sessionId: delegation.sessionCid, expiresAt: new Date(compact.payload.exp * 1e3).toISOString(), actions: envelope.actions, resource: envelope.resource };
|
|
9002
|
+
return this.session;
|
|
9003
|
+
}
|
|
9004
|
+
/** Decrypt a v3 KV value through a fresh recipient-signed decrypt invocation. */
|
|
9005
|
+
async decryptV3Content(bytes3) {
|
|
9006
|
+
const envelope = this.options.envelope;
|
|
9007
|
+
const signer = this.nativeSigner ?? this.signer;
|
|
9008
|
+
if (envelope.version !== 3 || this.session === void 0 || this.v3Authorization === void 0 || signer === void 0) throw new Error("v3 policy session signer is required");
|
|
9009
|
+
const encrypted = parseV3InlineEncryptedEnvelope(bytes3, envelope);
|
|
9010
|
+
const receiverPrivateKey = crypto.getRandomValues(new Uint8Array(32));
|
|
9011
|
+
const receiverPublicKey = toBase64Url(import_ed255198.x25519.getPublicKey(receiverPrivateKey));
|
|
9012
|
+
const receiverPublicKeyHash = canonicalHashHex(receiverPublicKey);
|
|
9013
|
+
const body = { type: "tinycloud.encryption.decrypt/v1", targetNode: envelope.target.nodeAudience, networkId: encrypted.networkId, alg: encrypted.alg, keyVersion: encrypted.keyVersion, encryptedSymmetricKey: encrypted.encryptedSymmetricKey, encryptedSymmetricKeyHash: encrypted.encryptedSymmetricKeyHash, receiverPublicKey, receiverPublicKeyHash };
|
|
9014
|
+
const bodyHash = hex((0, import_sha2564.sha256)(new TextEncoder().encode(canonicalize(body))));
|
|
9015
|
+
const session = verifyCompactUcanAuthorization(this.v3Authorization, this.session.sessionId);
|
|
9016
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
9017
|
+
const invocation = await signCompactUcanAuthorization({
|
|
9018
|
+
issuerDid: this.options.holderDid,
|
|
9019
|
+
audienceDid: envelope.target.nodeAudience,
|
|
9020
|
+
attenuation: { [encrypted.networkId]: { "tinycloud.encryption/decrypt": [{}] } },
|
|
9021
|
+
facts: [{ type: body.type, targetNode: body.targetNode, networkId: body.networkId, bodyHash, encryptedSymmetricKeyHash: body.encryptedSymmetricKeyHash, receiverPublicKeyHash, alg: body.alg, keyVersion: body.keyVersion }],
|
|
9022
|
+
proofs: [session.cid],
|
|
9023
|
+
notBefore: now,
|
|
9024
|
+
expiresAt: Math.min(now + 60, session.payload.exp),
|
|
9025
|
+
nonce: toBase64Url(crypto.getRandomValues(new Uint8Array(16))),
|
|
9026
|
+
sign: signer
|
|
9027
|
+
});
|
|
9028
|
+
try {
|
|
9029
|
+
const response = await this.fetchFn(new URL("/invoke", this.options.nodeOrigin), { method: "POST", redirect: "error", headers: { accept: "application/json", "content-type": "application/json", Authorization: invocation.authorization }, body: JSON.stringify(body) });
|
|
9030
|
+
if (!response.ok) throw new Error(`v3 decrypt invocation rejected (${response.status})`);
|
|
9031
|
+
const value = object2(await response.json(), "v3 decrypt response");
|
|
9032
|
+
const allowed = ["type", "targetNode", "networkId", "invocationCid", "encryptedSymmetricKeyHash", "receiverPublicKeyHash", "wrappedKey", "alg", "keyVersion", "requestHash", "nodeId", "nodeSignature"];
|
|
9033
|
+
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 !== hex((0, import_sha2564.sha256)(new TextEncoder().encode(`${invocation.cid}${bodyHash}`))) || typeof value.wrappedKey !== "string" || typeof value.nodeSignature !== "string") throw new Error("v3 decrypt response binding is invalid");
|
|
9034
|
+
const unsigned = { ...value };
|
|
9035
|
+
delete unsigned.nodeSignature;
|
|
9036
|
+
const signature = fromBase64(value.nodeSignature, "v3 decrypt response signature");
|
|
9037
|
+
if (signature.length !== 64 || !import_ed255198.ed25519.verify(signature, new TextEncoder().encode(canonicalize(unsigned)), ed25519PublicKeyFromDidKey(body.targetNode), { zip215: false })) throw new Error("v3 decrypt response signature is invalid");
|
|
9038
|
+
const wrapped = fromBase64(value.wrappedKey, "v3 wrapped content key");
|
|
9039
|
+
if (wrapped.length < 60) throw new Error("v3 wrapped content key is malformed");
|
|
9040
|
+
const shared = import_ed255198.x25519.getSharedSecret(receiverPrivateKey, wrapped.slice(0, 32));
|
|
9041
|
+
const symmetricKey = await aesGcmDecrypt(shared, wrapped.slice(32));
|
|
9042
|
+
shared.fill(0);
|
|
9043
|
+
if (symmetricKey.length !== 32) throw new Error("v3 content key is malformed");
|
|
9044
|
+
const plaintext = await aesGcmDecrypt(symmetricKey, fromBase64Url(encrypted.ciphertext));
|
|
9045
|
+
this.v3ContentKey?.fill(0);
|
|
9046
|
+
this.v3ContentKey = symmetricKey;
|
|
9047
|
+
this.v3ContentEnvelope = encrypted;
|
|
9048
|
+
return { bytes: plaintext, mediaType: encrypted.metadata?.contentType ?? envelope.metadata.mediaType ?? "application/octet-stream" };
|
|
9049
|
+
} finally {
|
|
9050
|
+
receiverPrivateKey.fill(0);
|
|
9051
|
+
}
|
|
9052
|
+
}
|
|
9053
|
+
/** Re-encrypt edited v3 content with the admitted content key. */
|
|
9054
|
+
async encryptV3Content(bytes3, mediaType) {
|
|
9055
|
+
if (this.options.envelope.version !== 3 || this.v3ContentKey === void 0 || this.v3ContentEnvelope === void 0) throw new Error("v3 content must be decrypted before it can be saved");
|
|
9056
|
+
return new TextEncoder().encode(canonicalize({ ...this.v3ContentEnvelope, ciphertext: toBase64Url(await aesGcmEncrypt(this.v3ContentKey, bytes3)), metadata: { ...this.v3ContentEnvelope.metadata ?? {}, contentType: mediaType } }));
|
|
8685
9057
|
}
|
|
8686
9058
|
async resumeWithProof(envelope, resumeToken, proof) {
|
|
8687
|
-
const material =
|
|
8688
|
-
const presentation =
|
|
8689
|
-
const presentationProof =
|
|
9059
|
+
const material = object2(proof, "share authorization proof");
|
|
9060
|
+
const presentation = object2(material.presentation, "share presentation");
|
|
9061
|
+
const presentationProof = object2(material.presentationProof, "share presentation proof");
|
|
8690
9062
|
const nonce = material.nonce;
|
|
8691
9063
|
const credential = material.credential;
|
|
8692
9064
|
const holderDid = material.holderDid;
|
|
8693
9065
|
const holderBinding = material.holderBinding;
|
|
8694
9066
|
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");
|
|
8695
|
-
const value =
|
|
9067
|
+
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");
|
|
8696
9068
|
const session = await verifyWrapped(value, "session", SESSION_DOMAIN, this.options.trustedNode);
|
|
8697
9069
|
const authority = envelope.ownerAuthority;
|
|
8698
9070
|
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");
|
|
@@ -8706,27 +9078,61 @@ var ShareRecipientClient = class {
|
|
|
8706
9078
|
}
|
|
8707
9079
|
async nativeInvoke(request) {
|
|
8708
9080
|
if (this.session === void 0) throw new Error("share policy session is required");
|
|
8709
|
-
|
|
8710
|
-
|
|
9081
|
+
const envelope = this.options.envelope;
|
|
9082
|
+
if (envelope.version === 3) return this.nativeInvokeV3(request, envelope);
|
|
9083
|
+
if (envelope.ownerAuthority === void 0 || this.signer === void 0 || this.holderProof === void 0) throw new Error("share holder signer is required");
|
|
9084
|
+
const authority = envelope.ownerAuthority;
|
|
8711
9085
|
const action = request.action === "list" ? "tinycloud.kv/list" : request.action === "put" ? "tinycloud.kv/put" : request.action === "metadata" ? "tinycloud.kv/metadata" : "tinycloud.kv/get";
|
|
8712
9086
|
const resource = typeof request.resource?.path === "string" ? request.resource.path : this.session.resource.path;
|
|
8713
9087
|
const actions = [...new Set(this.session.actions.map(nativeAction).concat(action === "tinycloud.kv/metadata" ? [action] : []))].sort();
|
|
8714
9088
|
const bodyBytes = request.body === void 0 ? void 0 : Uint8Array.from(request.body);
|
|
8715
9089
|
const bodyDigest = bodyBytes === void 0 ? void 0 : toBase64Url(new Uint8Array(await crypto.subtle.digest("SHA-256", bodyBytes)));
|
|
8716
|
-
const outer =
|
|
8717
|
-
const enforcement =
|
|
8718
|
-
const invocationBase = { type: "TinyCloudShareReadInvocation", version: 2, sessionId: this.session.sessionId, envelopeCid: authority.envelopeCid, shareCid: authority.shareCid, shareId:
|
|
9090
|
+
const outer = object2(authority.outerEnvelope, "owner authority outer envelope");
|
|
9091
|
+
const enforcement = object2(authority.enforcementDelegation, "owner authority enforcement delegation");
|
|
9092
|
+
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))) };
|
|
8719
9093
|
const requestBodyDigest = await digest3(invocationBase);
|
|
8720
9094
|
const invocation = { ...invocationBase, requestBodyDigest };
|
|
8721
9095
|
const proof = { ...this.holderProof, signature: toBase64Url(await this.signer(new TextEncoder().encode(`${INVOCATION_DOMAIN}${canonicalize(invocation)}`))) };
|
|
8722
|
-
const signedRequest = { sessionId: this.session.sessionId, envelopeCid: authority.envelopeCid, shareCid: authority.shareCid, shareId:
|
|
9096
|
+
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 };
|
|
8723
9097
|
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 } }) });
|
|
8724
9098
|
if (response.ok) await verifyDetachedResponse(response, this.options.trustedNode);
|
|
8725
9099
|
return response;
|
|
8726
9100
|
}
|
|
9101
|
+
async nativeInvokeV3(request, envelope) {
|
|
9102
|
+
const signer = this.nativeSigner ?? this.signer;
|
|
9103
|
+
if (this.session === void 0 || this.v3Authorization === void 0 || signer === void 0) throw new Error("v3 policy session signer is required");
|
|
9104
|
+
const session = verifyCompactUcanAuthorization(this.v3Authorization, this.session.sessionId);
|
|
9105
|
+
if (session.payload.aud !== this.options.holderDid) throw new Error("v3 session recipient mismatch");
|
|
9106
|
+
const action = request.action === "list" ? "tinycloud.kv/list" : request.action === "put" ? "tinycloud.kv/put" : request.action === "metadata" ? "tinycloud.kv/metadata" : "tinycloud.kv/get";
|
|
9107
|
+
const path = typeof request.resource?.path === "string" ? request.resource.path.replace(/^\//, "").replace(/\/$/, "") : envelope.resource.path;
|
|
9108
|
+
const capability = envelope.policy.capabilityCeiling.find((candidate) => candidate.kind === "kv");
|
|
9109
|
+
if (capability === void 0 || !capability.actions.includes(action)) throw new Error("v3 requested capability is not in the signed policy");
|
|
9110
|
+
const marker = "/kv/";
|
|
9111
|
+
const split = capability.resource.indexOf(marker);
|
|
9112
|
+
if (split < 0) throw new Error("v3 KV resource is invalid");
|
|
9113
|
+
const root = capability.resource.slice(split + marker.length).replace(/\/$/, "");
|
|
9114
|
+
if (path !== root && !(capability.selector === "prefix" && path.startsWith(`${root}/`))) throw new Error("v3 KV request is outside the signed selector");
|
|
9115
|
+
const resource = `${capability.resource.slice(0, split + marker.length)}${path}`;
|
|
9116
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
9117
|
+
const invocation = await signCompactUcanAuthorization({ issuerDid: this.options.holderDid, audienceDid: envelope.target.nodeAudience, attenuation: { [resource]: { [action]: [{ type: "xyz.tinycloud.resource/selector", kind: capability.selector, value: resource }] } }, facts: [{ type: "tinycloud.policy.invocation/v1", policyCid: envelope.policyCid, sessionCid: session.cid }], proofs: [session.cid], notBefore: now, expiresAt: Math.min(now + 60, session.payload.exp), nonce: toBase64Url(crypto.getRandomValues(new Uint8Array(16))), sign: signer });
|
|
9118
|
+
const headers = new Headers({ accept: "application/json", Authorization: invocation.authorization });
|
|
9119
|
+
let body;
|
|
9120
|
+
if (action === "tinycloud.kv/put") {
|
|
9121
|
+
if (request.body === void 0) throw new Error("v3 KV put requires bytes");
|
|
9122
|
+
body = Uint8Array.from(request.body);
|
|
9123
|
+
headers.set("content-type", request.contentType ?? "application/octet-stream");
|
|
9124
|
+
if (request.ifMatch !== void 0) headers.set("if-match", request.ifMatch);
|
|
9125
|
+
}
|
|
9126
|
+
return this.fetchFn(new URL("/invoke", this.options.nodeOrigin), { method: "POST", redirect: "error", headers, ...body === void 0 ? {} : { body } });
|
|
9127
|
+
}
|
|
8727
9128
|
};
|
|
8728
9129
|
function createAddressedAuthorization(input) {
|
|
8729
|
-
const
|
|
9130
|
+
const { buildPresentation, ...options } = input;
|
|
9131
|
+
const client = (envelope) => new ShareRecipientClient({
|
|
9132
|
+
...options,
|
|
9133
|
+
envelope,
|
|
9134
|
+
...buildPresentation === void 0 ? {} : { buildPresentation: ({ challenge, envelope: candidate, policy }) => buildPresentation({ challenge, envelope: candidate, policy }) }
|
|
9135
|
+
});
|
|
8730
9136
|
return {
|
|
8731
9137
|
async begin({ envelope, method }) {
|
|
8732
9138
|
const current = client(envelope);
|
|
@@ -8740,15 +9146,15 @@ function createAddressedAuthorization(input) {
|
|
|
8740
9146
|
},
|
|
8741
9147
|
async verifyResult({ envelope, value, proof }) {
|
|
8742
9148
|
try {
|
|
8743
|
-
const wrapper =
|
|
8744
|
-
const response =
|
|
9149
|
+
const wrapper = object2(proof, "share read proof");
|
|
9150
|
+
const response = object2(wrapper.response, "share read response proof");
|
|
8745
9151
|
const envelopeAction = nativeAction("read");
|
|
8746
9152
|
if (response.type !== "TinyCloudShareInvokeResponse" || response.version !== 2 || response.action !== envelopeAction || response.resource !== envelope.resource.path || typeof response.bodyDigest !== "string" || response.bodyDigest !== value.bodyDigest || response.bodyDigest !== await digestBytes2(value.bytes)) return false;
|
|
8747
|
-
const detached =
|
|
9153
|
+
const detached = object2(wrapper.detached, "share read detached proof");
|
|
8748
9154
|
if (detached.alg !== "EdDSA" || detached.kid !== input.trustedNode.invitationKid) return false;
|
|
8749
9155
|
const unsigned = { ...response };
|
|
8750
9156
|
delete unsigned.proof;
|
|
8751
|
-
return
|
|
9157
|
+
return import_ed255198.ed25519.verify(bytes2(detached.signature, "share read signature"), new TextEncoder().encode(`${SHARE_V2_PROTOCOL.readResponseDomain}${canonicalize(unsigned)}`), trustedPublicKey(input.trustedNode));
|
|
8752
9158
|
} catch {
|
|
8753
9159
|
return false;
|
|
8754
9160
|
}
|