@tinycloud/sdk-core 3.0.0-beta.1 → 3.0.0-beta.10
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/{credential-admission-C_cO1QqB.d.cts → credential-admission-CFjUChF8.d.cts} +122 -2
- package/dist/{credential-admission-cvHe-HUC.d.ts → credential-admission-DIEPgQ0o.d.ts} +122 -2
- package/dist/delegations/index.cjs +80 -133
- package/dist/delegations/index.cjs.map +1 -1
- package/dist/delegations/index.d.cts +3 -3
- package/dist/delegations/index.d.ts +3 -3
- package/dist/delegations/index.js +81 -138
- package/dist/delegations/index.js.map +1 -1
- package/dist/index.cjs +756 -306
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -7
- package/dist/index.d.ts +66 -7
- package/dist/index.js +758 -311
- package/dist/index.js.map +1 -1
- package/dist/policy/index.cjs +323 -8
- package/dist/policy/index.cjs.map +1 -1
- package/dist/policy/index.d.cts +2 -2
- package/dist/policy/index.d.ts +2 -2
- package/dist/policy/index.js +323 -8
- package/dist/policy/index.js.map +1 -1
- package/dist/{recipient-types.schema-G8NEKRoo.d.cts → recipient-types.schema-Cz5zJzED.d.cts} +50 -77
- package/dist/{recipient-types.schema-G8NEKRoo.d.ts → recipient-types.schema-Cz5zJzED.d.ts} +50 -77
- package/package.json +4 -4
package/dist/policy/index.cjs
CHANGED
|
@@ -38,6 +38,8 @@ __export(policy_exports, {
|
|
|
38
38
|
POLICY_ENGINE_RECORD_SCHEMA: () => POLICY_ENGINE_RECORD_SCHEMA,
|
|
39
39
|
POLICY_PRESENTATION_V3_DOMAIN: () => POLICY_PRESENTATION_V3_DOMAIN,
|
|
40
40
|
POLICY_PRESENTATION_V3_SCHEMA: () => POLICY_PRESENTATION_V3_SCHEMA,
|
|
41
|
+
POLICY_PRESENTATION_V4_DOMAIN: () => POLICY_PRESENTATION_V4_DOMAIN,
|
|
42
|
+
POLICY_PRESENTATION_V4_SCHEMA: () => POLICY_PRESENTATION_V4_SCHEMA,
|
|
41
43
|
POLICY_SCHEMA: () => POLICY_SCHEMA,
|
|
42
44
|
POLICY_SESSION_UCAN_V1_PROFILE: () => POLICY_SESSION_UCAN_V1_PROFILE,
|
|
43
45
|
POLICY_STATUS_SCHEMA: () => POLICY_STATUS_SCHEMA,
|
|
@@ -65,7 +67,9 @@ __export(policy_exports, {
|
|
|
65
67
|
UnsupportedSignatureSuiteError: () => UnsupportedSignatureSuiteError,
|
|
66
68
|
W3C_VC_CREDENTIAL_VERIFIER: () => W3C_VC_CREDENTIAL_VERIFIER,
|
|
67
69
|
admitPolicyCredentialV3: () => admitPolicyCredentialV3,
|
|
70
|
+
admitPolicyCredentialV4: () => admitPolicyCredentialV4,
|
|
68
71
|
buildPolicyCredentialPresentationV3: () => buildPolicyCredentialPresentationV3,
|
|
72
|
+
buildPolicyCredentialPresentationV4: () => buildPolicyCredentialPresentationV4,
|
|
69
73
|
canonicalizePolicyCapability: () => canonicalizePolicyCapability,
|
|
70
74
|
canonicalizeSignedObjectUnsigned: () => canonicalizeSignedObjectUnsigned,
|
|
71
75
|
canonicalizeUnifiedPolicyCapability: () => canonicalizeUnifiedPolicyCapability,
|
|
@@ -83,6 +87,7 @@ __export(policy_exports, {
|
|
|
83
87
|
createUnsignedPolicyEngineRecord: () => createUnsignedPolicyEngineRecord,
|
|
84
88
|
deriveSignedObjectMaterial: () => deriveSignedObjectMaterial,
|
|
85
89
|
getPolicyRootStatusV3: () => getPolicyRootStatusV3,
|
|
90
|
+
issuedCredentialEnvelopeFromVerified: () => issuedCredentialEnvelopeFromVerified,
|
|
86
91
|
jcsCanonicalize: () => jcsCanonicalize,
|
|
87
92
|
mintPolicySessionV3: () => mintPolicySessionV3,
|
|
88
93
|
normalizeJson: () => normalizeJson,
|
|
@@ -98,12 +103,14 @@ __export(policy_exports, {
|
|
|
98
103
|
policyV2DigestHex: () => policyV2DigestHex,
|
|
99
104
|
projectUnifiedPolicyCapabilities: () => projectUnifiedPolicyCapabilities,
|
|
100
105
|
projectUnifiedPolicyCapability: () => projectUnifiedPolicyCapability,
|
|
106
|
+
registerPolicyV3: () => registerPolicyV3,
|
|
101
107
|
renewPolicyRootStatusV3: () => renewPolicyRootStatusV3,
|
|
102
108
|
requestPolicyChallengeV3: () => requestPolicyChallengeV3,
|
|
103
109
|
revokePolicyRootV3: () => revokePolicyRootV3,
|
|
104
110
|
serializeJcsJson: () => serialize,
|
|
105
111
|
signCompactUcanAuthorization: () => signCompactUcanAuthorization,
|
|
106
112
|
signCompactUcanRootAuthorization: () => signCompactUcanRootAuthorization,
|
|
113
|
+
signPolicyCredentialPresentationV4: () => signPolicyCredentialPresentationV4,
|
|
107
114
|
signedObjectIdFor: () => signedObjectIdFor,
|
|
108
115
|
toSignedObjectError: () => toSignedObjectError,
|
|
109
116
|
unifiedNativeProjectionHashHex: () => unifiedNativeProjectionHashHex,
|
|
@@ -3335,9 +3342,11 @@ var MAX_LEGACY_ENVELOPE_EXPIRES_AT = "2026-12-29T00:00:00Z";
|
|
|
3335
3342
|
var LAST_V2_READ_AT = "2027-01-05T00:00:00Z";
|
|
3336
3343
|
async function requestPolicyChallengeV3(input) {
|
|
3337
3344
|
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3338
|
-
const
|
|
3345
|
+
const policyRuntimePath = policyRuntimeBasePath(input.policyRuntimePath);
|
|
3346
|
+
const response = await fetchFn(new URL(`${policyRuntimePath}/challenges`, input.nodeOrigin), {
|
|
3339
3347
|
method: "POST",
|
|
3340
3348
|
redirect: "error",
|
|
3349
|
+
signal: input.signal,
|
|
3341
3350
|
headers: { accept: "application/json", "content-type": "application/json" },
|
|
3342
3351
|
body: JSON.stringify({ policyCid: input.policyCid, recipientDid: input.recipientDid, requestedCapabilities: input.requestedCapabilities.map(normalizeUnifiedPolicyCapability) })
|
|
3343
3352
|
});
|
|
@@ -3347,14 +3356,22 @@ async function requestPolicyChallengeV3(input) {
|
|
|
3347
3356
|
throw new Error("policy challenge binding is invalid");
|
|
3348
3357
|
return value;
|
|
3349
3358
|
}
|
|
3359
|
+
function policyRuntimeBasePath(value) {
|
|
3360
|
+
const path = value ?? "/policy/v3";
|
|
3361
|
+
if (!/^\/policy(?:\/[a-z0-9-]+)*$/.test(path)) {
|
|
3362
|
+
throw new Error("policy runtime path is invalid");
|
|
3363
|
+
}
|
|
3364
|
+
return path;
|
|
3365
|
+
}
|
|
3350
3366
|
async function mintPolicySessionV3(input) {
|
|
3351
3367
|
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3352
3368
|
const challenge = input.challenge ?? await requestPolicyChallengeV3(input);
|
|
3353
3369
|
if (challenge.policyCid !== input.policyCid || challenge.recipientDid !== input.recipientDid)
|
|
3354
3370
|
throw new Error("policy challenge binding is invalid");
|
|
3355
|
-
const response = await fetchFn(new URL("/
|
|
3371
|
+
const response = await fetchFn(new URL("/policy/v3/delegations", input.nodeOrigin), {
|
|
3356
3372
|
method: "POST",
|
|
3357
3373
|
redirect: "error",
|
|
3374
|
+
signal: input.signal,
|
|
3358
3375
|
headers: { accept: "application/json", "content-type": "application/json" },
|
|
3359
3376
|
body: JSON.stringify({ policyCid: input.policyCid, challengeId: challenge.challengeId, nonce: challenge.nonce, claim: input.claim, presentation: input.presentation })
|
|
3360
3377
|
});
|
|
@@ -3413,7 +3430,7 @@ function verifyPolicyRootStatusCheckpointV3(input) {
|
|
|
3413
3430
|
}
|
|
3414
3431
|
async function getPolicyRootStatusV3(input) {
|
|
3415
3432
|
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3416
|
-
const response = await fetchFn(new URL(`/
|
|
3433
|
+
const response = await fetchFn(new URL(`/policy/v3/status/${encodeURIComponent(input.rootCid)}`, input.nodeOrigin), {
|
|
3417
3434
|
method: "GET",
|
|
3418
3435
|
redirect: "error",
|
|
3419
3436
|
headers: { accept: "application/json" }
|
|
@@ -3472,7 +3489,7 @@ async function renewPolicyRootStatusV3(input) {
|
|
|
3472
3489
|
};
|
|
3473
3490
|
const signature = await input.sign((0, import_sha256.sha256)(new TextEncoder().encode(ROOT_STATUS_RENEWAL_V1_DOMAIN + jcsCanonicalize(unsigned))));
|
|
3474
3491
|
if (signature.length !== 64) throw new Error("policy root renewal signature must be Ed25519");
|
|
3475
|
-
const response = await fetchFn(new URL("/
|
|
3492
|
+
const response = await fetchFn(new URL("/policy/v3/status", input.nodeOrigin), {
|
|
3476
3493
|
method: "POST",
|
|
3477
3494
|
redirect: "error",
|
|
3478
3495
|
headers: { accept: "application/json", "content-type": "application/json" },
|
|
@@ -3528,6 +3545,11 @@ var POLICY_SESSION_FACT_KEYS = [
|
|
|
3528
3545
|
"issuanceAuditDigestHex",
|
|
3529
3546
|
"remainingRedelegationDepth"
|
|
3530
3547
|
];
|
|
3548
|
+
var POLICY_SESSION_V4_AUDIT_FACT_KEYS = [
|
|
3549
|
+
"credentialIdAuditDigestHex",
|
|
3550
|
+
"presentationJtiAuditDigestHex"
|
|
3551
|
+
];
|
|
3552
|
+
var LOWER_SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
3531
3553
|
var KV_ACTIONS = /* @__PURE__ */ new Set([
|
|
3532
3554
|
"tinycloud.kv/get",
|
|
3533
3555
|
"tinycloud.kv/list",
|
|
@@ -3722,6 +3744,133 @@ function contentSourceDigestHex(input) {
|
|
|
3722
3744
|
)
|
|
3723
3745
|
);
|
|
3724
3746
|
}
|
|
3747
|
+
function exactObject(value, keys, label) {
|
|
3748
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
3749
|
+
throw new Error(`${label} is invalid`);
|
|
3750
|
+
}
|
|
3751
|
+
const record2 = value;
|
|
3752
|
+
if (Object.keys(record2).length !== keys.length || keys.some((key) => !Object.prototype.hasOwnProperty.call(record2, key))) {
|
|
3753
|
+
throw new Error(`${label} is invalid`);
|
|
3754
|
+
}
|
|
3755
|
+
return record2;
|
|
3756
|
+
}
|
|
3757
|
+
function didKeyEd25519PublicKey(value) {
|
|
3758
|
+
const principal = value.split("#", 1)[0];
|
|
3759
|
+
if (!principal.startsWith("did:key:")) {
|
|
3760
|
+
throw new Error("policy enforcer binding signer is invalid");
|
|
3761
|
+
}
|
|
3762
|
+
const material = base58btc.decode(principal.slice("did:key:".length));
|
|
3763
|
+
if (material.length !== 34 || material[0] !== 237 || material[1] !== 1) {
|
|
3764
|
+
throw new Error("policy enforcer binding signer is invalid");
|
|
3765
|
+
}
|
|
3766
|
+
return material.slice(2);
|
|
3767
|
+
}
|
|
3768
|
+
async function registerPolicyV3(input) {
|
|
3769
|
+
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3770
|
+
const rootExpiresAt = new Date(input.rootExpiresAt);
|
|
3771
|
+
const canonicalRootExpiresAt = new Date(
|
|
3772
|
+
Math.floor(rootExpiresAt.getTime() / 1e3) * 1e3
|
|
3773
|
+
).toISOString().replace(/\.000Z$/, "Z");
|
|
3774
|
+
if (!Number.isFinite(rootExpiresAt.getTime()) || canonicalRootExpiresAt !== input.rootExpiresAt || rootExpiresAt.getTime() <= Date.now()) {
|
|
3775
|
+
throw new Error("policy root expiry is invalid");
|
|
3776
|
+
}
|
|
3777
|
+
const bindingResponse = await fetchFn(
|
|
3778
|
+
new URL("/policy/v3/enforcer-bindings", input.nodeOrigin),
|
|
3779
|
+
{
|
|
3780
|
+
method: "POST",
|
|
3781
|
+
redirect: "error",
|
|
3782
|
+
signal: input.signal,
|
|
3783
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
3784
|
+
body: JSON.stringify({
|
|
3785
|
+
rootExpiresAt: input.rootExpiresAt,
|
|
3786
|
+
enforcerDid: input.enforcerDid
|
|
3787
|
+
})
|
|
3788
|
+
}
|
|
3789
|
+
);
|
|
3790
|
+
if (!bindingResponse.ok) {
|
|
3791
|
+
throw new Error(`policy enforcer binding rejected (${bindingResponse.status})`);
|
|
3792
|
+
}
|
|
3793
|
+
const binding = exactObject(
|
|
3794
|
+
await bindingResponse.json(),
|
|
3795
|
+
[
|
|
3796
|
+
"schema",
|
|
3797
|
+
"enforcerDid",
|
|
3798
|
+
"nodeAudience",
|
|
3799
|
+
"attestationBindingDigestHex",
|
|
3800
|
+
"issuedAt",
|
|
3801
|
+
"expiresAt",
|
|
3802
|
+
"signature"
|
|
3803
|
+
],
|
|
3804
|
+
"policy enforcer binding"
|
|
3805
|
+
);
|
|
3806
|
+
const signature = exactObject(
|
|
3807
|
+
binding.signature,
|
|
3808
|
+
["suite", "signerDid", "value"],
|
|
3809
|
+
"policy enforcer binding signature"
|
|
3810
|
+
);
|
|
3811
|
+
const expectedBindingDigestHex = hex(
|
|
3812
|
+
(0, import_sha256.sha256)(
|
|
3813
|
+
new TextEncoder().encode(
|
|
3814
|
+
jcsCanonicalize({
|
|
3815
|
+
enforcerDid: input.enforcerDid,
|
|
3816
|
+
nodeAudience: input.expectedNodeAudience
|
|
3817
|
+
})
|
|
3818
|
+
)
|
|
3819
|
+
)
|
|
3820
|
+
);
|
|
3821
|
+
if (binding.schema !== ATTESTED_ENFORCER_BINDING_V2_SCHEMA || binding.enforcerDid !== input.enforcerDid || binding.nodeAudience !== input.expectedNodeAudience || binding.attestationBindingDigestHex !== expectedBindingDigestHex || binding.expiresAt !== input.rootExpiresAt || typeof binding.issuedAt !== "string" || Date.parse(binding.issuedAt) > Date.now() || signature.suite !== "Ed25519" || signature.signerDid !== input.expectedNodeAudience || typeof signature.value !== "string") {
|
|
3822
|
+
throw new Error("policy enforcer binding is invalid");
|
|
3823
|
+
}
|
|
3824
|
+
const { signature: _signature, ...unsignedBinding } = binding;
|
|
3825
|
+
const signedBinding = (0, import_sha256.sha256)(
|
|
3826
|
+
new TextEncoder().encode(
|
|
3827
|
+
`xyz.tinycloud.policy/AttestedEnforcerBinding/v2\0${jcsCanonicalize(unsignedBinding)}`
|
|
3828
|
+
)
|
|
3829
|
+
);
|
|
3830
|
+
if (!import_ed255192.ed25519.verify(
|
|
3831
|
+
decodeBase64Url(signature.value),
|
|
3832
|
+
signedBinding,
|
|
3833
|
+
didKeyEd25519PublicKey(input.expectedNodeAudience),
|
|
3834
|
+
{ zip215: false }
|
|
3835
|
+
)) {
|
|
3836
|
+
throw new Error("policy enforcer binding signature is invalid");
|
|
3837
|
+
}
|
|
3838
|
+
const response = await fetchFn(
|
|
3839
|
+
new URL("/policy/v3/policies", input.nodeOrigin),
|
|
3840
|
+
{
|
|
3841
|
+
method: "POST",
|
|
3842
|
+
redirect: "error",
|
|
3843
|
+
signal: input.signal,
|
|
3844
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
3845
|
+
body: JSON.stringify({
|
|
3846
|
+
policyCid: input.policyCid,
|
|
3847
|
+
policy: input.policy,
|
|
3848
|
+
policyRoot: input.policyRoot.authorization.replace(/^Bearer\s+/i, ""),
|
|
3849
|
+
enforcementRoot: input.enforcementRoot.authorization.replace(/^Bearer\s+/i, ""),
|
|
3850
|
+
contentSourceDigestHex: input.contentSourceDigestHex,
|
|
3851
|
+
nativeProjectionHashHex: input.nativeProjectionHashHex,
|
|
3852
|
+
attestedEnforcerBinding: binding
|
|
3853
|
+
})
|
|
3854
|
+
}
|
|
3855
|
+
);
|
|
3856
|
+
if (!response.ok) {
|
|
3857
|
+
throw new Error(`policy registration rejected (${response.status})`);
|
|
3858
|
+
}
|
|
3859
|
+
const receipt = exactObject(
|
|
3860
|
+
await response.json(),
|
|
3861
|
+
["policyCid", "policyRootCid", "enforcementRootCid"],
|
|
3862
|
+
"policy registration response"
|
|
3863
|
+
);
|
|
3864
|
+
if (receipt.policyCid !== input.policyCid || receipt.policyRootCid !== input.policyRoot.cid || receipt.enforcementRootCid !== input.enforcementRoot.cid) {
|
|
3865
|
+
throw new Error("policy registration response binding is invalid");
|
|
3866
|
+
}
|
|
3867
|
+
return {
|
|
3868
|
+
policyCid: input.policyCid,
|
|
3869
|
+
policyRootCid: input.policyRoot.cid,
|
|
3870
|
+
enforcementRootCid: input.enforcementRoot.cid,
|
|
3871
|
+
attestedEnforcerBinding: binding
|
|
3872
|
+
};
|
|
3873
|
+
}
|
|
3725
3874
|
function policyDigestHex(policy) {
|
|
3726
3875
|
const canonicalPolicy = {
|
|
3727
3876
|
...policy,
|
|
@@ -3920,9 +4069,13 @@ function parsePolicySessionUcan(authorization, expectedProofs) {
|
|
|
3920
4069
|
if (expectedProofs !== void 0 && (parsed.payload.prf[0] !== expectedProofs[0] || parsed.payload.prf[1] !== expectedProofs[1]))
|
|
3921
4070
|
throw new Error("policy session UCAN ordered proofs do not match");
|
|
3922
4071
|
const factKeys = Object.keys(fact);
|
|
3923
|
-
|
|
4072
|
+
const hasLegacyFactShape = factKeys.length === POLICY_SESSION_FACT_KEYS.length && factKeys.every((key) => POLICY_SESSION_FACT_KEYS.includes(key)) && POLICY_SESSION_FACT_KEYS.every((key) => key in fact);
|
|
4073
|
+
const hasV4AuditFactShape = factKeys.length === POLICY_SESSION_FACT_KEYS.length + POLICY_SESSION_V4_AUDIT_FACT_KEYS.length && factKeys.every((key) => POLICY_SESSION_FACT_KEYS.includes(key) || POLICY_SESSION_V4_AUDIT_FACT_KEYS.includes(key)) && POLICY_SESSION_FACT_KEYS.every((key) => key in fact) && POLICY_SESSION_V4_AUDIT_FACT_KEYS.every((key) => key in fact);
|
|
4074
|
+
if (!hasLegacyFactShape && !hasV4AuditFactShape)
|
|
3924
4075
|
throw new Error("policy session UCAN fact is incomplete");
|
|
3925
|
-
if (
|
|
4076
|
+
if (hasV4AuditFactShape && (!LOWER_SHA256_HEX.test(fact.credentialIdAuditDigestHex) || !LOWER_SHA256_HEX.test(fact.presentationJtiAuditDigestHex)))
|
|
4077
|
+
throw new Error("policy session UCAN v4 audit facts are invalid");
|
|
4078
|
+
if (fact.profile !== POLICY_SESSION_UCAN_V1_PROFILE || typeof fact.remainingRedelegationDepth !== "number" || !Number.isInteger(fact.remainingRedelegationDepth) || fact.remainingRedelegationDepth < 0 || fact.remainingRedelegationDepth > 8 || parsed.payload.exp - parsed.payload.nbf > 60 || parsed.payload.iss.split("#", 1)[0] !== fact.nodeAudience || parsed.payload.aud !== fact.recipientDid)
|
|
3926
4079
|
throw new Error("policy session UCAN fact is invalid");
|
|
3927
4080
|
return {
|
|
3928
4081
|
authorization,
|
|
@@ -4050,6 +4203,8 @@ var POLICY_V2_DOMAIN = "xyz.tinycloud.policy/policy/v2\0";
|
|
|
4050
4203
|
var POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE = "TinyCloudPolicyCredentialRequirement";
|
|
4051
4204
|
var POLICY_PRESENTATION_V3_SCHEMA = "xyz.tinycloud.policy/presentation/v3";
|
|
4052
4205
|
var POLICY_PRESENTATION_V3_DOMAIN = "xyz.tinycloud.policy/Presentation/v3\0";
|
|
4206
|
+
var POLICY_PRESENTATION_V4_SCHEMA = "xyz.tinycloud.policy/presentation/v4";
|
|
4207
|
+
var POLICY_PRESENTATION_V4_DOMAIN = "xyz.tinycloud.policy/Presentation/v4\0";
|
|
4053
4208
|
function record(value, label) {
|
|
4054
4209
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
4055
4210
|
throw new Error(`${label} must be an object`);
|
|
@@ -4224,6 +4379,97 @@ function envelopeFromVerified(verified) {
|
|
|
4224
4379
|
} = verified;
|
|
4225
4380
|
return envelope;
|
|
4226
4381
|
}
|
|
4382
|
+
function issuedCredentialEnvelopeFromVerified(verified) {
|
|
4383
|
+
return envelopeFromVerified(verified);
|
|
4384
|
+
}
|
|
4385
|
+
async function signPolicyCredentialPresentationV4(value, sign) {
|
|
4386
|
+
const item = record(value, "Policy presentation v4");
|
|
4387
|
+
exactKeys2(item, [
|
|
4388
|
+
"schema",
|
|
4389
|
+
"jti",
|
|
4390
|
+
"challengeId",
|
|
4391
|
+
"nonce",
|
|
4392
|
+
"policyCid",
|
|
4393
|
+
"nodeAudience",
|
|
4394
|
+
"holderDid",
|
|
4395
|
+
"subjectDid",
|
|
4396
|
+
"credentialDigest",
|
|
4397
|
+
"requirementDigest",
|
|
4398
|
+
"descriptorDigest",
|
|
4399
|
+
"requestedCapabilities",
|
|
4400
|
+
"issuedAt",
|
|
4401
|
+
"expiresAt"
|
|
4402
|
+
], "Policy presentation v4");
|
|
4403
|
+
if (item.schema !== POLICY_PRESENTATION_V4_SCHEMA || item.holderDid !== item.subjectDid || !Array.isArray(item.requestedCapabilities)) throw new Error("Policy presentation v4 is invalid");
|
|
4404
|
+
const holderDid = nonEmpty(item.holderDid, "Policy presentation v4 holder DID");
|
|
4405
|
+
didKeyBytes(holderDid);
|
|
4406
|
+
const unsigned = Object.freeze({
|
|
4407
|
+
schema: POLICY_PRESENTATION_V4_SCHEMA,
|
|
4408
|
+
jti: nonEmpty(item.jti, "Policy presentation v4 jti"),
|
|
4409
|
+
challengeId: nonEmpty(item.challengeId, "Policy presentation v4 challenge"),
|
|
4410
|
+
nonce: nonEmpty(item.nonce, "Policy presentation v4 nonce"),
|
|
4411
|
+
policyCid: nonEmpty(item.policyCid, "Policy presentation v4 policy CID"),
|
|
4412
|
+
nodeAudience: nonEmpty(item.nodeAudience, "Policy presentation v4 audience"),
|
|
4413
|
+
holderDid,
|
|
4414
|
+
subjectDid: holderDid,
|
|
4415
|
+
credentialDigest: nonEmpty(item.credentialDigest, "Policy presentation v4 credential digest"),
|
|
4416
|
+
requirementDigest: nonEmpty(item.requirementDigest, "Policy presentation v4 requirement digest"),
|
|
4417
|
+
descriptorDigest: nonEmpty(item.descriptorDigest, "Policy presentation v4 descriptor digest"),
|
|
4418
|
+
// The low-level signer preserves the frozen wire bytes exactly. The
|
|
4419
|
+
// policy-aware builder performs capability normalization and containment.
|
|
4420
|
+
requestedCapabilities: Object.freeze(item.requestedCapabilities),
|
|
4421
|
+
issuedAt: date(item.issuedAt, "Policy presentation v4 issuedAt"),
|
|
4422
|
+
expiresAt: date(item.expiresAt, "Policy presentation v4 expiresAt")
|
|
4423
|
+
});
|
|
4424
|
+
const digest2 = (0, import_sha2562.sha256)(new TextEncoder().encode(
|
|
4425
|
+
POLICY_PRESENTATION_V4_DOMAIN + jcsCanonicalize(unsigned)
|
|
4426
|
+
));
|
|
4427
|
+
const signature = await sign(digest2);
|
|
4428
|
+
if (signature.length !== 64 || !import_ed255193.ed25519.verify(signature, digest2, didKeyBytes(holderDid))) throw new Error("policy presentation signature is invalid");
|
|
4429
|
+
return Object.freeze({
|
|
4430
|
+
...unsigned,
|
|
4431
|
+
signature: Object.freeze({
|
|
4432
|
+
suite: "Ed25519",
|
|
4433
|
+
signerDid: holderDid,
|
|
4434
|
+
value: encodeBase64Url2(signature)
|
|
4435
|
+
})
|
|
4436
|
+
});
|
|
4437
|
+
}
|
|
4438
|
+
async function buildPolicyCredentialPresentationV4(input) {
|
|
4439
|
+
const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
|
|
4440
|
+
const requirement = validateCredentialRequirement(input.requirement);
|
|
4441
|
+
const commitment = policy.credentialRequirement;
|
|
4442
|
+
const requirementDigest = await credentialRequirementDigest(requirement);
|
|
4443
|
+
const credentialDigest = await sha256Base64Url(input.credential.credential);
|
|
4444
|
+
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)) throw new Error("credential does not match the signed Policy/v2 requirement");
|
|
4445
|
+
const requestedCapabilities = input.requestedCapabilities.map(normalizeUnifiedPolicyCapability);
|
|
4446
|
+
if (requestedCapabilities.length === 0 || requestedCapabilities.some((requested) => !policy.capabilityCeiling.some((authority) => unifiedPolicyCapabilityContains(authority, requested)))) throw new Error("requested capabilities exceed the signed Policy/v2 ceiling");
|
|
4447
|
+
const holderDid = input.credential.holderDid;
|
|
4448
|
+
if (input.challenge.policyCid !== input.policyCid || input.challenge.recipientDid !== holderDid || input.credential.subjectDid !== holderDid || typeof input.challenge.nodeAudience !== "string" || input.challenge.nodeAudience.length === 0 || typeof input.challenge.expiresAt !== "string" || !Number.isFinite(Date.parse(input.challenge.expiresAt))) throw new Error("policy challenge binding is invalid");
|
|
4449
|
+
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
4450
|
+
const expiresAtMs = Math.min(
|
|
4451
|
+
now.getTime() + 6e4,
|
|
4452
|
+
Date.parse(input.challenge.expiresAt),
|
|
4453
|
+
Date.parse(input.credential.expiresAt)
|
|
4454
|
+
);
|
|
4455
|
+
if (expiresAtMs <= now.getTime()) throw new Error("policy challenge is expired");
|
|
4456
|
+
return signPolicyCredentialPresentationV4(Object.freeze({
|
|
4457
|
+
schema: POLICY_PRESENTATION_V4_SCHEMA,
|
|
4458
|
+
jti: input.jti ?? encodeBase64Url2(crypto.getRandomValues(new Uint8Array(16))),
|
|
4459
|
+
challengeId: input.challenge.challengeId,
|
|
4460
|
+
nonce: input.challenge.nonce,
|
|
4461
|
+
policyCid: input.policyCid,
|
|
4462
|
+
nodeAudience: input.challenge.nodeAudience,
|
|
4463
|
+
holderDid,
|
|
4464
|
+
subjectDid: holderDid,
|
|
4465
|
+
credentialDigest,
|
|
4466
|
+
requirementDigest,
|
|
4467
|
+
descriptorDigest: input.credential.descriptorDigest,
|
|
4468
|
+
requestedCapabilities: Object.freeze(requestedCapabilities),
|
|
4469
|
+
issuedAt: rfc3339Seconds(now),
|
|
4470
|
+
expiresAt: rfc3339Seconds(new Date(expiresAtMs))
|
|
4471
|
+
}), input.sign);
|
|
4472
|
+
}
|
|
4227
4473
|
async function buildPolicyCredentialPresentationV3(input) {
|
|
4228
4474
|
const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
|
|
4229
4475
|
const requirement = validateCredentialRequirement(input.requirement);
|
|
@@ -4310,10 +4556,12 @@ async function admitPolicyCredentialV3(input) {
|
|
|
4310
4556
|
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
4311
4557
|
const challenge = await requestPolicyChallengeV3({
|
|
4312
4558
|
nodeOrigin,
|
|
4559
|
+
policyRuntimePath: input.policyRuntimePath,
|
|
4313
4560
|
policyCid: input.policyCid,
|
|
4314
4561
|
recipientDid: input.credential.holderDid,
|
|
4315
4562
|
requestedCapabilities: input.requestedCapabilities,
|
|
4316
|
-
fetch: fetchFn
|
|
4563
|
+
fetch: fetchFn,
|
|
4564
|
+
signal: input.signal
|
|
4317
4565
|
});
|
|
4318
4566
|
const presentation = await buildPolicyCredentialPresentationV3({
|
|
4319
4567
|
...input,
|
|
@@ -4321,10 +4569,11 @@ async function admitPolicyCredentialV3(input) {
|
|
|
4321
4569
|
challenge
|
|
4322
4570
|
});
|
|
4323
4571
|
const response = await fetchFn(
|
|
4324
|
-
new URL(
|
|
4572
|
+
new URL(`${policyRuntimeBasePath2(input.policyRuntimePath)}/delegations`, nodeOrigin),
|
|
4325
4573
|
{
|
|
4326
4574
|
method: "POST",
|
|
4327
4575
|
redirect: "error",
|
|
4576
|
+
signal: input.signal,
|
|
4328
4577
|
headers: { accept: "application/json", "content-type": "application/json" },
|
|
4329
4578
|
body: JSON.stringify({
|
|
4330
4579
|
policyCid: input.policyCid,
|
|
@@ -4353,6 +4602,72 @@ async function admitPolicyCredentialV3(input) {
|
|
|
4353
4602
|
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
4603
|
throw new Error("policy delegation signed binding is invalid");
|
|
4355
4604
|
}
|
|
4605
|
+
input.signal?.throwIfAborted();
|
|
4356
4606
|
return Object.freeze({ challenge, presentation, session });
|
|
4357
4607
|
}
|
|
4608
|
+
function validatePolicyCredentialAdmissionV4Authority(session, expected) {
|
|
4609
|
+
if (session.iss.split("#", 1)[0] !== expected.nodeAudience || session.fact.nodeAudience !== expected.nodeAudience || session.fact.enforcerDid !== expected.enforcerDid) throw new Error("policy delegation authority binding is invalid");
|
|
4610
|
+
}
|
|
4611
|
+
async function admitPolicyCredentialV4(input) {
|
|
4612
|
+
const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
|
|
4613
|
+
const nodeOrigin = policyNodeOrigin(input.nodeOrigin);
|
|
4614
|
+
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
4615
|
+
const challenge = await requestPolicyChallengeV3({
|
|
4616
|
+
nodeOrigin,
|
|
4617
|
+
policyRuntimePath: input.policyRuntimePath,
|
|
4618
|
+
policyCid: input.policyCid,
|
|
4619
|
+
recipientDid: input.credential.holderDid,
|
|
4620
|
+
requestedCapabilities: input.requestedCapabilities,
|
|
4621
|
+
fetch: fetchFn,
|
|
4622
|
+
signal: input.signal
|
|
4623
|
+
});
|
|
4624
|
+
const presentation = await buildPolicyCredentialPresentationV4({
|
|
4625
|
+
...input,
|
|
4626
|
+
policy,
|
|
4627
|
+
challenge
|
|
4628
|
+
});
|
|
4629
|
+
const response = await fetchFn(new URL(`${policyRuntimeBasePath2(input.policyRuntimePath)}/delegations`, nodeOrigin), {
|
|
4630
|
+
method: "POST",
|
|
4631
|
+
redirect: "error",
|
|
4632
|
+
signal: input.signal,
|
|
4633
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
4634
|
+
body: JSON.stringify({
|
|
4635
|
+
policyCid: input.policyCid,
|
|
4636
|
+
challengeId: challenge.challengeId,
|
|
4637
|
+
nonce: challenge.nonce,
|
|
4638
|
+
requirement: validateCredentialRequirement(input.requirement),
|
|
4639
|
+
credential: envelopeFromVerified(input.credential),
|
|
4640
|
+
presentation
|
|
4641
|
+
})
|
|
4642
|
+
});
|
|
4643
|
+
if (!response.ok) throw new Error(`policy delegation rejected (${response.status})`);
|
|
4644
|
+
const value = record(await response.json(), "policy delegation");
|
|
4645
|
+
if (value.admitted !== true || typeof value.sessionCid !== "string" || typeof value.authorization !== "string") throw new Error("policy delegation response is invalid");
|
|
4646
|
+
const session = parsePolicySessionUcan(value.authorization, [
|
|
4647
|
+
input.policyRootCid,
|
|
4648
|
+
input.enforcementRootCid
|
|
4649
|
+
]);
|
|
4650
|
+
const expectedAttenuation = compactAttenuationForPolicyCapabilities(input.requestedCapabilities);
|
|
4651
|
+
validatePolicyCredentialAdmissionV4Authority(session, {
|
|
4652
|
+
nodeAudience: input.expectedNodeAudience,
|
|
4653
|
+
enforcerDid: input.expectedEnforcerDid
|
|
4654
|
+
});
|
|
4655
|
+
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)) throw new Error("policy delegation signed binding is invalid");
|
|
4656
|
+
const importResponse = await fetchFn(new URL("/delegate", nodeOrigin), {
|
|
4657
|
+
method: "POST",
|
|
4658
|
+
redirect: "error",
|
|
4659
|
+
signal: input.signal,
|
|
4660
|
+
headers: { Authorization: session.authorization }
|
|
4661
|
+
});
|
|
4662
|
+
if (!importResponse.ok) throw new Error(`delegation import rejected (${importResponse.status})`);
|
|
4663
|
+
input.signal?.throwIfAborted();
|
|
4664
|
+
return Object.freeze({ challenge, presentation, session, delegationImported: true });
|
|
4665
|
+
}
|
|
4666
|
+
function policyRuntimeBasePath2(value) {
|
|
4667
|
+
const path = value ?? "/policy/v3";
|
|
4668
|
+
if (!/^\/policy(?:\/[a-z0-9-]+)*$/.test(path)) {
|
|
4669
|
+
throw new Error("policy runtime path is invalid");
|
|
4670
|
+
}
|
|
4671
|
+
return path;
|
|
4672
|
+
}
|
|
4358
4673
|
//# sourceMappingURL=index.cjs.map
|