@tinycloud/sdk-core 3.0.0-beta.0 → 3.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{credential-admission-C_cO1QqB.d.cts → credential-admission-CFjUChF8.d.cts} +122 -2
- package/dist/{credential-admission-cvHe-HUC.d.ts → credential-admission-DIEPgQ0o.d.ts} +122 -2
- package/dist/delegations/index.cjs +80 -133
- package/dist/delegations/index.cjs.map +1 -1
- package/dist/delegations/index.d.cts +3 -3
- package/dist/delegations/index.d.ts +3 -3
- package/dist/delegations/index.js +81 -138
- package/dist/delegations/index.js.map +1 -1
- package/dist/index.cjs +994 -306
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +149 -7
- package/dist/index.d.ts +149 -7
- package/dist/index.js +996 -311
- package/dist/index.js.map +1 -1
- package/dist/policy/index.cjs +323 -8
- package/dist/policy/index.cjs.map +1 -1
- package/dist/policy/index.d.cts +2 -2
- package/dist/policy/index.d.ts +2 -2
- package/dist/policy/index.js +323 -8
- package/dist/policy/index.js.map +1 -1
- package/dist/{recipient-types.schema-G8NEKRoo.d.cts → recipient-types.schema-Cz5zJzED.d.cts} +50 -77
- package/dist/{recipient-types.schema-G8NEKRoo.d.ts → recipient-types.schema-Cz5zJzED.d.ts} +50 -77
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -565,6 +565,17 @@ function isDigest(value) {
|
|
|
565
565
|
|
|
566
566
|
// src/credentials/requirement.ts
|
|
567
567
|
var ID2 = /^[a-z0-9][a-z0-9._-]{0,127}(?:\/v1)?$/;
|
|
568
|
+
var EMAIL_CREDENTIAL_MAX_AGE_SECONDS = 3600;
|
|
569
|
+
function createEmailCredentialRequirement(input) {
|
|
570
|
+
return validateCredentialRequirement({
|
|
571
|
+
type: "TinyCloudCredentialRequirement",
|
|
572
|
+
version: 1,
|
|
573
|
+
profile: input.profile,
|
|
574
|
+
credentialType: input.credentialType,
|
|
575
|
+
claims: { email: input.email },
|
|
576
|
+
maxAgeSeconds: EMAIL_CREDENTIAL_MAX_AGE_SECONDS
|
|
577
|
+
});
|
|
578
|
+
}
|
|
568
579
|
function validateCredentialRequirement(value) {
|
|
569
580
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new CredentialError("UNSUPPORTED_PROFILE", "Credential requirement is invalid");
|
|
570
581
|
const raw = value;
|
|
@@ -1779,16 +1790,16 @@ function policyCapabilityContains(authority, request) {
|
|
|
1779
1790
|
return caveatsContain(auth.caveats, req.caveats);
|
|
1780
1791
|
}
|
|
1781
1792
|
function normalizePolicyCapabilityWithOptions(input, options2) {
|
|
1782
|
-
const
|
|
1783
|
-
assertExactKeys(
|
|
1784
|
-
const service = requiredString(
|
|
1793
|
+
const object4 = expectObject(input, "$");
|
|
1794
|
+
assertExactKeys(object4, ["service", "space", "path", "actions", "caveats"], "$");
|
|
1795
|
+
const service = requiredString(object4, "service", "$", "policy-capability-malformed-service");
|
|
1785
1796
|
if (!CEILING_SERVICES.has(service)) {
|
|
1786
1797
|
throw new PolicyCapabilityError(
|
|
1787
1798
|
"policy-capability-malformed-service",
|
|
1788
1799
|
"$.service is outside the frozen permissionsCeiling vocabulary"
|
|
1789
1800
|
);
|
|
1790
1801
|
}
|
|
1791
|
-
const space = requiredString(
|
|
1802
|
+
const space = requiredString(object4, "space", "$", "policy-capability-malformed-space");
|
|
1792
1803
|
validateConcreteSpace(space);
|
|
1793
1804
|
const canonicalSpace = space.normalize("NFC");
|
|
1794
1805
|
if (options2.requireCanonical && canonicalSpace !== space) {
|
|
@@ -1797,7 +1808,7 @@ function normalizePolicyCapabilityWithOptions(input, options2) {
|
|
|
1797
1808
|
"$.space must already be canonical NFC"
|
|
1798
1809
|
);
|
|
1799
1810
|
}
|
|
1800
|
-
const rawPath = requiredString(
|
|
1811
|
+
const rawPath = requiredString(object4, "path", "$", "policy-capability-malformed-path");
|
|
1801
1812
|
validateRawPath(rawPath, options2);
|
|
1802
1813
|
const path = normalizePath(rawPath);
|
|
1803
1814
|
if (options2.requireCanonical && path !== rawPath) {
|
|
@@ -1808,7 +1819,7 @@ function normalizePolicyCapabilityWithOptions(input, options2) {
|
|
|
1808
1819
|
}
|
|
1809
1820
|
validateRawPath(path, options2);
|
|
1810
1821
|
const actions = normalizeActions(
|
|
1811
|
-
requiredArray(
|
|
1822
|
+
requiredArray(object4, "actions", "$", "policy-capability-malformed-action"),
|
|
1812
1823
|
service,
|
|
1813
1824
|
options2
|
|
1814
1825
|
);
|
|
@@ -1817,9 +1828,9 @@ function normalizePolicyCapabilityWithOptions(input, options2) {
|
|
|
1817
1828
|
output.space = canonicalSpace;
|
|
1818
1829
|
output.path = path;
|
|
1819
1830
|
output.actions = actions;
|
|
1820
|
-
if (hasOwn(
|
|
1831
|
+
if (hasOwn(object4, "caveats")) {
|
|
1821
1832
|
output.caveats = validateCaveats(
|
|
1822
|
-
requiredValue(
|
|
1833
|
+
requiredValue(object4, "caveats", "$", "policy-capability-malformed-caveats"),
|
|
1823
1834
|
output.service
|
|
1824
1835
|
);
|
|
1825
1836
|
}
|
|
@@ -1842,9 +1853,9 @@ function expectObject(input, path) {
|
|
|
1842
1853
|
);
|
|
1843
1854
|
}
|
|
1844
1855
|
}
|
|
1845
|
-
function assertExactKeys(
|
|
1856
|
+
function assertExactKeys(object4, allowed, path) {
|
|
1846
1857
|
const allowedSet = new Set(allowed);
|
|
1847
|
-
for (const key of Object.keys(
|
|
1858
|
+
for (const key of Object.keys(object4)) {
|
|
1848
1859
|
if (!allowedSet.has(key)) {
|
|
1849
1860
|
throw new PolicyCapabilityError(
|
|
1850
1861
|
key === "id" || key === "scope" ? "policy-capability-malformed" : "policy-capability-unknown-key",
|
|
@@ -1853,21 +1864,21 @@ function assertExactKeys(object3, allowed, path) {
|
|
|
1853
1864
|
}
|
|
1854
1865
|
}
|
|
1855
1866
|
}
|
|
1856
|
-
function requiredValue(
|
|
1857
|
-
if (!hasOwn(
|
|
1867
|
+
function requiredValue(object4, key, path, code3) {
|
|
1868
|
+
if (!hasOwn(object4, key)) {
|
|
1858
1869
|
throw new PolicyCapabilityError(code3, `${path}.${key} is required`);
|
|
1859
1870
|
}
|
|
1860
|
-
return
|
|
1871
|
+
return object4[key];
|
|
1861
1872
|
}
|
|
1862
|
-
function requiredString(
|
|
1863
|
-
const value = requiredValue(
|
|
1873
|
+
function requiredString(object4, key, path, code3) {
|
|
1874
|
+
const value = requiredValue(object4, key, path, code3);
|
|
1864
1875
|
if (typeof value !== "string" || value.length === 0) {
|
|
1865
1876
|
throw new PolicyCapabilityError(code3, `${path}.${key} must be a non-empty string`);
|
|
1866
1877
|
}
|
|
1867
1878
|
return value;
|
|
1868
1879
|
}
|
|
1869
|
-
function requiredArray(
|
|
1870
|
-
const value = requiredValue(
|
|
1880
|
+
function requiredArray(object4, key, path, code3) {
|
|
1881
|
+
const value = requiredValue(object4, key, path, code3);
|
|
1871
1882
|
if (!Array.isArray(value)) {
|
|
1872
1883
|
throw new PolicyCapabilityError(code3, `${path}.${key} must be an array`);
|
|
1873
1884
|
}
|
|
@@ -2012,11 +2023,11 @@ function validateSqlStatement(input, path) {
|
|
|
2012
2023
|
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
2013
2024
|
throw new PolicyCapabilityError("policy-capability-malformed-caveats", `${path} must be an object`);
|
|
2014
2025
|
}
|
|
2015
|
-
const
|
|
2016
|
-
assertCaveatKeys(
|
|
2017
|
-
caveatString(
|
|
2018
|
-
caveatString(
|
|
2019
|
-
const fixedParams = requiredCaveat(
|
|
2026
|
+
const object4 = input;
|
|
2027
|
+
assertCaveatKeys(object4, ["name", "sql", "fixedParams"], path);
|
|
2028
|
+
caveatString(object4, "name", path);
|
|
2029
|
+
caveatString(object4, "sql", path);
|
|
2030
|
+
const fixedParams = requiredCaveat(object4, "fixedParams", path);
|
|
2020
2031
|
if (!Array.isArray(fixedParams)) {
|
|
2021
2032
|
throw new PolicyCapabilityError(
|
|
2022
2033
|
"policy-capability-malformed-caveats",
|
|
@@ -2031,20 +2042,20 @@ function validateFixedParam(input, path) {
|
|
|
2031
2042
|
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
2032
2043
|
throw new PolicyCapabilityError("policy-capability-malformed-caveats", `${path} must be an object`);
|
|
2033
2044
|
}
|
|
2034
|
-
const
|
|
2035
|
-
assertCaveatKeys(
|
|
2036
|
-
const index = requiredCaveat(
|
|
2045
|
+
const object4 = input;
|
|
2046
|
+
assertCaveatKeys(object4, ["index", "value"], path);
|
|
2047
|
+
const index = requiredCaveat(object4, "index", path);
|
|
2037
2048
|
if (typeof index !== "number" || !Number.isSafeInteger(index) || index < 0) {
|
|
2038
2049
|
throw new PolicyCapabilityError(
|
|
2039
2050
|
"policy-capability-malformed-caveats",
|
|
2040
2051
|
`${path}.index must be a non-negative integer`
|
|
2041
2052
|
);
|
|
2042
2053
|
}
|
|
2043
|
-
requiredCaveat(
|
|
2054
|
+
requiredCaveat(object4, "value", path);
|
|
2044
2055
|
}
|
|
2045
|
-
function assertCaveatKeys(
|
|
2056
|
+
function assertCaveatKeys(object4, allowed, path) {
|
|
2046
2057
|
const allowedSet = new Set(allowed);
|
|
2047
|
-
for (const key of Object.keys(
|
|
2058
|
+
for (const key of Object.keys(object4)) {
|
|
2048
2059
|
if (!allowedSet.has(key)) {
|
|
2049
2060
|
throw new PolicyCapabilityError(
|
|
2050
2061
|
"policy-capability-malformed-caveats",
|
|
@@ -2053,17 +2064,17 @@ function assertCaveatKeys(object3, allowed, path) {
|
|
|
2053
2064
|
}
|
|
2054
2065
|
}
|
|
2055
2066
|
}
|
|
2056
|
-
function requiredCaveat(
|
|
2057
|
-
if (!hasOwn(
|
|
2067
|
+
function requiredCaveat(object4, key, path) {
|
|
2068
|
+
if (!hasOwn(object4, key)) {
|
|
2058
2069
|
throw new PolicyCapabilityError(
|
|
2059
2070
|
"policy-capability-malformed-caveats",
|
|
2060
2071
|
`${path}.${key} is required`
|
|
2061
2072
|
);
|
|
2062
2073
|
}
|
|
2063
|
-
return
|
|
2074
|
+
return object4[key];
|
|
2064
2075
|
}
|
|
2065
|
-
function caveatString(
|
|
2066
|
-
const value = requiredCaveat(
|
|
2076
|
+
function caveatString(object4, key, path) {
|
|
2077
|
+
const value = requiredCaveat(object4, key, path);
|
|
2067
2078
|
if (typeof value !== "string" || value.length === 0) {
|
|
2068
2079
|
throw new PolicyCapabilityError(
|
|
2069
2080
|
"policy-capability-malformed-caveats",
|
|
@@ -2094,8 +2105,8 @@ function caveatsContain(authority, request) {
|
|
|
2094
2105
|
(statement) => authorityStatements.some((candidate) => jcsCanonicalize(candidate) === jcsCanonicalize(statement))
|
|
2095
2106
|
);
|
|
2096
2107
|
}
|
|
2097
|
-
function hasOwn(
|
|
2098
|
-
return objectHasOwn2(
|
|
2108
|
+
function hasOwn(object4, key) {
|
|
2109
|
+
return objectHasOwn2(object4, key);
|
|
2099
2110
|
}
|
|
2100
2111
|
|
|
2101
2112
|
// src/policy/signed-object.ts
|
|
@@ -2423,17 +2434,17 @@ async function createAndSignSignedObject(input, signer) {
|
|
|
2423
2434
|
}
|
|
2424
2435
|
function createAndSignPolicy(input, signer) {
|
|
2425
2436
|
return createAndSignSignedObject(input, signer).then(
|
|
2426
|
-
(
|
|
2437
|
+
(object4) => validatePolicySignedShape(object4)
|
|
2427
2438
|
);
|
|
2428
2439
|
}
|
|
2429
2440
|
function createAndSignPolicyStatus(input, signer) {
|
|
2430
2441
|
return createAndSignSignedObject(input, signer).then(
|
|
2431
|
-
(
|
|
2442
|
+
(object4) => validatePolicyStatusSignedShape(object4)
|
|
2432
2443
|
);
|
|
2433
2444
|
}
|
|
2434
2445
|
function createAndSignPolicyEngineRecord(input, signer) {
|
|
2435
2446
|
return createAndSignSignedObject(input, signer).then(
|
|
2436
|
-
(
|
|
2447
|
+
(object4) => validatePolicyEngineRecordSignedShape(object4)
|
|
2437
2448
|
);
|
|
2438
2449
|
}
|
|
2439
2450
|
async function verifySignedObject(input) {
|
|
@@ -2537,9 +2548,9 @@ function validateUnsignedForDescriptor(input, descriptor) {
|
|
|
2537
2548
|
}
|
|
2538
2549
|
}
|
|
2539
2550
|
function validatePolicyShape(input, signed) {
|
|
2540
|
-
const
|
|
2551
|
+
const object4 = expectJsonObject(normalizeJson(input), "$");
|
|
2541
2552
|
assertExactKeys2(
|
|
2542
|
-
|
|
2553
|
+
object4,
|
|
2543
2554
|
signed ? [
|
|
2544
2555
|
"schema",
|
|
2545
2556
|
"policyId",
|
|
@@ -2567,30 +2578,30 @@ function validatePolicyShape(input, signed) {
|
|
|
2567
2578
|
],
|
|
2568
2579
|
"$"
|
|
2569
2580
|
);
|
|
2570
|
-
expectConst(requiredString2(
|
|
2581
|
+
expectConst(requiredString2(object4, "schema", "$"), POLICY_SCHEMA, "$.schema");
|
|
2571
2582
|
if (signed) {
|
|
2572
|
-
requiredString2(
|
|
2573
|
-
validateSignature(requiredValue2(
|
|
2583
|
+
requiredString2(object4, "policyId", "$");
|
|
2584
|
+
validateSignature(requiredValue2(object4, "signature", "$"));
|
|
2574
2585
|
}
|
|
2575
|
-
requiredString2(
|
|
2576
|
-
requiredString2(
|
|
2577
|
-
requiredDateString(
|
|
2578
|
-
optionalDateString(
|
|
2579
|
-
validatePolicyResource(requiredValue2(
|
|
2580
|
-
validateExpression(requiredValue2(
|
|
2581
|
-
validateGrant(requiredValue2(
|
|
2582
|
-
if (hasOwn2(
|
|
2583
|
-
validateDisclosure(requiredValue2(
|
|
2586
|
+
requiredString2(object4, "ownerDid", "$");
|
|
2587
|
+
requiredString2(object4, "signingKeyDid", "$");
|
|
2588
|
+
requiredDateString(object4, "createdAt", "$");
|
|
2589
|
+
optionalDateString(object4, "expiresAt", "$");
|
|
2590
|
+
validatePolicyResource(requiredValue2(object4, "resource", "$"), "$.resource");
|
|
2591
|
+
validateExpression(requiredValue2(object4, "when", "$"), "$.when");
|
|
2592
|
+
validateGrant(requiredValue2(object4, "grant", "$"), "$.grant");
|
|
2593
|
+
if (hasOwn2(object4, "disclosure")) {
|
|
2594
|
+
validateDisclosure(requiredValue2(object4, "disclosure", "$"), "$.disclosure");
|
|
2584
2595
|
}
|
|
2585
|
-
if (hasOwn2(
|
|
2586
|
-
validateAudit(requiredValue2(
|
|
2596
|
+
if (hasOwn2(object4, "audit")) {
|
|
2597
|
+
validateAudit(requiredValue2(object4, "audit", "$"), "$.audit");
|
|
2587
2598
|
}
|
|
2588
|
-
return
|
|
2599
|
+
return object4;
|
|
2589
2600
|
}
|
|
2590
2601
|
function validatePolicyStatusShape(input, signed) {
|
|
2591
|
-
const
|
|
2602
|
+
const object4 = expectJsonObject(normalizeJson(input), "$");
|
|
2592
2603
|
assertExactKeys2(
|
|
2593
|
-
|
|
2604
|
+
object4,
|
|
2594
2605
|
signed ? [
|
|
2595
2606
|
"schema",
|
|
2596
2607
|
"statusId",
|
|
@@ -2614,28 +2625,28 @@ function validatePolicyStatusShape(input, signed) {
|
|
|
2614
2625
|
],
|
|
2615
2626
|
"$"
|
|
2616
2627
|
);
|
|
2617
|
-
expectConst(requiredString2(
|
|
2628
|
+
expectConst(requiredString2(object4, "schema", "$"), POLICY_STATUS_SCHEMA, "$.schema");
|
|
2618
2629
|
if (signed) {
|
|
2619
|
-
requiredString2(
|
|
2620
|
-
validateSignature(requiredValue2(
|
|
2630
|
+
requiredString2(object4, "statusId", "$");
|
|
2631
|
+
validateSignature(requiredValue2(object4, "signature", "$"));
|
|
2621
2632
|
}
|
|
2622
|
-
requiredString2(
|
|
2623
|
-
requiredString2(
|
|
2624
|
-
requiredInteger(
|
|
2625
|
-
expectOneOf(requiredString2(
|
|
2633
|
+
requiredString2(object4, "policyId", "$");
|
|
2634
|
+
requiredString2(object4, "ownerDid", "$");
|
|
2635
|
+
requiredInteger(object4, "sequence", "$", 0);
|
|
2636
|
+
expectOneOf(requiredString2(object4, "disposition", "$"), [
|
|
2626
2637
|
"active",
|
|
2627
2638
|
"suspended",
|
|
2628
2639
|
"revoked"
|
|
2629
2640
|
], "$.disposition");
|
|
2630
|
-
requiredDateString(
|
|
2631
|
-
optionalString(
|
|
2632
|
-
requiredString2(
|
|
2633
|
-
return
|
|
2641
|
+
requiredDateString(object4, "effectiveAt", "$");
|
|
2642
|
+
optionalString(object4, "reasonCode", "$");
|
|
2643
|
+
requiredString2(object4, "signingKeyDid", "$");
|
|
2644
|
+
return object4;
|
|
2634
2645
|
}
|
|
2635
2646
|
function validatePolicyEngineRecordShape(input, signed) {
|
|
2636
|
-
const
|
|
2647
|
+
const object4 = expectJsonObject(normalizeJson(input), "$");
|
|
2637
2648
|
assertExactKeys2(
|
|
2638
|
-
|
|
2649
|
+
object4,
|
|
2639
2650
|
signed ? [
|
|
2640
2651
|
"schema",
|
|
2641
2652
|
"engineRecordId",
|
|
@@ -2660,44 +2671,44 @@ function validatePolicyEngineRecordShape(input, signed) {
|
|
|
2660
2671
|
"$"
|
|
2661
2672
|
);
|
|
2662
2673
|
expectConst(
|
|
2663
|
-
requiredString2(
|
|
2674
|
+
requiredString2(object4, "schema", "$"),
|
|
2664
2675
|
POLICY_ENGINE_RECORD_SCHEMA,
|
|
2665
2676
|
"$.schema"
|
|
2666
2677
|
);
|
|
2667
2678
|
if (signed) {
|
|
2668
|
-
requiredString2(
|
|
2669
|
-
validateSignature(requiredValue2(
|
|
2679
|
+
requiredString2(object4, "engineRecordId", "$");
|
|
2680
|
+
validateSignature(requiredValue2(object4, "signature", "$"));
|
|
2670
2681
|
}
|
|
2671
|
-
requiredString2(
|
|
2672
|
-
requiredString2(
|
|
2673
|
-
requiredString2(
|
|
2682
|
+
requiredString2(object4, "ownerDid", "$");
|
|
2683
|
+
requiredString2(object4, "endpoint", "$");
|
|
2684
|
+
requiredString2(object4, "audience", "$");
|
|
2674
2685
|
requiredStringArray(
|
|
2675
|
-
|
|
2686
|
+
object4,
|
|
2676
2687
|
"supportedPolicyVersions",
|
|
2677
2688
|
"$",
|
|
2678
2689
|
(value, path) => expectConst(value, "v0", path)
|
|
2679
2690
|
);
|
|
2680
|
-
requiredStringArray(
|
|
2681
|
-
requiredString2(
|
|
2682
|
-
requiredDateString(
|
|
2683
|
-
return
|
|
2691
|
+
requiredStringArray(object4, "supportedEvidenceVerifiers", "$");
|
|
2692
|
+
requiredString2(object4, "grantIssuerDid", "$");
|
|
2693
|
+
requiredDateString(object4, "expiresAt", "$");
|
|
2694
|
+
return object4;
|
|
2684
2695
|
}
|
|
2685
2696
|
function validateSignature(input) {
|
|
2686
|
-
const
|
|
2687
|
-
assertExactKeys2(
|
|
2688
|
-
const suite = requiredString2(
|
|
2697
|
+
const object4 = expectJsonObject(input, "$.signature");
|
|
2698
|
+
assertExactKeys2(object4, ["suite", "signerDid", "value"], "$.signature");
|
|
2699
|
+
const suite = requiredString2(object4, "suite", "$.signature");
|
|
2689
2700
|
assertSupportedSignatureSuite(suite);
|
|
2690
|
-
const signerDid = requiredString2(
|
|
2691
|
-
const value = requiredString2(
|
|
2701
|
+
const signerDid = requiredString2(object4, "signerDid", "$.signature");
|
|
2702
|
+
const value = requiredString2(object4, "value", "$.signature");
|
|
2692
2703
|
decodeSignatureValue(value, suite);
|
|
2693
2704
|
return { suite, signerDid, value };
|
|
2694
2705
|
}
|
|
2695
2706
|
function validatePolicyResource(input, path) {
|
|
2696
|
-
const
|
|
2697
|
-
assertExactKeys2(
|
|
2698
|
-
requiredString2(
|
|
2699
|
-
requiredString2(
|
|
2700
|
-
const ceiling = requiredArray2(
|
|
2707
|
+
const object4 = expectJsonObject(input, path);
|
|
2708
|
+
assertExactKeys2(object4, ["resourceType", "resourceId", "permissionsCeiling"], path);
|
|
2709
|
+
requiredString2(object4, "resourceType", path);
|
|
2710
|
+
requiredString2(object4, "resourceId", path);
|
|
2711
|
+
const ceiling = requiredArray2(object4, "permissionsCeiling", path, 1);
|
|
2701
2712
|
for (let index = 0; index < ceiling.length; index++) {
|
|
2702
2713
|
validatePolicyCapability(ceiling[index], `${path}.permissionsCeiling[${index}]`);
|
|
2703
2714
|
}
|
|
@@ -2713,10 +2724,10 @@ function validatePolicyPermissionsCeilingForSigning(input) {
|
|
|
2713
2724
|
}
|
|
2714
2725
|
}
|
|
2715
2726
|
function validatePolicyCapability(input, path) {
|
|
2716
|
-
const
|
|
2727
|
+
const object4 = expectJsonObject(input, path);
|
|
2717
2728
|
try {
|
|
2718
|
-
const canonical = canonicalizePolicyCapability(
|
|
2719
|
-
if (jcsCanonicalize(
|
|
2729
|
+
const canonical = canonicalizePolicyCapability(object4);
|
|
2730
|
+
if (jcsCanonicalize(object4) !== jcsCanonicalize(canonical)) {
|
|
2720
2731
|
throw new SignedObjectSchemaError(`${path} must be canonical PolicyCapability JSON`);
|
|
2721
2732
|
}
|
|
2722
2733
|
} catch (error) {
|
|
@@ -2729,10 +2740,10 @@ function validatePolicyCapability(input, path) {
|
|
|
2729
2740
|
}
|
|
2730
2741
|
}
|
|
2731
2742
|
function validatePolicyCapabilityForSigning(input, path) {
|
|
2732
|
-
const
|
|
2743
|
+
const object4 = expectJsonObject(input, path);
|
|
2733
2744
|
try {
|
|
2734
|
-
const canonical = normalizePolicyCapability(
|
|
2735
|
-
if (jcsCanonicalize(
|
|
2745
|
+
const canonical = normalizePolicyCapability(object4);
|
|
2746
|
+
if (jcsCanonicalize(object4) !== jcsCanonicalize(canonical)) {
|
|
2736
2747
|
throw new SignedObjectSchemaError(`${path} must be strict canonical PolicyCapability JSON`);
|
|
2737
2748
|
}
|
|
2738
2749
|
} catch (error) {
|
|
@@ -2745,43 +2756,43 @@ function validatePolicyCapabilityForSigning(input, path) {
|
|
|
2745
2756
|
}
|
|
2746
2757
|
}
|
|
2747
2758
|
function validateExpression(input, path) {
|
|
2748
|
-
const
|
|
2749
|
-
const keys = Object.keys(
|
|
2759
|
+
const object4 = expectJsonObject(input, path);
|
|
2760
|
+
const keys = Object.keys(object4);
|
|
2750
2761
|
if (keys.length !== 1) {
|
|
2751
2762
|
throw new SignedObjectSchemaError(`${path} must have exactly one expression key`);
|
|
2752
2763
|
}
|
|
2753
2764
|
const key = keys[0];
|
|
2754
2765
|
if (key === "allOf" || key === "anyOf") {
|
|
2755
|
-
const values = requiredArray2(
|
|
2766
|
+
const values = requiredArray2(object4, key, path, 1);
|
|
2756
2767
|
for (let index = 0; index < values.length; index++) {
|
|
2757
2768
|
validateExpression(values[index], `${path}.${key}[${index}]`);
|
|
2758
2769
|
}
|
|
2759
2770
|
return;
|
|
2760
2771
|
}
|
|
2761
2772
|
if (key === "subject") {
|
|
2762
|
-
const subject = expectJsonObject(requiredValue2(
|
|
2773
|
+
const subject = expectJsonObject(requiredValue2(object4, "subject", path), `${path}.subject`);
|
|
2763
2774
|
assertExactKeys2(subject, ["did"], `${path}.subject`);
|
|
2764
2775
|
requiredString2(subject, "did", `${path}.subject`);
|
|
2765
2776
|
return;
|
|
2766
2777
|
}
|
|
2767
2778
|
if (key === "evidence") {
|
|
2768
|
-
validateEvidenceRequirement(requiredValue2(
|
|
2779
|
+
validateEvidenceRequirement(requiredValue2(object4, "evidence", path), `${path}.evidence`);
|
|
2769
2780
|
return;
|
|
2770
2781
|
}
|
|
2771
2782
|
throw new SignedObjectSchemaError(`${path} has unknown expression key ${key}`);
|
|
2772
2783
|
}
|
|
2773
2784
|
function validateEvidenceRequirement(input, path) {
|
|
2774
|
-
const
|
|
2785
|
+
const object4 = expectJsonObject(input, path);
|
|
2775
2786
|
assertExactKeys2(
|
|
2776
|
-
|
|
2787
|
+
object4,
|
|
2777
2788
|
["requirementId", "verifier", "requirements", "authority", "freshness"],
|
|
2778
2789
|
path
|
|
2779
2790
|
);
|
|
2780
|
-
requiredString2(
|
|
2781
|
-
requiredString2(
|
|
2782
|
-
requiredValue2(
|
|
2783
|
-
if (hasOwn2(
|
|
2784
|
-
const authority = expectJsonObject(requiredValue2(
|
|
2791
|
+
requiredString2(object4, "requirementId", path);
|
|
2792
|
+
requiredString2(object4, "verifier", path);
|
|
2793
|
+
requiredValue2(object4, "requirements", path);
|
|
2794
|
+
if (hasOwn2(object4, "authority")) {
|
|
2795
|
+
const authority = expectJsonObject(requiredValue2(object4, "authority", path), `${path}.authority`);
|
|
2785
2796
|
assertExactKeys2(
|
|
2786
2797
|
authority,
|
|
2787
2798
|
["profile", "acceptedIssuers", "allowOwnerAuthorizedIssuer"],
|
|
@@ -2793,35 +2804,35 @@ function validateEvidenceRequirement(input, path) {
|
|
|
2793
2804
|
}
|
|
2794
2805
|
optionalBoolean(authority, "allowOwnerAuthorizedIssuer", `${path}.authority`);
|
|
2795
2806
|
}
|
|
2796
|
-
if (hasOwn2(
|
|
2797
|
-
const freshness = expectJsonObject(requiredValue2(
|
|
2807
|
+
if (hasOwn2(object4, "freshness")) {
|
|
2808
|
+
const freshness = expectJsonObject(requiredValue2(object4, "freshness", path), `${path}.freshness`);
|
|
2798
2809
|
assertExactKeys2(freshness, ["maxStatusAgeSeconds"], `${path}.freshness`);
|
|
2799
2810
|
requiredInteger(freshness, "maxStatusAgeSeconds", `${path}.freshness`, 0);
|
|
2800
2811
|
}
|
|
2801
2812
|
}
|
|
2802
2813
|
function validateGrant(input, path) {
|
|
2803
|
-
const
|
|
2804
|
-
assertExactKeys2(
|
|
2805
|
-
expectConst(requiredString2(
|
|
2806
|
-
requiredInteger(
|
|
2807
|
-
expectOneOf(requiredString2(
|
|
2814
|
+
const object4 = expectJsonObject(input, path);
|
|
2815
|
+
assertExactKeys2(object4, ["output", "maxTtlSeconds", "delegationMode", "revocation"], path);
|
|
2816
|
+
expectConst(requiredString2(object4, "output", path), "portable-delegation", `${path}.output`);
|
|
2817
|
+
requiredInteger(object4, "maxTtlSeconds", path, 1);
|
|
2818
|
+
expectOneOf(requiredString2(object4, "delegationMode", path), [
|
|
2808
2819
|
"terminal",
|
|
2809
2820
|
"attenuable"
|
|
2810
2821
|
], `${path}.delegationMode`);
|
|
2811
|
-
expectOneOf(requiredString2(
|
|
2822
|
+
expectOneOf(requiredString2(object4, "revocation", path), [
|
|
2812
2823
|
"refresh_only",
|
|
2813
2824
|
"active_cutoff"
|
|
2814
2825
|
], `${path}.revocation`);
|
|
2815
2826
|
}
|
|
2816
2827
|
function validateDisclosure(input, path) {
|
|
2817
|
-
const
|
|
2818
|
-
assertExactKeys2(
|
|
2819
|
-
expectOneOf(requiredString2(
|
|
2828
|
+
const object4 = expectJsonObject(input, path);
|
|
2829
|
+
assertExactKeys2(object4, ["denial"], path);
|
|
2830
|
+
expectOneOf(requiredString2(object4, "denial", path), ["none", "code", "debug"], `${path}.denial`);
|
|
2820
2831
|
}
|
|
2821
2832
|
function validateAudit(input, path) {
|
|
2822
|
-
const
|
|
2823
|
-
assertExactKeys2(
|
|
2824
|
-
expectOneOf(requiredString2(
|
|
2833
|
+
const object4 = expectJsonObject(input, path);
|
|
2834
|
+
assertExactKeys2(object4, ["issuance"], path);
|
|
2835
|
+
expectOneOf(requiredString2(object4, "issuance", path), ["off", "security", "full"], `${path}.issuance`);
|
|
2825
2836
|
}
|
|
2826
2837
|
function materialForUnsigned(unsigned, descriptor) {
|
|
2827
2838
|
const jcs = jcsCanonicalize(unsigned);
|
|
@@ -2970,9 +2981,9 @@ function encodeSignatureValue(value, suite) {
|
|
|
2970
2981
|
decodeSignatureValue(encoded, suite);
|
|
2971
2982
|
return encoded;
|
|
2972
2983
|
}
|
|
2973
|
-
function stripOwnIdAndSignature(
|
|
2984
|
+
function stripOwnIdAndSignature(object4, descriptor) {
|
|
2974
2985
|
const output = /* @__PURE__ */ Object.create(null);
|
|
2975
|
-
for (const [key, value] of Object.entries(
|
|
2986
|
+
for (const [key, value] of Object.entries(object4)) {
|
|
2976
2987
|
if (key === descriptor.idField || key === "signature") {
|
|
2977
2988
|
continue;
|
|
2978
2989
|
}
|
|
@@ -2981,8 +2992,8 @@ function stripOwnIdAndSignature(object3, descriptor) {
|
|
|
2981
2992
|
return output;
|
|
2982
2993
|
}
|
|
2983
2994
|
function descriptorForUnsigned(input) {
|
|
2984
|
-
const
|
|
2985
|
-
return descriptorForSchema(requiredString2(
|
|
2995
|
+
const object4 = expectJsonObject(normalizeJson(input), "$");
|
|
2996
|
+
return descriptorForSchema(requiredString2(object4, "schema", "$"));
|
|
2986
2997
|
}
|
|
2987
2998
|
function descriptorForSchema(schema) {
|
|
2988
2999
|
if (schema === POLICY_SCHEMA) return DESCRIPTORS.Policy;
|
|
@@ -2996,28 +3007,28 @@ function expectJsonObject(input, path) {
|
|
|
2996
3007
|
}
|
|
2997
3008
|
return input;
|
|
2998
3009
|
}
|
|
2999
|
-
function assertExactKeys2(
|
|
3010
|
+
function assertExactKeys2(object4, allowed, path) {
|
|
3000
3011
|
const allowedSet = new Set(allowed);
|
|
3001
|
-
for (const key of Object.keys(
|
|
3012
|
+
for (const key of Object.keys(object4)) {
|
|
3002
3013
|
if (!allowedSet.has(key)) {
|
|
3003
3014
|
throw new SignedObjectSchemaError(`${path} has unknown field ${key}`);
|
|
3004
3015
|
}
|
|
3005
3016
|
}
|
|
3006
3017
|
}
|
|
3007
|
-
function requiredValue2(
|
|
3008
|
-
if (!hasOwn2(
|
|
3018
|
+
function requiredValue2(object4, key, path) {
|
|
3019
|
+
if (!hasOwn2(object4, key)) {
|
|
3009
3020
|
throw new SignedObjectSchemaError(`${path}.${key} is required`);
|
|
3010
3021
|
}
|
|
3011
|
-
return
|
|
3022
|
+
return object4[key];
|
|
3012
3023
|
}
|
|
3013
|
-
function requiredString2(
|
|
3014
|
-
return requireStringType(requiredValue2(
|
|
3024
|
+
function requiredString2(object4, key, path) {
|
|
3025
|
+
return requireStringType(requiredValue2(object4, key, path), `${path}.${key}`);
|
|
3015
3026
|
}
|
|
3016
|
-
function optionalString(
|
|
3017
|
-
if (!hasOwn2(
|
|
3027
|
+
function optionalString(object4, key, path) {
|
|
3028
|
+
if (!hasOwn2(object4, key)) {
|
|
3018
3029
|
return;
|
|
3019
3030
|
}
|
|
3020
|
-
requireStringType(requiredValue2(
|
|
3031
|
+
requireStringType(requiredValue2(object4, key, path), `${path}.${key}`);
|
|
3021
3032
|
}
|
|
3022
3033
|
function requireStringType(value, path) {
|
|
3023
3034
|
if (typeof value !== "string" || value.length === 0) {
|
|
@@ -3025,16 +3036,16 @@ function requireStringType(value, path) {
|
|
|
3025
3036
|
}
|
|
3026
3037
|
return value;
|
|
3027
3038
|
}
|
|
3028
|
-
function optionalBoolean(
|
|
3029
|
-
if (!hasOwn2(
|
|
3039
|
+
function optionalBoolean(object4, key, path) {
|
|
3040
|
+
if (!hasOwn2(object4, key)) {
|
|
3030
3041
|
return;
|
|
3031
3042
|
}
|
|
3032
|
-
if (typeof requiredValue2(
|
|
3043
|
+
if (typeof requiredValue2(object4, key, path) !== "boolean") {
|
|
3033
3044
|
throw new SignedObjectSchemaError(`${path}.${key} must be a boolean`);
|
|
3034
3045
|
}
|
|
3035
3046
|
}
|
|
3036
|
-
function requiredInteger(
|
|
3037
|
-
const value = requiredValue2(
|
|
3047
|
+
function requiredInteger(object4, key, path, minimum) {
|
|
3048
|
+
const value = requiredValue2(object4, key, path);
|
|
3038
3049
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum) {
|
|
3039
3050
|
throw new SignedObjectSchemaError(
|
|
3040
3051
|
`${path}.${key} must be an integer >= ${minimum}`
|
|
@@ -3042,8 +3053,8 @@ function requiredInteger(object3, key, path, minimum) {
|
|
|
3042
3053
|
}
|
|
3043
3054
|
return value;
|
|
3044
3055
|
}
|
|
3045
|
-
function requiredArray2(
|
|
3046
|
-
const value = requiredValue2(
|
|
3056
|
+
function requiredArray2(object4, key, path, minimum) {
|
|
3057
|
+
const value = requiredValue2(object4, key, path);
|
|
3047
3058
|
if (!Array.isArray(value) || value.length < minimum) {
|
|
3048
3059
|
throw new SignedObjectSchemaError(
|
|
3049
3060
|
`${path}.${key} must be an array with at least ${minimum} item(s)`
|
|
@@ -3051,22 +3062,22 @@ function requiredArray2(object3, key, path, minimum) {
|
|
|
3051
3062
|
}
|
|
3052
3063
|
return value;
|
|
3053
3064
|
}
|
|
3054
|
-
function requiredStringArray(
|
|
3055
|
-
const values = requiredArray2(
|
|
3065
|
+
function requiredStringArray(object4, key, path, check) {
|
|
3066
|
+
const values = requiredArray2(object4, key, path, 0);
|
|
3056
3067
|
for (let index = 0; index < values.length; index++) {
|
|
3057
3068
|
const itemPath = `${path}.${key}[${index}]`;
|
|
3058
3069
|
const value = requireStringType(values[index], itemPath);
|
|
3059
3070
|
check?.(value, itemPath);
|
|
3060
3071
|
}
|
|
3061
3072
|
}
|
|
3062
|
-
function requiredDateString(
|
|
3063
|
-
assertRfc3339(requiredString2(
|
|
3073
|
+
function requiredDateString(object4, key, path) {
|
|
3074
|
+
assertRfc3339(requiredString2(object4, key, path), `${path}.${key}`);
|
|
3064
3075
|
}
|
|
3065
|
-
function optionalDateString(
|
|
3066
|
-
if (!hasOwn2(
|
|
3076
|
+
function optionalDateString(object4, key, path) {
|
|
3077
|
+
if (!hasOwn2(object4, key)) {
|
|
3067
3078
|
return;
|
|
3068
3079
|
}
|
|
3069
|
-
assertRfc3339(requiredString2(
|
|
3080
|
+
assertRfc3339(requiredString2(object4, key, path), `${path}.${key}`);
|
|
3070
3081
|
}
|
|
3071
3082
|
function assertRfc3339(value, path) {
|
|
3072
3083
|
const match = value.match(
|
|
@@ -3098,8 +3109,8 @@ function expectOneOf(actual, allowed, path) {
|
|
|
3098
3109
|
}
|
|
3099
3110
|
throw new SignedObjectSchemaError(`${path} has unsupported value ${actual}`);
|
|
3100
3111
|
}
|
|
3101
|
-
function hasOwn2(
|
|
3102
|
-
return objectHasOwn3(
|
|
3112
|
+
function hasOwn2(object4, key) {
|
|
3113
|
+
return objectHasOwn3(object4, key);
|
|
3103
3114
|
}
|
|
3104
3115
|
function assertSupportedSignatureSuite(suite) {
|
|
3105
3116
|
if (suite !== ED25519_JCS_SIGNATURE_SUITE && suite !== EIP191_JCS_SIGNATURE_SUITE) {
|
|
@@ -3326,9 +3337,9 @@ function expectObject2(input, path, code3) {
|
|
|
3326
3337
|
);
|
|
3327
3338
|
}
|
|
3328
3339
|
}
|
|
3329
|
-
function assertExactKeys3(
|
|
3340
|
+
function assertExactKeys3(object4, allowed, path) {
|
|
3330
3341
|
const allowedSet = new Set(allowed);
|
|
3331
|
-
for (const key of Object.keys(
|
|
3342
|
+
for (const key of Object.keys(object4)) {
|
|
3332
3343
|
if (!allowedSet.has(key)) {
|
|
3333
3344
|
throw new PolicyAuthoringError(
|
|
3334
3345
|
"policy-authoring-unknown-key",
|
|
@@ -3337,17 +3348,17 @@ function assertExactKeys3(object3, allowed, path) {
|
|
|
3337
3348
|
}
|
|
3338
3349
|
}
|
|
3339
3350
|
}
|
|
3340
|
-
function requiredValue3(
|
|
3341
|
-
if (!hasOwn3(
|
|
3351
|
+
function requiredValue3(object4, key, path) {
|
|
3352
|
+
if (!hasOwn3(object4, key)) {
|
|
3342
3353
|
throw new PolicyAuthoringError(
|
|
3343
3354
|
"policy-authoring-malformed",
|
|
3344
3355
|
`${path}.${key} is required`
|
|
3345
3356
|
);
|
|
3346
3357
|
}
|
|
3347
|
-
return
|
|
3358
|
+
return object4[key];
|
|
3348
3359
|
}
|
|
3349
|
-
function requiredString3(
|
|
3350
|
-
const value = requiredValue3(
|
|
3360
|
+
function requiredString3(object4, key, path) {
|
|
3361
|
+
const value = requiredValue3(object4, key, path);
|
|
3351
3362
|
if (typeof value !== "string" || value.length === 0) {
|
|
3352
3363
|
throw new PolicyAuthoringError(
|
|
3353
3364
|
"policy-authoring-malformed",
|
|
@@ -3356,11 +3367,11 @@ function requiredString3(object3, key, path) {
|
|
|
3356
3367
|
}
|
|
3357
3368
|
return value;
|
|
3358
3369
|
}
|
|
3359
|
-
function fieldString(
|
|
3360
|
-
if (!hasOwn3(
|
|
3370
|
+
function fieldString(object4, key, path, code3) {
|
|
3371
|
+
if (!hasOwn3(object4, key)) {
|
|
3361
3372
|
throw new PolicyAuthoringError(code3, `${path}.${key} is required`);
|
|
3362
3373
|
}
|
|
3363
|
-
const value =
|
|
3374
|
+
const value = object4[key];
|
|
3364
3375
|
if (typeof value !== "string" || value.length === 0) {
|
|
3365
3376
|
throw new PolicyAuthoringError(
|
|
3366
3377
|
code3,
|
|
@@ -3398,8 +3409,8 @@ function parseStrictRfc3339(value, path) {
|
|
|
3398
3409
|
var objectHasOwn4 = Object.hasOwn ?? Object.prototype.hasOwnProperty.call.bind(
|
|
3399
3410
|
Object.prototype.hasOwnProperty
|
|
3400
3411
|
);
|
|
3401
|
-
function hasOwn3(
|
|
3402
|
-
return objectHasOwn4(
|
|
3412
|
+
function hasOwn3(object4, key) {
|
|
3413
|
+
return objectHasOwn4(object4, key);
|
|
3403
3414
|
}
|
|
3404
3415
|
|
|
3405
3416
|
// src/policy/unified.ts
|
|
@@ -3426,9 +3437,11 @@ var MAX_LEGACY_ENVELOPE_EXPIRES_AT = "2026-12-29T00:00:00Z";
|
|
|
3426
3437
|
var LAST_V2_READ_AT = "2027-01-05T00:00:00Z";
|
|
3427
3438
|
async function requestPolicyChallengeV3(input) {
|
|
3428
3439
|
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3429
|
-
const
|
|
3440
|
+
const policyRuntimePath = policyRuntimeBasePath(input.policyRuntimePath);
|
|
3441
|
+
const response = await fetchFn(new URL(`${policyRuntimePath}/challenges`, input.nodeOrigin), {
|
|
3430
3442
|
method: "POST",
|
|
3431
3443
|
redirect: "error",
|
|
3444
|
+
signal: input.signal,
|
|
3432
3445
|
headers: { accept: "application/json", "content-type": "application/json" },
|
|
3433
3446
|
body: JSON.stringify({ policyCid: input.policyCid, recipientDid: input.recipientDid, requestedCapabilities: input.requestedCapabilities.map(normalizeUnifiedPolicyCapability) })
|
|
3434
3447
|
});
|
|
@@ -3438,6 +3451,13 @@ async function requestPolicyChallengeV3(input) {
|
|
|
3438
3451
|
throw new Error("policy challenge binding is invalid");
|
|
3439
3452
|
return value;
|
|
3440
3453
|
}
|
|
3454
|
+
function policyRuntimeBasePath(value) {
|
|
3455
|
+
const path = value ?? "/policy/v3";
|
|
3456
|
+
if (!/^\/policy(?:\/[a-z0-9-]+)*$/.test(path)) {
|
|
3457
|
+
throw new Error("policy runtime path is invalid");
|
|
3458
|
+
}
|
|
3459
|
+
return path;
|
|
3460
|
+
}
|
|
3441
3461
|
function compactAttenuationForPolicyCapabilities(input) {
|
|
3442
3462
|
const attenuation = {};
|
|
3443
3463
|
for (const raw of input) {
|
|
@@ -3483,7 +3503,7 @@ function verifyPolicyRootStatusCheckpointV3(input) {
|
|
|
3483
3503
|
}
|
|
3484
3504
|
async function getPolicyRootStatusV3(input) {
|
|
3485
3505
|
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3486
|
-
const response = await fetchFn(new URL(`/
|
|
3506
|
+
const response = await fetchFn(new URL(`/policy/v3/status/${encodeURIComponent(input.rootCid)}`, input.nodeOrigin), {
|
|
3487
3507
|
method: "GET",
|
|
3488
3508
|
redirect: "error",
|
|
3489
3509
|
headers: { accept: "application/json" }
|
|
@@ -3542,7 +3562,7 @@ async function renewPolicyRootStatusV3(input) {
|
|
|
3542
3562
|
};
|
|
3543
3563
|
const signature = await input.sign(sha2564(new TextEncoder().encode(ROOT_STATUS_RENEWAL_V1_DOMAIN + jcsCanonicalize(unsigned))));
|
|
3544
3564
|
if (signature.length !== 64) throw new Error("policy root renewal signature must be Ed25519");
|
|
3545
|
-
const response = await fetchFn(new URL("/
|
|
3565
|
+
const response = await fetchFn(new URL("/policy/v3/status", input.nodeOrigin), {
|
|
3546
3566
|
method: "POST",
|
|
3547
3567
|
redirect: "error",
|
|
3548
3568
|
headers: { accept: "application/json", "content-type": "application/json" },
|
|
@@ -3598,6 +3618,11 @@ var POLICY_SESSION_FACT_KEYS = [
|
|
|
3598
3618
|
"issuanceAuditDigestHex",
|
|
3599
3619
|
"remainingRedelegationDepth"
|
|
3600
3620
|
];
|
|
3621
|
+
var POLICY_SESSION_V4_AUDIT_FACT_KEYS = [
|
|
3622
|
+
"credentialIdAuditDigestHex",
|
|
3623
|
+
"presentationJtiAuditDigestHex"
|
|
3624
|
+
];
|
|
3625
|
+
var LOWER_SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
3601
3626
|
var KV_ACTIONS = /* @__PURE__ */ new Set([
|
|
3602
3627
|
"tinycloud.kv/get",
|
|
3603
3628
|
"tinycloud.kv/list",
|
|
@@ -3792,6 +3817,133 @@ function contentSourceDigestHex(input) {
|
|
|
3792
3817
|
)
|
|
3793
3818
|
);
|
|
3794
3819
|
}
|
|
3820
|
+
function exactObject(value, keys, label) {
|
|
3821
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
3822
|
+
throw new Error(`${label} is invalid`);
|
|
3823
|
+
}
|
|
3824
|
+
const record2 = value;
|
|
3825
|
+
if (Object.keys(record2).length !== keys.length || keys.some((key) => !Object.prototype.hasOwnProperty.call(record2, key))) {
|
|
3826
|
+
throw new Error(`${label} is invalid`);
|
|
3827
|
+
}
|
|
3828
|
+
return record2;
|
|
3829
|
+
}
|
|
3830
|
+
function didKeyEd25519PublicKey(value) {
|
|
3831
|
+
const principal = value.split("#", 1)[0];
|
|
3832
|
+
if (!principal.startsWith("did:key:")) {
|
|
3833
|
+
throw new Error("policy enforcer binding signer is invalid");
|
|
3834
|
+
}
|
|
3835
|
+
const material = base58btc.decode(principal.slice("did:key:".length));
|
|
3836
|
+
if (material.length !== 34 || material[0] !== 237 || material[1] !== 1) {
|
|
3837
|
+
throw new Error("policy enforcer binding signer is invalid");
|
|
3838
|
+
}
|
|
3839
|
+
return material.slice(2);
|
|
3840
|
+
}
|
|
3841
|
+
async function registerPolicyV3(input) {
|
|
3842
|
+
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3843
|
+
const rootExpiresAt = new Date(input.rootExpiresAt);
|
|
3844
|
+
const canonicalRootExpiresAt = new Date(
|
|
3845
|
+
Math.floor(rootExpiresAt.getTime() / 1e3) * 1e3
|
|
3846
|
+
).toISOString().replace(/\.000Z$/, "Z");
|
|
3847
|
+
if (!Number.isFinite(rootExpiresAt.getTime()) || canonicalRootExpiresAt !== input.rootExpiresAt || rootExpiresAt.getTime() <= Date.now()) {
|
|
3848
|
+
throw new Error("policy root expiry is invalid");
|
|
3849
|
+
}
|
|
3850
|
+
const bindingResponse = await fetchFn(
|
|
3851
|
+
new URL("/policy/v3/enforcer-bindings", input.nodeOrigin),
|
|
3852
|
+
{
|
|
3853
|
+
method: "POST",
|
|
3854
|
+
redirect: "error",
|
|
3855
|
+
signal: input.signal,
|
|
3856
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
3857
|
+
body: JSON.stringify({
|
|
3858
|
+
rootExpiresAt: input.rootExpiresAt,
|
|
3859
|
+
enforcerDid: input.enforcerDid
|
|
3860
|
+
})
|
|
3861
|
+
}
|
|
3862
|
+
);
|
|
3863
|
+
if (!bindingResponse.ok) {
|
|
3864
|
+
throw new Error(`policy enforcer binding rejected (${bindingResponse.status})`);
|
|
3865
|
+
}
|
|
3866
|
+
const binding = exactObject(
|
|
3867
|
+
await bindingResponse.json(),
|
|
3868
|
+
[
|
|
3869
|
+
"schema",
|
|
3870
|
+
"enforcerDid",
|
|
3871
|
+
"nodeAudience",
|
|
3872
|
+
"attestationBindingDigestHex",
|
|
3873
|
+
"issuedAt",
|
|
3874
|
+
"expiresAt",
|
|
3875
|
+
"signature"
|
|
3876
|
+
],
|
|
3877
|
+
"policy enforcer binding"
|
|
3878
|
+
);
|
|
3879
|
+
const signature = exactObject(
|
|
3880
|
+
binding.signature,
|
|
3881
|
+
["suite", "signerDid", "value"],
|
|
3882
|
+
"policy enforcer binding signature"
|
|
3883
|
+
);
|
|
3884
|
+
const expectedBindingDigestHex = hex(
|
|
3885
|
+
sha2564(
|
|
3886
|
+
new TextEncoder().encode(
|
|
3887
|
+
jcsCanonicalize({
|
|
3888
|
+
enforcerDid: input.enforcerDid,
|
|
3889
|
+
nodeAudience: input.expectedNodeAudience
|
|
3890
|
+
})
|
|
3891
|
+
)
|
|
3892
|
+
)
|
|
3893
|
+
);
|
|
3894
|
+
if (binding.schema !== ATTESTED_ENFORCER_BINDING_V2_SCHEMA || binding.enforcerDid !== input.enforcerDid || binding.nodeAudience !== input.expectedNodeAudience || binding.attestationBindingDigestHex !== expectedBindingDigestHex || binding.expiresAt !== input.rootExpiresAt || typeof binding.issuedAt !== "string" || Date.parse(binding.issuedAt) > Date.now() || signature.suite !== "Ed25519" || signature.signerDid !== input.expectedNodeAudience || typeof signature.value !== "string") {
|
|
3895
|
+
throw new Error("policy enforcer binding is invalid");
|
|
3896
|
+
}
|
|
3897
|
+
const { signature: _signature, ...unsignedBinding } = binding;
|
|
3898
|
+
const signedBinding = sha2564(
|
|
3899
|
+
new TextEncoder().encode(
|
|
3900
|
+
`xyz.tinycloud.policy/AttestedEnforcerBinding/v2\0${jcsCanonicalize(unsignedBinding)}`
|
|
3901
|
+
)
|
|
3902
|
+
);
|
|
3903
|
+
if (!ed255193.verify(
|
|
3904
|
+
decodeBase64Url2(signature.value),
|
|
3905
|
+
signedBinding,
|
|
3906
|
+
didKeyEd25519PublicKey(input.expectedNodeAudience),
|
|
3907
|
+
{ zip215: false }
|
|
3908
|
+
)) {
|
|
3909
|
+
throw new Error("policy enforcer binding signature is invalid");
|
|
3910
|
+
}
|
|
3911
|
+
const response = await fetchFn(
|
|
3912
|
+
new URL("/policy/v3/policies", input.nodeOrigin),
|
|
3913
|
+
{
|
|
3914
|
+
method: "POST",
|
|
3915
|
+
redirect: "error",
|
|
3916
|
+
signal: input.signal,
|
|
3917
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
3918
|
+
body: JSON.stringify({
|
|
3919
|
+
policyCid: input.policyCid,
|
|
3920
|
+
policy: input.policy,
|
|
3921
|
+
policyRoot: input.policyRoot.authorization.replace(/^Bearer\s+/i, ""),
|
|
3922
|
+
enforcementRoot: input.enforcementRoot.authorization.replace(/^Bearer\s+/i, ""),
|
|
3923
|
+
contentSourceDigestHex: input.contentSourceDigestHex,
|
|
3924
|
+
nativeProjectionHashHex: input.nativeProjectionHashHex,
|
|
3925
|
+
attestedEnforcerBinding: binding
|
|
3926
|
+
})
|
|
3927
|
+
}
|
|
3928
|
+
);
|
|
3929
|
+
if (!response.ok) {
|
|
3930
|
+
throw new Error(`policy registration rejected (${response.status})`);
|
|
3931
|
+
}
|
|
3932
|
+
const receipt = exactObject(
|
|
3933
|
+
await response.json(),
|
|
3934
|
+
["policyCid", "policyRootCid", "enforcementRootCid"],
|
|
3935
|
+
"policy registration response"
|
|
3936
|
+
);
|
|
3937
|
+
if (receipt.policyCid !== input.policyCid || receipt.policyRootCid !== input.policyRoot.cid || receipt.enforcementRootCid !== input.enforcementRoot.cid) {
|
|
3938
|
+
throw new Error("policy registration response binding is invalid");
|
|
3939
|
+
}
|
|
3940
|
+
return {
|
|
3941
|
+
policyCid: input.policyCid,
|
|
3942
|
+
policyRootCid: input.policyRoot.cid,
|
|
3943
|
+
enforcementRootCid: input.enforcementRoot.cid,
|
|
3944
|
+
attestedEnforcerBinding: binding
|
|
3945
|
+
};
|
|
3946
|
+
}
|
|
3795
3947
|
function policyDigestHex(policy) {
|
|
3796
3948
|
const canonicalPolicy = {
|
|
3797
3949
|
...policy,
|
|
@@ -3990,9 +4142,13 @@ function parsePolicySessionUcan(authorization, expectedProofs) {
|
|
|
3990
4142
|
if (expectedProofs !== void 0 && (parsed.payload.prf[0] !== expectedProofs[0] || parsed.payload.prf[1] !== expectedProofs[1]))
|
|
3991
4143
|
throw new Error("policy session UCAN ordered proofs do not match");
|
|
3992
4144
|
const factKeys = Object.keys(fact);
|
|
3993
|
-
|
|
4145
|
+
const hasLegacyFactShape = factKeys.length === POLICY_SESSION_FACT_KEYS.length && factKeys.every((key) => POLICY_SESSION_FACT_KEYS.includes(key)) && POLICY_SESSION_FACT_KEYS.every((key) => key in fact);
|
|
4146
|
+
const hasV4AuditFactShape = factKeys.length === POLICY_SESSION_FACT_KEYS.length + POLICY_SESSION_V4_AUDIT_FACT_KEYS.length && factKeys.every((key) => POLICY_SESSION_FACT_KEYS.includes(key) || POLICY_SESSION_V4_AUDIT_FACT_KEYS.includes(key)) && POLICY_SESSION_FACT_KEYS.every((key) => key in fact) && POLICY_SESSION_V4_AUDIT_FACT_KEYS.every((key) => key in fact);
|
|
4147
|
+
if (!hasLegacyFactShape && !hasV4AuditFactShape)
|
|
3994
4148
|
throw new Error("policy session UCAN fact is incomplete");
|
|
3995
|
-
if (
|
|
4149
|
+
if (hasV4AuditFactShape && (!LOWER_SHA256_HEX.test(fact.credentialIdAuditDigestHex) || !LOWER_SHA256_HEX.test(fact.presentationJtiAuditDigestHex)))
|
|
4150
|
+
throw new Error("policy session UCAN v4 audit facts are invalid");
|
|
4151
|
+
if (fact.profile !== POLICY_SESSION_UCAN_V1_PROFILE || typeof fact.remainingRedelegationDepth !== "number" || !Number.isInteger(fact.remainingRedelegationDepth) || fact.remainingRedelegationDepth < 0 || fact.remainingRedelegationDepth > 8 || parsed.payload.exp - parsed.payload.nbf > 60 || parsed.payload.iss.split("#", 1)[0] !== fact.nodeAudience || parsed.payload.aud !== fact.recipientDid)
|
|
3996
4152
|
throw new Error("policy session UCAN fact is invalid");
|
|
3997
4153
|
return {
|
|
3998
4154
|
authorization,
|
|
@@ -4051,6 +4207,8 @@ var POLICY_V2_DOMAIN = "xyz.tinycloud.policy/policy/v2\0";
|
|
|
4051
4207
|
var POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE = "TinyCloudPolicyCredentialRequirement";
|
|
4052
4208
|
var POLICY_PRESENTATION_V3_SCHEMA = "xyz.tinycloud.policy/presentation/v3";
|
|
4053
4209
|
var POLICY_PRESENTATION_V3_DOMAIN = "xyz.tinycloud.policy/Presentation/v3\0";
|
|
4210
|
+
var POLICY_PRESENTATION_V4_SCHEMA = "xyz.tinycloud.policy/presentation/v4";
|
|
4211
|
+
var POLICY_PRESENTATION_V4_DOMAIN = "xyz.tinycloud.policy/Presentation/v4\0";
|
|
4054
4212
|
function record(value, label) {
|
|
4055
4213
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
4056
4214
|
throw new Error(`${label} must be an object`);
|
|
@@ -4225,6 +4383,97 @@ function envelopeFromVerified(verified) {
|
|
|
4225
4383
|
} = verified;
|
|
4226
4384
|
return envelope;
|
|
4227
4385
|
}
|
|
4386
|
+
function issuedCredentialEnvelopeFromVerified(verified) {
|
|
4387
|
+
return envelopeFromVerified(verified);
|
|
4388
|
+
}
|
|
4389
|
+
async function signPolicyCredentialPresentationV4(value, sign) {
|
|
4390
|
+
const item = record(value, "Policy presentation v4");
|
|
4391
|
+
exactKeys2(item, [
|
|
4392
|
+
"schema",
|
|
4393
|
+
"jti",
|
|
4394
|
+
"challengeId",
|
|
4395
|
+
"nonce",
|
|
4396
|
+
"policyCid",
|
|
4397
|
+
"nodeAudience",
|
|
4398
|
+
"holderDid",
|
|
4399
|
+
"subjectDid",
|
|
4400
|
+
"credentialDigest",
|
|
4401
|
+
"requirementDigest",
|
|
4402
|
+
"descriptorDigest",
|
|
4403
|
+
"requestedCapabilities",
|
|
4404
|
+
"issuedAt",
|
|
4405
|
+
"expiresAt"
|
|
4406
|
+
], "Policy presentation v4");
|
|
4407
|
+
if (item.schema !== POLICY_PRESENTATION_V4_SCHEMA || item.holderDid !== item.subjectDid || !Array.isArray(item.requestedCapabilities)) throw new Error("Policy presentation v4 is invalid");
|
|
4408
|
+
const holderDid = nonEmpty(item.holderDid, "Policy presentation v4 holder DID");
|
|
4409
|
+
didKeyBytes(holderDid);
|
|
4410
|
+
const unsigned = Object.freeze({
|
|
4411
|
+
schema: POLICY_PRESENTATION_V4_SCHEMA,
|
|
4412
|
+
jti: nonEmpty(item.jti, "Policy presentation v4 jti"),
|
|
4413
|
+
challengeId: nonEmpty(item.challengeId, "Policy presentation v4 challenge"),
|
|
4414
|
+
nonce: nonEmpty(item.nonce, "Policy presentation v4 nonce"),
|
|
4415
|
+
policyCid: nonEmpty(item.policyCid, "Policy presentation v4 policy CID"),
|
|
4416
|
+
nodeAudience: nonEmpty(item.nodeAudience, "Policy presentation v4 audience"),
|
|
4417
|
+
holderDid,
|
|
4418
|
+
subjectDid: holderDid,
|
|
4419
|
+
credentialDigest: nonEmpty(item.credentialDigest, "Policy presentation v4 credential digest"),
|
|
4420
|
+
requirementDigest: nonEmpty(item.requirementDigest, "Policy presentation v4 requirement digest"),
|
|
4421
|
+
descriptorDigest: nonEmpty(item.descriptorDigest, "Policy presentation v4 descriptor digest"),
|
|
4422
|
+
// The low-level signer preserves the frozen wire bytes exactly. The
|
|
4423
|
+
// policy-aware builder performs capability normalization and containment.
|
|
4424
|
+
requestedCapabilities: Object.freeze(item.requestedCapabilities),
|
|
4425
|
+
issuedAt: date(item.issuedAt, "Policy presentation v4 issuedAt"),
|
|
4426
|
+
expiresAt: date(item.expiresAt, "Policy presentation v4 expiresAt")
|
|
4427
|
+
});
|
|
4428
|
+
const digest4 = sha2565(new TextEncoder().encode(
|
|
4429
|
+
POLICY_PRESENTATION_V4_DOMAIN + jcsCanonicalize(unsigned)
|
|
4430
|
+
));
|
|
4431
|
+
const signature = await sign(digest4);
|
|
4432
|
+
if (signature.length !== 64 || !ed255194.verify(signature, digest4, didKeyBytes(holderDid))) throw new Error("policy presentation signature is invalid");
|
|
4433
|
+
return Object.freeze({
|
|
4434
|
+
...unsigned,
|
|
4435
|
+
signature: Object.freeze({
|
|
4436
|
+
suite: "Ed25519",
|
|
4437
|
+
signerDid: holderDid,
|
|
4438
|
+
value: encodeBase64Url(signature)
|
|
4439
|
+
})
|
|
4440
|
+
});
|
|
4441
|
+
}
|
|
4442
|
+
async function buildPolicyCredentialPresentationV4(input) {
|
|
4443
|
+
const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
|
|
4444
|
+
const requirement = validateCredentialRequirement(input.requirement);
|
|
4445
|
+
const commitment = policy.credentialRequirement;
|
|
4446
|
+
const requirementDigest = await credentialRequirementDigest(requirement);
|
|
4447
|
+
const credentialDigest = await sha256Base64Url(input.credential.credential);
|
|
4448
|
+
if (requirementDigest !== commitment.requirementDigest || input.credential.descriptorDigest !== commitment.descriptorDigest || input.credential.credentialDigest !== credentialDigest || input.credential.issuerDid !== commitment.issuerDid || input.credential.issuerKid !== commitment.issuerKid || jcsCanonicalize(input.credential.profile) !== jcsCanonicalize(commitment.profile) || jcsCanonicalize(input.credential.credentialType) !== jcsCanonicalize(commitment.credentialType)) throw new Error("credential does not match the signed Policy/v2 requirement");
|
|
4449
|
+
const requestedCapabilities = input.requestedCapabilities.map(normalizeUnifiedPolicyCapability);
|
|
4450
|
+
if (requestedCapabilities.length === 0 || requestedCapabilities.some((requested) => !policy.capabilityCeiling.some((authority) => unifiedPolicyCapabilityContains(authority, requested)))) throw new Error("requested capabilities exceed the signed Policy/v2 ceiling");
|
|
4451
|
+
const holderDid = input.credential.holderDid;
|
|
4452
|
+
if (input.challenge.policyCid !== input.policyCid || input.challenge.recipientDid !== holderDid || input.credential.subjectDid !== holderDid || typeof input.challenge.nodeAudience !== "string" || input.challenge.nodeAudience.length === 0 || typeof input.challenge.expiresAt !== "string" || !Number.isFinite(Date.parse(input.challenge.expiresAt))) throw new Error("policy challenge binding is invalid");
|
|
4453
|
+
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
4454
|
+
const expiresAtMs = Math.min(
|
|
4455
|
+
now.getTime() + 6e4,
|
|
4456
|
+
Date.parse(input.challenge.expiresAt),
|
|
4457
|
+
Date.parse(input.credential.expiresAt)
|
|
4458
|
+
);
|
|
4459
|
+
if (expiresAtMs <= now.getTime()) throw new Error("policy challenge is expired");
|
|
4460
|
+
return signPolicyCredentialPresentationV4(Object.freeze({
|
|
4461
|
+
schema: POLICY_PRESENTATION_V4_SCHEMA,
|
|
4462
|
+
jti: input.jti ?? encodeBase64Url(crypto.getRandomValues(new Uint8Array(16))),
|
|
4463
|
+
challengeId: input.challenge.challengeId,
|
|
4464
|
+
nonce: input.challenge.nonce,
|
|
4465
|
+
policyCid: input.policyCid,
|
|
4466
|
+
nodeAudience: input.challenge.nodeAudience,
|
|
4467
|
+
holderDid,
|
|
4468
|
+
subjectDid: holderDid,
|
|
4469
|
+
credentialDigest,
|
|
4470
|
+
requirementDigest,
|
|
4471
|
+
descriptorDigest: input.credential.descriptorDigest,
|
|
4472
|
+
requestedCapabilities: Object.freeze(requestedCapabilities),
|
|
4473
|
+
issuedAt: rfc3339Seconds(now),
|
|
4474
|
+
expiresAt: rfc3339Seconds(new Date(expiresAtMs))
|
|
4475
|
+
}), input.sign);
|
|
4476
|
+
}
|
|
4228
4477
|
async function buildPolicyCredentialPresentationV3(input) {
|
|
4229
4478
|
const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
|
|
4230
4479
|
const requirement = validateCredentialRequirement(input.requirement);
|
|
@@ -4311,10 +4560,12 @@ async function admitPolicyCredentialV3(input) {
|
|
|
4311
4560
|
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
4312
4561
|
const challenge = await requestPolicyChallengeV3({
|
|
4313
4562
|
nodeOrigin,
|
|
4563
|
+
policyRuntimePath: input.policyRuntimePath,
|
|
4314
4564
|
policyCid: input.policyCid,
|
|
4315
4565
|
recipientDid: input.credential.holderDid,
|
|
4316
4566
|
requestedCapabilities: input.requestedCapabilities,
|
|
4317
|
-
fetch: fetchFn
|
|
4567
|
+
fetch: fetchFn,
|
|
4568
|
+
signal: input.signal
|
|
4318
4569
|
});
|
|
4319
4570
|
const presentation = await buildPolicyCredentialPresentationV3({
|
|
4320
4571
|
...input,
|
|
@@ -4322,10 +4573,11 @@ async function admitPolicyCredentialV3(input) {
|
|
|
4322
4573
|
challenge
|
|
4323
4574
|
});
|
|
4324
4575
|
const response = await fetchFn(
|
|
4325
|
-
new URL(
|
|
4576
|
+
new URL(`${policyRuntimeBasePath2(input.policyRuntimePath)}/delegations`, nodeOrigin),
|
|
4326
4577
|
{
|
|
4327
4578
|
method: "POST",
|
|
4328
4579
|
redirect: "error",
|
|
4580
|
+
signal: input.signal,
|
|
4329
4581
|
headers: { accept: "application/json", "content-type": "application/json" },
|
|
4330
4582
|
body: JSON.stringify({
|
|
4331
4583
|
policyCid: input.policyCid,
|
|
@@ -4354,8 +4606,74 @@ async function admitPolicyCredentialV3(input) {
|
|
|
4354
4606
|
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)) {
|
|
4355
4607
|
throw new Error("policy delegation signed binding is invalid");
|
|
4356
4608
|
}
|
|
4609
|
+
input.signal?.throwIfAborted();
|
|
4357
4610
|
return Object.freeze({ challenge, presentation, session });
|
|
4358
4611
|
}
|
|
4612
|
+
function validatePolicyCredentialAdmissionV4Authority(session, expected) {
|
|
4613
|
+
if (session.iss.split("#", 1)[0] !== expected.nodeAudience || session.fact.nodeAudience !== expected.nodeAudience || session.fact.enforcerDid !== expected.enforcerDid) throw new Error("policy delegation authority binding is invalid");
|
|
4614
|
+
}
|
|
4615
|
+
async function admitPolicyCredentialV4(input) {
|
|
4616
|
+
const policy = validateUnifiedPolicyV2(input.policy, input.policyCid);
|
|
4617
|
+
const nodeOrigin = policyNodeOrigin(input.nodeOrigin);
|
|
4618
|
+
const fetchFn = input.fetch ?? globalThis.fetch.bind(globalThis);
|
|
4619
|
+
const challenge = await requestPolicyChallengeV3({
|
|
4620
|
+
nodeOrigin,
|
|
4621
|
+
policyRuntimePath: input.policyRuntimePath,
|
|
4622
|
+
policyCid: input.policyCid,
|
|
4623
|
+
recipientDid: input.credential.holderDid,
|
|
4624
|
+
requestedCapabilities: input.requestedCapabilities,
|
|
4625
|
+
fetch: fetchFn,
|
|
4626
|
+
signal: input.signal
|
|
4627
|
+
});
|
|
4628
|
+
const presentation = await buildPolicyCredentialPresentationV4({
|
|
4629
|
+
...input,
|
|
4630
|
+
policy,
|
|
4631
|
+
challenge
|
|
4632
|
+
});
|
|
4633
|
+
const response = await fetchFn(new URL(`${policyRuntimeBasePath2(input.policyRuntimePath)}/delegations`, nodeOrigin), {
|
|
4634
|
+
method: "POST",
|
|
4635
|
+
redirect: "error",
|
|
4636
|
+
signal: input.signal,
|
|
4637
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
4638
|
+
body: JSON.stringify({
|
|
4639
|
+
policyCid: input.policyCid,
|
|
4640
|
+
challengeId: challenge.challengeId,
|
|
4641
|
+
nonce: challenge.nonce,
|
|
4642
|
+
requirement: validateCredentialRequirement(input.requirement),
|
|
4643
|
+
credential: envelopeFromVerified(input.credential),
|
|
4644
|
+
presentation
|
|
4645
|
+
})
|
|
4646
|
+
});
|
|
4647
|
+
if (!response.ok) throw new Error(`policy delegation rejected (${response.status})`);
|
|
4648
|
+
const value = record(await response.json(), "policy delegation");
|
|
4649
|
+
if (value.admitted !== true || typeof value.sessionCid !== "string" || typeof value.authorization !== "string") throw new Error("policy delegation response is invalid");
|
|
4650
|
+
const session = parsePolicySessionUcan(value.authorization, [
|
|
4651
|
+
input.policyRootCid,
|
|
4652
|
+
input.enforcementRootCid
|
|
4653
|
+
]);
|
|
4654
|
+
const expectedAttenuation = compactAttenuationForPolicyCapabilities(input.requestedCapabilities);
|
|
4655
|
+
validatePolicyCredentialAdmissionV4Authority(session, {
|
|
4656
|
+
nodeAudience: input.expectedNodeAudience,
|
|
4657
|
+
enforcerDid: input.expectedEnforcerDid
|
|
4658
|
+
});
|
|
4659
|
+
if (session.cid !== value.sessionCid || session.aud !== input.credential.holderDid || session.fact.policyCid !== input.policyCid || session.fact.recipientDid !== input.credential.holderDid || session.fact.policyDelegationCid !== input.policyRootCid || session.fact.enforcementDelegationCid !== input.enforcementRootCid || jcsCanonicalize(session.att) !== jcsCanonicalize(expectedAttenuation)) throw new Error("policy delegation signed binding is invalid");
|
|
4660
|
+
const importResponse = await fetchFn(new URL("/delegate", nodeOrigin), {
|
|
4661
|
+
method: "POST",
|
|
4662
|
+
redirect: "error",
|
|
4663
|
+
signal: input.signal,
|
|
4664
|
+
headers: { Authorization: session.authorization }
|
|
4665
|
+
});
|
|
4666
|
+
if (!importResponse.ok) throw new Error(`delegation import rejected (${importResponse.status})`);
|
|
4667
|
+
input.signal?.throwIfAborted();
|
|
4668
|
+
return Object.freeze({ challenge, presentation, session, delegationImported: true });
|
|
4669
|
+
}
|
|
4670
|
+
function policyRuntimeBasePath2(value) {
|
|
4671
|
+
const path = value ?? "/policy/v3";
|
|
4672
|
+
if (!/^\/policy(?:\/[a-z0-9-]+)*$/.test(path)) {
|
|
4673
|
+
throw new Error("policy runtime path is invalid");
|
|
4674
|
+
}
|
|
4675
|
+
return path;
|
|
4676
|
+
}
|
|
4359
4677
|
|
|
4360
4678
|
// src/requester/index.ts
|
|
4361
4679
|
var REQUESTER_NEAR_EXPIRY_SECONDS = 30;
|
|
@@ -7762,13 +8080,13 @@ function canonicalizeRecapCaveats(caveats) {
|
|
|
7762
8080
|
}
|
|
7763
8081
|
if (Array.isArray(value)) return `[${value.map(canonicalize).join(",")}]`;
|
|
7764
8082
|
if (typeof value === "object") {
|
|
7765
|
-
const
|
|
7766
|
-
const prototype = Object.getPrototypeOf(
|
|
8083
|
+
const object4 = value;
|
|
8084
|
+
const prototype = Object.getPrototypeOf(object4);
|
|
7767
8085
|
if (prototype !== Object.prototype && prototype !== null) {
|
|
7768
8086
|
throw new Error("ReCap caveats must contain only JSON values.");
|
|
7769
8087
|
}
|
|
7770
|
-
return `{${Object.keys(
|
|
7771
|
-
(key) => `${JSON.stringify(key)}:${canonicalize(
|
|
8088
|
+
return `{${Object.keys(object4).sort().map(
|
|
8089
|
+
(key) => `${JSON.stringify(key)}:${canonicalize(object4[key])}`
|
|
7772
8090
|
).join(",")}}`;
|
|
7773
8091
|
}
|
|
7774
8092
|
throw new Error("ReCap caveats must contain only JSON values.");
|
|
@@ -7856,7 +8174,6 @@ import { ed25519 as ed255195 } from "@noble/curves/ed25519";
|
|
|
7856
8174
|
import { blake3 as blake33 } from "@noble/hashes/blake3";
|
|
7857
8175
|
import { sha256 as sha2567 } from "@noble/hashes/sha256";
|
|
7858
8176
|
var ENVELOPE_SCHEMA = "xyz.tinycloud.share/envelope";
|
|
7859
|
-
var INLINE_PREFIX = "tc2:";
|
|
7860
8177
|
var RAW_CID_CODEC = 85;
|
|
7861
8178
|
var BLAKE3_256_CODE = 30;
|
|
7862
8179
|
var DEFAULT_MAX_INLINE_BYTES = 256 * 1024;
|
|
@@ -7903,8 +8220,8 @@ function canonicalJson(value) {
|
|
|
7903
8220
|
}
|
|
7904
8221
|
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
7905
8222
|
if (typeof value === "object") {
|
|
7906
|
-
const
|
|
7907
|
-
return `{${Object.keys(
|
|
8223
|
+
const object4 = value;
|
|
8224
|
+
return `{${Object.keys(object4).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(object4[key])}`).join(",")}}`;
|
|
7908
8225
|
}
|
|
7909
8226
|
throw new ShareEnvelopeError("invalid-envelope", "unsupported JSON value");
|
|
7910
8227
|
}
|
|
@@ -8561,25 +8878,8 @@ function parsePublishedShareLink(link, options2 = {}) {
|
|
|
8561
8878
|
}
|
|
8562
8879
|
const origin2 = normalizeOrigin(url.origin);
|
|
8563
8880
|
if (options2.trustedOrigins !== void 0 && !options2.trustedOrigins.map(normalizeOrigin).includes(origin2)) throw new ShareEnvelopeError("origin-mismatch", "share link origin is not trusted");
|
|
8564
|
-
if (url.
|
|
8565
|
-
if (url.
|
|
8566
|
-
let payload;
|
|
8567
|
-
try {
|
|
8568
|
-
const bytes2 = base64UrlToBytes(url.hash.slice("#v=2&p=".length));
|
|
8569
|
-
if (bytes2.length > (options2.maxInlineBytes ?? DEFAULT_MAX_INLINE_BYTES) * 2) throw new Error("inline payload too large");
|
|
8570
|
-
const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes2));
|
|
8571
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("inline payload shape");
|
|
8572
|
-
payload = value;
|
|
8573
|
-
} catch (error) {
|
|
8574
|
-
throw new ShareEnvelopeError("invalid-link", error instanceof Error ? error.message : "invalid inline payload");
|
|
8575
|
-
}
|
|
8576
|
-
if (payload.v !== 2 || typeof payload.c !== "string" || typeof payload.cid !== "string" || payload.k !== void 0 && typeof payload.k !== "string" || Object.keys(payload).some((key3) => !["v", "c", "cid", "k"].includes(key3))) throw new ShareEnvelopeError("invalid-link", "inline payload fields are invalid");
|
|
8577
|
-
const sealed = base64UrlToBytes(payload.c);
|
|
8578
|
-
if (!verifyPublishedShareCid(payload.cid, sealed)) throw new ShareEnvelopeError("cid-mismatch", "inline payload CID does not match");
|
|
8579
|
-
const key2 = payload.k === void 0 ? void 0 : base64UrlToBytes(payload.k);
|
|
8580
|
-
if (key2 !== void 0 && key2.length !== 32) throw new ShareEnvelopeError("invalid-link", "inline payload key is invalid");
|
|
8581
|
-
return { origin: origin2, cid: payload.cid, ...key2 === void 0 ? {} : { key: key2 }, sealed };
|
|
8582
|
-
}
|
|
8881
|
+
if (url.username !== "" || url.password !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
|
|
8882
|
+
if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share link is not canonical");
|
|
8583
8883
|
const match = /^\/s\/([a-z2-7]+)$/.exec(url.pathname);
|
|
8584
8884
|
if (match === null || !url.hash.startsWith("#k=")) throw new ShareEnvelopeError("invalid-link", "published share link is invalid");
|
|
8585
8885
|
const key = base64UrlToBytes(url.hash.slice(3));
|
|
@@ -8599,13 +8899,10 @@ function parseShareUrl(link, options2 = {}) {
|
|
|
8599
8899
|
} catch {
|
|
8600
8900
|
throw new ShareEnvelopeError("invalid-link", "share link must be an absolute URL");
|
|
8601
8901
|
}
|
|
8602
|
-
if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share links must not contain query parameters");
|
|
8603
8902
|
const origin2 = normalizeOrigin(url.origin);
|
|
8604
8903
|
if (options2.trustedOrigins !== void 0 && !options2.trustedOrigins.map(normalizeOrigin).includes(origin2)) throw new ShareEnvelopeError("origin-mismatch", "share link origin is not trusted");
|
|
8605
|
-
if (url.pathname === "/
|
|
8606
|
-
|
|
8607
|
-
return { kind: "inline", origin: origin2, cid: published.cid, key: published.key, url: `${url.origin}${url.pathname}`, protocol: "share-envelope-v2" };
|
|
8608
|
-
}
|
|
8904
|
+
if (url.pathname === "/viewer") throw new ShareEnvelopeError("invalid-link", "retired viewer links are not accepted");
|
|
8905
|
+
if (url.search !== "") throw new ShareEnvelopeError("invalid-link", "share links must not contain query parameters");
|
|
8609
8906
|
const match = url.pathname.match(/^\/s\/(i\/)?([^/]+)$/);
|
|
8610
8907
|
if (!match) throw new ShareEnvelopeError("invalid-link", "share link path is invalid");
|
|
8611
8908
|
const fragmentParams = url.hash === "" ? void 0 : new URLSearchParams(url.hash.slice(1));
|
|
@@ -8628,9 +8925,10 @@ function parseShareUrl(link, options2 = {}) {
|
|
|
8628
8925
|
}
|
|
8629
8926
|
function isV2ShareLink(link) {
|
|
8630
8927
|
try {
|
|
8631
|
-
|
|
8928
|
+
const url = new URL(link);
|
|
8929
|
+
return url.search === "" && url.pathname.startsWith("/s/");
|
|
8632
8930
|
} catch {
|
|
8633
|
-
return
|
|
8931
|
+
return false;
|
|
8634
8932
|
}
|
|
8635
8933
|
}
|
|
8636
8934
|
async function encryptShareBytes(bytes2, key) {
|
|
@@ -9086,8 +9384,8 @@ function canonicalJsonForProof(value) {
|
|
|
9086
9384
|
}
|
|
9087
9385
|
if (Array.isArray(value)) return `[${value.map(canonicalJsonForProof).join(",")}]`;
|
|
9088
9386
|
if (typeof value === "object") {
|
|
9089
|
-
const
|
|
9090
|
-
return `{${Object.keys(
|
|
9387
|
+
const object4 = value;
|
|
9388
|
+
return `{${Object.keys(object4).sort().map((key) => `${JSON.stringify(key)}:${canonicalJsonForProof(object4[key])}`).join(",")}}`;
|
|
9091
9389
|
}
|
|
9092
9390
|
throw new ShareAccessError("SHARE_SESSION_INVALID");
|
|
9093
9391
|
}
|
|
@@ -9796,12 +10094,12 @@ var ShareRecipientClient = class {
|
|
|
9796
10094
|
this.assertDetachedProof(request.proof);
|
|
9797
10095
|
}
|
|
9798
10096
|
parseSessionCapability(value) {
|
|
9799
|
-
const
|
|
9800
|
-
if (typeof
|
|
9801
|
-
if (!Array.isArray(
|
|
9802
|
-
const resource = this.requireObject(
|
|
10097
|
+
const object4 = this.requireObject(value, "session capability");
|
|
10098
|
+
if (typeof object4.spaceId !== "string") throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability space is invalid");
|
|
10099
|
+
if (!Array.isArray(object4.actions) || object4.actions.some((action) => !["read", "list", "edit"].includes(action))) throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability actions are invalid");
|
|
10100
|
+
const resource = this.requireObject(object4.resource, "session capability resource");
|
|
9803
10101
|
if (resource.kind !== "exact" && resource.kind !== "prefix" || typeof resource.path !== "string") throw new ShareAccessError("SHARE_SESSION_INVALID", "session capability resource is invalid");
|
|
9804
|
-
return { spaceId:
|
|
10102
|
+
return { spaceId: object4.spaceId, actions: [...new Set(object4.actions)], resource: { kind: resource.kind, path: resource.path } };
|
|
9805
10103
|
}
|
|
9806
10104
|
assertProvidedPolicySession(session, envelope) {
|
|
9807
10105
|
const sessionExpiry = dateValue(session.expiresAt);
|
|
@@ -11441,62 +11739,21 @@ function validateOwnerSharePolicyRegistrationBytes(bytesValue, expected) {
|
|
|
11441
11739
|
return validateOwnerSharePolicyRegistration(value, expected);
|
|
11442
11740
|
}
|
|
11443
11741
|
|
|
11444
|
-
// src/delegations/share-delivery.ts
|
|
11742
|
+
// src/delegations/share-delivery-v3.ts
|
|
11445
11743
|
import { ed25519 as ed255199 } from "@noble/curves/ed25519";
|
|
11446
|
-
|
|
11447
|
-
|
|
11448
|
-
|
|
11449
|
-
|
|
11450
|
-
|
|
11451
|
-
|
|
11452
|
-
return false;
|
|
11453
|
-
}
|
|
11454
|
-
return url.protocol === "https:" && url.origin === value && url.pathname === "/" && url.search === "" && url.hash === "" && url.username === "" && url.password === "";
|
|
11455
|
-
}
|
|
11456
|
-
var AUTHORIZATION_KEYS = [
|
|
11457
|
-
"type",
|
|
11458
|
-
"version",
|
|
11459
|
-
"jti",
|
|
11460
|
-
"shareCid",
|
|
11461
|
-
"shareId",
|
|
11462
|
-
"registrationCid",
|
|
11463
|
-
"delegationCid",
|
|
11464
|
-
"enforcementDelegationCid",
|
|
11465
|
-
"envelopeCid",
|
|
11466
|
-
"policyCid",
|
|
11467
|
-
"nodeAudience",
|
|
11468
|
-
"targetOrigin",
|
|
11469
|
-
"openCredentialsAudience",
|
|
11470
|
-
"holder",
|
|
11471
|
-
"recipientMatcher",
|
|
11472
|
-
"deliveryEmail",
|
|
11473
|
-
"shareUrl",
|
|
11474
|
-
"returnOrigin",
|
|
11475
|
-
"documentName",
|
|
11476
|
-
"senderDid",
|
|
11477
|
-
"senderTrust",
|
|
11478
|
-
"contentSource",
|
|
11479
|
-
"contentSourceDigest",
|
|
11480
|
-
"shareExpiresAt",
|
|
11481
|
-
"issuedAt",
|
|
11482
|
-
"reportAbuseToken",
|
|
11483
|
-
"actions",
|
|
11484
|
-
"resource",
|
|
11485
|
-
"authorityMaterialHandle",
|
|
11486
|
-
"authorityMaterialDigest",
|
|
11487
|
-
"requestBodyDigest",
|
|
11488
|
-
"idempotencyKey",
|
|
11489
|
-
"expiresAt",
|
|
11490
|
-
"dataAuthority"
|
|
11491
|
-
];
|
|
11492
|
-
function assertObject2(value, keys, label) {
|
|
11744
|
+
import { sha256 as sha25611 } from "@noble/hashes/sha256";
|
|
11745
|
+
var CREDENTIAL_INVITATION_REQUEST_DOMAIN = "xyz.tinycloud.credentials/invitation-request/v1\0";
|
|
11746
|
+
var DELIVERY_ADMISSION_DOMAIN = "xyz.tinycloud.policy/delivery-admission/v0\0";
|
|
11747
|
+
var REQUEST_KEYS = ["schema", "policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "audience", "issuedAt", "expiresAt", "nonce"];
|
|
11748
|
+
var ADMISSION_KEYS = ["schema", "policyId", "ownerDid", "recipient", "resource", "actions", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "senderKeyDid", "audience", "issuedAt", "expiresAt", "nonce", "signature"];
|
|
11749
|
+
function exactObject2(value, keys, label) {
|
|
11493
11750
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
|
|
11494
11751
|
const record2 = value;
|
|
11495
11752
|
if (Object.keys(record2).length !== keys.length || keys.some((key) => !Object.prototype.hasOwnProperty.call(record2, key))) throw new Error(`${label} has unknown or missing fields`);
|
|
11496
11753
|
return record2;
|
|
11497
11754
|
}
|
|
11498
|
-
function
|
|
11499
|
-
if (!/^[A-Za-z0-9_-]+$/.test(value)) throw new Error("share delivery value is not canonical base64url");
|
|
11755
|
+
function decodeBase64Url3(value) {
|
|
11756
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9_-]+$/.test(value)) throw new Error("share delivery value is not canonical base64url");
|
|
11500
11757
|
if (typeof atob === "function") {
|
|
11501
11758
|
const padded = value.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - value.length % 4) % 4);
|
|
11502
11759
|
return Uint8Array.from(atob(padded), (character) => character.charCodeAt(0));
|
|
@@ -11504,42 +11761,45 @@ function fromB642(value) {
|
|
|
11504
11761
|
if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(value, "base64url"));
|
|
11505
11762
|
throw new Error("base64url decoding is unavailable");
|
|
11506
11763
|
}
|
|
11507
|
-
function
|
|
11508
|
-
|
|
11764
|
+
function object3(value, label) {
|
|
11765
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
|
|
11766
|
+
return value;
|
|
11767
|
+
}
|
|
11768
|
+
function didKeyBytes2(did) {
|
|
11769
|
+
if (!did.startsWith("did:key:z") || /[#/?]/.test(did.slice("did:key:".length))) throw new Error("v3 delivery admission signer is invalid");
|
|
11770
|
+
const decoded = base58btc.decode(did.slice("did:key:".length));
|
|
11771
|
+
if (decoded.length !== 34 || decoded[0] !== 237 || decoded[1] !== 1) throw new Error("v3 delivery admission signer is invalid");
|
|
11772
|
+
return decoded.slice(2);
|
|
11773
|
+
}
|
|
11774
|
+
function validateShareDeliveryAuthorizationV3Bytes(bytes2, expected) {
|
|
11775
|
+
let parsed;
|
|
11509
11776
|
try {
|
|
11510
|
-
|
|
11777
|
+
parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes2));
|
|
11511
11778
|
} catch {
|
|
11512
|
-
throw new Error("share delivery
|
|
11513
|
-
}
|
|
11514
|
-
const root =
|
|
11515
|
-
const
|
|
11516
|
-
const
|
|
11517
|
-
const
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
|
|
11521
|
-
|
|
11522
|
-
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
if (
|
|
11528
|
-
const
|
|
11529
|
-
const
|
|
11530
|
-
if (
|
|
11531
|
-
const
|
|
11532
|
-
const
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
}
|
|
11536
|
-
function shareDeliveryTrustedKid(publicKey) {
|
|
11537
|
-
if (publicKey.length !== 32) throw new Error("share delivery node key must be a raw 32-byte Ed25519 public key");
|
|
11538
|
-
const prefixed = new Uint8Array(34);
|
|
11539
|
-
prefixed.set([237, 1]);
|
|
11540
|
-
prefixed.set(publicKey, 2);
|
|
11541
|
-
const did = `did:key:${base58btc.encode(prefixed)}`;
|
|
11542
|
-
return `${did}#${did.slice("did:key:".length)}`;
|
|
11779
|
+
throw new Error("v3 share delivery response is not valid UTF-8 JSON");
|
|
11780
|
+
}
|
|
11781
|
+
const root = exactObject2(parsed, ["request", "admission"], "v3 share delivery response");
|
|
11782
|
+
const request = exactObject2(root.request, REQUEST_KEYS, "credential invitation request");
|
|
11783
|
+
const admission = exactObject2(root.admission, ADMISSION_KEYS, "delivery admission");
|
|
11784
|
+
const signature = exactObject2(admission.signature, ["suite", "signerDid", "value"], "delivery admission signature");
|
|
11785
|
+
const envelope = object3(expected.request.envelope, "v3 share envelope");
|
|
11786
|
+
const policy = object3(envelope.policy, "v3 share policy");
|
|
11787
|
+
const requirement = object3(policy.credentialRequirement, "v3 credential requirement");
|
|
11788
|
+
const credentialType = object3(requirement.credentialType, "v3 credential type");
|
|
11789
|
+
const contentSource = object3(envelope.contentSource, "v3 content source");
|
|
11790
|
+
const envelopeSignature = object3(envelope.signature, "v3 envelope signature");
|
|
11791
|
+
const target = object3(envelope.target, "v3 target");
|
|
11792
|
+
const binding = object3(envelope.attestedEnforcerBinding, "v3 attested enforcer binding");
|
|
11793
|
+
const fields = ["policyId", "recipient", "resource", "credentialType", "returnLink", "envelopeRef", "label", "shareExpiresAt", "audience", "issuedAt", "expiresAt", "nonce"];
|
|
11794
|
+
if (request.schema !== "xyz.tinycloud.credentials/invitation-request/v1" || admission.schema !== "xyz.tinycloud.policy/delivery-admission/v0" || fields.some((field) => request[field] !== admission[field]) || request.policyId !== envelope.policyCid || request.recipient !== expected.request.recipientEmail || request.resource !== contentSource.kvResource || request.credentialType !== credentialType.id || request.credentialType !== "opencredentials.email/v1" || request.returnLink !== expected.request.shareUrl || request.envelopeRef !== expected.request.shareCid || request.label !== expected.request.documentName || request.shareExpiresAt !== envelope.expiry || request.audience !== expected.deliveryAudience || request.expiresAt !== expected.request.expiresAt || request.nonce !== expected.request.jti || admission.ownerDid !== envelopeSignature.signerDid || admission.senderKeyDid !== expected.senderKeyDid || canonicalizeSignedObjectUnsigned(admission.actions) !== canonicalizeSignedObjectUnsigned(["tinycloud.kv/get"]) || signature.suite !== "eddsa-ed25519-sha256-jcs-v1" || target.nodeAudience !== binding.nodeAudience || signature.signerDid !== binding.nodeAudience) throw new Error("v3 share delivery authorization is not bound to the submitted request");
|
|
11795
|
+
const issuedAt = Date.parse(String(request.issuedAt));
|
|
11796
|
+
const expiresAt = Date.parse(String(request.expiresAt));
|
|
11797
|
+
if (!Number.isFinite(issuedAt) || !Number.isFinite(expiresAt) || expiresAt <= issuedAt || expiresAt - issuedAt > 15 * 6e4) throw new Error("v3 share delivery authorization time is invalid");
|
|
11798
|
+
const admissionSignature = decodeBase64Url3(signature.value);
|
|
11799
|
+
const { signature: _signature, ...unsignedAdmission } = admission;
|
|
11800
|
+
const digest4 = sha25611(new TextEncoder().encode(`${DELIVERY_ADMISSION_DOMAIN}${canonicalizeSignedObjectUnsigned(unsignedAdmission)}`));
|
|
11801
|
+
if (admissionSignature.length !== 64 || !ed255199.verify(admissionSignature, digest4, didKeyBytes2(String(signature.signerDid)))) throw new Error("v3 share delivery admission signature is invalid");
|
|
11802
|
+
return { request, admission };
|
|
11543
11803
|
}
|
|
11544
11804
|
|
|
11545
11805
|
// src/delegations/sender-share-store.ts
|
|
@@ -11662,9 +11922,6 @@ import {
|
|
|
11662
11922
|
publishPolicyShare,
|
|
11663
11923
|
claimShare,
|
|
11664
11924
|
resumeShareAuthorization,
|
|
11665
|
-
isLegacyShareLink,
|
|
11666
|
-
receiveLegacyShare,
|
|
11667
|
-
migrateShare,
|
|
11668
11925
|
MemoryEncryptedShareHistoryStorage,
|
|
11669
11926
|
EncryptedSenderShareHistory
|
|
11670
11927
|
} from "@tinycloud/share-sdk";
|
|
@@ -11672,8 +11929,7 @@ import {
|
|
|
11672
11929
|
inspectShare,
|
|
11673
11930
|
receiveShare,
|
|
11674
11931
|
ShareReceiveError,
|
|
11675
|
-
toShareErrorInfo
|
|
11676
|
-
verifyBearerEnvelope
|
|
11932
|
+
toShareErrorInfo
|
|
11677
11933
|
} from "@tinycloud/share-sdk";
|
|
11678
11934
|
|
|
11679
11935
|
// src/networkId.ts
|
|
@@ -14302,8 +14558,8 @@ function stableJson(value) {
|
|
|
14302
14558
|
if (Array.isArray(value)) {
|
|
14303
14559
|
return `[${value.map(stableJson).join(",")}]`;
|
|
14304
14560
|
}
|
|
14305
|
-
const
|
|
14306
|
-
return `{${Object.keys(
|
|
14561
|
+
const object4 = value;
|
|
14562
|
+
return `{${Object.keys(object4).sort().map((key) => `${JSON.stringify(key)}:${stableJson(object4[key])}`).join(",")}}`;
|
|
14307
14563
|
}
|
|
14308
14564
|
function indexedDelegationFromRow(row) {
|
|
14309
14565
|
const [
|
|
@@ -14406,6 +14662,7 @@ import {
|
|
|
14406
14662
|
SecretsService,
|
|
14407
14663
|
SECRET_NAME_RE as SECRET_NAME_RE2,
|
|
14408
14664
|
canonicalizeSecretScope,
|
|
14665
|
+
parseSecretCatalogKey,
|
|
14409
14666
|
resolveSecretListPrefix,
|
|
14410
14667
|
resolveSecretPath as resolveSecretPath2,
|
|
14411
14668
|
EncryptionService,
|
|
@@ -15048,6 +15305,236 @@ function createOpenKeyCallbackSigningStrategy(options2) {
|
|
|
15048
15305
|
}
|
|
15049
15306
|
var defaultSignStrategy = { type: "auto-sign" };
|
|
15050
15307
|
|
|
15308
|
+
// src/authorization/manage-key.ts
|
|
15309
|
+
var TINYCLOUD_MANAGE_KEY_SCOPE = "tinycloud:manage-key";
|
|
15310
|
+
var TINYCLOUD_CANONICAL_IDENTITY_CLAIM = "https://tinycloud.xyz/canonical_identity";
|
|
15311
|
+
function requestTinyCloudManageKeyScope(scopes = []) {
|
|
15312
|
+
const requested = typeof scopes === "string" ? scopes.split(/\s+/u) : scopes;
|
|
15313
|
+
return [
|
|
15314
|
+
...new Set([...requested, TINYCLOUD_MANAGE_KEY_SCOPE].filter(Boolean))
|
|
15315
|
+
].sort().join(" ");
|
|
15316
|
+
}
|
|
15317
|
+
function hasTinyCloudManageKeyScope(scopes) {
|
|
15318
|
+
const values = Array.isArray(scopes) ? scopes : typeof scopes === "string" ? scopes.split(/\s+/u) : [];
|
|
15319
|
+
return values.includes(TINYCLOUD_MANAGE_KEY_SCOPE);
|
|
15320
|
+
}
|
|
15321
|
+
var OpenKeyManageKeyError = class extends Error {
|
|
15322
|
+
constructor(code3, message, approvalUrl, retryable = false) {
|
|
15323
|
+
super(message);
|
|
15324
|
+
this.code = code3;
|
|
15325
|
+
this.approvalUrl = approvalUrl;
|
|
15326
|
+
this.retryable = retryable;
|
|
15327
|
+
this.name = "OpenKeyManageKeyError";
|
|
15328
|
+
}
|
|
15329
|
+
};
|
|
15330
|
+
function isRecord2(value) {
|
|
15331
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
15332
|
+
}
|
|
15333
|
+
function rejected(message) {
|
|
15334
|
+
return new OpenKeyManageKeyError("MESSAGE_REJECTED", message);
|
|
15335
|
+
}
|
|
15336
|
+
function identityMismatch(message) {
|
|
15337
|
+
return new OpenKeyManageKeyError("IDENTITY_MISMATCH", message);
|
|
15338
|
+
}
|
|
15339
|
+
function parseCanonicalTinyCloudIdentity(value) {
|
|
15340
|
+
if (!isRecord2(value))
|
|
15341
|
+
throw rejected("Canonical TinyCloud identity is missing");
|
|
15342
|
+
const expectedKeys = [
|
|
15343
|
+
"address",
|
|
15344
|
+
"chainId",
|
|
15345
|
+
"did",
|
|
15346
|
+
"keyId",
|
|
15347
|
+
"spaceId",
|
|
15348
|
+
"version"
|
|
15349
|
+
];
|
|
15350
|
+
const actualKeys = Object.keys(value).sort();
|
|
15351
|
+
if (actualKeys.length !== expectedKeys.length || actualKeys.some((key, index) => key !== expectedKeys[index])) {
|
|
15352
|
+
throw rejected("Canonical TinyCloud identity has an invalid shape");
|
|
15353
|
+
}
|
|
15354
|
+
if (value.version !== "v1")
|
|
15355
|
+
throw rejected("Canonical TinyCloud identity version is unsupported");
|
|
15356
|
+
if (typeof value.keyId !== "string" || value.keyId.trim() === "") {
|
|
15357
|
+
throw rejected("Canonical TinyCloud identity key ID is invalid");
|
|
15358
|
+
}
|
|
15359
|
+
if (typeof value.address !== "string") {
|
|
15360
|
+
throw rejected("Canonical TinyCloud identity address is invalid");
|
|
15361
|
+
}
|
|
15362
|
+
let address;
|
|
15363
|
+
try {
|
|
15364
|
+
address = canonicalizeAddress(value.address);
|
|
15365
|
+
} catch {
|
|
15366
|
+
throw rejected("Canonical TinyCloud identity address is invalid");
|
|
15367
|
+
}
|
|
15368
|
+
if (address !== value.address) {
|
|
15369
|
+
throw rejected(
|
|
15370
|
+
"Canonical TinyCloud identity address must use EIP-55 checksum casing"
|
|
15371
|
+
);
|
|
15372
|
+
}
|
|
15373
|
+
if (typeof value.chainId !== "number" || !Number.isSafeInteger(value.chainId) || value.chainId <= 0) {
|
|
15374
|
+
throw rejected("Canonical TinyCloud identity chain ID is invalid");
|
|
15375
|
+
}
|
|
15376
|
+
const did = pkhDid(address, value.chainId);
|
|
15377
|
+
const spacePrefix = `tinycloud:pkh:eip155:${value.chainId}:${address}:`;
|
|
15378
|
+
if (typeof value.spaceId !== "string" || !value.spaceId.startsWith(spacePrefix)) {
|
|
15379
|
+
throw rejected("Canonical TinyCloud identity space is invalid");
|
|
15380
|
+
}
|
|
15381
|
+
const spaceName = value.spaceId.slice(spacePrefix.length);
|
|
15382
|
+
let spaceId;
|
|
15383
|
+
try {
|
|
15384
|
+
spaceId = makePkhSpaceId(address, value.chainId, spaceName);
|
|
15385
|
+
} catch {
|
|
15386
|
+
throw rejected("Canonical TinyCloud identity space is invalid");
|
|
15387
|
+
}
|
|
15388
|
+
if (value.did !== did || value.spaceId !== spaceId) {
|
|
15389
|
+
throw rejected("Canonical TinyCloud identity does not match its address");
|
|
15390
|
+
}
|
|
15391
|
+
return {
|
|
15392
|
+
version: "v1",
|
|
15393
|
+
keyId: value.keyId,
|
|
15394
|
+
address,
|
|
15395
|
+
chainId: value.chainId,
|
|
15396
|
+
did,
|
|
15397
|
+
spaceId
|
|
15398
|
+
};
|
|
15399
|
+
}
|
|
15400
|
+
function parseCanonicalTinyCloudIdentityClaims(claims) {
|
|
15401
|
+
if (!isRecord2(claims)) throw rejected("OAuth claims are missing");
|
|
15402
|
+
return parseCanonicalTinyCloudIdentity(
|
|
15403
|
+
claims[TINYCLOUD_CANONICAL_IDENTITY_CLAIM]
|
|
15404
|
+
);
|
|
15405
|
+
}
|
|
15406
|
+
function identitiesEqual(left, right) {
|
|
15407
|
+
return left.version === right.version && left.keyId === right.keyId && left.address === right.address && left.chainId === right.chainId && left.did === right.did && left.spaceId === right.spaceId;
|
|
15408
|
+
}
|
|
15409
|
+
function errorForResponse(body, status) {
|
|
15410
|
+
const message = body?.reason ?? body?.error ?? `OpenKey manage-key signing failed with HTTP ${status}`;
|
|
15411
|
+
switch (body?.code ?? body?.error) {
|
|
15412
|
+
case "missing_scope":
|
|
15413
|
+
case "consent_required":
|
|
15414
|
+
return new OpenKeyManageKeyError("CONSENT_REQUIRED", message, body?.approvalUrl);
|
|
15415
|
+
case "client_disabled":
|
|
15416
|
+
case "signing_disabled":
|
|
15417
|
+
case "grant_disabled":
|
|
15418
|
+
return new OpenKeyManageKeyError("GRANT_DISABLED", message, body?.approvalUrl);
|
|
15419
|
+
case "user_exclusive":
|
|
15420
|
+
return new OpenKeyManageKeyError("USER_EXCLUSIVE", message, body?.approvalUrl);
|
|
15421
|
+
case "token_expired":
|
|
15422
|
+
case "token_too_old":
|
|
15423
|
+
case "invalid_token":
|
|
15424
|
+
case "expired_token":
|
|
15425
|
+
return new OpenKeyManageKeyError("TOKEN_EXPIRED", message, body?.approvalUrl);
|
|
15426
|
+
case "canonical_key_unavailable":
|
|
15427
|
+
case "signer_failed":
|
|
15428
|
+
return new OpenKeyManageKeyError(
|
|
15429
|
+
"SIGNER_UNAVAILABLE",
|
|
15430
|
+
"The canonical key signer is temporarily unavailable",
|
|
15431
|
+
void 0,
|
|
15432
|
+
true
|
|
15433
|
+
);
|
|
15434
|
+
default:
|
|
15435
|
+
return new OpenKeyManageKeyError(
|
|
15436
|
+
status === 401 ? "TOKEN_EXPIRED" : "MESSAGE_REJECTED",
|
|
15437
|
+
message,
|
|
15438
|
+
body?.approvalUrl
|
|
15439
|
+
);
|
|
15440
|
+
}
|
|
15441
|
+
}
|
|
15442
|
+
async function resolveToken(token) {
|
|
15443
|
+
const value = typeof token === "function" ? await token() : token;
|
|
15444
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
15445
|
+
throw new OpenKeyManageKeyError(
|
|
15446
|
+
"CONSENT_REQUIRED",
|
|
15447
|
+
"A manage-key OAuth bearer token is required"
|
|
15448
|
+
);
|
|
15449
|
+
}
|
|
15450
|
+
return value;
|
|
15451
|
+
}
|
|
15452
|
+
function normalizeEndpoint(value) {
|
|
15453
|
+
let endpoint;
|
|
15454
|
+
try {
|
|
15455
|
+
endpoint = new URL(value);
|
|
15456
|
+
} catch {
|
|
15457
|
+
throw rejected("OpenKey manage-key signing endpoint must be an absolute URL");
|
|
15458
|
+
}
|
|
15459
|
+
const rawHttpAuthority = value.match(/^http:\/\/([^/?#]*)/iu)?.[1];
|
|
15460
|
+
const loopback = rawHttpAuthority !== void 0 && /^(?:localhost|127\.0\.0\.1|\[::1\])(?::[0-9]+)?$/u.test(rawHttpAuthority);
|
|
15461
|
+
if (endpoint.protocol !== "https:" && !(endpoint.protocol === "http:" && loopback) || endpoint.username !== "" || endpoint.password !== "" || endpoint.search !== "" || endpoint.hash !== "") {
|
|
15462
|
+
throw rejected("OpenKey manage-key signing endpoint must use HTTPS or exact loopback HTTP");
|
|
15463
|
+
}
|
|
15464
|
+
return endpoint.href;
|
|
15465
|
+
}
|
|
15466
|
+
function assertSignInRequest(request, identity3) {
|
|
15467
|
+
if (request.type !== "siwe" || request.purpose !== "sign-in" || request.chainId !== identity3.chainId || request.address !== identity3.address || typeof request.message !== "string" || request.message.length === 0) {
|
|
15468
|
+
throw rejected(
|
|
15469
|
+
"Manage-key signing only permits the canonical sign-in SIWE request"
|
|
15470
|
+
);
|
|
15471
|
+
}
|
|
15472
|
+
}
|
|
15473
|
+
function createOpenKeyManageKeySigningStrategy(options2) {
|
|
15474
|
+
const identity3 = parseCanonicalTinyCloudIdentity(options2.identity);
|
|
15475
|
+
const endpoint = normalizeEndpoint(options2.endpoint);
|
|
15476
|
+
return {
|
|
15477
|
+
type: "callback",
|
|
15478
|
+
openKeyAutoSign: true,
|
|
15479
|
+
handler: async (request) => {
|
|
15480
|
+
assertSignInRequest(request, identity3);
|
|
15481
|
+
const fetchImpl = options2.fetch ?? globalThis.fetch;
|
|
15482
|
+
if (!fetchImpl)
|
|
15483
|
+
throw rejected("OpenKey manage-key signing requires fetch");
|
|
15484
|
+
if (!hasTinyCloudManageKeyScope(options2.scopes)) {
|
|
15485
|
+
throw new OpenKeyManageKeyError(
|
|
15486
|
+
"CONSENT_REQUIRED",
|
|
15487
|
+
"The OAuth bearer token was not granted tinycloud:manage-key"
|
|
15488
|
+
);
|
|
15489
|
+
}
|
|
15490
|
+
const token = await resolveToken(options2.token);
|
|
15491
|
+
const response = await fetchImpl(endpoint, {
|
|
15492
|
+
method: "POST",
|
|
15493
|
+
credentials: "omit",
|
|
15494
|
+
headers: {
|
|
15495
|
+
authorization: `Bearer ${token}`,
|
|
15496
|
+
"content-type": "application/json"
|
|
15497
|
+
},
|
|
15498
|
+
body: JSON.stringify({
|
|
15499
|
+
address: request.address,
|
|
15500
|
+
chainId: request.chainId,
|
|
15501
|
+
message: request.message,
|
|
15502
|
+
type: "siwe"
|
|
15503
|
+
})
|
|
15504
|
+
});
|
|
15505
|
+
let body;
|
|
15506
|
+
try {
|
|
15507
|
+
const value = await response.json();
|
|
15508
|
+
body = isRecord2(value) ? value : void 0;
|
|
15509
|
+
} catch {
|
|
15510
|
+
body = void 0;
|
|
15511
|
+
}
|
|
15512
|
+
if (!response.ok || body?.approved !== true)
|
|
15513
|
+
throw errorForResponse(body, response.status);
|
|
15514
|
+
if (typeof body.signature !== "string")
|
|
15515
|
+
throw rejected("OpenKey manage-key signer did not return a signature");
|
|
15516
|
+
const responseIdentity = parseCanonicalTinyCloudIdentity(
|
|
15517
|
+
body.canonicalIdentity
|
|
15518
|
+
);
|
|
15519
|
+
if (!identitiesEqual(identity3, responseIdentity)) {
|
|
15520
|
+
throw identityMismatch(
|
|
15521
|
+
"OpenKey manage-key signer returned a different canonical identity"
|
|
15522
|
+
);
|
|
15523
|
+
}
|
|
15524
|
+
if (!await verifyEip191MessageSignature(
|
|
15525
|
+
request.message,
|
|
15526
|
+
body.signature,
|
|
15527
|
+
identity3.address
|
|
15528
|
+
)) {
|
|
15529
|
+
throw rejected(
|
|
15530
|
+
"OpenKey manage-key signer returned an invalid signature"
|
|
15531
|
+
);
|
|
15532
|
+
}
|
|
15533
|
+
return { approved: true, signature: body.signature };
|
|
15534
|
+
}
|
|
15535
|
+
};
|
|
15536
|
+
}
|
|
15537
|
+
|
|
15051
15538
|
// src/authorization/spaceCreation.ts
|
|
15052
15539
|
var AutoApproveSpaceCreationHandler = class {
|
|
15053
15540
|
/**
|
|
@@ -17086,6 +17573,7 @@ var DEFAULT_LOCAL_NODE_URL = "http://127.0.0.1:8000";
|
|
|
17086
17573
|
var LOCAL_LOOPBACK_PROBE_TIMEOUT_MS = 250;
|
|
17087
17574
|
var LOCAL_LINK_PROBE_TIMEOUT_MS = 750;
|
|
17088
17575
|
var LOCAL_LINK_HOST_SUFFIX = ".local.tinycloud.link";
|
|
17576
|
+
var OWNER_NODE_BINDING_TIMEOUT_MS = 5e3;
|
|
17089
17577
|
function createInMemoryLocalNodeIdentityStore() {
|
|
17090
17578
|
const pins = /* @__PURE__ */ new Map();
|
|
17091
17579
|
return {
|
|
@@ -17218,21 +17706,80 @@ async function verifyLocationRecord(input) {
|
|
|
17218
17706
|
}
|
|
17219
17707
|
return false;
|
|
17220
17708
|
}
|
|
17221
|
-
async function fetchLocationRecord(registryUrl, subject, fetchFn = globalThis.fetch) {
|
|
17709
|
+
async function fetchLocationRecord(registryUrl, subject, fetchFn = globalThis.fetch, signal) {
|
|
17222
17710
|
const url = `${registryUrl.replace(/\/$/, "")}/v1/locations/${encodeURIComponent(subject)}`;
|
|
17223
|
-
const response = await fetchFn(url
|
|
17711
|
+
const response = await fetchFn(url, {
|
|
17712
|
+
redirect: "error",
|
|
17713
|
+
headers: { accept: "application/json" },
|
|
17714
|
+
...signal === void 0 ? {} : { signal }
|
|
17715
|
+
});
|
|
17224
17716
|
if (response.status === 404) {
|
|
17225
17717
|
return null;
|
|
17226
17718
|
}
|
|
17227
17719
|
if (!response.ok) {
|
|
17228
17720
|
throw new Error(`location registry returned HTTP ${response.status}`);
|
|
17229
17721
|
}
|
|
17230
|
-
const
|
|
17722
|
+
const text2 = await boundedResponseText(response, "location registry", 64 * 1024);
|
|
17723
|
+
let body;
|
|
17724
|
+
try {
|
|
17725
|
+
body = JSON.parse(text2);
|
|
17726
|
+
} catch {
|
|
17727
|
+
throw new LocationRecordValidationError("registry response is not JSON");
|
|
17728
|
+
}
|
|
17231
17729
|
if (body.record === void 0) {
|
|
17232
17730
|
throw new LocationRecordValidationError("registry response missing record");
|
|
17233
17731
|
}
|
|
17234
17732
|
return validateLocationRecord(body.record);
|
|
17235
17733
|
}
|
|
17734
|
+
async function publishLocationRecord(options2) {
|
|
17735
|
+
validateSubject(options2.subject);
|
|
17736
|
+
const registryUrl = exactHttpOrigin(options2.registryUrl, "registry URL");
|
|
17737
|
+
const nodeOrigin = exactHttpOrigin(options2.nodeOrigin, "node origin");
|
|
17738
|
+
const fetchFn = options2.fetch ?? globalThis.fetch.bind(globalThis);
|
|
17739
|
+
const current = await fetchLocationRecord(registryUrl, options2.subject, fetchFn);
|
|
17740
|
+
if (current !== null) {
|
|
17741
|
+
if (current.subject !== options2.subject || !await verifyLocationRecord(current)) {
|
|
17742
|
+
throw new LocationRecordValidationError("existing location record signature is invalid");
|
|
17743
|
+
}
|
|
17744
|
+
if (publishedHttpOrigins(current).includes(nodeOrigin)) {
|
|
17745
|
+
return current;
|
|
17746
|
+
}
|
|
17747
|
+
}
|
|
17748
|
+
const record2 = await signLocationRecord({
|
|
17749
|
+
version: 1,
|
|
17750
|
+
subject: options2.subject,
|
|
17751
|
+
multiaddrs: [...current?.multiaddrs ?? [], httpUrlToMultiaddr(nodeOrigin)],
|
|
17752
|
+
updated_at: (options2.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
17753
|
+
sequence: (current?.sequence ?? -1) + 1
|
|
17754
|
+
}, options2.signer);
|
|
17755
|
+
const response = await fetchFn(
|
|
17756
|
+
`${registryUrl}/v1/locations/${encodeURIComponent(options2.subject)}`,
|
|
17757
|
+
{
|
|
17758
|
+
method: "PUT",
|
|
17759
|
+
redirect: "error",
|
|
17760
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
17761
|
+
body: JSON.stringify(record2)
|
|
17762
|
+
}
|
|
17763
|
+
);
|
|
17764
|
+
if (!response.ok) {
|
|
17765
|
+
throw new Error(`location registry publish returned HTTP ${response.status}`);
|
|
17766
|
+
}
|
|
17767
|
+
const text2 = await boundedResponseText(response, "location registry", 64 * 1024);
|
|
17768
|
+
let body;
|
|
17769
|
+
try {
|
|
17770
|
+
body = JSON.parse(text2);
|
|
17771
|
+
} catch {
|
|
17772
|
+
throw new LocationRecordValidationError("registry publish response is not JSON");
|
|
17773
|
+
}
|
|
17774
|
+
if (body.record === void 0) {
|
|
17775
|
+
throw new LocationRecordValidationError("registry publish response missing record");
|
|
17776
|
+
}
|
|
17777
|
+
const published = validateLocationRecord(body.record);
|
|
17778
|
+
if (canonicalLocationPayload(locationPayloadForRecord(published)) !== canonicalLocationPayload(locationPayloadForRecord(record2)) || published.signature !== record2.signature || !await verifyLocationRecord(published)) {
|
|
17779
|
+
throw new LocationRecordValidationError("registry publish response does not match the signed record");
|
|
17780
|
+
}
|
|
17781
|
+
return published;
|
|
17782
|
+
}
|
|
17236
17783
|
async function resolveCloudLocation(subject, options2 = {}) {
|
|
17237
17784
|
validateSubject(subject);
|
|
17238
17785
|
const verifyRecords = options2.verifyRecords ?? true;
|
|
@@ -17483,6 +18030,123 @@ function multiaddrToHttpUrl(input) {
|
|
|
17483
18030
|
}
|
|
17484
18031
|
return uri;
|
|
17485
18032
|
}
|
|
18033
|
+
function exactHttpOrigin(value, label) {
|
|
18034
|
+
let url;
|
|
18035
|
+
try {
|
|
18036
|
+
url = new URL(value);
|
|
18037
|
+
} catch {
|
|
18038
|
+
throw new LocationRecordValidationError(`${label} must be an absolute URL origin`);
|
|
18039
|
+
}
|
|
18040
|
+
const loopback = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
|
|
18041
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) {
|
|
18042
|
+
throw new LocationRecordValidationError(`${label} must use HTTPS (or loopback HTTP)`);
|
|
18043
|
+
}
|
|
18044
|
+
if (url.username !== "" || url.password !== "" || url.pathname !== "/" || url.search !== "" || url.hash !== "") {
|
|
18045
|
+
throw new LocationRecordValidationError(`${label} must be an origin without credentials, path, query, or fragment`);
|
|
18046
|
+
}
|
|
18047
|
+
return url.origin;
|
|
18048
|
+
}
|
|
18049
|
+
function publishedHttpOrigins(record2) {
|
|
18050
|
+
const origins = [];
|
|
18051
|
+
for (const address of record2.multiaddrs) {
|
|
18052
|
+
try {
|
|
18053
|
+
origins.push(exactHttpOrigin(multiaddrToHttpUrl(address), "published node URL"));
|
|
18054
|
+
} catch {
|
|
18055
|
+
}
|
|
18056
|
+
}
|
|
18057
|
+
return origins;
|
|
18058
|
+
}
|
|
18059
|
+
async function boundedResponseText(response, label, maxBytes) {
|
|
18060
|
+
const declaredLength = response.headers.get("content-length");
|
|
18061
|
+
if (declaredLength !== null && (!/^\d+$/.test(declaredLength) || Number(declaredLength) > maxBytes)) {
|
|
18062
|
+
throw new LocationRecordValidationError(`${label} response is too large`);
|
|
18063
|
+
}
|
|
18064
|
+
if (response.body === null) return "";
|
|
18065
|
+
const reader = response.body.getReader();
|
|
18066
|
+
const chunks = [];
|
|
18067
|
+
let total = 0;
|
|
18068
|
+
try {
|
|
18069
|
+
while (true) {
|
|
18070
|
+
const { done, value } = await reader.read();
|
|
18071
|
+
if (done) break;
|
|
18072
|
+
if (value === void 0) continue;
|
|
18073
|
+
total += value.byteLength;
|
|
18074
|
+
if (total > maxBytes) {
|
|
18075
|
+
await reader.cancel().catch(() => void 0);
|
|
18076
|
+
throw new LocationRecordValidationError(`${label} response is too large`);
|
|
18077
|
+
}
|
|
18078
|
+
chunks.push(value);
|
|
18079
|
+
}
|
|
18080
|
+
} finally {
|
|
18081
|
+
reader.releaseLock();
|
|
18082
|
+
}
|
|
18083
|
+
const bytes2 = new Uint8Array(total);
|
|
18084
|
+
let offset = 0;
|
|
18085
|
+
for (const chunk of chunks) {
|
|
18086
|
+
bytes2.set(chunk, offset);
|
|
18087
|
+
offset += chunk.byteLength;
|
|
18088
|
+
}
|
|
18089
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes2);
|
|
18090
|
+
}
|
|
18091
|
+
function boundedSignal(parent, timeoutMs) {
|
|
18092
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
18093
|
+
throw new LocationRecordValidationError("owner-node binding timeout must be positive");
|
|
18094
|
+
}
|
|
18095
|
+
const controller = new AbortController();
|
|
18096
|
+
const abortFromParent = () => controller.abort(parent?.reason);
|
|
18097
|
+
if (parent?.aborted) abortFromParent();
|
|
18098
|
+
else parent?.addEventListener("abort", abortFromParent, { once: true });
|
|
18099
|
+
const timer = setTimeout(() => controller.abort(new DOMException("owner-node binding timed out", "TimeoutError")), timeoutMs);
|
|
18100
|
+
return {
|
|
18101
|
+
signal: controller.signal,
|
|
18102
|
+
dispose() {
|
|
18103
|
+
clearTimeout(timer);
|
|
18104
|
+
parent?.removeEventListener("abort", abortFromParent);
|
|
18105
|
+
}
|
|
18106
|
+
};
|
|
18107
|
+
}
|
|
18108
|
+
async function verifyOwnerNodeBinding(options2) {
|
|
18109
|
+
validateSubject(options2.ownerDid);
|
|
18110
|
+
validateSubject(options2.nodeDid);
|
|
18111
|
+
const registryUrl = exactHttpOrigin(options2.registryUrl, "registry URL");
|
|
18112
|
+
const nodeOrigin = exactHttpOrigin(options2.nodeOrigin, "node origin");
|
|
18113
|
+
const fetchFn = options2.fetch ?? globalThis.fetch.bind(globalThis);
|
|
18114
|
+
const lookup = boundedSignal(options2.signal, options2.timeoutMs ?? OWNER_NODE_BINDING_TIMEOUT_MS);
|
|
18115
|
+
try {
|
|
18116
|
+
const record2 = await fetchLocationRecord(registryUrl, options2.ownerDid, fetchFn, lookup.signal);
|
|
18117
|
+
if (record2 === null) {
|
|
18118
|
+
throw new LocationRecordValidationError("owner has no published location record");
|
|
18119
|
+
}
|
|
18120
|
+
if (record2.subject !== options2.ownerDid || !await verifyLocationRecord(record2)) {
|
|
18121
|
+
throw new LocationRecordValidationError("owner location record signature is invalid");
|
|
18122
|
+
}
|
|
18123
|
+
const publishedOrigins = publishedHttpOrigins(record2);
|
|
18124
|
+
if (!publishedOrigins.includes(nodeOrigin)) {
|
|
18125
|
+
throw new LocationRecordValidationError("share target is not in the owner's signed location record");
|
|
18126
|
+
}
|
|
18127
|
+
const response = await fetchFn(new URL("/info", nodeOrigin), {
|
|
18128
|
+
redirect: "error",
|
|
18129
|
+
headers: { accept: "application/json" },
|
|
18130
|
+
signal: lookup.signal
|
|
18131
|
+
});
|
|
18132
|
+
if (!response.ok) {
|
|
18133
|
+
throw new LocationRecordValidationError(`target node /info returned HTTP ${response.status}`);
|
|
18134
|
+
}
|
|
18135
|
+
const text2 = await boundedResponseText(response, "target node /info", 16 * 1024);
|
|
18136
|
+
let body;
|
|
18137
|
+
try {
|
|
18138
|
+
body = JSON.parse(text2);
|
|
18139
|
+
} catch {
|
|
18140
|
+
throw new LocationRecordValidationError("target node /info response is not JSON");
|
|
18141
|
+
}
|
|
18142
|
+
if (body === null || typeof body !== "object" || Array.isArray(body) || body.nodeId !== options2.nodeDid) {
|
|
18143
|
+
throw new LocationRecordValidationError("target node identity does not match the share attestation");
|
|
18144
|
+
}
|
|
18145
|
+
return Object.freeze({ record: record2, nodeOrigin, nodeDid: options2.nodeDid });
|
|
18146
|
+
} finally {
|
|
18147
|
+
lookup.dispose();
|
|
18148
|
+
}
|
|
18149
|
+
}
|
|
17486
18150
|
function httpUrlToMultiaddr(input) {
|
|
17487
18151
|
const url = new URL(input);
|
|
17488
18152
|
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
@@ -17644,7 +18308,7 @@ async function verifyPkhSignature(did, payload, signature) {
|
|
|
17644
18308
|
}
|
|
17645
18309
|
function verifyDidKeySignature(did, payload, signature) {
|
|
17646
18310
|
const publicKey = ed25519PublicKeyFromDidKey2(did);
|
|
17647
|
-
const signatureBytes =
|
|
18311
|
+
const signatureBytes = decodeBase64Url4(signature);
|
|
17648
18312
|
if (signatureBytes.length !== 64) {
|
|
17649
18313
|
throw new LocationRecordValidationError(
|
|
17650
18314
|
"did:key signature must be a base64url Ed25519 signature"
|
|
@@ -17697,7 +18361,7 @@ function base64UrlEncode3(bytes2) {
|
|
|
17697
18361
|
}
|
|
17698
18362
|
return output;
|
|
17699
18363
|
}
|
|
17700
|
-
function
|
|
18364
|
+
function decodeBase64Url4(value) {
|
|
17701
18365
|
const alphabet2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
17702
18366
|
const bytes2 = [];
|
|
17703
18367
|
let buffer = 0;
|
|
@@ -17744,6 +18408,7 @@ export {
|
|
|
17744
18408
|
CREDENTIAL_CONTRACT_VERSION,
|
|
17745
18409
|
CREDENTIAL_ERROR_CODES,
|
|
17746
18410
|
CREDENTIAL_FORMAT,
|
|
18411
|
+
CREDENTIAL_INVITATION_REQUEST_DOMAIN,
|
|
17747
18412
|
CREDENTIAL_ACQUISITION_PROTOCOL as CREDENTIAL_PROTOCOL,
|
|
17748
18413
|
CREDENTIAL_STEP_REGISTRY_VERSION,
|
|
17749
18414
|
CapabilityKeyRegistry,
|
|
@@ -17767,6 +18432,7 @@ export {
|
|
|
17767
18432
|
DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
|
|
17768
18433
|
DEFAULT_TINYCLOUD_FALLBACK_HOST,
|
|
17769
18434
|
DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL,
|
|
18435
|
+
DELIVERY_ADMISSION_DOMAIN,
|
|
17770
18436
|
DUCKDB,
|
|
17771
18437
|
DataVaultService,
|
|
17772
18438
|
DatabaseHandle,
|
|
@@ -17778,6 +18444,7 @@ export {
|
|
|
17778
18444
|
DuckDbService2 as DuckDbService,
|
|
17779
18445
|
ED25519_JCS_SIGNATURE_SUITE,
|
|
17780
18446
|
EIP191_JCS_SIGNATURE_SUITE,
|
|
18447
|
+
EMAIL_CREDENTIAL_MAX_AGE_SECONDS,
|
|
17781
18448
|
ENCRYPTION,
|
|
17782
18449
|
ENCRYPTION_MANIFEST_SPACE,
|
|
17783
18450
|
ENCRYPTION_NETWORK_URN_PREFIX,
|
|
@@ -17816,6 +18483,8 @@ export {
|
|
|
17816
18483
|
NETWORK_NAME_PATTERN,
|
|
17817
18484
|
NetworkIdError,
|
|
17818
18485
|
OPENKEY_ACTION_ID_SEPARATOR,
|
|
18486
|
+
OWNER_NODE_BINDING_TIMEOUT_MS,
|
|
18487
|
+
OpenKeyManageKeyError,
|
|
17819
18488
|
POLICY_CAPABILITY_V1_DOMAIN,
|
|
17820
18489
|
POLICY_CAPABILITY_V1_SCHEMA,
|
|
17821
18490
|
POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE,
|
|
@@ -17828,6 +18497,8 @@ export {
|
|
|
17828
18497
|
POLICY_ENGINE_RESOLVE_REQUEST_SCHEMA,
|
|
17829
18498
|
POLICY_PRESENTATION_V3_DOMAIN,
|
|
17830
18499
|
POLICY_PRESENTATION_V3_SCHEMA,
|
|
18500
|
+
POLICY_PRESENTATION_V4_DOMAIN,
|
|
18501
|
+
POLICY_PRESENTATION_V4_SCHEMA,
|
|
17831
18502
|
POLICY_SCHEMA,
|
|
17832
18503
|
POLICY_SESSION_UCAN_V1_PROFILE,
|
|
17833
18504
|
POLICY_STATUS_SCHEMA,
|
|
@@ -17852,7 +18523,6 @@ export {
|
|
|
17852
18523
|
SECRET_RECORDS_SCHEMA,
|
|
17853
18524
|
SERVICE_LONG_TO_SHORT,
|
|
17854
18525
|
SERVICE_SHORT_TO_LONG,
|
|
17855
|
-
SHARE_DELIVERY_AUTHORIZATION_DOMAIN,
|
|
17856
18526
|
SPACE,
|
|
17857
18527
|
SQL,
|
|
17858
18528
|
SQLAction,
|
|
@@ -17886,7 +18556,9 @@ export {
|
|
|
17886
18556
|
SpaceService,
|
|
17887
18557
|
TINYCLOUD_ACCOUNT_SPACE_MANIFEST,
|
|
17888
18558
|
TINYCLOUD_APPLICATIONS_SPACE_MANIFEST,
|
|
18559
|
+
TINYCLOUD_CANONICAL_IDENTITY_CLAIM,
|
|
17889
18560
|
TINYCLOUD_DEFAULT_SPACE_MANIFEST,
|
|
18561
|
+
TINYCLOUD_MANAGE_KEY_SCOPE,
|
|
17890
18562
|
TINYCLOUD_PUBLIC_SPACE_MANIFEST,
|
|
17891
18563
|
TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST,
|
|
17892
18564
|
TinyCloud,
|
|
@@ -17903,6 +18575,7 @@ export {
|
|
|
17903
18575
|
activateSessionWithHost,
|
|
17904
18576
|
addressStorageKey,
|
|
17905
18577
|
admitPolicyCredentialV3,
|
|
18578
|
+
admitPolicyCredentialV4,
|
|
17906
18579
|
applyPrefix,
|
|
17907
18580
|
bootstrapEncryptionNetworkId,
|
|
17908
18581
|
bootstrapSpaceId,
|
|
@@ -17913,6 +18586,7 @@ export {
|
|
|
17913
18586
|
buildDecryptInvocation,
|
|
17914
18587
|
buildNetworkId2 as buildNetworkId,
|
|
17915
18588
|
buildPolicyCredentialPresentationV3,
|
|
18589
|
+
buildPolicyCredentialPresentationV4,
|
|
17916
18590
|
buildSpaceUri,
|
|
17917
18591
|
canonicalDigest,
|
|
17918
18592
|
canonicalHashHex,
|
|
@@ -17943,10 +18617,12 @@ export {
|
|
|
17943
18617
|
createAndSignSignedObject,
|
|
17944
18618
|
createCapabilityKeyRegistry,
|
|
17945
18619
|
createDelegatedShareKey,
|
|
18620
|
+
createEmailCredentialRequirement,
|
|
17946
18621
|
createHolderBinding,
|
|
17947
18622
|
createInMemoryLocalNodeIdentityStore,
|
|
17948
18623
|
createLocalStorageLocalNodeIdentityStore,
|
|
17949
18624
|
createOpenKeyCallbackSigningStrategy,
|
|
18625
|
+
createOpenKeyManageKeySigningStrategy,
|
|
17950
18626
|
createPolicyEnforcementDelegation,
|
|
17951
18627
|
createShareArtifact,
|
|
17952
18628
|
createShareArtifactAsync,
|
|
@@ -18005,6 +18681,7 @@ export {
|
|
|
18005
18681
|
generateRandomReceiverKey,
|
|
18006
18682
|
getPolicyRootStatusV3,
|
|
18007
18683
|
getTinyCloudDebugLogs,
|
|
18684
|
+
hasTinyCloudManageKeyScope,
|
|
18008
18685
|
hexDecode,
|
|
18009
18686
|
hexEncode,
|
|
18010
18687
|
holderBindingCanonicalBytes,
|
|
@@ -18019,6 +18696,7 @@ export {
|
|
|
18019
18696
|
isNetworkId,
|
|
18020
18697
|
isPlausibleOpenKeyActionId,
|
|
18021
18698
|
isV2ShareLink,
|
|
18699
|
+
issuedCredentialEnvelopeFromVerified,
|
|
18022
18700
|
jcsCanonicalize,
|
|
18023
18701
|
loadManifest,
|
|
18024
18702
|
locationPayloadForRecord,
|
|
@@ -18037,6 +18715,8 @@ export {
|
|
|
18037
18715
|
openWrappedKey,
|
|
18038
18716
|
parseCanonicalNetworkId,
|
|
18039
18717
|
parseCanonicalRecapResource,
|
|
18718
|
+
parseCanonicalTinyCloudIdentity,
|
|
18719
|
+
parseCanonicalTinyCloudIdentityClaims,
|
|
18040
18720
|
parseCompactUcanAuthorization,
|
|
18041
18721
|
parseExpiry,
|
|
18042
18722
|
parseNetworkId2 as parseNetworkId,
|
|
@@ -18045,6 +18725,7 @@ export {
|
|
|
18045
18725
|
parsePolicySessionUcan,
|
|
18046
18726
|
parsePortableDelegation,
|
|
18047
18727
|
parseRecapCapabilities,
|
|
18728
|
+
parseSecretCatalogKey,
|
|
18048
18729
|
parseShareArtifact,
|
|
18049
18730
|
parseShareUrl,
|
|
18050
18731
|
parseSpaceUri,
|
|
@@ -18057,8 +18738,11 @@ export {
|
|
|
18057
18738
|
principalDidEquals,
|
|
18058
18739
|
projectUnifiedPolicyCapabilities,
|
|
18059
18740
|
projectUnifiedPolicyCapability,
|
|
18741
|
+
publishLocationRecord,
|
|
18060
18742
|
recapCaveatsEqual,
|
|
18743
|
+
registerPolicyV3,
|
|
18061
18744
|
renewPolicyRootStatusV3,
|
|
18745
|
+
requestTinyCloudManageKeyScope,
|
|
18062
18746
|
resolveCloudLocation,
|
|
18063
18747
|
resolveManifest,
|
|
18064
18748
|
resolveManifestKnowledgeRoot,
|
|
@@ -18074,10 +18758,10 @@ export {
|
|
|
18074
18758
|
serviceError5 as serviceError,
|
|
18075
18759
|
sha256Base64Url,
|
|
18076
18760
|
shareCapabilityAllows,
|
|
18077
|
-
shareDeliveryTrustedKid,
|
|
18078
18761
|
signCompactUcanAuthorization,
|
|
18079
18762
|
signCompactUcanRootAuthorization,
|
|
18080
18763
|
signLocationRecord,
|
|
18764
|
+
signPolicyCredentialPresentationV4,
|
|
18081
18765
|
signShareEnvelope,
|
|
18082
18766
|
signedObjectIdFor,
|
|
18083
18767
|
submitHostDelegation,
|
|
@@ -18110,13 +18794,14 @@ export {
|
|
|
18110
18794
|
validatePolicyStatusSignedShape,
|
|
18111
18795
|
validatePolicyStatusUnsigned,
|
|
18112
18796
|
validatePolicyUnsigned,
|
|
18113
|
-
|
|
18797
|
+
validateShareDeliveryAuthorizationV3Bytes,
|
|
18114
18798
|
validateUnifiedPolicyV2,
|
|
18115
18799
|
verifyDecryptResponse,
|
|
18116
18800
|
verifyDidKeyEd25519Signature,
|
|
18117
18801
|
verifyEip191MessageSignature,
|
|
18118
18802
|
verifyIssuedCredential,
|
|
18119
18803
|
verifyLocationRecord,
|
|
18804
|
+
verifyOwnerNodeBinding,
|
|
18120
18805
|
verifyPolicy,
|
|
18121
18806
|
verifyPolicyEngineRecord,
|
|
18122
18807
|
verifyPolicyRootRevocationV3,
|