@tinycloud/share-sdk 0.2.0-beta.0 → 0.2.0-beta.1
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 +432 -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 +432 -62
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
}
|
|
@@ -8555,12 +8758,12 @@ function trustedPublicKey(value) {
|
|
|
8555
8758
|
return value.invitationPublicKey;
|
|
8556
8759
|
}
|
|
8557
8760
|
async function verifyWrapped(value, key, domain, trust) {
|
|
8558
|
-
const wrapper =
|
|
8761
|
+
const wrapper = object2(value, `${key} response`);
|
|
8559
8762
|
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 =
|
|
8763
|
+
const artifact = object2(wrapper[key], `${key} artifact`);
|
|
8764
|
+
const proof = object2(wrapper.proof, `${key} proof`);
|
|
8562
8765
|
if (proof.alg !== "EdDSA" || proof.kid !== trust.invitationKid) throw new Error(`${key} proof is invalid`);
|
|
8563
|
-
if (!
|
|
8766
|
+
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
8767
|
return artifact;
|
|
8565
8768
|
}
|
|
8566
8769
|
function nativeAction(action) {
|
|
@@ -8572,6 +8775,42 @@ function uiAction(action) {
|
|
|
8572
8775
|
function selectedAction(envelope) {
|
|
8573
8776
|
return envelope.actions.includes("list") ? "tinycloud.kv/list" : envelope.actions.includes("edit") ? "tinycloud.kv/put" : "tinycloud.kv/get";
|
|
8574
8777
|
}
|
|
8778
|
+
function hex(value) {
|
|
8779
|
+
return [...value].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
8780
|
+
}
|
|
8781
|
+
function canonicalHashHex(value) {
|
|
8782
|
+
return hex((0, import_sha2564.sha256)(new TextEncoder().encode(canonicalize(value))));
|
|
8783
|
+
}
|
|
8784
|
+
async function aesGcmDecrypt(key, blob) {
|
|
8785
|
+
if (key.length !== 32 || blob.length < 28) throw new Error("encrypted content is malformed");
|
|
8786
|
+
const cryptoKey = await crypto.subtle.importKey("raw", key, "AES-GCM", false, ["decrypt"]);
|
|
8787
|
+
return new Uint8Array(await crypto.subtle.decrypt({ name: "AES-GCM", iv: blob.slice(0, 12) }, cryptoKey, blob.slice(12)));
|
|
8788
|
+
}
|
|
8789
|
+
async function aesGcmEncrypt(key, plaintext) {
|
|
8790
|
+
if (key.length !== 32) throw new Error("encrypted content key is malformed");
|
|
8791
|
+
const nonce = crypto.getRandomValues(new Uint8Array(12));
|
|
8792
|
+
const cryptoKey = await crypto.subtle.importKey("raw", key, "AES-GCM", false, ["encrypt"]);
|
|
8793
|
+
const ciphertext = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv: nonce }, cryptoKey, plaintext));
|
|
8794
|
+
const output = new Uint8Array(nonce.length + ciphertext.length);
|
|
8795
|
+
output.set(nonce);
|
|
8796
|
+
output.set(ciphertext, nonce.length);
|
|
8797
|
+
return output;
|
|
8798
|
+
}
|
|
8799
|
+
function parseV3InlineEncryptedEnvelope(bytes3, expected) {
|
|
8800
|
+
let value;
|
|
8801
|
+
try {
|
|
8802
|
+
value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes3));
|
|
8803
|
+
} catch {
|
|
8804
|
+
throw new Error("encrypted content envelope is malformed");
|
|
8805
|
+
}
|
|
8806
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("encrypted content envelope is malformed");
|
|
8807
|
+
const record2 = value;
|
|
8808
|
+
const allowed = ["v", "networkId", "alg", "keyVersion", "encryptedSymmetricKey", "encryptedSymmetricKeyHash", "ciphertext", "metadata"];
|
|
8809
|
+
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"))) {
|
|
8810
|
+
throw new Error("encrypted content envelope binding is invalid");
|
|
8811
|
+
}
|
|
8812
|
+
return record2;
|
|
8813
|
+
}
|
|
8575
8814
|
async function verifyDetachedResponse(response, trust) {
|
|
8576
8815
|
let value;
|
|
8577
8816
|
try {
|
|
@@ -8579,12 +8818,12 @@ async function verifyDetachedResponse(response, trust) {
|
|
|
8579
8818
|
} catch {
|
|
8580
8819
|
throw new Error("share read response is invalid");
|
|
8581
8820
|
}
|
|
8582
|
-
const record2 =
|
|
8583
|
-
const proof =
|
|
8821
|
+
const record2 = object2(value, "share read response");
|
|
8822
|
+
const proof = object2(record2.proof, "share read detached proof");
|
|
8584
8823
|
if (proof.alg !== "EdDSA" || proof.kid !== trust.invitationKid) throw new Error("share read detached proof is invalid");
|
|
8585
8824
|
const unsigned = { ...record2 };
|
|
8586
8825
|
delete unsigned.proof;
|
|
8587
|
-
if (!
|
|
8826
|
+
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
8827
|
}
|
|
8589
8828
|
async function post(fetchFn, origin, path, body) {
|
|
8590
8829
|
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 +8839,19 @@ var ShareRecipientClient = class {
|
|
|
8600
8839
|
fetchFn;
|
|
8601
8840
|
session;
|
|
8602
8841
|
signer;
|
|
8842
|
+
nativeSigner;
|
|
8603
8843
|
holderProof;
|
|
8844
|
+
v3Authorization;
|
|
8845
|
+
v3ContentKey;
|
|
8846
|
+
v3ContentEnvelope;
|
|
8604
8847
|
async beginChallenge(envelope) {
|
|
8605
8848
|
const authority = envelope.ownerAuthority;
|
|
8606
8849
|
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 =
|
|
8850
|
+
const outer = object2(authority.outerEnvelope, "owner authority outer envelope");
|
|
8851
|
+
const enforcement = object2(authority.enforcementDelegation, "owner authority enforcement delegation");
|
|
8852
|
+
const target = object2(outer.target, "owner authority target");
|
|
8853
|
+
const outerResource = object2(outer.resource, "owner authority resource");
|
|
8854
|
+
const source = object2(outer.contentSource, "owner authority content source");
|
|
8612
8855
|
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
8856
|
const actions = [...outer.actions];
|
|
8614
8857
|
const action = selectedAction(envelope);
|
|
@@ -8621,11 +8864,11 @@ var ShareRecipientClient = class {
|
|
|
8621
8864
|
async establish(envelope) {
|
|
8622
8865
|
const authority = envelope.ownerAuthority;
|
|
8623
8866
|
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 =
|
|
8867
|
+
const outer = object2(authority.outerEnvelope, "owner authority outer envelope");
|
|
8868
|
+
const enforcement = object2(authority.enforcementDelegation, "owner authority enforcement delegation");
|
|
8869
|
+
const target = object2(outer.target, "owner authority target");
|
|
8870
|
+
const outerResource = object2(outer.resource, "owner authority resource");
|
|
8871
|
+
const source = object2(outer.contentSource, "owner authority content source");
|
|
8629
8872
|
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
8873
|
const actions = [...outer.actions];
|
|
8631
8874
|
const action = selectedAction(envelope);
|
|
@@ -8637,6 +8880,7 @@ var ShareRecipientClient = class {
|
|
|
8637
8880
|
const material = await this.options.buildPresentation({ challenge, envelope, policy: {} });
|
|
8638
8881
|
this.holderProof = material.proof;
|
|
8639
8882
|
this.signer = material.sign;
|
|
8883
|
+
this.nativeSigner = material.sign;
|
|
8640
8884
|
if (this.signer === void 0) throw new Error("share holder signer is required");
|
|
8641
8885
|
const presentation = {
|
|
8642
8886
|
type: "TinyCloudSharePolicyPresentation",
|
|
@@ -8672,7 +8916,7 @@ var ShareRecipientClient = class {
|
|
|
8672
8916
|
if (this.session === void 0) await this.establish(envelope);
|
|
8673
8917
|
const response = await this.nativeInvoke({ action: "get", resource: envelope.resource });
|
|
8674
8918
|
if (!response.ok) throw new Error("share recipient read was rejected");
|
|
8675
|
-
const value =
|
|
8919
|
+
const value = object2(await response.json(), "share read response");
|
|
8676
8920
|
const expectedAction = nativeAction("read");
|
|
8677
8921
|
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
8922
|
const content = fromBase64Url(value.content);
|
|
@@ -8681,18 +8925,110 @@ var ShareRecipientClient = class {
|
|
|
8681
8925
|
}
|
|
8682
8926
|
async establishPolicySession() {
|
|
8683
8927
|
if (this.session !== void 0) return this.session;
|
|
8684
|
-
|
|
8928
|
+
const envelope = this.options.envelope;
|
|
8929
|
+
if (envelope.version === 3) return this.establishV3(envelope);
|
|
8930
|
+
return this.establish(envelope);
|
|
8931
|
+
}
|
|
8932
|
+
async establishV3(envelope) {
|
|
8933
|
+
if (this.options.buildPresentation === void 0) throw new Error("share presentation builder is required");
|
|
8934
|
+
const challengeResponse = await this.fetchFn(new URL("/share/v3/policy/challenges", this.options.nodeOrigin), {
|
|
8935
|
+
method: "POST",
|
|
8936
|
+
redirect: "error",
|
|
8937
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
8938
|
+
body: JSON.stringify({ policyCid: envelope.policyCid, recipientDid: this.options.holderDid, requestedCapabilities: envelope.policy.capabilityCeiling })
|
|
8939
|
+
});
|
|
8940
|
+
if (!challengeResponse.ok) throw new Error(`v3 policy challenge rejected (${challengeResponse.status})`);
|
|
8941
|
+
const challenge = object2(await challengeResponse.json(), "v3 policy challenge");
|
|
8942
|
+
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");
|
|
8943
|
+
const material = await this.options.buildPresentation({
|
|
8944
|
+
challenge,
|
|
8945
|
+
envelope,
|
|
8946
|
+
policy: envelope.policy
|
|
8947
|
+
});
|
|
8948
|
+
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");
|
|
8949
|
+
const delegationResponse = await this.fetchFn(new URL("/share/v3/policy/delegations", this.options.nodeOrigin), {
|
|
8950
|
+
method: "POST",
|
|
8951
|
+
redirect: "error",
|
|
8952
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
8953
|
+
body: JSON.stringify({ policyCid: envelope.policyCid, challengeId: challenge.challengeId, nonce: challenge.nonce, claim: material.claim, presentation: material.presentation })
|
|
8954
|
+
});
|
|
8955
|
+
if (!delegationResponse.ok) throw new Error(`v3 policy delegation rejected (${delegationResponse.status})`);
|
|
8956
|
+
const delegation = object2(await delegationResponse.json(), "v3 policy delegation");
|
|
8957
|
+
if (delegation.admitted !== true || typeof delegation.sessionCid !== "string" || typeof delegation.authorization !== "string") throw new Error("v3 policy delegation response is not admitted");
|
|
8958
|
+
const compact = verifyCompactUcanAuthorization(delegation.authorization, delegation.sessionCid);
|
|
8959
|
+
const fact = compact.payload.fct[0];
|
|
8960
|
+
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");
|
|
8961
|
+
const imported = await this.fetchFn(new URL("/delegate", this.options.nodeOrigin), { method: "POST", redirect: "error", headers: { Authorization: delegation.authorization } });
|
|
8962
|
+
if (!imported.ok) throw new Error(`ordinary delegation import rejected (${imported.status})`);
|
|
8963
|
+
this.signer = material.sign;
|
|
8964
|
+
this.v3Authorization = delegation.authorization;
|
|
8965
|
+
this.session = { sessionId: delegation.sessionCid, expiresAt: new Date(compact.payload.exp * 1e3).toISOString(), actions: envelope.actions, resource: envelope.resource };
|
|
8966
|
+
return this.session;
|
|
8967
|
+
}
|
|
8968
|
+
/** Decrypt a v3 KV value through a fresh recipient-signed decrypt invocation. */
|
|
8969
|
+
async decryptV3Content(bytes3) {
|
|
8970
|
+
const envelope = this.options.envelope;
|
|
8971
|
+
const signer = this.nativeSigner ?? this.signer;
|
|
8972
|
+
if (envelope.version !== 3 || this.session === void 0 || this.v3Authorization === void 0 || signer === void 0) throw new Error("v3 policy session signer is required");
|
|
8973
|
+
const encrypted = parseV3InlineEncryptedEnvelope(bytes3, envelope);
|
|
8974
|
+
const receiverPrivateKey = crypto.getRandomValues(new Uint8Array(32));
|
|
8975
|
+
const receiverPublicKey = toBase64Url(import_ed255198.x25519.getPublicKey(receiverPrivateKey));
|
|
8976
|
+
const receiverPublicKeyHash = canonicalHashHex(receiverPublicKey);
|
|
8977
|
+
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 };
|
|
8978
|
+
const bodyHash = hex((0, import_sha2564.sha256)(new TextEncoder().encode(canonicalize(body))));
|
|
8979
|
+
const session = verifyCompactUcanAuthorization(this.v3Authorization, this.session.sessionId);
|
|
8980
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
8981
|
+
const invocation = await signCompactUcanAuthorization({
|
|
8982
|
+
issuerDid: this.options.holderDid,
|
|
8983
|
+
audienceDid: envelope.target.nodeAudience,
|
|
8984
|
+
attenuation: { [encrypted.networkId]: { "tinycloud.encryption/decrypt": [{}] } },
|
|
8985
|
+
facts: [{ type: body.type, targetNode: body.targetNode, networkId: body.networkId, bodyHash, encryptedSymmetricKeyHash: body.encryptedSymmetricKeyHash, receiverPublicKeyHash, alg: body.alg, keyVersion: body.keyVersion }],
|
|
8986
|
+
proofs: [session.cid],
|
|
8987
|
+
notBefore: now,
|
|
8988
|
+
expiresAt: Math.min(now + 60, session.payload.exp),
|
|
8989
|
+
nonce: toBase64Url(crypto.getRandomValues(new Uint8Array(16))),
|
|
8990
|
+
sign: signer
|
|
8991
|
+
});
|
|
8992
|
+
try {
|
|
8993
|
+
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) });
|
|
8994
|
+
if (!response.ok) throw new Error(`v3 decrypt invocation rejected (${response.status})`);
|
|
8995
|
+
const value = object2(await response.json(), "v3 decrypt response");
|
|
8996
|
+
const allowed = ["type", "targetNode", "networkId", "invocationCid", "encryptedSymmetricKeyHash", "receiverPublicKeyHash", "wrappedKey", "alg", "keyVersion", "requestHash", "nodeId", "nodeSignature"];
|
|
8997
|
+
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");
|
|
8998
|
+
const unsigned = { ...value };
|
|
8999
|
+
delete unsigned.nodeSignature;
|
|
9000
|
+
const signature = fromBase64Url(value.nodeSignature);
|
|
9001
|
+
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");
|
|
9002
|
+
const wrapped = fromBase64Url(value.wrappedKey);
|
|
9003
|
+
if (wrapped.length < 60) throw new Error("v3 wrapped content key is malformed");
|
|
9004
|
+
const shared = import_ed255198.x25519.getSharedSecret(receiverPrivateKey, wrapped.slice(0, 32));
|
|
9005
|
+
const symmetricKey = await aesGcmDecrypt(shared, wrapped.slice(32));
|
|
9006
|
+
shared.fill(0);
|
|
9007
|
+
if (symmetricKey.length !== 32) throw new Error("v3 content key is malformed");
|
|
9008
|
+
const plaintext = await aesGcmDecrypt(symmetricKey, fromBase64Url(encrypted.ciphertext));
|
|
9009
|
+
this.v3ContentKey?.fill(0);
|
|
9010
|
+
this.v3ContentKey = symmetricKey;
|
|
9011
|
+
this.v3ContentEnvelope = encrypted;
|
|
9012
|
+
return { bytes: plaintext, mediaType: encrypted.metadata?.contentType ?? envelope.metadata.mediaType ?? "application/octet-stream" };
|
|
9013
|
+
} finally {
|
|
9014
|
+
receiverPrivateKey.fill(0);
|
|
9015
|
+
}
|
|
9016
|
+
}
|
|
9017
|
+
/** Re-encrypt edited v3 content with the admitted content key. */
|
|
9018
|
+
async encryptV3Content(bytes3, mediaType) {
|
|
9019
|
+
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");
|
|
9020
|
+
return new TextEncoder().encode(canonicalize({ ...this.v3ContentEnvelope, ciphertext: toBase64Url(await aesGcmEncrypt(this.v3ContentKey, bytes3)), metadata: { ...this.v3ContentEnvelope.metadata ?? {}, contentType: mediaType } }));
|
|
8685
9021
|
}
|
|
8686
9022
|
async resumeWithProof(envelope, resumeToken, proof) {
|
|
8687
|
-
const material =
|
|
8688
|
-
const presentation =
|
|
8689
|
-
const presentationProof =
|
|
9023
|
+
const material = object2(proof, "share authorization proof");
|
|
9024
|
+
const presentation = object2(material.presentation, "share presentation");
|
|
9025
|
+
const presentationProof = object2(material.presentationProof, "share presentation proof");
|
|
8690
9026
|
const nonce = material.nonce;
|
|
8691
9027
|
const credential = material.credential;
|
|
8692
9028
|
const holderDid = material.holderDid;
|
|
8693
9029
|
const holderBinding = material.holderBinding;
|
|
8694
9030
|
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 =
|
|
9031
|
+
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
9032
|
const session = await verifyWrapped(value, "session", SESSION_DOMAIN, this.options.trustedNode);
|
|
8697
9033
|
const authority = envelope.ownerAuthority;
|
|
8698
9034
|
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 +9042,61 @@ var ShareRecipientClient = class {
|
|
|
8706
9042
|
}
|
|
8707
9043
|
async nativeInvoke(request) {
|
|
8708
9044
|
if (this.session === void 0) throw new Error("share policy session is required");
|
|
8709
|
-
|
|
8710
|
-
|
|
9045
|
+
const envelope = this.options.envelope;
|
|
9046
|
+
if (envelope.version === 3) return this.nativeInvokeV3(request, envelope);
|
|
9047
|
+
if (envelope.ownerAuthority === void 0 || this.signer === void 0 || this.holderProof === void 0) throw new Error("share holder signer is required");
|
|
9048
|
+
const authority = envelope.ownerAuthority;
|
|
8711
9049
|
const action = request.action === "list" ? "tinycloud.kv/list" : request.action === "put" ? "tinycloud.kv/put" : request.action === "metadata" ? "tinycloud.kv/metadata" : "tinycloud.kv/get";
|
|
8712
9050
|
const resource = typeof request.resource?.path === "string" ? request.resource.path : this.session.resource.path;
|
|
8713
9051
|
const actions = [...new Set(this.session.actions.map(nativeAction).concat(action === "tinycloud.kv/metadata" ? [action] : []))].sort();
|
|
8714
9052
|
const bodyBytes = request.body === void 0 ? void 0 : Uint8Array.from(request.body);
|
|
8715
9053
|
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:
|
|
9054
|
+
const outer = object2(authority.outerEnvelope, "owner authority outer envelope");
|
|
9055
|
+
const enforcement = object2(authority.enforcementDelegation, "owner authority enforcement delegation");
|
|
9056
|
+
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
9057
|
const requestBodyDigest = await digest3(invocationBase);
|
|
8720
9058
|
const invocation = { ...invocationBase, requestBodyDigest };
|
|
8721
9059
|
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:
|
|
9060
|
+
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
9061
|
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
9062
|
if (response.ok) await verifyDetachedResponse(response, this.options.trustedNode);
|
|
8725
9063
|
return response;
|
|
8726
9064
|
}
|
|
9065
|
+
async nativeInvokeV3(request, envelope) {
|
|
9066
|
+
const signer = this.nativeSigner ?? this.signer;
|
|
9067
|
+
if (this.session === void 0 || this.v3Authorization === void 0 || signer === void 0) throw new Error("v3 policy session signer is required");
|
|
9068
|
+
const session = verifyCompactUcanAuthorization(this.v3Authorization, this.session.sessionId);
|
|
9069
|
+
if (session.payload.aud !== this.options.holderDid) throw new Error("v3 session recipient mismatch");
|
|
9070
|
+
const action = request.action === "list" ? "tinycloud.kv/list" : request.action === "put" ? "tinycloud.kv/put" : request.action === "metadata" ? "tinycloud.kv/metadata" : "tinycloud.kv/get";
|
|
9071
|
+
const path = typeof request.resource?.path === "string" ? request.resource.path.replace(/^\//, "").replace(/\/$/, "") : envelope.resource.path;
|
|
9072
|
+
const capability = envelope.policy.capabilityCeiling.find((candidate) => candidate.kind === "kv");
|
|
9073
|
+
if (capability === void 0 || !capability.actions.includes(action)) throw new Error("v3 requested capability is not in the signed policy");
|
|
9074
|
+
const marker = "/kv/";
|
|
9075
|
+
const split = capability.resource.indexOf(marker);
|
|
9076
|
+
if (split < 0) throw new Error("v3 KV resource is invalid");
|
|
9077
|
+
const root = capability.resource.slice(split + marker.length).replace(/\/$/, "");
|
|
9078
|
+
if (path !== root && !(capability.selector === "prefix" && path.startsWith(`${root}/`))) throw new Error("v3 KV request is outside the signed selector");
|
|
9079
|
+
const resource = `${capability.resource.slice(0, split + marker.length)}${path}`;
|
|
9080
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
9081
|
+
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 });
|
|
9082
|
+
const headers = new Headers({ accept: "application/json", Authorization: invocation.authorization });
|
|
9083
|
+
let body;
|
|
9084
|
+
if (action === "tinycloud.kv/put") {
|
|
9085
|
+
if (request.body === void 0) throw new Error("v3 KV put requires bytes");
|
|
9086
|
+
body = Uint8Array.from(request.body);
|
|
9087
|
+
headers.set("content-type", request.contentType ?? "application/octet-stream");
|
|
9088
|
+
if (request.ifMatch !== void 0) headers.set("if-match", request.ifMatch);
|
|
9089
|
+
}
|
|
9090
|
+
return this.fetchFn(new URL("/invoke", this.options.nodeOrigin), { method: "POST", redirect: "error", headers, ...body === void 0 ? {} : { body } });
|
|
9091
|
+
}
|
|
8727
9092
|
};
|
|
8728
9093
|
function createAddressedAuthorization(input) {
|
|
8729
|
-
const
|
|
9094
|
+
const { buildPresentation, ...options } = input;
|
|
9095
|
+
const client = (envelope) => new ShareRecipientClient({
|
|
9096
|
+
...options,
|
|
9097
|
+
envelope,
|
|
9098
|
+
...buildPresentation === void 0 ? {} : { buildPresentation: ({ challenge, envelope: candidate, policy }) => buildPresentation({ challenge, envelope: candidate, policy }) }
|
|
9099
|
+
});
|
|
8730
9100
|
return {
|
|
8731
9101
|
async begin({ envelope, method }) {
|
|
8732
9102
|
const current = client(envelope);
|
|
@@ -8740,15 +9110,15 @@ function createAddressedAuthorization(input) {
|
|
|
8740
9110
|
},
|
|
8741
9111
|
async verifyResult({ envelope, value, proof }) {
|
|
8742
9112
|
try {
|
|
8743
|
-
const wrapper =
|
|
8744
|
-
const response =
|
|
9113
|
+
const wrapper = object2(proof, "share read proof");
|
|
9114
|
+
const response = object2(wrapper.response, "share read response proof");
|
|
8745
9115
|
const envelopeAction = nativeAction("read");
|
|
8746
9116
|
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 =
|
|
9117
|
+
const detached = object2(wrapper.detached, "share read detached proof");
|
|
8748
9118
|
if (detached.alg !== "EdDSA" || detached.kid !== input.trustedNode.invitationKid) return false;
|
|
8749
9119
|
const unsigned = { ...response };
|
|
8750
9120
|
delete unsigned.proof;
|
|
8751
|
-
return
|
|
9121
|
+
return import_ed255198.ed25519.verify(bytes2(detached.signature, "share read signature"), new TextEncoder().encode(`${SHARE_V2_PROTOCOL.readResponseDomain}${canonicalize(unsigned)}`), trustedPublicKey(input.trustedNode));
|
|
8752
9122
|
} catch {
|
|
8753
9123
|
return false;
|
|
8754
9124
|
}
|