@tinycloud/sdk-core 2.11.0 → 2.11.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,13 +33,18 @@ __export(policy_exports, {
33
33
  OWNER_NODE_ENDPOINT_SCHEMA: () => OWNER_NODE_ENDPOINT_SCHEMA,
34
34
  POLICY_CAPABILITY_V1_DOMAIN: () => POLICY_CAPABILITY_V1_DOMAIN,
35
35
  POLICY_CAPABILITY_V1_SCHEMA: () => POLICY_CAPABILITY_V1_SCHEMA,
36
+ POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE: () => POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE,
36
37
  POLICY_ENFORCEMENT_DELEGATION_V2_SCHEMA: () => POLICY_ENFORCEMENT_DELEGATION_V2_SCHEMA,
37
38
  POLICY_ENGINE_RECORD_SCHEMA: () => POLICY_ENGINE_RECORD_SCHEMA,
39
+ POLICY_PRESENTATION_V3_DOMAIN: () => POLICY_PRESENTATION_V3_DOMAIN,
40
+ POLICY_PRESENTATION_V3_SCHEMA: () => POLICY_PRESENTATION_V3_SCHEMA,
38
41
  POLICY_SCHEMA: () => POLICY_SCHEMA,
39
42
  POLICY_SESSION_UCAN_V1_PROFILE: () => POLICY_SESSION_UCAN_V1_PROFILE,
40
43
  POLICY_STATUS_SCHEMA: () => POLICY_STATUS_SCHEMA,
41
44
  POLICY_V1_DOMAIN: () => POLICY_V1_DOMAIN,
42
45
  POLICY_V1_SCHEMA: () => POLICY_V1_SCHEMA,
46
+ POLICY_V2_DOMAIN: () => POLICY_V2_DOMAIN,
47
+ POLICY_V2_SCHEMA: () => POLICY_V2_SCHEMA,
43
48
  POLICY_VERSION_V0: () => POLICY_VERSION_V0,
44
49
  PolicyAuthoringError: () => PolicyAuthoringError,
45
50
  PolicyCapabilityError: () => PolicyCapabilityError,
@@ -59,6 +64,8 @@ __export(policy_exports, {
59
64
  TRANSCRIPT_SHARE_BOOTSTRAP_SCHEMA: () => TRANSCRIPT_SHARE_BOOTSTRAP_SCHEMA,
60
65
  UnsupportedSignatureSuiteError: () => UnsupportedSignatureSuiteError,
61
66
  W3C_VC_CREDENTIAL_VERIFIER: () => W3C_VC_CREDENTIAL_VERIFIER,
67
+ admitPolicyCredentialV3: () => admitPolicyCredentialV3,
68
+ buildPolicyCredentialPresentationV3: () => buildPolicyCredentialPresentationV3,
62
69
  canonicalizePolicyCapability: () => canonicalizePolicyCapability,
63
70
  canonicalizeSignedObjectUnsigned: () => canonicalizeSignedObjectUnsigned,
64
71
  canonicalizeUnifiedPolicyCapability: () => canonicalizeUnifiedPolicyCapability,
@@ -88,6 +95,7 @@ __export(policy_exports, {
88
95
  policyCidFromCanonicalBytes: () => policyCidFromCanonicalBytes,
89
96
  policyDigestHex: () => policyDigestHex,
90
97
  policyIdForDigestHex: () => policyIdForDigestHex,
98
+ policyV2DigestHex: () => policyV2DigestHex,
91
99
  projectUnifiedPolicyCapabilities: () => projectUnifiedPolicyCapabilities,
92
100
  projectUnifiedPolicyCapability: () => projectUnifiedPolicyCapability,
93
101
  renewPolicyRootStatusV3: () => renewPolicyRootStatusV3,
@@ -95,12 +103,14 @@ __export(policy_exports, {
95
103
  revokePolicyRootV3: () => revokePolicyRootV3,
96
104
  serializeJcsJson: () => serialize,
97
105
  signCompactUcanAuthorization: () => signCompactUcanAuthorization,
106
+ signCompactUcanRootAuthorization: () => signCompactUcanRootAuthorization,
98
107
  signedObjectIdFor: () => signedObjectIdFor,
99
108
  toSignedObjectError: () => toSignedObjectError,
100
109
  unifiedNativeProjectionHashHex: () => unifiedNativeProjectionHashHex,
101
110
  unifiedPolicyCapabilityContains: () => unifiedPolicyCapabilityContains,
102
111
  unifiedPolicyCapabilityDigestHex: () => unifiedPolicyCapabilityDigestHex,
103
112
  unifiedPolicyCapabilityFromNative: () => unifiedPolicyCapabilityFromNative,
113
+ validatePolicyCredentialRequirementV1: () => validatePolicyCredentialRequirementV1,
104
114
  validatePolicyEngineRecordSigned: () => validatePolicyEngineRecordSigned,
105
115
  validatePolicyEngineRecordSignedShape: () => validatePolicyEngineRecordSignedShape,
106
116
  validatePolicyEngineRecordUnsigned: () => validatePolicyEngineRecordUnsigned,
@@ -110,6 +120,7 @@ __export(policy_exports, {
110
120
  validatePolicyStatusSignedShape: () => validatePolicyStatusSignedShape,
111
121
  validatePolicyStatusUnsigned: () => validatePolicyStatusUnsigned,
112
122
  validatePolicyUnsigned: () => validatePolicyUnsigned,
123
+ validateUnifiedPolicyV2: () => validateUnifiedPolicyV2,
113
124
  verifyPolicy: () => verifyPolicy,
114
125
  verifyPolicyEngineRecord: () => verifyPolicyEngineRecord,
115
126
  verifyPolicyEngineRecordForRequester: () => verifyPolicyEngineRecordForRequester,
@@ -2778,13 +2789,13 @@ function base64UrlDecode(value) {
2778
2789
  return decoded;
2779
2790
  }
2780
2791
  function hexToBytes(value) {
2781
- const hex2 = value.slice(2);
2782
- if (hex2.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(hex2)) {
2792
+ const hex3 = value.slice(2);
2793
+ if (hex3.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(hex3)) {
2783
2794
  throw new SignatureMaterialError("hex signature must have even length");
2784
2795
  }
2785
- const output = new Uint8Array(hex2.length / 2);
2796
+ const output = new Uint8Array(hex3.length / 2);
2786
2797
  for (let index = 0; index < output.length; index++) {
2787
- output[index] = Number.parseInt(hex2.slice(index * 2, index * 2 + 2), 16);
2798
+ output[index] = Number.parseInt(hex3.slice(index * 2, index * 2 + 2), 16);
2788
2799
  }
2789
2800
  return output;
2790
2801
  }
@@ -3540,11 +3551,15 @@ function nonEmptyString(value, label) {
3540
3551
  return value;
3541
3552
  }
3542
3553
  function parseKvResource(resource) {
3543
- const match = /^tinycloud:\/\/([^/]+)\/kv\/(.+)$/.exec(resource);
3544
- if (!match || match[1].includes(":") || match[1].includes("?") || match[1].includes("#") || match[1].includes("%") || match[2].startsWith("/") || match[2].endsWith("/") || match[2].includes("//") || match[2].split("/").some((part) => part === "." || part === ".." || part.length === 0)) {
3554
+ const marker = resource.indexOf("/kv/");
3555
+ const encodedSpace = marker < 1 ? "" : resource.slice(0, marker);
3556
+ const legacy = encodedSpace.startsWith("tinycloud://");
3557
+ const space = legacy ? encodedSpace.slice("tinycloud://".length) : encodedSpace;
3558
+ const path = marker < 1 ? "" : resource.slice(marker + 4);
3559
+ if (marker < 1 || (legacy ? /[:/?#%]/.test(space) : !encodedSpace.startsWith("tinycloud:") || /[/?#%]/.test(encodedSpace)) || path.startsWith("/") || path.endsWith("/") || path.includes("//") || path.split("/").some((part) => part === "." || part === ".." || part.length === 0)) {
3545
3560
  throw new Error("KV resource is not canonical");
3546
3561
  }
3547
- return { space: match[1], path: match[2] };
3562
+ return { space, path };
3548
3563
  }
3549
3564
  function parseEncryptionResource(resource) {
3550
3565
  if (!resource.startsWith("urn:tinycloud:encryption:")) throw new Error("encryption capability is invalid");
@@ -3689,7 +3704,7 @@ function unifiedPolicyCapabilityFromNative(input) {
3689
3704
  throw new Error("native capability is not supported");
3690
3705
  const caveat = object(value.caveat, "native.caveat");
3691
3706
  exactKeys(caveat, ["type", "kind", "value"], "native selector caveat");
3692
- if (caveat.type !== "xyz.tinycloud.resource/selector" || caveat.kind !== "exact" && caveat.kind !== "prefix" || caveat.value !== `tinycloud://${value.space}/kv/${value.path}`)
3707
+ if (caveat.type !== "xyz.tinycloud.resource/selector" || caveat.kind !== "exact" && caveat.kind !== "prefix" || caveat.value !== `${value.space.startsWith("tinycloud:") ? value.space : `tinycloud://${value.space}`}/kv/${value.path}`)
3693
3708
  throw new Error("native selector caveat does not match capability");
3694
3709
  return normalizeUnifiedPolicyCapability({
3695
3710
  kind: "kv",
@@ -3748,9 +3763,7 @@ var CompactPayloadSchema = import_zod.z.object({
3748
3763
  nnc: import_zod.z.string().min(1),
3749
3764
  prf: import_zod.z.array(import_zod.z.string().min(1))
3750
3765
  }).strict();
3751
- async function signCompactUcanAuthorization(input) {
3752
- if (input.notBefore >= input.expiresAt || input.expiresAt - input.notBefore > 60)
3753
- throw new Error("compact invocation lifetime must be between one and 60 seconds");
3766
+ async function signCompactUcan(input) {
3754
3767
  const principal = input.issuerDid.split("#", 1)[0];
3755
3768
  const didMaterial = principal.startsWith("did:key:") ? base58btc.decode(principal.slice("did:key:".length)) : new Uint8Array();
3756
3769
  if (didMaterial.length !== 34 || didMaterial[0] !== 237 || didMaterial[1] !== 1)
@@ -3772,6 +3785,16 @@ async function signCompactUcanAuthorization(input) {
3772
3785
  if (signature.length !== 64) throw new Error("compact Authorization signature must be Ed25519");
3773
3786
  return parseCompactUcanAuthorization(`${protectedSegment}.${payloadSegment}.${encodeBase64Url(signature)}`);
3774
3787
  }
3788
+ async function signCompactUcanAuthorization(input) {
3789
+ if (input.notBefore >= input.expiresAt || input.expiresAt - input.notBefore > 60)
3790
+ throw new Error("compact invocation lifetime must be between one and 60 seconds");
3791
+ return signCompactUcan(input);
3792
+ }
3793
+ async function signCompactUcanRootAuthorization(input) {
3794
+ if (input.notBefore >= input.expiresAt || input.expiresAt - input.notBefore > 31 * 24 * 60 * 60)
3795
+ throw new Error("compact policy root lifetime must be between one second and 31 days");
3796
+ return signCompactUcan({ ...input, proofs: [] });
3797
+ }
3775
3798
  function createCompactPolicyInvocation(input) {
3776
3799
  const session = parsePolicySessionUcan(input.sessionAuthorization);
3777
3800
  if (session.cid !== input.sessionCid || session.aud !== input.recipientDid)
@@ -3918,14 +3941,14 @@ function decodeBase64Url(value) {
3918
3941
  if (!/^[A-Za-z0-9_-]+$/.test(value) || value.length % 4 === 1)
3919
3942
  throw new Error("value is not canonical base64url");
3920
3943
  const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
3921
- const binary = typeof Buffer !== "undefined" ? Buffer.from(normalized, "base64").toString("binary") : atob(normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "="));
3944
+ const binary = typeof atob === "function" ? atob(normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=")) : Buffer.from(normalized, "base64").toString("binary");
3922
3945
  const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
3923
- const encoded = typeof Buffer !== "undefined" ? Buffer.from(bytes).toString("base64url") : btoa(String.fromCharCode(...bytes)).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
3946
+ const encoded = typeof btoa === "function" ? btoa(String.fromCharCode(...bytes)).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_") : Buffer.from(bytes).toString("base64url");
3924
3947
  if (encoded !== value) throw new Error("value is not canonical base64url");
3925
3948
  return bytes;
3926
3949
  }
3927
3950
  function encodeBase64Url(value) {
3928
- return typeof Buffer !== "undefined" ? Buffer.from(value).toString("base64url") : btoa(String.fromCharCode(...value)).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
3951
+ return typeof btoa === "function" ? btoa(String.fromCharCode(...value)).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_") : Buffer.from(value).toString("base64url");
3929
3952
  }
3930
3953
  function equalBytes(left, right) {
3931
3954
  return left.length === right.length && left.every((byte, index) => byte === right[index]);
@@ -3949,4 +3972,387 @@ function base32Lower(bytes) {
3949
3972
  if (bits > 0) output += alphabet2[buffer << 5 - bits & 31];
3950
3973
  return output;
3951
3974
  }
3975
+
3976
+ // src/policy/credential-admission.ts
3977
+ var import_ed255193 = require("@noble/curves/ed25519");
3978
+ var import_sha2562 = require("@noble/hashes/sha256");
3979
+
3980
+ // src/credentials/digest.ts
3981
+ function encodeBase64Url2(bytes) {
3982
+ if (typeof btoa === "function") {
3983
+ let binary = "";
3984
+ for (const byte of bytes) binary += String.fromCharCode(byte);
3985
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
3986
+ }
3987
+ return Buffer.from(bytes).toString("base64url");
3988
+ }
3989
+ async function sha256Base64Url(value) {
3990
+ const bytes = typeof value === "string" ? new TextEncoder().encode(value) : value;
3991
+ return encodeBase64Url2(new Uint8Array(await crypto.subtle.digest("SHA-256", bytes)));
3992
+ }
3993
+ async function canonicalDigest(value) {
3994
+ return sha256Base64Url(jcsCanonicalize(value));
3995
+ }
3996
+ function isDigest(value) {
3997
+ return typeof value === "string" && /^[A-Za-z0-9_-]{43}$/.test(value);
3998
+ }
3999
+
4000
+ // src/credentials/errors.ts
4001
+ var RECOVERABLE = /* @__PURE__ */ new Set([
4002
+ "CANCELED",
4003
+ "ISSUER_UNREADY",
4004
+ "OFFLINE",
4005
+ "POPUP_BLOCKED",
4006
+ "REQUEST_EXPIRED",
4007
+ "SIGNATURE_REJECTED",
4008
+ "UNSUPPORTED_PROFILE",
4009
+ "UNSUPPORTED_VERSION",
4010
+ "VERIFIED_NOT_SAVED"
4011
+ ]);
4012
+ var CredentialError = class extends Error {
4013
+ constructor(code2, message, details = {}) {
4014
+ super(message);
4015
+ this.code = code2;
4016
+ this.details = details;
4017
+ this.name = "CredentialError";
4018
+ this.recoverable = RECOVERABLE.has(code2);
4019
+ }
4020
+ };
4021
+
4022
+ // src/credentials/requirement.ts
4023
+ var ID = /^[a-z0-9][a-z0-9._-]{0,127}(?:\/v1)?$/;
4024
+ function validateCredentialRequirement(value) {
4025
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new CredentialError("UNSUPPORTED_PROFILE", "Credential requirement is invalid");
4026
+ const raw = value;
4027
+ if (Object.keys(raw).some((key) => !["type", "version", "profile", "credentialType", "claims", "maxAgeSeconds"].includes(key)) || raw.type !== "TinyCloudCredentialRequirement" || raw.version !== 1) throw new CredentialError("UNSUPPORTED_PROFILE", "Credential requirement version is unsupported");
4028
+ const pair = (candidate, label) => {
4029
+ if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate)) throw new CredentialError("UNSUPPORTED_PROFILE", `${label} is invalid`);
4030
+ const item = candidate;
4031
+ if (Object.keys(item).length !== 2 || typeof item.id !== "string" || !ID.test(item.id) || item.version !== 1) throw new CredentialError("UNSUPPORTED_PROFILE", `${label} version is unsupported`);
4032
+ return { id: item.id, version: 1 };
4033
+ };
4034
+ if (typeof raw.claims !== "object" || raw.claims === null || Array.isArray(raw.claims)) throw new CredentialError("UNSUPPORTED_PROFILE", "Credential claims are invalid");
4035
+ const claims = {};
4036
+ for (const [key, value2] of Object.entries(raw.claims).sort(([a], [b]) => a.localeCompare(b))) {
4037
+ if (!ID.test(key) || typeof value2 !== "string" || value2.length === 0 || value2.length > 4096) throw new CredentialError("UNSUPPORTED_PROFILE", "Credential claim is invalid");
4038
+ claims[key] = value2;
4039
+ }
4040
+ if (raw.maxAgeSeconds !== void 0 && (!Number.isSafeInteger(raw.maxAgeSeconds) || raw.maxAgeSeconds < 1)) throw new CredentialError("UNSUPPORTED_PROFILE", "Credential freshness is invalid");
4041
+ return Object.freeze({ type: "TinyCloudCredentialRequirement", version: 1, profile: pair(raw.profile, "profile"), credentialType: pair(raw.credentialType, "credentialType"), claims: Object.freeze(claims), ...raw.maxAgeSeconds === void 0 ? {} : { maxAgeSeconds: raw.maxAgeSeconds } });
4042
+ }
4043
+ async function credentialRequirementDigest(requirement) {
4044
+ return canonicalDigest(validateCredentialRequirement(requirement));
4045
+ }
4046
+
4047
+ // src/policy/credential-admission.ts
4048
+ var POLICY_V2_SCHEMA = "xyz.tinycloud.policy/policy/v2";
4049
+ var POLICY_V2_DOMAIN = "xyz.tinycloud.policy/policy/v2\0";
4050
+ var POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE = "TinyCloudPolicyCredentialRequirement";
4051
+ var POLICY_PRESENTATION_V3_SCHEMA = "xyz.tinycloud.policy/presentation/v3";
4052
+ var POLICY_PRESENTATION_V3_DOMAIN = "xyz.tinycloud.policy/Presentation/v3\0";
4053
+ function record(value, label) {
4054
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
4055
+ throw new Error(`${label} must be an object`);
4056
+ }
4057
+ return value;
4058
+ }
4059
+ function exactKeys2(value, keys, label) {
4060
+ const expected = [...keys].sort();
4061
+ const actual = Object.keys(value).sort();
4062
+ if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) {
4063
+ throw new Error(`${label} contains an unknown or missing field`);
4064
+ }
4065
+ }
4066
+ function nonEmpty(value, label) {
4067
+ if (typeof value !== "string" || value.length === 0) {
4068
+ throw new Error(`${label} must be a non-empty string`);
4069
+ }
4070
+ return value;
4071
+ }
4072
+ function policyNodeOrigin(value) {
4073
+ let url;
4074
+ try {
4075
+ url = new URL(value);
4076
+ } catch {
4077
+ throw new Error("policy node origin is invalid");
4078
+ }
4079
+ if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.pathname !== "/" || url.search !== "" || url.hash !== "") {
4080
+ throw new Error("policy node origin is invalid");
4081
+ }
4082
+ return url.origin;
4083
+ }
4084
+ function versionPair(value, label) {
4085
+ const item = record(value, label);
4086
+ exactKeys2(item, ["id", "version"], label);
4087
+ if (item.version !== 1) throw new Error(`${label} version is unsupported`);
4088
+ return Object.freeze({ id: nonEmpty(item.id, `${label}.id`), version: 1 });
4089
+ }
4090
+ function validatePolicyCredentialRequirementV1(value) {
4091
+ const item = record(value, "policy credential requirement");
4092
+ exactKeys2(
4093
+ item,
4094
+ [
4095
+ "type",
4096
+ "version",
4097
+ "requirementDigest",
4098
+ "descriptorDigest",
4099
+ "issuerDid",
4100
+ "issuerKid",
4101
+ "profile",
4102
+ "credentialType"
4103
+ ],
4104
+ "policy credential requirement"
4105
+ );
4106
+ if (item.type !== POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE || item.version !== 1 || !isDigest(item.requirementDigest) || !isDigest(item.descriptorDigest)) {
4107
+ throw new Error("policy credential requirement is invalid");
4108
+ }
4109
+ return Object.freeze({
4110
+ type: POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE,
4111
+ version: 1,
4112
+ requirementDigest: item.requirementDigest,
4113
+ descriptorDigest: item.descriptorDigest,
4114
+ issuerDid: nonEmpty(item.issuerDid, "policy credential issuer"),
4115
+ issuerKid: nonEmpty(item.issuerKid, "policy credential issuer key"),
4116
+ profile: versionPair(item.profile, "policy credential profile"),
4117
+ credentialType: versionPair(
4118
+ item.credentialType,
4119
+ "policy credential type"
4120
+ )
4121
+ });
4122
+ }
4123
+ function hex2(bytes) {
4124
+ return [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
4125
+ }
4126
+ function policyV2DigestHex(input) {
4127
+ const canonicalPolicy = {
4128
+ ...input,
4129
+ credentialRequirement: validatePolicyCredentialRequirementV1(
4130
+ input.credentialRequirement
4131
+ ),
4132
+ capabilityCeiling: input.capabilityCeiling.map(
4133
+ normalizeUnifiedPolicyCapability
4134
+ )
4135
+ };
4136
+ return hex2(
4137
+ (0, import_sha2562.sha256)(
4138
+ new TextEncoder().encode(
4139
+ POLICY_V2_DOMAIN + jcsCanonicalize(canonicalPolicy)
4140
+ )
4141
+ )
4142
+ );
4143
+ }
4144
+ function date(value, label) {
4145
+ const text = nonEmpty(value, label);
4146
+ if (!Number.isFinite(Date.parse(text))) throw new Error(`${label} is invalid`);
4147
+ return text;
4148
+ }
4149
+ function rfc3339Seconds(value) {
4150
+ return new Date(Math.floor(value.getTime() / 1e3) * 1e3).toISOString().replace(/\.000Z$/, "Z");
4151
+ }
4152
+ function validateUnifiedPolicyV2(value, expectedPolicyCid) {
4153
+ const item = record(value, "Policy/v2");
4154
+ const allowed = [
4155
+ "schema",
4156
+ "policyId",
4157
+ "ownerDid",
4158
+ "createdAt",
4159
+ "contentSource",
4160
+ "capabilityCeiling",
4161
+ "credentialRequirement",
4162
+ "signature",
4163
+ ...item.expiresAt === void 0 ? [] : ["expiresAt"]
4164
+ ];
4165
+ exactKeys2(item, allowed, "Policy/v2");
4166
+ if (item.schema !== POLICY_V2_SCHEMA || !Array.isArray(item.capabilityCeiling)) {
4167
+ throw new Error("Policy/v2 is invalid");
4168
+ }
4169
+ const ownerDid = nonEmpty(item.ownerDid, "Policy/v2 ownerDid");
4170
+ const signature = record(item.signature, "Policy/v2 signature");
4171
+ exactKeys2(signature, ["suite", "signerDid", "value"], "Policy/v2 signature");
4172
+ if (signature.suite !== "Ed25519" || signature.signerDid !== ownerDid || typeof signature.value !== "string" || signature.value.length === 0) {
4173
+ throw new Error("Policy/v2 signature binding is invalid");
4174
+ }
4175
+ const policy = Object.freeze({
4176
+ schema: POLICY_V2_SCHEMA,
4177
+ policyId: nonEmpty(item.policyId, "Policy/v2 policyId"),
4178
+ ownerDid,
4179
+ createdAt: date(item.createdAt, "Policy/v2 createdAt"),
4180
+ ...item.expiresAt === void 0 ? {} : { expiresAt: date(item.expiresAt, "Policy/v2 expiresAt") },
4181
+ contentSource: item.contentSource,
4182
+ capabilityCeiling: Object.freeze(
4183
+ item.capabilityCeiling.map(normalizeUnifiedPolicyCapability)
4184
+ ),
4185
+ credentialRequirement: validatePolicyCredentialRequirementV1(
4186
+ item.credentialRequirement
4187
+ ),
4188
+ signature: Object.freeze({
4189
+ suite: "Ed25519",
4190
+ signerDid: ownerDid,
4191
+ value: signature.value
4192
+ })
4193
+ });
4194
+ const unsigned = { ...policy };
4195
+ delete unsigned.policyId;
4196
+ delete unsigned.signature;
4197
+ const digestHex = policyV2DigestHex(unsigned);
4198
+ if (policy.policyId !== policyIdForDigestHex(digestHex)) {
4199
+ throw new Error("Policy/v2 policyId binding is invalid");
4200
+ }
4201
+ if (expectedPolicyCid !== void 0) {
4202
+ const cid = policyCidFromCanonicalBytes(
4203
+ new TextEncoder().encode(jcsCanonicalize(policy))
4204
+ );
4205
+ if (cid !== expectedPolicyCid) throw new Error("Policy/v2 CID binding is invalid");
4206
+ }
4207
+ return policy;
4208
+ }
4209
+ function didKeyBytes(did) {
4210
+ const principal = did.split("#", 1)[0];
4211
+ if (!principal.startsWith("did:key:")) throw new Error("holder DID must be did:key");
4212
+ const material = base58btc.decode(principal.slice("did:key:".length));
4213
+ if (material.length !== 34 || material[0] !== 237 || material[1] !== 1) {
4214
+ throw new Error("holder DID must contain an Ed25519 key");
4215
+ }
4216
+ return material.slice(2);
4217
+ }
4218
+ function envelopeFromVerified(verified) {
4219
+ const {
4220
+ verifiedAt: _verifiedAt,
4221
+ credentialDigest: _credentialDigest,
4222
+ statusCheckedAt: _statusCheckedAt,
4223
+ ...envelope
4224
+ } = verified;
4225
+ return envelope;
4226
+ }
4227
+ async function buildPolicyCredentialPresentationV3(input) {
4228
+ const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
4229
+ const requirement = validateCredentialRequirement(input.requirement);
4230
+ const commitment = policy.credentialRequirement;
4231
+ const requirementDigest = await credentialRequirementDigest(requirement);
4232
+ const credentialDigest = await sha256Base64Url(input.credential.credential);
4233
+ if (requirementDigest !== commitment.requirementDigest || input.credential.descriptorDigest !== commitment.descriptorDigest || input.credential.credentialDigest !== credentialDigest || input.credential.issuerDid !== commitment.issuerDid || input.credential.issuerKid !== commitment.issuerKid || jcsCanonicalize(input.credential.profile) !== jcsCanonicalize(commitment.profile) || jcsCanonicalize(input.credential.credentialType) !== jcsCanonicalize(commitment.credentialType)) {
4234
+ throw new Error("credential does not match the signed Policy/v2 requirement");
4235
+ }
4236
+ const requestedCapabilities = input.requestedCapabilities.map(
4237
+ normalizeUnifiedPolicyCapability
4238
+ );
4239
+ if (requestedCapabilities.length === 0 || requestedCapabilities.some(
4240
+ (requested) => !policy.capabilityCeiling.some(
4241
+ (authority) => unifiedPolicyCapabilityContains(authority, requested)
4242
+ )
4243
+ )) {
4244
+ throw new Error("requested capabilities exceed the signed Policy/v2 ceiling");
4245
+ }
4246
+ const challenge = input.challenge;
4247
+ const holderDid = input.credential.holderDid;
4248
+ if (challenge.policyCid !== input.policyCid || challenge.recipientDid !== holderDid || input.credential.subjectDid !== holderDid || typeof challenge.nodeAudience !== "string" || challenge.nodeAudience.length === 0 || typeof challenge.expiresAt !== "string" || !Number.isFinite(Date.parse(challenge.expiresAt))) {
4249
+ throw new Error("policy challenge binding is invalid");
4250
+ }
4251
+ didKeyBytes(holderDid);
4252
+ const now = input.now ?? /* @__PURE__ */ new Date();
4253
+ const challengeExpiry = Date.parse(challenge.expiresAt);
4254
+ const credentialExpiry = Date.parse(input.credential.expiresAt);
4255
+ const expiresAtMs = Math.min(
4256
+ now.getTime() + 6e4,
4257
+ challengeExpiry,
4258
+ credentialExpiry
4259
+ );
4260
+ if (expiresAtMs <= now.getTime()) throw new Error("policy challenge is expired");
4261
+ const unsigned = Object.freeze({
4262
+ schema: POLICY_PRESENTATION_V3_SCHEMA,
4263
+ jti: input.jti ?? encodeBase64Url2(crypto.getRandomValues(new Uint8Array(16))),
4264
+ challengeId: challenge.challengeId,
4265
+ nonce: challenge.nonce,
4266
+ policyCid: input.policyCid,
4267
+ nodeAudience: challenge.nodeAudience,
4268
+ holderDid,
4269
+ subjectDid: input.credential.subjectDid,
4270
+ credentialSpaceOwnerDid: nonEmpty(
4271
+ input.credentialSpaceOwnerDid,
4272
+ "credential space owner DID"
4273
+ ),
4274
+ credentialDigest,
4275
+ requirementDigest,
4276
+ descriptorDigest: input.credential.descriptorDigest,
4277
+ requestedCapabilities: Object.freeze(requestedCapabilities),
4278
+ issuedAt: rfc3339Seconds(now),
4279
+ expiresAt: rfc3339Seconds(new Date(expiresAtMs))
4280
+ });
4281
+ const digest2 = (0, import_sha2562.sha256)(
4282
+ new TextEncoder().encode(
4283
+ POLICY_PRESENTATION_V3_DOMAIN + jcsCanonicalize(unsigned)
4284
+ )
4285
+ );
4286
+ const signature = await input.sign(digest2);
4287
+ if (signature.length !== 64 || !import_ed255193.ed25519.verify(signature, digest2, didKeyBytes(holderDid))) {
4288
+ throw new Error("policy presentation signature is invalid");
4289
+ }
4290
+ return Object.freeze({
4291
+ ...unsigned,
4292
+ signature: Object.freeze({
4293
+ suite: "Ed25519",
4294
+ signerDid: holderDid,
4295
+ value: encodeBase64Url2(signature)
4296
+ })
4297
+ });
4298
+ }
4299
+ async function admitPolicyCredentialV3(input) {
4300
+ const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
4301
+ const nodeOrigin = policyNodeOrigin(input.nodeOrigin);
4302
+ const accountAuthorizationCid = nonEmpty(
4303
+ input.accountAuthorizationCid,
4304
+ "account authorization CID"
4305
+ );
4306
+ const credentialSpaceId = nonEmpty(
4307
+ input.credentialSpaceId,
4308
+ "credential space ID"
4309
+ );
4310
+ const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
4311
+ const challenge = await requestPolicyChallengeV3({
4312
+ nodeOrigin,
4313
+ policyCid: input.policyCid,
4314
+ recipientDid: input.credential.holderDid,
4315
+ requestedCapabilities: input.requestedCapabilities,
4316
+ fetch: fetchFn
4317
+ });
4318
+ const presentation = await buildPolicyCredentialPresentationV3({
4319
+ ...input,
4320
+ policy,
4321
+ challenge
4322
+ });
4323
+ const response = await fetchFn(
4324
+ new URL("/share/v3/policy/delegations", nodeOrigin),
4325
+ {
4326
+ method: "POST",
4327
+ redirect: "error",
4328
+ headers: { accept: "application/json", "content-type": "application/json" },
4329
+ body: JSON.stringify({
4330
+ policyCid: input.policyCid,
4331
+ challengeId: challenge.challengeId,
4332
+ nonce: challenge.nonce,
4333
+ requirement: validateCredentialRequirement(input.requirement),
4334
+ credential: envelopeFromVerified(input.credential),
4335
+ accountAuthorizationCid,
4336
+ credentialSpaceId,
4337
+ presentation
4338
+ })
4339
+ }
4340
+ );
4341
+ if (!response.ok) throw new Error(`policy delegation rejected (${response.status})`);
4342
+ const value = record(await response.json(), "policy delegation");
4343
+ if (value.admitted !== true || typeof value.sessionCid !== "string" || typeof value.authorization !== "string") {
4344
+ throw new Error("policy delegation response is invalid");
4345
+ }
4346
+ const session = parsePolicySessionUcan(value.authorization, [
4347
+ input.policyRootCid,
4348
+ input.enforcementRootCid
4349
+ ]);
4350
+ const expectedAttenuation = compactAttenuationForPolicyCapabilities(
4351
+ input.requestedCapabilities
4352
+ );
4353
+ if (session.cid !== value.sessionCid || session.aud !== input.credential.holderDid || session.fact.policyCid !== input.policyCid || session.fact.recipientDid !== input.credential.holderDid || session.fact.policyDelegationCid !== input.policyRootCid || session.fact.enforcementDelegationCid !== input.enforcementRootCid || jcsCanonicalize(session.att) !== jcsCanonicalize(expectedAttenuation)) {
4354
+ throw new Error("policy delegation signed binding is invalid");
4355
+ }
4356
+ return Object.freeze({ challenge, presentation, session });
4357
+ }
3952
4358
  //# sourceMappingURL=index.cjs.map