@tinycloud/sdk-core 2.11.0-beta.9 → 3.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{capability-BA49Uboq.d.cts → capability-BjzXwtNw.d.cts} +1 -1
- package/dist/{capability-BA49Uboq.d.ts → capability-BjzXwtNw.d.ts} +1 -1
- package/dist/credential-admission-C_cO1QqB.d.cts +781 -0
- package/dist/credential-admission-cvHe-HUC.d.ts +781 -0
- package/dist/delegations/index.cjs +620 -221
- package/dist/delegations/index.cjs.map +1 -1
- package/dist/delegations/index.d.cts +32 -32
- package/dist/delegations/index.d.ts +32 -32
- package/dist/delegations/index.js +622 -223
- package/dist/delegations/index.js.map +1 -1
- package/dist/index.cjs +3162 -1255
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +348 -18
- package/dist/index.d.ts +348 -18
- package/dist/index.js +3959 -2050
- package/dist/index.js.map +1 -1
- package/dist/policy/index.cjs +1273 -175
- package/dist/policy/index.cjs.map +1 -1
- package/dist/policy/index.d.cts +5 -4
- package/dist/policy/index.d.ts +5 -4
- package/dist/policy/index.js +1274 -176
- package/dist/policy/index.js.map +1 -1
- package/dist/{recipient-types.schema-D5pDlIqn.d.cts → recipient-types.schema-G8NEKRoo.d.cts} +240 -183
- package/dist/{recipient-types.schema-D5pDlIqn.d.ts → recipient-types.schema-G8NEKRoo.d.ts} +240 -183
- package/dist/requester/index.cjs.map +1 -1
- package/dist/requester/index.d.cts +1 -1
- package/dist/requester/index.d.ts +1 -1
- package/dist/requester/index.js +1 -1
- package/dist/requester/index.js.map +1 -1
- package/package.json +5 -5
- package/dist/signed-object-B3dMzh8O.d.ts +0 -149
- package/dist/signed-object-DwPZl6kV.d.cts +0 -149
package/dist/policy/index.cjs
CHANGED
|
@@ -22,15 +22,37 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
22
22
|
// src/policy/index.ts
|
|
23
23
|
var policy_exports = {};
|
|
24
24
|
__export(policy_exports, {
|
|
25
|
+
ATTESTED_ENFORCER_BINDING_V2_SCHEMA: () => ATTESTED_ENFORCER_BINDING_V2_SCHEMA,
|
|
26
|
+
CONTENT_SOURCE_V1_DOMAIN: () => CONTENT_SOURCE_V1_DOMAIN,
|
|
25
27
|
ED25519_JCS_SIGNATURE_SUITE: () => ED25519_JCS_SIGNATURE_SUITE,
|
|
26
28
|
EIP191_JCS_SIGNATURE_SUITE: () => EIP191_JCS_SIGNATURE_SUITE,
|
|
29
|
+
LAST_V2_CREATE_AT: () => LAST_V2_CREATE_AT,
|
|
30
|
+
LAST_V2_READ_AT: () => LAST_V2_READ_AT,
|
|
31
|
+
MAX_LEGACY_ENVELOPE_EXPIRES_AT: () => MAX_LEGACY_ENVELOPE_EXPIRES_AT,
|
|
32
|
+
NATIVE_PROJECTION_V1_DOMAIN: () => NATIVE_PROJECTION_V1_DOMAIN,
|
|
27
33
|
OWNER_NODE_ENDPOINT_SCHEMA: () => OWNER_NODE_ENDPOINT_SCHEMA,
|
|
34
|
+
POLICY_CAPABILITY_V1_DOMAIN: () => POLICY_CAPABILITY_V1_DOMAIN,
|
|
35
|
+
POLICY_CAPABILITY_V1_SCHEMA: () => POLICY_CAPABILITY_V1_SCHEMA,
|
|
36
|
+
POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE: () => POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE,
|
|
37
|
+
POLICY_ENFORCEMENT_DELEGATION_V2_SCHEMA: () => POLICY_ENFORCEMENT_DELEGATION_V2_SCHEMA,
|
|
28
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,
|
|
29
41
|
POLICY_SCHEMA: () => POLICY_SCHEMA,
|
|
42
|
+
POLICY_SESSION_UCAN_V1_PROFILE: () => POLICY_SESSION_UCAN_V1_PROFILE,
|
|
30
43
|
POLICY_STATUS_SCHEMA: () => POLICY_STATUS_SCHEMA,
|
|
44
|
+
POLICY_V1_DOMAIN: () => POLICY_V1_DOMAIN,
|
|
45
|
+
POLICY_V1_SCHEMA: () => POLICY_V1_SCHEMA,
|
|
46
|
+
POLICY_V2_DOMAIN: () => POLICY_V2_DOMAIN,
|
|
47
|
+
POLICY_V2_SCHEMA: () => POLICY_V2_SCHEMA,
|
|
31
48
|
POLICY_VERSION_V0: () => POLICY_VERSION_V0,
|
|
32
49
|
PolicyAuthoringError: () => PolicyAuthoringError,
|
|
33
50
|
PolicyCapabilityError: () => PolicyCapabilityError,
|
|
51
|
+
ROOT_REVOCATION_V1_DOMAIN: () => ROOT_REVOCATION_V1_DOMAIN,
|
|
52
|
+
ROOT_REVOCATION_V1_SCHEMA: () => ROOT_REVOCATION_V1_SCHEMA,
|
|
53
|
+
ROOT_STATUS_CHECKPOINT_V1_SCHEMA: () => ROOT_STATUS_CHECKPOINT_V1_SCHEMA,
|
|
54
|
+
ROOT_STATUS_RENEWAL_V1_DOMAIN: () => ROOT_STATUS_RENEWAL_V1_DOMAIN,
|
|
55
|
+
ROOT_STATUS_V1_DOMAIN: () => ROOT_STATUS_V1_DOMAIN,
|
|
34
56
|
SignatureMaterialError: () => SignatureMaterialError,
|
|
35
57
|
SignatureVerificationError: () => SignatureVerificationError,
|
|
36
58
|
SignedObjectCanonicalizationError: () => SignedObjectCanonicalizationError,
|
|
@@ -42,25 +64,53 @@ __export(policy_exports, {
|
|
|
42
64
|
TRANSCRIPT_SHARE_BOOTSTRAP_SCHEMA: () => TRANSCRIPT_SHARE_BOOTSTRAP_SCHEMA,
|
|
43
65
|
UnsupportedSignatureSuiteError: () => UnsupportedSignatureSuiteError,
|
|
44
66
|
W3C_VC_CREDENTIAL_VERIFIER: () => W3C_VC_CREDENTIAL_VERIFIER,
|
|
67
|
+
admitPolicyCredentialV3: () => admitPolicyCredentialV3,
|
|
68
|
+
buildPolicyCredentialPresentationV3: () => buildPolicyCredentialPresentationV3,
|
|
45
69
|
canonicalizePolicyCapability: () => canonicalizePolicyCapability,
|
|
46
70
|
canonicalizeSignedObjectUnsigned: () => canonicalizeSignedObjectUnsigned,
|
|
71
|
+
canonicalizeUnifiedPolicyCapability: () => canonicalizeUnifiedPolicyCapability,
|
|
72
|
+
compactAttenuationContains: () => compactAttenuationContains,
|
|
47
73
|
composeTranscriptShareBootstrap: () => composeTranscriptShareBootstrap,
|
|
74
|
+
contentSourceDigestHex: () => contentSourceDigestHex,
|
|
48
75
|
createAndSignPolicy: () => createAndSignPolicy,
|
|
49
76
|
createAndSignPolicyEngineRecord: () => createAndSignPolicyEngineRecord,
|
|
50
77
|
createAndSignPolicyStatus: () => createAndSignPolicyStatus,
|
|
51
78
|
createAndSignRequesterPolicyEngineRecord: () => createAndSignRequesterPolicyEngineRecord,
|
|
52
79
|
createAndSignSignedObject: () => createAndSignSignedObject,
|
|
53
80
|
createAndSignTranscriptSharePolicy: () => createAndSignTranscriptSharePolicy,
|
|
81
|
+
createCompactPolicyDescendant: () => createCompactPolicyDescendant,
|
|
82
|
+
createCompactPolicyInvocation: () => createCompactPolicyInvocation,
|
|
54
83
|
createUnsignedPolicyEngineRecord: () => createUnsignedPolicyEngineRecord,
|
|
55
84
|
deriveSignedObjectMaterial: () => deriveSignedObjectMaterial,
|
|
85
|
+
getPolicyRootStatusV3: () => getPolicyRootStatusV3,
|
|
56
86
|
jcsCanonicalize: () => jcsCanonicalize,
|
|
87
|
+
mintPolicySessionV3: () => mintPolicySessionV3,
|
|
57
88
|
normalizeJson: () => normalizeJson,
|
|
58
89
|
normalizePolicyCapability: () => normalizePolicyCapability,
|
|
90
|
+
normalizeUnifiedPolicyCapability: () => normalizeUnifiedPolicyCapability,
|
|
91
|
+
parseCompactUcanAuthorization: () => parseCompactUcanAuthorization,
|
|
92
|
+
parsePolicySessionUcan: () => parsePolicySessionUcan,
|
|
59
93
|
policyCapabilityContains: () => policyCapabilityContains,
|
|
60
94
|
policyCapabilityDigestHex: () => policyCapabilityDigestHex,
|
|
95
|
+
policyCidFromCanonicalBytes: () => policyCidFromCanonicalBytes,
|
|
96
|
+
policyDigestHex: () => policyDigestHex,
|
|
97
|
+
policyIdForDigestHex: () => policyIdForDigestHex,
|
|
98
|
+
policyV2DigestHex: () => policyV2DigestHex,
|
|
99
|
+
projectUnifiedPolicyCapabilities: () => projectUnifiedPolicyCapabilities,
|
|
100
|
+
projectUnifiedPolicyCapability: () => projectUnifiedPolicyCapability,
|
|
101
|
+
renewPolicyRootStatusV3: () => renewPolicyRootStatusV3,
|
|
102
|
+
requestPolicyChallengeV3: () => requestPolicyChallengeV3,
|
|
103
|
+
revokePolicyRootV3: () => revokePolicyRootV3,
|
|
61
104
|
serializeJcsJson: () => serialize,
|
|
105
|
+
signCompactUcanAuthorization: () => signCompactUcanAuthorization,
|
|
106
|
+
signCompactUcanRootAuthorization: () => signCompactUcanRootAuthorization,
|
|
62
107
|
signedObjectIdFor: () => signedObjectIdFor,
|
|
63
108
|
toSignedObjectError: () => toSignedObjectError,
|
|
109
|
+
unifiedNativeProjectionHashHex: () => unifiedNativeProjectionHashHex,
|
|
110
|
+
unifiedPolicyCapabilityContains: () => unifiedPolicyCapabilityContains,
|
|
111
|
+
unifiedPolicyCapabilityDigestHex: () => unifiedPolicyCapabilityDigestHex,
|
|
112
|
+
unifiedPolicyCapabilityFromNative: () => unifiedPolicyCapabilityFromNative,
|
|
113
|
+
validatePolicyCredentialRequirementV1: () => validatePolicyCredentialRequirementV1,
|
|
64
114
|
validatePolicyEngineRecordSigned: () => validatePolicyEngineRecordSigned,
|
|
65
115
|
validatePolicyEngineRecordSignedShape: () => validatePolicyEngineRecordSignedShape,
|
|
66
116
|
validatePolicyEngineRecordUnsigned: () => validatePolicyEngineRecordUnsigned,
|
|
@@ -70,9 +120,12 @@ __export(policy_exports, {
|
|
|
70
120
|
validatePolicyStatusSignedShape: () => validatePolicyStatusSignedShape,
|
|
71
121
|
validatePolicyStatusUnsigned: () => validatePolicyStatusUnsigned,
|
|
72
122
|
validatePolicyUnsigned: () => validatePolicyUnsigned,
|
|
123
|
+
validateUnifiedPolicyV2: () => validateUnifiedPolicyV2,
|
|
73
124
|
verifyPolicy: () => verifyPolicy,
|
|
74
125
|
verifyPolicyEngineRecord: () => verifyPolicyEngineRecord,
|
|
75
126
|
verifyPolicyEngineRecordForRequester: () => verifyPolicyEngineRecordForRequester,
|
|
127
|
+
verifyPolicyRootRevocationV3: () => verifyPolicyRootRevocationV3,
|
|
128
|
+
verifyPolicyRootStatusCheckpointV3: () => verifyPolicyRootStatusCheckpointV3,
|
|
76
129
|
verifyPolicyStatus: () => verifyPolicyStatus,
|
|
77
130
|
verifySignedObject: () => verifySignedObject
|
|
78
131
|
});
|
|
@@ -395,16 +448,16 @@ function policyCapabilityContains(authority, request) {
|
|
|
395
448
|
return caveatsContain(auth.caveats, req.caveats);
|
|
396
449
|
}
|
|
397
450
|
function normalizePolicyCapabilityWithOptions(input, options) {
|
|
398
|
-
const
|
|
399
|
-
assertExactKeys(
|
|
400
|
-
const service = requiredString(
|
|
451
|
+
const object2 = expectObject(input, "$");
|
|
452
|
+
assertExactKeys(object2, ["service", "space", "path", "actions", "caveats"], "$");
|
|
453
|
+
const service = requiredString(object2, "service", "$", "policy-capability-malformed-service");
|
|
401
454
|
if (!CEILING_SERVICES.has(service)) {
|
|
402
455
|
throw new PolicyCapabilityError(
|
|
403
456
|
"policy-capability-malformed-service",
|
|
404
457
|
"$.service is outside the frozen permissionsCeiling vocabulary"
|
|
405
458
|
);
|
|
406
459
|
}
|
|
407
|
-
const space = requiredString(
|
|
460
|
+
const space = requiredString(object2, "space", "$", "policy-capability-malformed-space");
|
|
408
461
|
validateConcreteSpace(space);
|
|
409
462
|
const canonicalSpace = space.normalize("NFC");
|
|
410
463
|
if (options.requireCanonical && canonicalSpace !== space) {
|
|
@@ -413,7 +466,7 @@ function normalizePolicyCapabilityWithOptions(input, options) {
|
|
|
413
466
|
"$.space must already be canonical NFC"
|
|
414
467
|
);
|
|
415
468
|
}
|
|
416
|
-
const rawPath = requiredString(
|
|
469
|
+
const rawPath = requiredString(object2, "path", "$", "policy-capability-malformed-path");
|
|
417
470
|
validateRawPath(rawPath, options);
|
|
418
471
|
const path = normalizePath(rawPath);
|
|
419
472
|
if (options.requireCanonical && path !== rawPath) {
|
|
@@ -424,7 +477,7 @@ function normalizePolicyCapabilityWithOptions(input, options) {
|
|
|
424
477
|
}
|
|
425
478
|
validateRawPath(path, options);
|
|
426
479
|
const actions = normalizeActions(
|
|
427
|
-
requiredArray(
|
|
480
|
+
requiredArray(object2, "actions", "$", "policy-capability-malformed-action"),
|
|
428
481
|
service,
|
|
429
482
|
options
|
|
430
483
|
);
|
|
@@ -433,9 +486,9 @@ function normalizePolicyCapabilityWithOptions(input, options) {
|
|
|
433
486
|
output.space = canonicalSpace;
|
|
434
487
|
output.path = path;
|
|
435
488
|
output.actions = actions;
|
|
436
|
-
if (hasOwn(
|
|
489
|
+
if (hasOwn(object2, "caveats")) {
|
|
437
490
|
output.caveats = validateCaveats(
|
|
438
|
-
requiredValue(
|
|
491
|
+
requiredValue(object2, "caveats", "$", "policy-capability-malformed-caveats"),
|
|
439
492
|
output.service
|
|
440
493
|
);
|
|
441
494
|
}
|
|
@@ -458,9 +511,9 @@ function expectObject(input, path) {
|
|
|
458
511
|
);
|
|
459
512
|
}
|
|
460
513
|
}
|
|
461
|
-
function assertExactKeys(
|
|
514
|
+
function assertExactKeys(object2, allowed, path) {
|
|
462
515
|
const allowedSet = new Set(allowed);
|
|
463
|
-
for (const key of Object.keys(
|
|
516
|
+
for (const key of Object.keys(object2)) {
|
|
464
517
|
if (!allowedSet.has(key)) {
|
|
465
518
|
throw new PolicyCapabilityError(
|
|
466
519
|
key === "id" || key === "scope" ? "policy-capability-malformed" : "policy-capability-unknown-key",
|
|
@@ -469,21 +522,21 @@ function assertExactKeys(object, allowed, path) {
|
|
|
469
522
|
}
|
|
470
523
|
}
|
|
471
524
|
}
|
|
472
|
-
function requiredValue(
|
|
473
|
-
if (!hasOwn(
|
|
525
|
+
function requiredValue(object2, key, path, code2) {
|
|
526
|
+
if (!hasOwn(object2, key)) {
|
|
474
527
|
throw new PolicyCapabilityError(code2, `${path}.${key} is required`);
|
|
475
528
|
}
|
|
476
|
-
return
|
|
529
|
+
return object2[key];
|
|
477
530
|
}
|
|
478
|
-
function requiredString(
|
|
479
|
-
const value = requiredValue(
|
|
531
|
+
function requiredString(object2, key, path, code2) {
|
|
532
|
+
const value = requiredValue(object2, key, path, code2);
|
|
480
533
|
if (typeof value !== "string" || value.length === 0) {
|
|
481
534
|
throw new PolicyCapabilityError(code2, `${path}.${key} must be a non-empty string`);
|
|
482
535
|
}
|
|
483
536
|
return value;
|
|
484
537
|
}
|
|
485
|
-
function requiredArray(
|
|
486
|
-
const value = requiredValue(
|
|
538
|
+
function requiredArray(object2, key, path, code2) {
|
|
539
|
+
const value = requiredValue(object2, key, path, code2);
|
|
487
540
|
if (!Array.isArray(value)) {
|
|
488
541
|
throw new PolicyCapabilityError(code2, `${path}.${key} must be an array`);
|
|
489
542
|
}
|
|
@@ -628,11 +681,11 @@ function validateSqlStatement(input, path) {
|
|
|
628
681
|
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
629
682
|
throw new PolicyCapabilityError("policy-capability-malformed-caveats", `${path} must be an object`);
|
|
630
683
|
}
|
|
631
|
-
const
|
|
632
|
-
assertCaveatKeys(
|
|
633
|
-
caveatString(
|
|
634
|
-
caveatString(
|
|
635
|
-
const fixedParams = requiredCaveat(
|
|
684
|
+
const object2 = input;
|
|
685
|
+
assertCaveatKeys(object2, ["name", "sql", "fixedParams"], path);
|
|
686
|
+
caveatString(object2, "name", path);
|
|
687
|
+
caveatString(object2, "sql", path);
|
|
688
|
+
const fixedParams = requiredCaveat(object2, "fixedParams", path);
|
|
636
689
|
if (!Array.isArray(fixedParams)) {
|
|
637
690
|
throw new PolicyCapabilityError(
|
|
638
691
|
"policy-capability-malformed-caveats",
|
|
@@ -647,20 +700,20 @@ function validateFixedParam(input, path) {
|
|
|
647
700
|
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
648
701
|
throw new PolicyCapabilityError("policy-capability-malformed-caveats", `${path} must be an object`);
|
|
649
702
|
}
|
|
650
|
-
const
|
|
651
|
-
assertCaveatKeys(
|
|
652
|
-
const index = requiredCaveat(
|
|
703
|
+
const object2 = input;
|
|
704
|
+
assertCaveatKeys(object2, ["index", "value"], path);
|
|
705
|
+
const index = requiredCaveat(object2, "index", path);
|
|
653
706
|
if (typeof index !== "number" || !Number.isSafeInteger(index) || index < 0) {
|
|
654
707
|
throw new PolicyCapabilityError(
|
|
655
708
|
"policy-capability-malformed-caveats",
|
|
656
709
|
`${path}.index must be a non-negative integer`
|
|
657
710
|
);
|
|
658
711
|
}
|
|
659
|
-
requiredCaveat(
|
|
712
|
+
requiredCaveat(object2, "value", path);
|
|
660
713
|
}
|
|
661
|
-
function assertCaveatKeys(
|
|
714
|
+
function assertCaveatKeys(object2, allowed, path) {
|
|
662
715
|
const allowedSet = new Set(allowed);
|
|
663
|
-
for (const key of Object.keys(
|
|
716
|
+
for (const key of Object.keys(object2)) {
|
|
664
717
|
if (!allowedSet.has(key)) {
|
|
665
718
|
throw new PolicyCapabilityError(
|
|
666
719
|
"policy-capability-malformed-caveats",
|
|
@@ -669,17 +722,17 @@ function assertCaveatKeys(object, allowed, path) {
|
|
|
669
722
|
}
|
|
670
723
|
}
|
|
671
724
|
}
|
|
672
|
-
function requiredCaveat(
|
|
673
|
-
if (!hasOwn(
|
|
725
|
+
function requiredCaveat(object2, key, path) {
|
|
726
|
+
if (!hasOwn(object2, key)) {
|
|
674
727
|
throw new PolicyCapabilityError(
|
|
675
728
|
"policy-capability-malformed-caveats",
|
|
676
729
|
`${path}.${key} is required`
|
|
677
730
|
);
|
|
678
731
|
}
|
|
679
|
-
return
|
|
732
|
+
return object2[key];
|
|
680
733
|
}
|
|
681
|
-
function caveatString(
|
|
682
|
-
const value = requiredCaveat(
|
|
734
|
+
function caveatString(object2, key, path) {
|
|
735
|
+
const value = requiredCaveat(object2, key, path);
|
|
683
736
|
if (typeof value !== "string" || value.length === 0) {
|
|
684
737
|
throw new PolicyCapabilityError(
|
|
685
738
|
"policy-capability-malformed-caveats",
|
|
@@ -710,8 +763,8 @@ function caveatsContain(authority, request) {
|
|
|
710
763
|
(statement) => authorityStatements.some((candidate) => jcsCanonicalize(candidate) === jcsCanonicalize(statement))
|
|
711
764
|
);
|
|
712
765
|
}
|
|
713
|
-
function hasOwn(
|
|
714
|
-
return objectHasOwn2(
|
|
766
|
+
function hasOwn(object2, key) {
|
|
767
|
+
return objectHasOwn2(object2, key);
|
|
715
768
|
}
|
|
716
769
|
|
|
717
770
|
// src/policy/signed-object.ts
|
|
@@ -1981,17 +2034,17 @@ async function createAndSignSignedObject(input, signer) {
|
|
|
1981
2034
|
}
|
|
1982
2035
|
function createAndSignPolicy(input, signer) {
|
|
1983
2036
|
return createAndSignSignedObject(input, signer).then(
|
|
1984
|
-
(
|
|
2037
|
+
(object2) => validatePolicySignedShape(object2)
|
|
1985
2038
|
);
|
|
1986
2039
|
}
|
|
1987
2040
|
function createAndSignPolicyStatus(input, signer) {
|
|
1988
2041
|
return createAndSignSignedObject(input, signer).then(
|
|
1989
|
-
(
|
|
2042
|
+
(object2) => validatePolicyStatusSignedShape(object2)
|
|
1990
2043
|
);
|
|
1991
2044
|
}
|
|
1992
2045
|
function createAndSignPolicyEngineRecord(input, signer) {
|
|
1993
2046
|
return createAndSignSignedObject(input, signer).then(
|
|
1994
|
-
(
|
|
2047
|
+
(object2) => validatePolicyEngineRecordSignedShape(object2)
|
|
1995
2048
|
);
|
|
1996
2049
|
}
|
|
1997
2050
|
async function verifySignedObject(input) {
|
|
@@ -2095,9 +2148,9 @@ function validateUnsignedForDescriptor(input, descriptor) {
|
|
|
2095
2148
|
}
|
|
2096
2149
|
}
|
|
2097
2150
|
function validatePolicyShape(input, signed) {
|
|
2098
|
-
const
|
|
2151
|
+
const object2 = expectJsonObject(normalizeJson(input), "$");
|
|
2099
2152
|
assertExactKeys2(
|
|
2100
|
-
|
|
2153
|
+
object2,
|
|
2101
2154
|
signed ? [
|
|
2102
2155
|
"schema",
|
|
2103
2156
|
"policyId",
|
|
@@ -2125,30 +2178,30 @@ function validatePolicyShape(input, signed) {
|
|
|
2125
2178
|
],
|
|
2126
2179
|
"$"
|
|
2127
2180
|
);
|
|
2128
|
-
expectConst(requiredString2(
|
|
2181
|
+
expectConst(requiredString2(object2, "schema", "$"), POLICY_SCHEMA, "$.schema");
|
|
2129
2182
|
if (signed) {
|
|
2130
|
-
requiredString2(
|
|
2131
|
-
validateSignature(requiredValue2(
|
|
2183
|
+
requiredString2(object2, "policyId", "$");
|
|
2184
|
+
validateSignature(requiredValue2(object2, "signature", "$"));
|
|
2132
2185
|
}
|
|
2133
|
-
requiredString2(
|
|
2134
|
-
requiredString2(
|
|
2135
|
-
requiredDateString(
|
|
2136
|
-
optionalDateString(
|
|
2137
|
-
validatePolicyResource(requiredValue2(
|
|
2138
|
-
validateExpression(requiredValue2(
|
|
2139
|
-
validateGrant(requiredValue2(
|
|
2140
|
-
if (hasOwn2(
|
|
2141
|
-
validateDisclosure(requiredValue2(
|
|
2186
|
+
requiredString2(object2, "ownerDid", "$");
|
|
2187
|
+
requiredString2(object2, "signingKeyDid", "$");
|
|
2188
|
+
requiredDateString(object2, "createdAt", "$");
|
|
2189
|
+
optionalDateString(object2, "expiresAt", "$");
|
|
2190
|
+
validatePolicyResource(requiredValue2(object2, "resource", "$"), "$.resource");
|
|
2191
|
+
validateExpression(requiredValue2(object2, "when", "$"), "$.when");
|
|
2192
|
+
validateGrant(requiredValue2(object2, "grant", "$"), "$.grant");
|
|
2193
|
+
if (hasOwn2(object2, "disclosure")) {
|
|
2194
|
+
validateDisclosure(requiredValue2(object2, "disclosure", "$"), "$.disclosure");
|
|
2142
2195
|
}
|
|
2143
|
-
if (hasOwn2(
|
|
2144
|
-
validateAudit(requiredValue2(
|
|
2196
|
+
if (hasOwn2(object2, "audit")) {
|
|
2197
|
+
validateAudit(requiredValue2(object2, "audit", "$"), "$.audit");
|
|
2145
2198
|
}
|
|
2146
|
-
return
|
|
2199
|
+
return object2;
|
|
2147
2200
|
}
|
|
2148
2201
|
function validatePolicyStatusShape(input, signed) {
|
|
2149
|
-
const
|
|
2202
|
+
const object2 = expectJsonObject(normalizeJson(input), "$");
|
|
2150
2203
|
assertExactKeys2(
|
|
2151
|
-
|
|
2204
|
+
object2,
|
|
2152
2205
|
signed ? [
|
|
2153
2206
|
"schema",
|
|
2154
2207
|
"statusId",
|
|
@@ -2172,28 +2225,28 @@ function validatePolicyStatusShape(input, signed) {
|
|
|
2172
2225
|
],
|
|
2173
2226
|
"$"
|
|
2174
2227
|
);
|
|
2175
|
-
expectConst(requiredString2(
|
|
2228
|
+
expectConst(requiredString2(object2, "schema", "$"), POLICY_STATUS_SCHEMA, "$.schema");
|
|
2176
2229
|
if (signed) {
|
|
2177
|
-
requiredString2(
|
|
2178
|
-
validateSignature(requiredValue2(
|
|
2230
|
+
requiredString2(object2, "statusId", "$");
|
|
2231
|
+
validateSignature(requiredValue2(object2, "signature", "$"));
|
|
2179
2232
|
}
|
|
2180
|
-
requiredString2(
|
|
2181
|
-
requiredString2(
|
|
2182
|
-
requiredInteger(
|
|
2183
|
-
expectOneOf(requiredString2(
|
|
2233
|
+
requiredString2(object2, "policyId", "$");
|
|
2234
|
+
requiredString2(object2, "ownerDid", "$");
|
|
2235
|
+
requiredInteger(object2, "sequence", "$", 0);
|
|
2236
|
+
expectOneOf(requiredString2(object2, "disposition", "$"), [
|
|
2184
2237
|
"active",
|
|
2185
2238
|
"suspended",
|
|
2186
2239
|
"revoked"
|
|
2187
2240
|
], "$.disposition");
|
|
2188
|
-
requiredDateString(
|
|
2189
|
-
optionalString(
|
|
2190
|
-
requiredString2(
|
|
2191
|
-
return
|
|
2241
|
+
requiredDateString(object2, "effectiveAt", "$");
|
|
2242
|
+
optionalString(object2, "reasonCode", "$");
|
|
2243
|
+
requiredString2(object2, "signingKeyDid", "$");
|
|
2244
|
+
return object2;
|
|
2192
2245
|
}
|
|
2193
2246
|
function validatePolicyEngineRecordShape(input, signed) {
|
|
2194
|
-
const
|
|
2247
|
+
const object2 = expectJsonObject(normalizeJson(input), "$");
|
|
2195
2248
|
assertExactKeys2(
|
|
2196
|
-
|
|
2249
|
+
object2,
|
|
2197
2250
|
signed ? [
|
|
2198
2251
|
"schema",
|
|
2199
2252
|
"engineRecordId",
|
|
@@ -2218,44 +2271,44 @@ function validatePolicyEngineRecordShape(input, signed) {
|
|
|
2218
2271
|
"$"
|
|
2219
2272
|
);
|
|
2220
2273
|
expectConst(
|
|
2221
|
-
requiredString2(
|
|
2274
|
+
requiredString2(object2, "schema", "$"),
|
|
2222
2275
|
POLICY_ENGINE_RECORD_SCHEMA,
|
|
2223
2276
|
"$.schema"
|
|
2224
2277
|
);
|
|
2225
2278
|
if (signed) {
|
|
2226
|
-
requiredString2(
|
|
2227
|
-
validateSignature(requiredValue2(
|
|
2279
|
+
requiredString2(object2, "engineRecordId", "$");
|
|
2280
|
+
validateSignature(requiredValue2(object2, "signature", "$"));
|
|
2228
2281
|
}
|
|
2229
|
-
requiredString2(
|
|
2230
|
-
requiredString2(
|
|
2231
|
-
requiredString2(
|
|
2282
|
+
requiredString2(object2, "ownerDid", "$");
|
|
2283
|
+
requiredString2(object2, "endpoint", "$");
|
|
2284
|
+
requiredString2(object2, "audience", "$");
|
|
2232
2285
|
requiredStringArray(
|
|
2233
|
-
|
|
2286
|
+
object2,
|
|
2234
2287
|
"supportedPolicyVersions",
|
|
2235
2288
|
"$",
|
|
2236
2289
|
(value, path) => expectConst(value, "v0", path)
|
|
2237
2290
|
);
|
|
2238
|
-
requiredStringArray(
|
|
2239
|
-
requiredString2(
|
|
2240
|
-
requiredDateString(
|
|
2241
|
-
return
|
|
2291
|
+
requiredStringArray(object2, "supportedEvidenceVerifiers", "$");
|
|
2292
|
+
requiredString2(object2, "grantIssuerDid", "$");
|
|
2293
|
+
requiredDateString(object2, "expiresAt", "$");
|
|
2294
|
+
return object2;
|
|
2242
2295
|
}
|
|
2243
2296
|
function validateSignature(input) {
|
|
2244
|
-
const
|
|
2245
|
-
assertExactKeys2(
|
|
2246
|
-
const suite = requiredString2(
|
|
2297
|
+
const object2 = expectJsonObject(input, "$.signature");
|
|
2298
|
+
assertExactKeys2(object2, ["suite", "signerDid", "value"], "$.signature");
|
|
2299
|
+
const suite = requiredString2(object2, "suite", "$.signature");
|
|
2247
2300
|
assertSupportedSignatureSuite(suite);
|
|
2248
|
-
const signerDid = requiredString2(
|
|
2249
|
-
const value = requiredString2(
|
|
2301
|
+
const signerDid = requiredString2(object2, "signerDid", "$.signature");
|
|
2302
|
+
const value = requiredString2(object2, "value", "$.signature");
|
|
2250
2303
|
decodeSignatureValue(value, suite);
|
|
2251
2304
|
return { suite, signerDid, value };
|
|
2252
2305
|
}
|
|
2253
2306
|
function validatePolicyResource(input, path) {
|
|
2254
|
-
const
|
|
2255
|
-
assertExactKeys2(
|
|
2256
|
-
requiredString2(
|
|
2257
|
-
requiredString2(
|
|
2258
|
-
const ceiling = requiredArray2(
|
|
2307
|
+
const object2 = expectJsonObject(input, path);
|
|
2308
|
+
assertExactKeys2(object2, ["resourceType", "resourceId", "permissionsCeiling"], path);
|
|
2309
|
+
requiredString2(object2, "resourceType", path);
|
|
2310
|
+
requiredString2(object2, "resourceId", path);
|
|
2311
|
+
const ceiling = requiredArray2(object2, "permissionsCeiling", path, 1);
|
|
2259
2312
|
for (let index = 0; index < ceiling.length; index++) {
|
|
2260
2313
|
validatePolicyCapability(ceiling[index], `${path}.permissionsCeiling[${index}]`);
|
|
2261
2314
|
}
|
|
@@ -2271,10 +2324,10 @@ function validatePolicyPermissionsCeilingForSigning(input) {
|
|
|
2271
2324
|
}
|
|
2272
2325
|
}
|
|
2273
2326
|
function validatePolicyCapability(input, path) {
|
|
2274
|
-
const
|
|
2327
|
+
const object2 = expectJsonObject(input, path);
|
|
2275
2328
|
try {
|
|
2276
|
-
const canonical = canonicalizePolicyCapability(
|
|
2277
|
-
if (jcsCanonicalize(
|
|
2329
|
+
const canonical = canonicalizePolicyCapability(object2);
|
|
2330
|
+
if (jcsCanonicalize(object2) !== jcsCanonicalize(canonical)) {
|
|
2278
2331
|
throw new SignedObjectSchemaError(`${path} must be canonical PolicyCapability JSON`);
|
|
2279
2332
|
}
|
|
2280
2333
|
} catch (error) {
|
|
@@ -2287,10 +2340,10 @@ function validatePolicyCapability(input, path) {
|
|
|
2287
2340
|
}
|
|
2288
2341
|
}
|
|
2289
2342
|
function validatePolicyCapabilityForSigning(input, path) {
|
|
2290
|
-
const
|
|
2343
|
+
const object2 = expectJsonObject(input, path);
|
|
2291
2344
|
try {
|
|
2292
|
-
const canonical = normalizePolicyCapability(
|
|
2293
|
-
if (jcsCanonicalize(
|
|
2345
|
+
const canonical = normalizePolicyCapability(object2);
|
|
2346
|
+
if (jcsCanonicalize(object2) !== jcsCanonicalize(canonical)) {
|
|
2294
2347
|
throw new SignedObjectSchemaError(`${path} must be strict canonical PolicyCapability JSON`);
|
|
2295
2348
|
}
|
|
2296
2349
|
} catch (error) {
|
|
@@ -2303,43 +2356,43 @@ function validatePolicyCapabilityForSigning(input, path) {
|
|
|
2303
2356
|
}
|
|
2304
2357
|
}
|
|
2305
2358
|
function validateExpression(input, path) {
|
|
2306
|
-
const
|
|
2307
|
-
const keys = Object.keys(
|
|
2359
|
+
const object2 = expectJsonObject(input, path);
|
|
2360
|
+
const keys = Object.keys(object2);
|
|
2308
2361
|
if (keys.length !== 1) {
|
|
2309
2362
|
throw new SignedObjectSchemaError(`${path} must have exactly one expression key`);
|
|
2310
2363
|
}
|
|
2311
2364
|
const key = keys[0];
|
|
2312
2365
|
if (key === "allOf" || key === "anyOf") {
|
|
2313
|
-
const values = requiredArray2(
|
|
2366
|
+
const values = requiredArray2(object2, key, path, 1);
|
|
2314
2367
|
for (let index = 0; index < values.length; index++) {
|
|
2315
2368
|
validateExpression(values[index], `${path}.${key}[${index}]`);
|
|
2316
2369
|
}
|
|
2317
2370
|
return;
|
|
2318
2371
|
}
|
|
2319
2372
|
if (key === "subject") {
|
|
2320
|
-
const subject = expectJsonObject(requiredValue2(
|
|
2373
|
+
const subject = expectJsonObject(requiredValue2(object2, "subject", path), `${path}.subject`);
|
|
2321
2374
|
assertExactKeys2(subject, ["did"], `${path}.subject`);
|
|
2322
2375
|
requiredString2(subject, "did", `${path}.subject`);
|
|
2323
2376
|
return;
|
|
2324
2377
|
}
|
|
2325
2378
|
if (key === "evidence") {
|
|
2326
|
-
validateEvidenceRequirement(requiredValue2(
|
|
2379
|
+
validateEvidenceRequirement(requiredValue2(object2, "evidence", path), `${path}.evidence`);
|
|
2327
2380
|
return;
|
|
2328
2381
|
}
|
|
2329
2382
|
throw new SignedObjectSchemaError(`${path} has unknown expression key ${key}`);
|
|
2330
2383
|
}
|
|
2331
2384
|
function validateEvidenceRequirement(input, path) {
|
|
2332
|
-
const
|
|
2385
|
+
const object2 = expectJsonObject(input, path);
|
|
2333
2386
|
assertExactKeys2(
|
|
2334
|
-
|
|
2387
|
+
object2,
|
|
2335
2388
|
["requirementId", "verifier", "requirements", "authority", "freshness"],
|
|
2336
2389
|
path
|
|
2337
2390
|
);
|
|
2338
|
-
requiredString2(
|
|
2339
|
-
requiredString2(
|
|
2340
|
-
requiredValue2(
|
|
2341
|
-
if (hasOwn2(
|
|
2342
|
-
const authority = expectJsonObject(requiredValue2(
|
|
2391
|
+
requiredString2(object2, "requirementId", path);
|
|
2392
|
+
requiredString2(object2, "verifier", path);
|
|
2393
|
+
requiredValue2(object2, "requirements", path);
|
|
2394
|
+
if (hasOwn2(object2, "authority")) {
|
|
2395
|
+
const authority = expectJsonObject(requiredValue2(object2, "authority", path), `${path}.authority`);
|
|
2343
2396
|
assertExactKeys2(
|
|
2344
2397
|
authority,
|
|
2345
2398
|
["profile", "acceptedIssuers", "allowOwnerAuthorizedIssuer"],
|
|
@@ -2351,35 +2404,35 @@ function validateEvidenceRequirement(input, path) {
|
|
|
2351
2404
|
}
|
|
2352
2405
|
optionalBoolean(authority, "allowOwnerAuthorizedIssuer", `${path}.authority`);
|
|
2353
2406
|
}
|
|
2354
|
-
if (hasOwn2(
|
|
2355
|
-
const freshness = expectJsonObject(requiredValue2(
|
|
2407
|
+
if (hasOwn2(object2, "freshness")) {
|
|
2408
|
+
const freshness = expectJsonObject(requiredValue2(object2, "freshness", path), `${path}.freshness`);
|
|
2356
2409
|
assertExactKeys2(freshness, ["maxStatusAgeSeconds"], `${path}.freshness`);
|
|
2357
2410
|
requiredInteger(freshness, "maxStatusAgeSeconds", `${path}.freshness`, 0);
|
|
2358
2411
|
}
|
|
2359
2412
|
}
|
|
2360
2413
|
function validateGrant(input, path) {
|
|
2361
|
-
const
|
|
2362
|
-
assertExactKeys2(
|
|
2363
|
-
expectConst(requiredString2(
|
|
2364
|
-
requiredInteger(
|
|
2365
|
-
expectOneOf(requiredString2(
|
|
2414
|
+
const object2 = expectJsonObject(input, path);
|
|
2415
|
+
assertExactKeys2(object2, ["output", "maxTtlSeconds", "delegationMode", "revocation"], path);
|
|
2416
|
+
expectConst(requiredString2(object2, "output", path), "portable-delegation", `${path}.output`);
|
|
2417
|
+
requiredInteger(object2, "maxTtlSeconds", path, 1);
|
|
2418
|
+
expectOneOf(requiredString2(object2, "delegationMode", path), [
|
|
2366
2419
|
"terminal",
|
|
2367
2420
|
"attenuable"
|
|
2368
2421
|
], `${path}.delegationMode`);
|
|
2369
|
-
expectOneOf(requiredString2(
|
|
2422
|
+
expectOneOf(requiredString2(object2, "revocation", path), [
|
|
2370
2423
|
"refresh_only",
|
|
2371
2424
|
"active_cutoff"
|
|
2372
2425
|
], `${path}.revocation`);
|
|
2373
2426
|
}
|
|
2374
2427
|
function validateDisclosure(input, path) {
|
|
2375
|
-
const
|
|
2376
|
-
assertExactKeys2(
|
|
2377
|
-
expectOneOf(requiredString2(
|
|
2428
|
+
const object2 = expectJsonObject(input, path);
|
|
2429
|
+
assertExactKeys2(object2, ["denial"], path);
|
|
2430
|
+
expectOneOf(requiredString2(object2, "denial", path), ["none", "code", "debug"], `${path}.denial`);
|
|
2378
2431
|
}
|
|
2379
2432
|
function validateAudit(input, path) {
|
|
2380
|
-
const
|
|
2381
|
-
assertExactKeys2(
|
|
2382
|
-
expectOneOf(requiredString2(
|
|
2433
|
+
const object2 = expectJsonObject(input, path);
|
|
2434
|
+
assertExactKeys2(object2, ["issuance"], path);
|
|
2435
|
+
expectOneOf(requiredString2(object2, "issuance", path), ["off", "security", "full"], `${path}.issuance`);
|
|
2383
2436
|
}
|
|
2384
2437
|
function materialForUnsigned(unsigned, descriptor) {
|
|
2385
2438
|
const jcs = jcsCanonicalize(unsigned);
|
|
@@ -2528,9 +2581,9 @@ function encodeSignatureValue(value, suite) {
|
|
|
2528
2581
|
decodeSignatureValue(encoded, suite);
|
|
2529
2582
|
return encoded;
|
|
2530
2583
|
}
|
|
2531
|
-
function stripOwnIdAndSignature(
|
|
2584
|
+
function stripOwnIdAndSignature(object2, descriptor) {
|
|
2532
2585
|
const output = /* @__PURE__ */ Object.create(null);
|
|
2533
|
-
for (const [key, value] of Object.entries(
|
|
2586
|
+
for (const [key, value] of Object.entries(object2)) {
|
|
2534
2587
|
if (key === descriptor.idField || key === "signature") {
|
|
2535
2588
|
continue;
|
|
2536
2589
|
}
|
|
@@ -2539,8 +2592,8 @@ function stripOwnIdAndSignature(object, descriptor) {
|
|
|
2539
2592
|
return output;
|
|
2540
2593
|
}
|
|
2541
2594
|
function descriptorForUnsigned(input) {
|
|
2542
|
-
const
|
|
2543
|
-
return descriptorForSchema(requiredString2(
|
|
2595
|
+
const object2 = expectJsonObject(normalizeJson(input), "$");
|
|
2596
|
+
return descriptorForSchema(requiredString2(object2, "schema", "$"));
|
|
2544
2597
|
}
|
|
2545
2598
|
function descriptorForSchema(schema) {
|
|
2546
2599
|
if (schema === POLICY_SCHEMA) return DESCRIPTORS.Policy;
|
|
@@ -2554,28 +2607,28 @@ function expectJsonObject(input, path) {
|
|
|
2554
2607
|
}
|
|
2555
2608
|
return input;
|
|
2556
2609
|
}
|
|
2557
|
-
function assertExactKeys2(
|
|
2610
|
+
function assertExactKeys2(object2, allowed, path) {
|
|
2558
2611
|
const allowedSet = new Set(allowed);
|
|
2559
|
-
for (const key of Object.keys(
|
|
2612
|
+
for (const key of Object.keys(object2)) {
|
|
2560
2613
|
if (!allowedSet.has(key)) {
|
|
2561
2614
|
throw new SignedObjectSchemaError(`${path} has unknown field ${key}`);
|
|
2562
2615
|
}
|
|
2563
2616
|
}
|
|
2564
2617
|
}
|
|
2565
|
-
function requiredValue2(
|
|
2566
|
-
if (!hasOwn2(
|
|
2618
|
+
function requiredValue2(object2, key, path) {
|
|
2619
|
+
if (!hasOwn2(object2, key)) {
|
|
2567
2620
|
throw new SignedObjectSchemaError(`${path}.${key} is required`);
|
|
2568
2621
|
}
|
|
2569
|
-
return
|
|
2622
|
+
return object2[key];
|
|
2570
2623
|
}
|
|
2571
|
-
function requiredString2(
|
|
2572
|
-
return requireStringType(requiredValue2(
|
|
2624
|
+
function requiredString2(object2, key, path) {
|
|
2625
|
+
return requireStringType(requiredValue2(object2, key, path), `${path}.${key}`);
|
|
2573
2626
|
}
|
|
2574
|
-
function optionalString(
|
|
2575
|
-
if (!hasOwn2(
|
|
2627
|
+
function optionalString(object2, key, path) {
|
|
2628
|
+
if (!hasOwn2(object2, key)) {
|
|
2576
2629
|
return;
|
|
2577
2630
|
}
|
|
2578
|
-
requireStringType(requiredValue2(
|
|
2631
|
+
requireStringType(requiredValue2(object2, key, path), `${path}.${key}`);
|
|
2579
2632
|
}
|
|
2580
2633
|
function requireStringType(value, path) {
|
|
2581
2634
|
if (typeof value !== "string" || value.length === 0) {
|
|
@@ -2583,16 +2636,16 @@ function requireStringType(value, path) {
|
|
|
2583
2636
|
}
|
|
2584
2637
|
return value;
|
|
2585
2638
|
}
|
|
2586
|
-
function optionalBoolean(
|
|
2587
|
-
if (!hasOwn2(
|
|
2639
|
+
function optionalBoolean(object2, key, path) {
|
|
2640
|
+
if (!hasOwn2(object2, key)) {
|
|
2588
2641
|
return;
|
|
2589
2642
|
}
|
|
2590
|
-
if (typeof requiredValue2(
|
|
2643
|
+
if (typeof requiredValue2(object2, key, path) !== "boolean") {
|
|
2591
2644
|
throw new SignedObjectSchemaError(`${path}.${key} must be a boolean`);
|
|
2592
2645
|
}
|
|
2593
2646
|
}
|
|
2594
|
-
function requiredInteger(
|
|
2595
|
-
const value = requiredValue2(
|
|
2647
|
+
function requiredInteger(object2, key, path, minimum) {
|
|
2648
|
+
const value = requiredValue2(object2, key, path);
|
|
2596
2649
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum) {
|
|
2597
2650
|
throw new SignedObjectSchemaError(
|
|
2598
2651
|
`${path}.${key} must be an integer >= ${minimum}`
|
|
@@ -2600,8 +2653,8 @@ function requiredInteger(object, key, path, minimum) {
|
|
|
2600
2653
|
}
|
|
2601
2654
|
return value;
|
|
2602
2655
|
}
|
|
2603
|
-
function requiredArray2(
|
|
2604
|
-
const value = requiredValue2(
|
|
2656
|
+
function requiredArray2(object2, key, path, minimum) {
|
|
2657
|
+
const value = requiredValue2(object2, key, path);
|
|
2605
2658
|
if (!Array.isArray(value) || value.length < minimum) {
|
|
2606
2659
|
throw new SignedObjectSchemaError(
|
|
2607
2660
|
`${path}.${key} must be an array with at least ${minimum} item(s)`
|
|
@@ -2609,22 +2662,22 @@ function requiredArray2(object, key, path, minimum) {
|
|
|
2609
2662
|
}
|
|
2610
2663
|
return value;
|
|
2611
2664
|
}
|
|
2612
|
-
function requiredStringArray(
|
|
2613
|
-
const values = requiredArray2(
|
|
2665
|
+
function requiredStringArray(object2, key, path, check) {
|
|
2666
|
+
const values = requiredArray2(object2, key, path, 0);
|
|
2614
2667
|
for (let index = 0; index < values.length; index++) {
|
|
2615
2668
|
const itemPath = `${path}.${key}[${index}]`;
|
|
2616
2669
|
const value = requireStringType(values[index], itemPath);
|
|
2617
2670
|
check?.(value, itemPath);
|
|
2618
2671
|
}
|
|
2619
2672
|
}
|
|
2620
|
-
function requiredDateString(
|
|
2621
|
-
assertRfc3339(requiredString2(
|
|
2673
|
+
function requiredDateString(object2, key, path) {
|
|
2674
|
+
assertRfc3339(requiredString2(object2, key, path), `${path}.${key}`);
|
|
2622
2675
|
}
|
|
2623
|
-
function optionalDateString(
|
|
2624
|
-
if (!hasOwn2(
|
|
2676
|
+
function optionalDateString(object2, key, path) {
|
|
2677
|
+
if (!hasOwn2(object2, key)) {
|
|
2625
2678
|
return;
|
|
2626
2679
|
}
|
|
2627
|
-
assertRfc3339(requiredString2(
|
|
2680
|
+
assertRfc3339(requiredString2(object2, key, path), `${path}.${key}`);
|
|
2628
2681
|
}
|
|
2629
2682
|
function assertRfc3339(value, path) {
|
|
2630
2683
|
const match = value.match(
|
|
@@ -2656,8 +2709,8 @@ function expectOneOf(actual, allowed, path) {
|
|
|
2656
2709
|
}
|
|
2657
2710
|
throw new SignedObjectSchemaError(`${path} has unsupported value ${actual}`);
|
|
2658
2711
|
}
|
|
2659
|
-
function hasOwn2(
|
|
2660
|
-
return objectHasOwn3(
|
|
2712
|
+
function hasOwn2(object2, key) {
|
|
2713
|
+
return objectHasOwn3(object2, key);
|
|
2661
2714
|
}
|
|
2662
2715
|
function assertSupportedSignatureSuite(suite) {
|
|
2663
2716
|
if (suite !== ED25519_JCS_SIGNATURE_SUITE && suite !== EIP191_JCS_SIGNATURE_SUITE) {
|
|
@@ -2736,13 +2789,13 @@ function base64UrlDecode(value) {
|
|
|
2736
2789
|
return decoded;
|
|
2737
2790
|
}
|
|
2738
2791
|
function hexToBytes(value) {
|
|
2739
|
-
const
|
|
2740
|
-
if (
|
|
2792
|
+
const hex3 = value.slice(2);
|
|
2793
|
+
if (hex3.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(hex3)) {
|
|
2741
2794
|
throw new SignatureMaterialError("hex signature must have even length");
|
|
2742
2795
|
}
|
|
2743
|
-
const output = new Uint8Array(
|
|
2796
|
+
const output = new Uint8Array(hex3.length / 2);
|
|
2744
2797
|
for (let index = 0; index < output.length; index++) {
|
|
2745
|
-
output[index] = Number.parseInt(
|
|
2798
|
+
output[index] = Number.parseInt(hex3.slice(index * 2, index * 2 + 2), 16);
|
|
2746
2799
|
}
|
|
2747
2800
|
return output;
|
|
2748
2801
|
}
|
|
@@ -3093,9 +3146,9 @@ function expectPolicyEngineRecord(input) {
|
|
|
3093
3146
|
);
|
|
3094
3147
|
}
|
|
3095
3148
|
}
|
|
3096
|
-
function assertExactKeys3(
|
|
3149
|
+
function assertExactKeys3(object2, allowed, path) {
|
|
3097
3150
|
const allowedSet = new Set(allowed);
|
|
3098
|
-
for (const key of Object.keys(
|
|
3151
|
+
for (const key of Object.keys(object2)) {
|
|
3099
3152
|
if (!allowedSet.has(key)) {
|
|
3100
3153
|
throw new PolicyAuthoringError(
|
|
3101
3154
|
"policy-authoring-unknown-key",
|
|
@@ -3104,17 +3157,17 @@ function assertExactKeys3(object, allowed, path) {
|
|
|
3104
3157
|
}
|
|
3105
3158
|
}
|
|
3106
3159
|
}
|
|
3107
|
-
function requiredValue3(
|
|
3108
|
-
if (!hasOwn3(
|
|
3160
|
+
function requiredValue3(object2, key, path) {
|
|
3161
|
+
if (!hasOwn3(object2, key)) {
|
|
3109
3162
|
throw new PolicyAuthoringError(
|
|
3110
3163
|
"policy-authoring-malformed",
|
|
3111
3164
|
`${path}.${key} is required`
|
|
3112
3165
|
);
|
|
3113
3166
|
}
|
|
3114
|
-
return
|
|
3167
|
+
return object2[key];
|
|
3115
3168
|
}
|
|
3116
|
-
function requiredString3(
|
|
3117
|
-
const value = requiredValue3(
|
|
3169
|
+
function requiredString3(object2, key, path) {
|
|
3170
|
+
const value = requiredValue3(object2, key, path);
|
|
3118
3171
|
if (typeof value !== "string" || value.length === 0) {
|
|
3119
3172
|
throw new PolicyAuthoringError(
|
|
3120
3173
|
"policy-authoring-malformed",
|
|
@@ -3123,11 +3176,11 @@ function requiredString3(object, key, path) {
|
|
|
3123
3176
|
}
|
|
3124
3177
|
return value;
|
|
3125
3178
|
}
|
|
3126
|
-
function fieldString(
|
|
3127
|
-
if (!hasOwn3(
|
|
3179
|
+
function fieldString(object2, key, path, code2) {
|
|
3180
|
+
if (!hasOwn3(object2, key)) {
|
|
3128
3181
|
throw new PolicyAuthoringError(code2, `${path}.${key} is required`);
|
|
3129
3182
|
}
|
|
3130
|
-
const value =
|
|
3183
|
+
const value = object2[key];
|
|
3131
3184
|
if (typeof value !== "string" || value.length === 0) {
|
|
3132
3185
|
throw new PolicyAuthoringError(
|
|
3133
3186
|
code2,
|
|
@@ -3136,8 +3189,8 @@ function fieldString(object, key, path, code2) {
|
|
|
3136
3189
|
}
|
|
3137
3190
|
return value;
|
|
3138
3191
|
}
|
|
3139
|
-
function requiredObject(
|
|
3140
|
-
const value = requiredValue3(
|
|
3192
|
+
function requiredObject(object2, key, path) {
|
|
3193
|
+
const value = requiredValue3(object2, key, path);
|
|
3141
3194
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
3142
3195
|
throw new PolicyAuthoringError(
|
|
3143
3196
|
"policy-authoring-malformed",
|
|
@@ -3146,8 +3199,8 @@ function requiredObject(object, key, path) {
|
|
|
3146
3199
|
}
|
|
3147
3200
|
return value;
|
|
3148
3201
|
}
|
|
3149
|
-
function requiredArray3(
|
|
3150
|
-
const value = requiredValue3(
|
|
3202
|
+
function requiredArray3(object2, key, path) {
|
|
3203
|
+
const value = requiredValue3(object2, key, path);
|
|
3151
3204
|
if (!Array.isArray(value)) {
|
|
3152
3205
|
throw new PolicyAuthoringError(
|
|
3153
3206
|
"policy-authoring-malformed",
|
|
@@ -3156,8 +3209,8 @@ function requiredArray3(object, key, path) {
|
|
|
3156
3209
|
}
|
|
3157
3210
|
return value;
|
|
3158
3211
|
}
|
|
3159
|
-
function requiredStringArray2(
|
|
3160
|
-
const values = requiredArray3(
|
|
3212
|
+
function requiredStringArray2(object2, key, path) {
|
|
3213
|
+
const values = requiredArray3(object2, key, path);
|
|
3161
3214
|
return values.map((value, index) => {
|
|
3162
3215
|
if (typeof value !== "string" || value.length === 0) {
|
|
3163
3216
|
throw new PolicyAuthoringError(
|
|
@@ -3254,7 +3307,1052 @@ function parseStrictRfc3339(value, path) {
|
|
|
3254
3307
|
var objectHasOwn4 = Object.hasOwn ?? Object.prototype.hasOwnProperty.call.bind(
|
|
3255
3308
|
Object.prototype.hasOwnProperty
|
|
3256
3309
|
);
|
|
3257
|
-
function hasOwn3(
|
|
3258
|
-
return objectHasOwn4(
|
|
3310
|
+
function hasOwn3(object2, key) {
|
|
3311
|
+
return objectHasOwn4(object2, key);
|
|
3312
|
+
}
|
|
3313
|
+
|
|
3314
|
+
// src/policy/unified.ts
|
|
3315
|
+
var import_sha256 = require("@noble/hashes/sha256");
|
|
3316
|
+
var import_blake3 = require("@noble/hashes/blake3");
|
|
3317
|
+
var import_ed255192 = require("@noble/curves/ed25519");
|
|
3318
|
+
var import_zod = require("zod");
|
|
3319
|
+
var POLICY_V1_SCHEMA = "xyz.tinycloud.policy/policy/v1";
|
|
3320
|
+
var POLICY_CAPABILITY_V1_SCHEMA = "xyz.tinycloud.policy/capability/v1";
|
|
3321
|
+
var POLICY_ENFORCEMENT_DELEGATION_V2_SCHEMA = "xyz.tinycloud.policy/enforcement-delegation/v2";
|
|
3322
|
+
var ATTESTED_ENFORCER_BINDING_V2_SCHEMA = "xyz.tinycloud.policy/attested-enforcer/v2";
|
|
3323
|
+
var ROOT_STATUS_CHECKPOINT_V1_SCHEMA = "xyz.tinycloud.policy/root-status/v1";
|
|
3324
|
+
var ROOT_REVOCATION_V1_SCHEMA = "xyz.tinycloud.policy/root-revocation/v1";
|
|
3325
|
+
var POLICY_SESSION_UCAN_V1_PROFILE = "policy-session-ucan/v1";
|
|
3326
|
+
var POLICY_V1_DOMAIN = "xyz.tinycloud.policy/policy/v1\0";
|
|
3327
|
+
var POLICY_CAPABILITY_V1_DOMAIN = "xyz.tinycloud.policy/PolicyCapability/v1\0";
|
|
3328
|
+
var CONTENT_SOURCE_V1_DOMAIN = "xyz.tinycloud.policy/ContentSource/v1\0";
|
|
3329
|
+
var NATIVE_PROJECTION_V1_DOMAIN = "xyz.tinycloud.policy/NativeProjection/v1\0";
|
|
3330
|
+
var ROOT_STATUS_V1_DOMAIN = "xyz.tinycloud.policy/RootStatusCheckpoint/v1\0";
|
|
3331
|
+
var ROOT_STATUS_RENEWAL_V1_DOMAIN = "xyz.tinycloud.policy/RootStatusRenewal/v1\0";
|
|
3332
|
+
var ROOT_REVOCATION_V1_DOMAIN = "xyz.tinycloud.policy/RootRevocation/v1\0";
|
|
3333
|
+
var LAST_V2_CREATE_AT = "2026-09-30T00:00:00Z";
|
|
3334
|
+
var MAX_LEGACY_ENVELOPE_EXPIRES_AT = "2026-12-29T00:00:00Z";
|
|
3335
|
+
var LAST_V2_READ_AT = "2027-01-05T00:00:00Z";
|
|
3336
|
+
async function requestPolicyChallengeV3(input) {
|
|
3337
|
+
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3338
|
+
const response = await fetchFn(new URL("/share/v3/policy/challenges", input.nodeOrigin), {
|
|
3339
|
+
method: "POST",
|
|
3340
|
+
redirect: "error",
|
|
3341
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
3342
|
+
body: JSON.stringify({ policyCid: input.policyCid, recipientDid: input.recipientDid, requestedCapabilities: input.requestedCapabilities.map(normalizeUnifiedPolicyCapability) })
|
|
3343
|
+
});
|
|
3344
|
+
if (!response.ok) throw new Error(`policy challenge rejected (${response.status})`);
|
|
3345
|
+
const value = object(await response.json(), "policy challenge");
|
|
3346
|
+
if (typeof value.challengeId !== "string" || typeof value.nonce !== "string" || value.policyCid !== input.policyCid || value.recipientDid !== input.recipientDid)
|
|
3347
|
+
throw new Error("policy challenge binding is invalid");
|
|
3348
|
+
return value;
|
|
3349
|
+
}
|
|
3350
|
+
async function mintPolicySessionV3(input) {
|
|
3351
|
+
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3352
|
+
const challenge = input.challenge ?? await requestPolicyChallengeV3(input);
|
|
3353
|
+
if (challenge.policyCid !== input.policyCid || challenge.recipientDid !== input.recipientDid)
|
|
3354
|
+
throw new Error("policy challenge binding is invalid");
|
|
3355
|
+
const response = await fetchFn(new URL("/share/v3/policy/delegations", input.nodeOrigin), {
|
|
3356
|
+
method: "POST",
|
|
3357
|
+
redirect: "error",
|
|
3358
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
3359
|
+
body: JSON.stringify({ policyCid: input.policyCid, challengeId: challenge.challengeId, nonce: challenge.nonce, claim: input.claim, presentation: input.presentation })
|
|
3360
|
+
});
|
|
3361
|
+
if (!response.ok) throw new Error(`policy delegation rejected (${response.status})`);
|
|
3362
|
+
const value = object(await response.json(), "policy delegation");
|
|
3363
|
+
if (value.admitted !== true || typeof value.sessionCid !== "string" || typeof value.authorization !== "string")
|
|
3364
|
+
throw new Error("policy delegation response is invalid");
|
|
3365
|
+
const session = parsePolicySessionUcan(value.authorization, [input.policyRootCid, input.enforcementRootCid]);
|
|
3366
|
+
const expectedAttenuation = compactAttenuationForPolicyCapabilities(input.requestedCapabilities);
|
|
3367
|
+
if (session.cid !== value.sessionCid || session.aud !== input.recipientDid || session.fact.policyCid !== input.policyCid || session.fact.recipientDid !== input.recipientDid || session.fact.policyDelegationCid !== input.policyRootCid || session.fact.enforcementDelegationCid !== input.enforcementRootCid || jcsCanonicalize(session.att) !== jcsCanonicalize(expectedAttenuation) || Object.entries(input.expectedFacts ?? {}).some(([key, expected]) => session.fact[key] !== expected))
|
|
3368
|
+
throw new Error("policy delegation signed binding is invalid");
|
|
3369
|
+
return session;
|
|
3370
|
+
}
|
|
3371
|
+
function compactAttenuationForPolicyCapabilities(input) {
|
|
3372
|
+
const attenuation = {};
|
|
3373
|
+
for (const raw of input) {
|
|
3374
|
+
const capability = normalizeUnifiedPolicyCapability(raw);
|
|
3375
|
+
if (attenuation[capability.resource] !== void 0)
|
|
3376
|
+
throw new Error("policy capabilities contain a duplicate resource");
|
|
3377
|
+
attenuation[capability.resource] = capability.kind === "encryption" ? { [capability.action]: [{}] } : Object.fromEntries(capability.actions.map((action) => [action, [{
|
|
3378
|
+
type: "xyz.tinycloud.resource/selector",
|
|
3379
|
+
kind: capability.selector,
|
|
3380
|
+
value: capability.resource
|
|
3381
|
+
}]]));
|
|
3382
|
+
}
|
|
3383
|
+
return attenuation;
|
|
3384
|
+
}
|
|
3385
|
+
function verifyPolicyRootStatusCheckpointV3(input) {
|
|
3386
|
+
const checkpoint = input.checkpoint;
|
|
3387
|
+
const allowed = /* @__PURE__ */ new Set(["schema", "targetCid", "targetRole", "ownerDid", "nodeAudience", "state", "sequence", "checkedAt", "freshUntil", "revokedAt", "revocationCid", "previousCheckpointDigestHex", "issuerDid", "signature"]);
|
|
3388
|
+
if (Object.keys(checkpoint).some((key) => !allowed.has(key)) || checkpoint.schema !== ROOT_STATUS_CHECKPOINT_V1_SCHEMA || checkpoint.targetCid !== input.rootCid || checkpoint.targetRole !== "policy-authority" && checkpoint.targetRole !== "policy-enforcement" || checkpoint.state !== "active" && checkpoint.state !== "revoked" || typeof checkpoint.ownerDid !== "string" || typeof checkpoint.nodeAudience !== "string" || checkpoint.issuerDid !== checkpoint.nodeAudience || input.expectedNodeAudience !== void 0 && checkpoint.nodeAudience !== input.expectedNodeAudience || !Number.isInteger(checkpoint.sequence) || checkpoint.sequence < 1 || typeof checkpoint.checkedAt !== "string" || typeof checkpoint.freshUntil !== "string") return false;
|
|
3389
|
+
const checkedAt = Date.parse(checkpoint.checkedAt);
|
|
3390
|
+
const freshUntil = Date.parse(checkpoint.freshUntil);
|
|
3391
|
+
const now = (input.now ?? /* @__PURE__ */ new Date()).getTime();
|
|
3392
|
+
if (!Number.isFinite(checkedAt) || !Number.isFinite(freshUntil) || freshUntil < now || freshUntil <= checkedAt || freshUntil - checkedAt > 3e5) return false;
|
|
3393
|
+
if (checkpoint.sequence === 1 !== (checkpoint.previousCheckpointDigestHex === void 0)) return false;
|
|
3394
|
+
if (checkpoint.sequence !== 1 && (typeof checkpoint.previousCheckpointDigestHex !== "string" || !/^[0-9a-f]{64}$/.test(checkpoint.previousCheckpointDigestHex))) return false;
|
|
3395
|
+
if (checkpoint.state === "active" && (checkpoint.revokedAt !== void 0 || checkpoint.revocationCid !== void 0)) return false;
|
|
3396
|
+
if (checkpoint.state === "revoked" && (typeof checkpoint.revokedAt !== "string" || typeof checkpoint.revocationCid !== "string")) return false;
|
|
3397
|
+
const signature = checkpoint.signature;
|
|
3398
|
+
if (!isRecordValue(signature) || Object.keys(signature).length !== 3 || signature.suite !== "Ed25519" || signature.signerDid !== checkpoint.issuerDid || typeof signature.value !== "string") return false;
|
|
3399
|
+
try {
|
|
3400
|
+
const principal = checkpoint.issuerDid.split("#", 1)[0];
|
|
3401
|
+
const didMaterial = principal.startsWith("did:key:") ? base58btc.decode(principal.slice("did:key:".length)) : new Uint8Array();
|
|
3402
|
+
if (didMaterial.length !== 34 || didMaterial[0] !== 237 || didMaterial[1] !== 1) return false;
|
|
3403
|
+
const unsigned = { ...checkpoint };
|
|
3404
|
+
delete unsigned.signature;
|
|
3405
|
+
return import_ed255192.ed25519.verify(
|
|
3406
|
+
decodeBase64Url(signature.value),
|
|
3407
|
+
(0, import_sha256.sha256)(new TextEncoder().encode(ROOT_STATUS_V1_DOMAIN + jcsCanonicalize(unsigned))),
|
|
3408
|
+
didMaterial.slice(2)
|
|
3409
|
+
);
|
|
3410
|
+
} catch {
|
|
3411
|
+
return false;
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3414
|
+
async function getPolicyRootStatusV3(input) {
|
|
3415
|
+
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3416
|
+
const response = await fetchFn(new URL(`/share/v3/policy/status/${encodeURIComponent(input.rootCid)}`, input.nodeOrigin), {
|
|
3417
|
+
method: "GET",
|
|
3418
|
+
redirect: "error",
|
|
3419
|
+
headers: { accept: "application/json" }
|
|
3420
|
+
});
|
|
3421
|
+
if (!response.ok) throw new Error(`policy root status rejected (${response.status})`);
|
|
3422
|
+
const value = object(await response.json(), "policy root status");
|
|
3423
|
+
const checkpoint = object(value.checkpoint, "policy root checkpoint");
|
|
3424
|
+
if (value.rootCid !== input.rootCid || value.state !== "active" && value.state !== "revoked" || value.state !== checkpoint.state || !verifyPolicyRootStatusCheckpointV3({ rootCid: input.rootCid, checkpoint, expectedNodeAudience: input.expectedNodeAudience, now: input.now }))
|
|
3425
|
+
throw new Error("policy root status binding is invalid");
|
|
3426
|
+
const revocation = value.revocation === void 0 ? void 0 : object(value.revocation, "policy root revocation");
|
|
3427
|
+
if (value.state === "revoked" !== (revocation !== void 0) || revocation !== void 0 && !verifyPolicyRootRevocationV3({
|
|
3428
|
+
rootCid: input.rootCid,
|
|
3429
|
+
checkpoint,
|
|
3430
|
+
revocation,
|
|
3431
|
+
expectedEnforcerDid: input.expectedEnforcerDid
|
|
3432
|
+
})) throw new Error("policy root status revocation evidence is invalid");
|
|
3433
|
+
return { rootCid: input.rootCid, state: value.state, checkpoint, ...revocation === void 0 ? {} : { revocation } };
|
|
3434
|
+
}
|
|
3435
|
+
function verifyPolicyRootRevocationV3(input) {
|
|
3436
|
+
const value = input.revocation;
|
|
3437
|
+
const allowed = ["schema", "targetCid", "targetRole", "ownerDid", "nodeAudience", "revokedAt", "reason", "issuerDid", "signature"];
|
|
3438
|
+
if (Object.keys(value).length !== allowed.length || Object.keys(value).some((key) => !allowed.includes(key)) || value.schema !== ROOT_REVOCATION_V1_SCHEMA || value.targetCid !== input.rootCid || value.targetRole !== input.checkpoint.targetRole || value.ownerDid !== input.checkpoint.ownerDid || value.nodeAudience !== input.checkpoint.nodeAudience || value.revokedAt !== input.checkpoint.revokedAt || typeof value.reason !== "string" || value.reason.length === 0 || typeof value.issuerDid !== "string") return false;
|
|
3439
|
+
const authorized = value.issuerDid === value.ownerDid || value.targetRole === "policy-enforcement" && value.issuerDid === input.expectedEnforcerDid;
|
|
3440
|
+
if (!authorized) return false;
|
|
3441
|
+
const signature = value.signature;
|
|
3442
|
+
if (!isRecordValue(signature) || Object.keys(signature).length !== 3 || signature.suite !== "Ed25519" || signature.signerDid !== value.issuerDid || typeof signature.value !== "string") return false;
|
|
3443
|
+
try {
|
|
3444
|
+
const principal = value.issuerDid.split("#", 1)[0];
|
|
3445
|
+
const didMaterial = principal.startsWith("did:key:") ? base58btc.decode(principal.slice("did:key:".length)) : new Uint8Array();
|
|
3446
|
+
if (didMaterial.length !== 34 || didMaterial[0] !== 237 || didMaterial[1] !== 1) return false;
|
|
3447
|
+
const unsigned = { ...value };
|
|
3448
|
+
delete unsigned.signature;
|
|
3449
|
+
const digest2 = (0, import_sha256.sha256)(new TextEncoder().encode(ROOT_REVOCATION_V1_DOMAIN + jcsCanonicalize(unsigned)));
|
|
3450
|
+
return input.checkpoint.revocationCid === hex(digest2) && import_ed255192.ed25519.verify(decodeBase64Url(signature.value), digest2, didMaterial.slice(2));
|
|
3451
|
+
} catch {
|
|
3452
|
+
return false;
|
|
3453
|
+
}
|
|
3454
|
+
}
|
|
3455
|
+
async function renewPolicyRootStatusV3(input) {
|
|
3456
|
+
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3457
|
+
const current = await getPolicyRootStatusV3({ nodeOrigin: input.nodeOrigin, rootCid: input.rootCid, expectedNodeAudience: input.nodeAudience, now: input.now, fetch: fetchFn });
|
|
3458
|
+
if (current.state !== "active" || current.checkpoint.ownerDid !== input.ownerDid || current.checkpoint.nodeAudience !== input.nodeAudience || current.checkpoint.state !== "active" || !Number.isInteger(current.checkpoint.sequence))
|
|
3459
|
+
throw new Error("policy root status cannot be renewed");
|
|
3460
|
+
const currentUnsigned = { ...current.checkpoint };
|
|
3461
|
+
delete currentUnsigned.signature;
|
|
3462
|
+
const previousCheckpointDigestHex = hex((0, import_sha256.sha256)(new TextEncoder().encode(ROOT_STATUS_V1_DOMAIN + jcsCanonicalize(currentUnsigned))));
|
|
3463
|
+
const unsigned = {
|
|
3464
|
+
schema: "xyz.tinycloud.policy/root-status-renewal/v1",
|
|
3465
|
+
targetCid: input.rootCid,
|
|
3466
|
+
ownerDid: input.ownerDid,
|
|
3467
|
+
nodeAudience: input.nodeAudience,
|
|
3468
|
+
issuedAt: (input.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
3469
|
+
nonce: input.nonce ?? encodeBase64Url(crypto.getRandomValues(new Uint8Array(16))),
|
|
3470
|
+
sequence: current.checkpoint.sequence + 1,
|
|
3471
|
+
previousCheckpointDigestHex
|
|
3472
|
+
};
|
|
3473
|
+
const signature = await input.sign((0, import_sha256.sha256)(new TextEncoder().encode(ROOT_STATUS_RENEWAL_V1_DOMAIN + jcsCanonicalize(unsigned))));
|
|
3474
|
+
if (signature.length !== 64) throw new Error("policy root renewal signature must be Ed25519");
|
|
3475
|
+
const response = await fetchFn(new URL("/share/v3/policy/status", input.nodeOrigin), {
|
|
3476
|
+
method: "POST",
|
|
3477
|
+
redirect: "error",
|
|
3478
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
3479
|
+
body: JSON.stringify({ rootCid: input.rootCid, renewal: { ...unsigned, signature: { suite: "Ed25519", signerDid: input.ownerDid, value: encodeBase64Url(signature) } } })
|
|
3480
|
+
});
|
|
3481
|
+
if (!response.ok) throw new Error(`policy root renewal rejected (${response.status})`);
|
|
3482
|
+
return object(await response.json(), "policy root renewal");
|
|
3483
|
+
}
|
|
3484
|
+
async function revokePolicyRootV3(input) {
|
|
3485
|
+
if (input.reason.length === 0) throw new Error("policy root revocation reason is required");
|
|
3486
|
+
const unsigned = {
|
|
3487
|
+
schema: ROOT_REVOCATION_V1_SCHEMA,
|
|
3488
|
+
targetCid: input.rootCid,
|
|
3489
|
+
targetRole: input.targetRole,
|
|
3490
|
+
ownerDid: input.ownerDid,
|
|
3491
|
+
nodeAudience: input.nodeAudience,
|
|
3492
|
+
revokedAt: (input.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
3493
|
+
reason: input.reason,
|
|
3494
|
+
issuerDid: input.issuerDid
|
|
3495
|
+
};
|
|
3496
|
+
const signature = await input.sign((0, import_sha256.sha256)(new TextEncoder().encode(ROOT_REVOCATION_V1_DOMAIN + jcsCanonicalize(unsigned))));
|
|
3497
|
+
if (signature.length !== 64) throw new Error("policy root revocation signature must be Ed25519");
|
|
3498
|
+
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3499
|
+
const response = await fetchFn(new URL("/revoke", input.nodeOrigin), {
|
|
3500
|
+
method: "POST",
|
|
3501
|
+
redirect: "error",
|
|
3502
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
3503
|
+
body: JSON.stringify({ revocation: { ...unsigned, signature: { suite: "Ed25519", signerDid: input.issuerDid, value: encodeBase64Url(signature) } } })
|
|
3504
|
+
});
|
|
3505
|
+
if (!response.ok) throw new Error(`policy root revocation rejected (${response.status})`);
|
|
3506
|
+
return object(await response.json(), "policy root revocation");
|
|
3507
|
+
}
|
|
3508
|
+
var POLICY_SESSION_FACT_KEYS = [
|
|
3509
|
+
"profile",
|
|
3510
|
+
"ownerDid",
|
|
3511
|
+
"policyId",
|
|
3512
|
+
"policyDigestHex",
|
|
3513
|
+
"policyCid",
|
|
3514
|
+
"policyDelegationCid",
|
|
3515
|
+
"enforcementDelegationCid",
|
|
3516
|
+
"contentSourceDigestHex",
|
|
3517
|
+
"capabilityCeilingHashHex",
|
|
3518
|
+
"nativeProjectionHashHex",
|
|
3519
|
+
"enforcerDid",
|
|
3520
|
+
"nodeAudience",
|
|
3521
|
+
"recipientDid",
|
|
3522
|
+
"challengeId",
|
|
3523
|
+
"claimDigestHex",
|
|
3524
|
+
"claimJti",
|
|
3525
|
+
"vpDigestHex",
|
|
3526
|
+
"credentialEvidenceDigestHex",
|
|
3527
|
+
"decisionContextDigestHex",
|
|
3528
|
+
"issuanceAuditDigestHex",
|
|
3529
|
+
"remainingRedelegationDepth"
|
|
3530
|
+
];
|
|
3531
|
+
var KV_ACTIONS = /* @__PURE__ */ new Set([
|
|
3532
|
+
"tinycloud.kv/get",
|
|
3533
|
+
"tinycloud.kv/list",
|
|
3534
|
+
"tinycloud.kv/metadata",
|
|
3535
|
+
"tinycloud.kv/put"
|
|
3536
|
+
]);
|
|
3537
|
+
function object(value, label) {
|
|
3538
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
3539
|
+
throw new Error(`${label} must be an object`);
|
|
3540
|
+
}
|
|
3541
|
+
return value;
|
|
3542
|
+
}
|
|
3543
|
+
function exactKeys(value, allowed, label) {
|
|
3544
|
+
const keys = new Set(allowed);
|
|
3545
|
+
if (Object.keys(value).some((key) => !keys.has(key)))
|
|
3546
|
+
throw new Error(`${label} contains an unknown field`);
|
|
3547
|
+
}
|
|
3548
|
+
function nonEmptyString(value, label) {
|
|
3549
|
+
if (typeof value !== "string" || value.length === 0)
|
|
3550
|
+
throw new Error(`${label} must be a non-empty string`);
|
|
3551
|
+
return value;
|
|
3552
|
+
}
|
|
3553
|
+
function parseKvResource(resource) {
|
|
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)) {
|
|
3560
|
+
throw new Error("KV resource is not canonical");
|
|
3561
|
+
}
|
|
3562
|
+
return { space, path };
|
|
3563
|
+
}
|
|
3564
|
+
function parseEncryptionResource(resource) {
|
|
3565
|
+
if (!resource.startsWith("urn:tinycloud:encryption:")) throw new Error("encryption capability is invalid");
|
|
3566
|
+
const rest = resource.slice("urn:tinycloud:encryption:".length);
|
|
3567
|
+
const separator = rest.lastIndexOf(":");
|
|
3568
|
+
const ownerDid = separator < 0 ? "" : rest.slice(0, separator);
|
|
3569
|
+
const network = separator < 0 ? "" : rest.slice(separator + 1);
|
|
3570
|
+
if (!ownerDid.startsWith("did:") || network.length === 0 || network.includes(":") || network.includes("/") || network.includes("%") || network.includes("?") || network.includes("#")) {
|
|
3571
|
+
throw new Error("encryption capability is invalid");
|
|
3572
|
+
}
|
|
3573
|
+
}
|
|
3574
|
+
function normalizeUnifiedPolicyCapability(input) {
|
|
3575
|
+
const value = object(input, "capability");
|
|
3576
|
+
const kind = nonEmptyString(value.kind, "capability.kind");
|
|
3577
|
+
if (kind === "kv") {
|
|
3578
|
+
exactKeys(
|
|
3579
|
+
value,
|
|
3580
|
+
["kind", "resource", "selector", "actions"],
|
|
3581
|
+
"KV capability"
|
|
3582
|
+
);
|
|
3583
|
+
const resource = nonEmptyString(value.resource, "capability.resource");
|
|
3584
|
+
parseKvResource(resource);
|
|
3585
|
+
if (value.selector !== "exact" && value.selector !== "prefix")
|
|
3586
|
+
throw new Error("capability.selector is invalid");
|
|
3587
|
+
if (!Array.isArray(value.actions) || value.actions.length === 0)
|
|
3588
|
+
throw new Error("capability.actions is invalid");
|
|
3589
|
+
const actions = value.actions.map(
|
|
3590
|
+
(action) => nonEmptyString(action, "capability.actions[]")
|
|
3591
|
+
);
|
|
3592
|
+
if (new Set(actions).size !== actions.length || actions.some((action) => !KV_ACTIONS.has(action)))
|
|
3593
|
+
throw new Error("capability.actions is invalid");
|
|
3594
|
+
return {
|
|
3595
|
+
kind: "kv",
|
|
3596
|
+
resource,
|
|
3597
|
+
selector: value.selector,
|
|
3598
|
+
actions: [...actions].sort()
|
|
3599
|
+
};
|
|
3600
|
+
}
|
|
3601
|
+
if (kind === "encryption") {
|
|
3602
|
+
exactKeys(value, ["kind", "resource", "action"], "encryption capability");
|
|
3603
|
+
const resource = nonEmptyString(value.resource, "capability.resource");
|
|
3604
|
+
if (value.action !== "tinycloud.encryption/decrypt")
|
|
3605
|
+
throw new Error("encryption capability is invalid");
|
|
3606
|
+
parseEncryptionResource(resource);
|
|
3607
|
+
return {
|
|
3608
|
+
kind: "encryption",
|
|
3609
|
+
resource,
|
|
3610
|
+
action: "tinycloud.encryption/decrypt"
|
|
3611
|
+
};
|
|
3612
|
+
}
|
|
3613
|
+
throw new Error("capability.kind is invalid");
|
|
3614
|
+
}
|
|
3615
|
+
function canonicalizeUnifiedPolicyCapability(input) {
|
|
3616
|
+
return normalizeUnifiedPolicyCapability(input);
|
|
3617
|
+
}
|
|
3618
|
+
function unifiedPolicyCapabilityDigestHex(input) {
|
|
3619
|
+
return hex(
|
|
3620
|
+
(0, import_sha256.sha256)(
|
|
3621
|
+
new TextEncoder().encode(
|
|
3622
|
+
POLICY_CAPABILITY_V1_DOMAIN + jcsCanonicalize(normalizeUnifiedPolicyCapability(input))
|
|
3623
|
+
)
|
|
3624
|
+
)
|
|
3625
|
+
);
|
|
3626
|
+
}
|
|
3627
|
+
function unifiedPolicyCapabilityContains(authorityInput, requestedInput) {
|
|
3628
|
+
try {
|
|
3629
|
+
const authority = normalizeUnifiedPolicyCapability(authorityInput);
|
|
3630
|
+
const requested = normalizeUnifiedPolicyCapability(requestedInput);
|
|
3631
|
+
if (authority.kind !== requested.kind) return false;
|
|
3632
|
+
if (authority.kind === "encryption" && requested.kind === "encryption")
|
|
3633
|
+
return authority.resource === requested.resource && authority.action === requested.action;
|
|
3634
|
+
if (authority.kind !== "kv" || requested.kind !== "kv") return false;
|
|
3635
|
+
const { resource: authorized } = authority;
|
|
3636
|
+
const { resource: requestedResource } = requested;
|
|
3637
|
+
const pathContained = authority.selector === "exact" ? authorized === requestedResource : authorized === requestedResource || requestedResource.startsWith(`${authorized}/`);
|
|
3638
|
+
if (!pathContained || authority.selector === "exact" && requested.selector === "prefix")
|
|
3639
|
+
return false;
|
|
3640
|
+
return requested.actions.every(
|
|
3641
|
+
(action) => authority.actions.includes(action)
|
|
3642
|
+
);
|
|
3643
|
+
} catch {
|
|
3644
|
+
return false;
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
function projectUnifiedPolicyCapability(input) {
|
|
3648
|
+
const capability = normalizeUnifiedPolicyCapability(input);
|
|
3649
|
+
if (capability.kind === "encryption") {
|
|
3650
|
+
return {
|
|
3651
|
+
service: "tinycloud.encryption",
|
|
3652
|
+
space: capability.resource,
|
|
3653
|
+
path: capability.resource,
|
|
3654
|
+
actions: [capability.action]
|
|
3655
|
+
};
|
|
3656
|
+
}
|
|
3657
|
+
const { space, path } = parseKvResource(capability.resource);
|
|
3658
|
+
return {
|
|
3659
|
+
service: "tinycloud.kv",
|
|
3660
|
+
space,
|
|
3661
|
+
path,
|
|
3662
|
+
actions: [...capability.actions],
|
|
3663
|
+
caveat: {
|
|
3664
|
+
type: "xyz.tinycloud.resource/selector",
|
|
3665
|
+
kind: capability.selector,
|
|
3666
|
+
value: capability.resource
|
|
3667
|
+
}
|
|
3668
|
+
};
|
|
3669
|
+
}
|
|
3670
|
+
function projectUnifiedPolicyCapabilities(input) {
|
|
3671
|
+
return input.map(projectUnifiedPolicyCapability).sort((left, right) => {
|
|
3672
|
+
const a = jcsCanonicalize(left);
|
|
3673
|
+
const b = jcsCanonicalize(right);
|
|
3674
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
3675
|
+
});
|
|
3676
|
+
}
|
|
3677
|
+
function unifiedNativeProjectionHashHex(input) {
|
|
3678
|
+
return hex(
|
|
3679
|
+
(0, import_sha256.sha256)(
|
|
3680
|
+
new TextEncoder().encode(
|
|
3681
|
+
NATIVE_PROJECTION_V1_DOMAIN + jcsCanonicalize(projectUnifiedPolicyCapabilities(input))
|
|
3682
|
+
)
|
|
3683
|
+
)
|
|
3684
|
+
);
|
|
3685
|
+
}
|
|
3686
|
+
function unifiedPolicyCapabilityFromNative(input) {
|
|
3687
|
+
const value = object(input, "native capability");
|
|
3688
|
+
exactKeys(
|
|
3689
|
+
value,
|
|
3690
|
+
["service", "space", "path", "actions", "caveat"],
|
|
3691
|
+
"native capability"
|
|
3692
|
+
);
|
|
3693
|
+
const service = nonEmptyString(value.service, "native.service");
|
|
3694
|
+
const actions = value.actions;
|
|
3695
|
+
if (!Array.isArray(actions)) throw new Error("native.actions is invalid");
|
|
3696
|
+
if (service === "tinycloud.encryption" && value.caveat === void 0 && value.space === value.path && actions.length === 1 && actions[0] === "tinycloud.encryption/decrypt") {
|
|
3697
|
+
return normalizeUnifiedPolicyCapability({
|
|
3698
|
+
kind: "encryption",
|
|
3699
|
+
resource: value.space,
|
|
3700
|
+
action: actions[0]
|
|
3701
|
+
});
|
|
3702
|
+
}
|
|
3703
|
+
if (service !== "tinycloud.kv" || typeof value.space !== "string" || typeof value.path !== "string")
|
|
3704
|
+
throw new Error("native capability is not supported");
|
|
3705
|
+
const caveat = object(value.caveat, "native.caveat");
|
|
3706
|
+
exactKeys(caveat, ["type", "kind", "value"], "native selector caveat");
|
|
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}`)
|
|
3708
|
+
throw new Error("native selector caveat does not match capability");
|
|
3709
|
+
return normalizeUnifiedPolicyCapability({
|
|
3710
|
+
kind: "kv",
|
|
3711
|
+
resource: caveat.value,
|
|
3712
|
+
selector: caveat.kind,
|
|
3713
|
+
actions
|
|
3714
|
+
});
|
|
3715
|
+
}
|
|
3716
|
+
function contentSourceDigestHex(input) {
|
|
3717
|
+
return hex(
|
|
3718
|
+
(0, import_sha256.sha256)(
|
|
3719
|
+
new TextEncoder().encode(
|
|
3720
|
+
CONTENT_SOURCE_V1_DOMAIN + jcsCanonicalize(input)
|
|
3721
|
+
)
|
|
3722
|
+
)
|
|
3723
|
+
);
|
|
3724
|
+
}
|
|
3725
|
+
function policyDigestHex(policy) {
|
|
3726
|
+
const canonicalPolicy = {
|
|
3727
|
+
...policy,
|
|
3728
|
+
capabilityCeiling: policy.capabilityCeiling.map(
|
|
3729
|
+
normalizeUnifiedPolicyCapability
|
|
3730
|
+
)
|
|
3731
|
+
};
|
|
3732
|
+
return hex(
|
|
3733
|
+
(0, import_sha256.sha256)(
|
|
3734
|
+
new TextEncoder().encode(
|
|
3735
|
+
POLICY_V1_DOMAIN + jcsCanonicalize(canonicalPolicy)
|
|
3736
|
+
)
|
|
3737
|
+
)
|
|
3738
|
+
);
|
|
3739
|
+
}
|
|
3740
|
+
function policyIdForDigestHex(digestHex) {
|
|
3741
|
+
const digest2 = Uint8Array.from(
|
|
3742
|
+
(digestHex.match(/.{2}/g) ?? []).map((byte) => Number.parseInt(byte, 16))
|
|
3743
|
+
);
|
|
3744
|
+
if (digest2.length !== 32) throw new Error("policy digest must be 32 bytes");
|
|
3745
|
+
return `pol_${base32Lower(digest2)}`;
|
|
3746
|
+
}
|
|
3747
|
+
function policyCidFromCanonicalBytes(bytes) {
|
|
3748
|
+
return CID.createV1(85, create(18, (0, import_sha256.sha256)(bytes))).toString();
|
|
3749
|
+
}
|
|
3750
|
+
var CompactHeaderSchema = import_zod.z.object({
|
|
3751
|
+
alg: import_zod.z.literal("EdDSA"),
|
|
3752
|
+
jwk: import_zod.z.object({ alg: import_zod.z.literal("EdDSA"), crv: import_zod.z.literal("Ed25519"), kty: import_zod.z.literal("OKP"), x: import_zod.z.string().min(1) }).strict(),
|
|
3753
|
+
typ: import_zod.z.literal("JWT"),
|
|
3754
|
+
ucv: import_zod.z.literal("0.10.0")
|
|
3755
|
+
}).strict();
|
|
3756
|
+
var CompactPayloadSchema = import_zod.z.object({
|
|
3757
|
+
att: import_zod.z.record(import_zod.z.record(import_zod.z.array(import_zod.z.unknown()).min(1))),
|
|
3758
|
+
aud: import_zod.z.string().min(1),
|
|
3759
|
+
exp: import_zod.z.number().int(),
|
|
3760
|
+
fct: import_zod.z.array(import_zod.z.record(import_zod.z.unknown())).length(1),
|
|
3761
|
+
iss: import_zod.z.string().min(1),
|
|
3762
|
+
nbf: import_zod.z.number().int(),
|
|
3763
|
+
nnc: import_zod.z.string().min(1),
|
|
3764
|
+
prf: import_zod.z.array(import_zod.z.string().min(1))
|
|
3765
|
+
}).strict();
|
|
3766
|
+
async function signCompactUcan(input) {
|
|
3767
|
+
const principal = input.issuerDid.split("#", 1)[0];
|
|
3768
|
+
const didMaterial = principal.startsWith("did:key:") ? base58btc.decode(principal.slice("did:key:".length)) : new Uint8Array();
|
|
3769
|
+
if (didMaterial.length !== 34 || didMaterial[0] !== 237 || didMaterial[1] !== 1)
|
|
3770
|
+
throw new Error("compact UCAN signer must be did:key Ed25519");
|
|
3771
|
+
const header = { alg: "EdDSA", jwk: { alg: "EdDSA", crv: "Ed25519", kty: "OKP", x: encodeBase64Url(didMaterial.slice(2)) }, typ: "JWT", ucv: "0.10.0" };
|
|
3772
|
+
const payload = {
|
|
3773
|
+
att: input.attenuation,
|
|
3774
|
+
aud: input.audienceDid,
|
|
3775
|
+
exp: input.expiresAt,
|
|
3776
|
+
fct: input.facts,
|
|
3777
|
+
iss: input.issuerDid.includes("#") ? input.issuerDid : `${principal}#${principal.slice("did:key:".length)}`,
|
|
3778
|
+
nbf: input.notBefore,
|
|
3779
|
+
nnc: input.nonce,
|
|
3780
|
+
prf: input.proofs
|
|
3781
|
+
};
|
|
3782
|
+
const protectedSegment = encodeBase64Url(new TextEncoder().encode(jcsCanonicalize(header)));
|
|
3783
|
+
const payloadSegment = encodeBase64Url(new TextEncoder().encode(jcsCanonicalize(payload)));
|
|
3784
|
+
const signature = await input.sign(new TextEncoder().encode(`${protectedSegment}.${payloadSegment}`));
|
|
3785
|
+
if (signature.length !== 64) throw new Error("compact Authorization signature must be Ed25519");
|
|
3786
|
+
return parseCompactUcanAuthorization(`${protectedSegment}.${payloadSegment}.${encodeBase64Url(signature)}`);
|
|
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
|
+
}
|
|
3798
|
+
function createCompactPolicyInvocation(input) {
|
|
3799
|
+
const session = parsePolicySessionUcan(input.sessionAuthorization);
|
|
3800
|
+
if (session.cid !== input.sessionCid || session.aud !== input.recipientDid)
|
|
3801
|
+
throw new Error("policy session does not bind the recipient signer");
|
|
3802
|
+
const principal = input.recipientDid.split("#", 1)[0];
|
|
3803
|
+
const didMaterial = base58btc.decode(principal.slice("did:key:".length));
|
|
3804
|
+
if (didMaterial.length !== 34 || didMaterial[0] !== 237 || didMaterial[1] !== 1)
|
|
3805
|
+
throw new Error("policy invocation signer must be did:key Ed25519");
|
|
3806
|
+
const publicKey = import_ed255192.ed25519.getPublicKey(input.privateKey);
|
|
3807
|
+
if (!equalBytes(publicKey, didMaterial.slice(2)))
|
|
3808
|
+
throw new Error("policy invocation private key does not bind recipient DID");
|
|
3809
|
+
const now = input.now ?? Math.floor(Date.now() / 1e3);
|
|
3810
|
+
const exp = Math.min(now + 60, session.exp);
|
|
3811
|
+
if (exp <= now) throw new Error("policy session has expired");
|
|
3812
|
+
const header = { alg: "EdDSA", jwk: { alg: "EdDSA", crv: "Ed25519", kty: "OKP", x: encodeBase64Url(publicKey) }, typ: "JWT", ucv: "0.10.0" };
|
|
3813
|
+
const payload = {
|
|
3814
|
+
att: { [input.resource]: { [input.action]: [input.caveat ?? {}] } },
|
|
3815
|
+
aud: input.audienceDid,
|
|
3816
|
+
exp,
|
|
3817
|
+
fct: [input.facts ?? { type: "tinycloud.policy.invocation/v1", policyCid: session.fact.policyCid, sessionCid: session.cid }],
|
|
3818
|
+
iss: input.recipientDid.includes("#") ? input.recipientDid : `${principal}#${principal.slice("did:key:".length)}`,
|
|
3819
|
+
nbf: now,
|
|
3820
|
+
nnc: input.nonce ?? encodeBase64Url(crypto.getRandomValues(new Uint8Array(16))),
|
|
3821
|
+
prf: [session.cid]
|
|
3822
|
+
};
|
|
3823
|
+
const protectedSegment = encodeBase64Url(new TextEncoder().encode(jcsCanonicalize(header)));
|
|
3824
|
+
const payloadSegment = encodeBase64Url(new TextEncoder().encode(jcsCanonicalize(payload)));
|
|
3825
|
+
const signingInput = new TextEncoder().encode(`${protectedSegment}.${payloadSegment}`);
|
|
3826
|
+
const signature = import_ed255192.ed25519.sign(signingInput, input.privateKey);
|
|
3827
|
+
return parseCompactUcanAuthorization(`${protectedSegment}.${payloadSegment}.${encodeBase64Url(signature)}`);
|
|
3828
|
+
}
|
|
3829
|
+
function createCompactPolicyDescendant(input) {
|
|
3830
|
+
const parent = parseCompactUcanAuthorization(input.parentAuthorization, input.parentCid);
|
|
3831
|
+
if (parent.payload.aud !== input.issuerDid) throw new Error("policy descendant issuer is not the parent audience");
|
|
3832
|
+
const parentFact = parent.payload.fct[0];
|
|
3833
|
+
const depth = parentFact.remainingRedelegationDepth;
|
|
3834
|
+
if (!Number.isInteger(depth) || depth <= 0) throw new Error("policy descendant depth is exhausted");
|
|
3835
|
+
if (!compactAttenuationContains(parent.payload.att, input.attenuation))
|
|
3836
|
+
throw new Error("policy descendant exceeds the parent attenuation");
|
|
3837
|
+
const principal = input.issuerDid.split("#", 1)[0];
|
|
3838
|
+
const didMaterial = base58btc.decode(principal.slice("did:key:".length));
|
|
3839
|
+
const publicKey = import_ed255192.ed25519.getPublicKey(input.privateKey);
|
|
3840
|
+
if (didMaterial.length !== 34 || !equalBytes(publicKey, didMaterial.slice(2))) throw new Error("policy descendant signer does not bind issuer DID");
|
|
3841
|
+
const now = Math.max(input.now ?? Math.floor(Date.now() / 1e3), parent.payload.nbf + 1);
|
|
3842
|
+
const exp = Math.min(input.expiresAt ?? now + 60, now + 60, parent.payload.exp - 1);
|
|
3843
|
+
if (exp <= now) throw new Error("policy parent has no strictly narrower validity window");
|
|
3844
|
+
const header = { alg: "EdDSA", jwk: { alg: "EdDSA", crv: "Ed25519", kty: "OKP", x: encodeBase64Url(publicKey) }, typ: "JWT", ucv: "0.10.0" };
|
|
3845
|
+
const payload = {
|
|
3846
|
+
att: input.attenuation,
|
|
3847
|
+
aud: input.audienceDid,
|
|
3848
|
+
exp,
|
|
3849
|
+
fct: [{ ...parentFact, remainingRedelegationDepth: depth - 1 }],
|
|
3850
|
+
iss: input.issuerDid.includes("#") ? input.issuerDid : `${principal}#${principal.slice("did:key:".length)}`,
|
|
3851
|
+
nbf: now,
|
|
3852
|
+
nnc: input.nonce ?? encodeBase64Url(crypto.getRandomValues(new Uint8Array(16))),
|
|
3853
|
+
prf: [parent.cid]
|
|
3854
|
+
};
|
|
3855
|
+
const protectedSegment = encodeBase64Url(new TextEncoder().encode(jcsCanonicalize(header)));
|
|
3856
|
+
const payloadSegment = encodeBase64Url(new TextEncoder().encode(jcsCanonicalize(payload)));
|
|
3857
|
+
const signature = import_ed255192.ed25519.sign(new TextEncoder().encode(`${protectedSegment}.${payloadSegment}`), input.privateKey);
|
|
3858
|
+
return parseCompactUcanAuthorization(`${protectedSegment}.${payloadSegment}.${encodeBase64Url(signature)}`);
|
|
3859
|
+
}
|
|
3860
|
+
function compactAttenuationContains(parent, child) {
|
|
3861
|
+
const selector = (value) => {
|
|
3862
|
+
if (value.length !== 1 || !isRecordValue(value[0])) return void 0;
|
|
3863
|
+
const caveat = value[0];
|
|
3864
|
+
if (Object.keys(caveat).length !== 3 || caveat.type !== "xyz.tinycloud.resource/selector" || caveat.kind !== "exact" && caveat.kind !== "prefix" || typeof caveat.value !== "string") return void 0;
|
|
3865
|
+
return { kind: caveat.kind, value: caveat.value };
|
|
3866
|
+
};
|
|
3867
|
+
const equal = (left, right) => jcsCanonicalize(left) === jcsCanonicalize(right);
|
|
3868
|
+
return Object.entries(child).every(
|
|
3869
|
+
([childResource, childActions]) => Object.entries(childActions).every(
|
|
3870
|
+
([action, childCaveats]) => Object.entries(parent).some(([parentResource, parentActions]) => {
|
|
3871
|
+
const parentCaveats = parentActions[action];
|
|
3872
|
+
if (parentCaveats === void 0) return false;
|
|
3873
|
+
if (parentResource === childResource && equal(parentCaveats, childCaveats)) return true;
|
|
3874
|
+
const parentSelector = selector(parentCaveats);
|
|
3875
|
+
const childSelector = selector(childCaveats);
|
|
3876
|
+
return parentSelector?.kind === "prefix" && childSelector !== void 0 && parentSelector.value === parentResource && childSelector.value === childResource && (childResource === parentResource || childResource.startsWith(`${parentResource}/`));
|
|
3877
|
+
})
|
|
3878
|
+
)
|
|
3879
|
+
);
|
|
3880
|
+
}
|
|
3881
|
+
function isRecordValue(value) {
|
|
3882
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3883
|
+
}
|
|
3884
|
+
function parseCompactUcanAuthorization(input, expectedCid) {
|
|
3885
|
+
if (typeof input !== "string" || /\s/.test(input))
|
|
3886
|
+
throw new Error("Authorization must be compact UCAN bytes");
|
|
3887
|
+
const segments = input.split(".");
|
|
3888
|
+
if (segments.length !== 3 || segments.some((segment) => segment.length === 0))
|
|
3889
|
+
throw new Error("Authorization must have three compact segments");
|
|
3890
|
+
const [headerSegment, payloadSegment, signatureSegment] = segments;
|
|
3891
|
+
const headerBytes = decodeBase64Url(headerSegment);
|
|
3892
|
+
const payloadBytes = decodeBase64Url(payloadSegment);
|
|
3893
|
+
const signature = decodeBase64Url(signatureSegment);
|
|
3894
|
+
const headerValue = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(headerBytes));
|
|
3895
|
+
const payloadValue = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(payloadBytes));
|
|
3896
|
+
if (jcsCanonicalize(headerValue) !== new TextDecoder().decode(headerBytes) || jcsCanonicalize(payloadValue) !== new TextDecoder().decode(payloadBytes))
|
|
3897
|
+
throw new Error("compact UCAN segments are not canonical JCS");
|
|
3898
|
+
const header = CompactHeaderSchema.parse(headerValue);
|
|
3899
|
+
const payload = CompactPayloadSchema.parse(payloadValue);
|
|
3900
|
+
if (payload.nbf >= payload.exp) throw new Error("compact UCAN validity is empty");
|
|
3901
|
+
const principal = payload.iss.split("#", 1)[0];
|
|
3902
|
+
const didMaterial = principal.startsWith("did:key:") ? base58btc.decode(principal.slice("did:key:".length)) : new Uint8Array();
|
|
3903
|
+
if (didMaterial.length !== 34 || didMaterial[0] !== 237 || didMaterial[1] !== 1)
|
|
3904
|
+
throw new Error("compact UCAN issuer must be did:key Ed25519");
|
|
3905
|
+
const publicKey = didMaterial.slice(2);
|
|
3906
|
+
if (!equalBytes(publicKey, decodeBase64Url(header.jwk.x)))
|
|
3907
|
+
throw new Error("compact UCAN JWK does not bind issuer");
|
|
3908
|
+
if (!import_ed255192.ed25519.verify(signature, new TextEncoder().encode(`${headerSegment}.${payloadSegment}`), publicKey))
|
|
3909
|
+
throw new Error("compact UCAN signature is invalid");
|
|
3910
|
+
const cid = CID.createV1(85, create(30, (0, import_blake3.blake3)(new TextEncoder().encode(input)))).toString();
|
|
3911
|
+
if (expectedCid !== void 0 && cid !== expectedCid)
|
|
3912
|
+
throw new Error("compact UCAN CID does not match exact Authorization bytes");
|
|
3913
|
+
return { authorization: input, cid, header, payload };
|
|
3914
|
+
}
|
|
3915
|
+
function parsePolicySessionUcan(authorization, expectedProofs) {
|
|
3916
|
+
const parsed = parseCompactUcanAuthorization(authorization);
|
|
3917
|
+
const fact = parsed.payload.fct[0];
|
|
3918
|
+
if (parsed.payload.prf.length !== 2 || parsed.payload.prf[0] === parsed.payload.prf[1])
|
|
3919
|
+
throw new Error("policy session UCAN requires two distinct ordered proofs");
|
|
3920
|
+
if (expectedProofs !== void 0 && (parsed.payload.prf[0] !== expectedProofs[0] || parsed.payload.prf[1] !== expectedProofs[1]))
|
|
3921
|
+
throw new Error("policy session UCAN ordered proofs do not match");
|
|
3922
|
+
const factKeys = Object.keys(fact);
|
|
3923
|
+
if (factKeys.length !== POLICY_SESSION_FACT_KEYS.length || factKeys.some((key) => !POLICY_SESSION_FACT_KEYS.includes(key)) || POLICY_SESSION_FACT_KEYS.some((key) => !(key in fact)))
|
|
3924
|
+
throw new Error("policy session UCAN fact is incomplete");
|
|
3925
|
+
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.enforcerDid || parsed.payload.aud !== fact.recipientDid)
|
|
3926
|
+
throw new Error("policy session UCAN fact is invalid");
|
|
3927
|
+
return {
|
|
3928
|
+
authorization,
|
|
3929
|
+
cid: parsed.cid,
|
|
3930
|
+
iss: parsed.payload.iss,
|
|
3931
|
+
aud: parsed.payload.aud,
|
|
3932
|
+
prf: parsed.payload.prf,
|
|
3933
|
+
att: parsed.payload.att,
|
|
3934
|
+
nbf: parsed.payload.nbf,
|
|
3935
|
+
exp: parsed.payload.exp,
|
|
3936
|
+
nnc: parsed.payload.nnc,
|
|
3937
|
+
fact
|
|
3938
|
+
};
|
|
3939
|
+
}
|
|
3940
|
+
function decodeBase64Url(value) {
|
|
3941
|
+
if (!/^[A-Za-z0-9_-]+$/.test(value) || value.length % 4 === 1)
|
|
3942
|
+
throw new Error("value is not canonical base64url");
|
|
3943
|
+
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
3944
|
+
const binary = typeof atob === "function" ? atob(normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=")) : Buffer.from(normalized, "base64").toString("binary");
|
|
3945
|
+
const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
3946
|
+
const encoded = typeof btoa === "function" ? btoa(String.fromCharCode(...bytes)).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_") : Buffer.from(bytes).toString("base64url");
|
|
3947
|
+
if (encoded !== value) throw new Error("value is not canonical base64url");
|
|
3948
|
+
return bytes;
|
|
3949
|
+
}
|
|
3950
|
+
function encodeBase64Url(value) {
|
|
3951
|
+
return typeof btoa === "function" ? btoa(String.fromCharCode(...value)).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_") : Buffer.from(value).toString("base64url");
|
|
3952
|
+
}
|
|
3953
|
+
function equalBytes(left, right) {
|
|
3954
|
+
return left.length === right.length && left.every((byte, index) => byte === right[index]);
|
|
3955
|
+
}
|
|
3956
|
+
function hex(bytes) {
|
|
3957
|
+
return [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
3958
|
+
}
|
|
3959
|
+
function base32Lower(bytes) {
|
|
3960
|
+
const alphabet2 = "abcdefghijklmnopqrstuvwxyz234567";
|
|
3961
|
+
let output = "";
|
|
3962
|
+
let buffer = 0;
|
|
3963
|
+
let bits = 0;
|
|
3964
|
+
for (const byte of bytes) {
|
|
3965
|
+
buffer = buffer << 8 | byte;
|
|
3966
|
+
bits += 8;
|
|
3967
|
+
while (bits >= 5) {
|
|
3968
|
+
output += alphabet2[buffer >>> bits - 5 & 31];
|
|
3969
|
+
bits -= 5;
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
if (bits > 0) output += alphabet2[buffer << 5 - bits & 31];
|
|
3973
|
+
return output;
|
|
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 });
|
|
3259
4357
|
}
|
|
3260
4358
|
//# sourceMappingURL=index.cjs.map
|