wire-mesh-core 3.8.0 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/domain/capability-grant.cjs +4 -4
- package/dist/domain/capability-grant.d.cts +1 -1
- package/dist/domain/capability-grant.d.mts +1 -1
- package/dist/domain/capability-grant.mjs +2 -2
- package/dist/domain/capability-request.cjs +18 -6
- package/dist/domain/capability-request.d.cts +8 -1
- package/dist/domain/capability-request.d.mts +8 -1
- package/dist/domain/capability-request.mjs +15 -4
- package/dist/domain/grant-candidates.cjs +2 -2
- package/dist/domain/grant-candidates.mjs +1 -1
- package/dist/domain/mesh-session.cjs +2 -2
- package/dist/domain/mesh-session.mjs +1 -1
- package/dist/domain/notice-board.d.cts +1 -1
- package/dist/domain/notice-board.d.mts +1 -1
- package/dist/domain/peer-advert.cjs +2 -2
- package/dist/domain/peer-advert.mjs +1 -1
- package/dist/domain/revocation-view.cjs +2 -2
- package/dist/domain/revocation-view.d.cts +1 -1
- package/dist/domain/revocation-view.d.mts +1 -1
- package/dist/domain/revocation-view.mjs +1 -1
- package/dist/domain/room-rekey.d.cts +1 -1
- package/dist/domain/room-rekey.d.mts +1 -1
- package/dist/domain/room-token-verification.cjs +2 -2
- package/dist/domain/room-token-verification.d.cts +1 -1
- package/dist/domain/room-token-verification.d.mts +1 -1
- package/dist/domain/room-token-verification.mjs +1 -1
- package/dist/domain/room.d.cts +2 -2
- package/dist/domain/room.d.mts +2 -2
- package/dist/domain/threshold-subject.cjs +2 -2
- package/dist/domain/threshold-subject.mjs +1 -1
- package/dist/domain/token-predicates.cjs +184 -0
- package/dist/domain/token-predicates.d.cts +2 -0
- package/dist/domain/token-predicates.d.mts +2 -0
- package/dist/domain/token-predicates.mjs +173 -0
- package/dist/{token-scope-CxHXTT3u.cjs → domain/token-scope.cjs} +3 -12
- package/dist/domain/token-scope.d.cts +11 -0
- package/dist/domain/token-scope.d.mts +11 -0
- package/dist/{token-scope-Z4bmci4M.mjs → domain/token-scope.mjs} +1 -1
- package/dist/domain/tokens.cjs +466 -9
- package/dist/domain/tokens.d.cts +131 -2
- package/dist/domain/tokens.d.mts +131 -2
- package/dist/domain/tokens.mjs +458 -1
- package/dist/domain/webrtc-signaling.cjs +2 -2
- package/dist/domain/webrtc-signaling.d.cts +1 -1
- package/dist/domain/webrtc-signaling.d.mts +1 -1
- package/dist/domain/webrtc-signaling.mjs +1 -1
- package/dist/{room-token-verification-hFtBDZPO.d.cts → room-token-verification-BpQ6yJtj.d.cts} +1 -1
- package/dist/{room-token-verification-7zD4ib-A.d.mts → room-token-verification-CTQSihUO.d.mts} +1 -1
- package/dist/token-predicates-C-aIe1DT.d.cts +72 -0
- package/dist/token-predicates-DqvyS7Y8.d.mts +72 -0
- package/package.json +9 -1
- package/dist/tokens-BMWCnQEF.d.cts +0 -158
- package/dist/tokens-D8JlTJNA.mjs +0 -617
- package/dist/tokens-DRkm_QOR.d.mts +0 -158
- package/dist/tokens-tPJQ7Vzt.cjs +0 -664
package/dist/tokens-D8JlTJNA.mjs
DELETED
|
@@ -1,617 +0,0 @@
|
|
|
1
|
-
import { capabilityTokenSchema, revocationClaimsSchema, tokenClaimsSchema } from "./generated/protocol.mjs";
|
|
2
|
-
import { bytesToHex, deviceIdToHex } from "./domain/device-id.mjs";
|
|
3
|
-
import { n as scopeNarrows, t as bytesEqual } from "./token-scope-Z4bmci4M.mjs";
|
|
4
|
-
import { cdeDecodeOptions, cdeEncodeOptions, decode, encode } from "cbor2";
|
|
5
|
-
import { z } from "zod";
|
|
6
|
-
import { PredicateNodeSchema, evaluatePredicate } from "trilean";
|
|
7
|
-
//#region src/domain/token-predicates.ts
|
|
8
|
-
/** The wire shape of `token-claims.conditions` once CBOR-decoded: trilean's own PredicateNodeSchema is the single source of truth for what a condition entry may contain, re-validated here rather than trusted from a CDDL-generated shadow schema (see tokens.cddl's own comment on why `conditions` is an opaque bstr, not a native CDDL type) -- a token from an untrusted peer must pass trilean's real schema before any of its conditions are evaluated. Explicitly annotated: trilean's PredicateNodeSchema is a deeply recursive z.lazy() type whose inferred shape is too large for tsdown's declaration-file generator to serialise (TS7056) without this. */
|
|
9
|
-
const conditionsListSchema = z.array(PredicateNodeSchema);
|
|
10
|
-
/**
|
|
11
|
-
* tokens.cddl's mandated core predicate-op vocabulary (issue #85): the five narrowing checks a verifier previously enforced as five hardcoded `if`s, now expressed as `delegate` systems every conforming verifier registers a handler for. Order matches the historical check order in verifyTokenChain/checkNarrowing, preserved so a caller mapping a failing system to its own reason vocabulary (MintRefusalReason's five distinct values; verifyTokenChain's single collapsed "delegation_exceeds_parent") sees the same check run first that always ran first.
|
|
12
|
-
*/
|
|
13
|
-
const BEARER_IS = "bearer-is";
|
|
14
|
-
const EXPIRES_AT = "expires-at";
|
|
15
|
-
const SCOPE_NARROWS = "scope-narrows";
|
|
16
|
-
const CAPABILITY_IS = "capability-is";
|
|
17
|
-
const DEPTH_REMAINING = "depth-remaining";
|
|
18
|
-
const NARROWING_SYSTEMS = [
|
|
19
|
-
BEARER_IS,
|
|
20
|
-
EXPIRES_AT,
|
|
21
|
-
SCOPE_NARROWS,
|
|
22
|
-
CAPABILITY_IS,
|
|
23
|
-
DEPTH_REMAINING
|
|
24
|
-
];
|
|
25
|
-
function isNarrowingSystem(system) {
|
|
26
|
-
return system === "bearer-is" || system === "expires-at" || system === "scope-narrows" || system === "capability-is" || system === "depth-remaining";
|
|
27
|
-
}
|
|
28
|
-
/** The two subject-mode delegate systems (wire-mesh#323), registered only when a ConditionsContext carries a subject, and indeterminate (fail-closed) whenever one does not. */
|
|
29
|
-
const GRANTEE_IS = "grantee-is";
|
|
30
|
-
const GRANTED_CAPABILITY_IS = "granted-capability-is";
|
|
31
|
-
function isSubjectSystem(system) {
|
|
32
|
-
return system === "grantee-is" || system === "granted-capability-is";
|
|
33
|
-
}
|
|
34
|
-
const subjectHandlers = {
|
|
35
|
-
[GRANTEE_IS]: (payload, context) => {
|
|
36
|
-
if (context.subject === void 0) return { found: false };
|
|
37
|
-
if (typeof payload !== "string") return { found: false };
|
|
38
|
-
return booleanFound(deviceIdToHex(context.subject.bearer) === payload);
|
|
39
|
-
},
|
|
40
|
-
[GRANTED_CAPABILITY_IS]: (payload, context) => {
|
|
41
|
-
if (context.subject === void 0) return { found: false };
|
|
42
|
-
if (typeof payload !== "string") return { found: false };
|
|
43
|
-
return booleanFound(context.subject.capability === payload);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
function booleanFound(value) {
|
|
47
|
-
return {
|
|
48
|
-
found: true,
|
|
49
|
-
value: {
|
|
50
|
-
kind: "boolean",
|
|
51
|
-
value
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
/** The five mandatory narrowing ops, each reading only from TokenPredicateContext -- never the delegate node's own payload, since every fact each op needs (which parent, which candidate) already travels via the context the caller constructs per evaluation, not via wire-encoded payload data. A context with no parentClaims (narrowing is being checked with nothing to narrow against) resolves every op to `false` rather than throwing -- fail-closed, matching how an indeterminate result already refuses a token. */
|
|
56
|
-
const narrowingHandlers = {
|
|
57
|
-
[BEARER_IS]: (_payload, context) => {
|
|
58
|
-
if (context.parentClaims === void 0) return booleanFound(false);
|
|
59
|
-
return booleanFound(bytesEqual(context.parentClaims.bearer, context.childIssuer));
|
|
60
|
-
},
|
|
61
|
-
[EXPIRES_AT]: (_payload, context) => {
|
|
62
|
-
if (context.parentClaims === void 0) return booleanFound(false);
|
|
63
|
-
return booleanFound(context.candidate.expires <= context.parentClaims.expires);
|
|
64
|
-
},
|
|
65
|
-
[SCOPE_NARROWS]: (_payload, context) => {
|
|
66
|
-
if (context.parentClaims === void 0) return booleanFound(false);
|
|
67
|
-
return booleanFound(scopeNarrows(context.parentClaims.scope, context.candidate.scope));
|
|
68
|
-
},
|
|
69
|
-
[CAPABILITY_IS]: (_payload, context) => {
|
|
70
|
-
if (context.parentClaims === void 0) return booleanFound(false);
|
|
71
|
-
return booleanFound(context.parentClaims.capability === context.candidate.capability);
|
|
72
|
-
},
|
|
73
|
-
[DEPTH_REMAINING]: (_payload, context) => {
|
|
74
|
-
if (context.parentClaims === void 0) return booleanFound(false);
|
|
75
|
-
const parentRemaining = context.parentClaims["delegations-remaining"];
|
|
76
|
-
if (parentRemaining === void 0) return booleanFound(true);
|
|
77
|
-
return booleanFound(context.candidate.delegationsRemaining !== void 0 && context.candidate.delegationsRemaining < parentRemaining);
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
/** `{kind:"compare", op:"eq", left:{kind:"delegate", system, payload}, right:{kind:"booleanLiteral", value:true}}` -- delegate is an ExpressionNode, not itself a PredicateNode (trilean's PredicateNodeSchema has no `delegate` member), so every predicate op is expressed this way: resolve the delegate to a boolean ComputedValue, then compare it against the literal `true`. The narrowing checks pass no payload (every fact they need arrives via context); the subject-mode systems take their comparison value as the payload. */
|
|
81
|
-
function delegateIsTrue(system, payload = null) {
|
|
82
|
-
return {
|
|
83
|
-
kind: "compare",
|
|
84
|
-
op: "eq",
|
|
85
|
-
left: {
|
|
86
|
-
kind: "delegate",
|
|
87
|
-
system,
|
|
88
|
-
payload
|
|
89
|
-
},
|
|
90
|
-
right: {
|
|
91
|
-
kind: "booleanLiteral",
|
|
92
|
-
value: true
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
/** trilean's three required resolvers are never exercised by any node this module constructs (narrowing/conditions nodes are built entirely from `compare`+`delegate`+`booleanLiteral`, never `reference`/`lookup`/`fold`/`some`/`every`) -- these stubs exist only because `Resolvers` requires them structurally. */
|
|
97
|
-
const unusedResolveValue = async () => Promise.resolve({ found: false });
|
|
98
|
-
const unusedResolveLookup = async () => Promise.resolve({ found: false });
|
|
99
|
-
const unusedResolveCollection = async () => Promise.resolve([]);
|
|
100
|
-
/**
|
|
101
|
-
* Evaluates the five mandatory narrowing predicates against parentClaims/candidate, in their historical check order, short-circuiting on and returning the first that does not hold. Returns undefined when every check holds. This is the single mechanism both mintCapabilityToken's own pre-mint check and verifyTokenChain's own delegation-chain walk call -- the two can never silently drift into different ideas of what "narrows" means, because there is now exactly one implementation of each check, not two hardcoded copies.
|
|
102
|
-
*/
|
|
103
|
-
async function evaluateNarrowing(parentClaims, childIssuer, candidate) {
|
|
104
|
-
const context = {
|
|
105
|
-
childIssuer,
|
|
106
|
-
candidate,
|
|
107
|
-
parentClaims
|
|
108
|
-
};
|
|
109
|
-
const resolvers = {
|
|
110
|
-
resolveValue: unusedResolveValue,
|
|
111
|
-
resolveLookup: unusedResolveLookup,
|
|
112
|
-
resolveCollection: unusedResolveCollection,
|
|
113
|
-
resolveDelegate: async (system, payload) => {
|
|
114
|
-
if (!isNarrowingSystem(system)) return { found: false };
|
|
115
|
-
return narrowingHandlers[system](payload, context);
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
for (const system of NARROWING_SYSTEMS) {
|
|
119
|
-
const result = await evaluatePredicate(delegateIsTrue(system), context, resolvers);
|
|
120
|
-
if (result.status !== "definite" || !result.value) return system;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Evaluates token-claims' own `conditions` field (issue #85): additional, issuer-chosen predicates strictly additive to the five mandatory narrowing checks above, which are computed structurally (from a delegated token's own claims and its parent's) and never read from this field -- the five built-ins are inherently about a parent/child relationship, so they are not meaningfully reusable inside a bare, parent-less conditions evaluation, unlike a genuine domain-specific condition (a message TTL, #84's future revoke-authorization check) which reasons about the presented token's own claims directly. `extraHandlers` is the registration point for those: none exist yet, so a `conditions` entry naming any system with no registered handler resolves `{found: false}` -- indeterminate, fail-closed. Absent `nodes` (an empty array, the caller's own signal for "no conditions field present") is trivially `{ok: true}` -- no extra restrictions, exactly today's behaviour. Every entry MUST evaluate to a definite `true`; the loop short-circuits on the first indeterminate or false result.
|
|
125
|
-
*/
|
|
126
|
-
async function evaluateConditions(nodes, claims, clock, extraHandlers = {}, subject) {
|
|
127
|
-
const context = subject === void 0 ? {
|
|
128
|
-
clock,
|
|
129
|
-
claims
|
|
130
|
-
} : {
|
|
131
|
-
clock,
|
|
132
|
-
claims,
|
|
133
|
-
subject
|
|
134
|
-
};
|
|
135
|
-
const resolvers = {
|
|
136
|
-
resolveValue: unusedResolveValue,
|
|
137
|
-
resolveLookup: unusedResolveLookup,
|
|
138
|
-
resolveCollection: unusedResolveCollection,
|
|
139
|
-
resolveDelegate: async (system, payload) => {
|
|
140
|
-
if (subject !== void 0 && isSubjectSystem(system)) try {
|
|
141
|
-
return await subjectHandlers[system](payload, context);
|
|
142
|
-
} catch {
|
|
143
|
-
return { found: false };
|
|
144
|
-
}
|
|
145
|
-
const handler = Object.hasOwn(extraHandlers, system) ? extraHandlers[system] : void 0;
|
|
146
|
-
if (handler === void 0) return { found: false };
|
|
147
|
-
try {
|
|
148
|
-
return await handler(payload, context);
|
|
149
|
-
} catch {
|
|
150
|
-
return { found: false };
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
for (const node of nodes) {
|
|
155
|
-
const result = await evaluatePredicate(node, context, resolvers);
|
|
156
|
-
if (result.status !== "definite" || !result.value) return {
|
|
157
|
-
ok: false,
|
|
158
|
-
reason: "not_satisfied"
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
return { ok: true };
|
|
162
|
-
}
|
|
163
|
-
//#endregion
|
|
164
|
-
//#region src/domain/tokens.ts
|
|
165
|
-
/** The capability verb of a grant-capability (wire-mesh#323): authority to mint other capability tokens, the same register manage:revoke already established for authority over tokens. */
|
|
166
|
-
const MANAGE_GRANT_CAPABILITY = "manage:grant";
|
|
167
|
-
/** Both link kinds walk at most this many hops before the verifier gives up, mirroring Rust's own MAX_CHAIN_DEPTH. Two colluding issuers can construct a mutually-bearing parent cycle that would otherwise recurse forever, and the authorised-by link makes the attack cheaper to mount, so TS gains the bound rather than relying on JS recursion limits. */
|
|
168
|
-
const MAX_CHAIN_DEPTH = 64;
|
|
169
|
-
/** RFC 9052 §4.4 Sig_structure for a COSE_Sign1 with no external AAD: ["Signature1", protected, external_aad, payload]. Exported (not just used internally by verifyTokenChain) so any other self-certifying COSE_Sign1 construction in this codebase -- e.g. threshold-subject.ts's own to-be-signed bytes for a FROST-signed capability-token/revocation-entry/handle-record/room-notice -- reuses this single construction rather than a second hand-rolled one. */
|
|
170
|
-
function sig1ToBeSigned(protectedHeader, payload) {
|
|
171
|
-
return encode([
|
|
172
|
-
"Signature1",
|
|
173
|
-
protectedHeader,
|
|
174
|
-
/* @__PURE__ */ new Uint8Array(0),
|
|
175
|
-
payload
|
|
176
|
-
], cdeEncodeOptions);
|
|
177
|
-
}
|
|
178
|
-
/** Normalises cbor2's encode() (and any other Uint8Array<ArrayBufferLike>-typed construction) to a fresh, non-shared, whole-buffer Uint8Array<ArrayBuffer> -- what the generated schemas' concrete-typed fields require. */
|
|
179
|
-
function buf(bytes) {
|
|
180
|
-
return Uint8Array.from(bytes);
|
|
181
|
-
}
|
|
182
|
-
function encodeBuf(value) {
|
|
183
|
-
return buf(encode(value, cdeEncodeOptions));
|
|
184
|
-
}
|
|
185
|
-
/** The COSE protected header every capability-token/revocation-entry envelope in this codebase actually signs over: label 1 (alg) and label 4 (kid, the issuer's own device-id) -- matching the frozen conformance vectors, not the empty header a token merely needs to verify against itself. */
|
|
186
|
-
function protectedHeaderFor(identity) {
|
|
187
|
-
return encodeBuf({
|
|
188
|
-
1: identity.identityKey.alg,
|
|
189
|
-
4: identity.deviceId
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
/** Decodes and validates a parent token's own claims from its raw CapabilityToken tuple -- the same decode `verifyTokenChain` performs on `claims.parent`, extracted here so mint can check narrowing against a parent's real claims without duplicating the CBOR/schema plumbing. Returns undefined for anything that doesn't parse; the caller turns that into its own refusal reason since "malformed" means something different at mint time than at verify time. */
|
|
193
|
-
function decodeTokenClaims(token) {
|
|
194
|
-
const [, , payload] = token;
|
|
195
|
-
if (payload === null) return void 0;
|
|
196
|
-
let decoded;
|
|
197
|
-
try {
|
|
198
|
-
decoded = decode(payload, cdeDecodeOptions);
|
|
199
|
-
} catch {
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
const result = tokenClaimsSchema.safeParse(decoded);
|
|
203
|
-
return result.success ? result.data : void 0;
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Verifies one capability token per tokens.cddl's own documented rules: the token is a well-formed COSE_Sign1 whose signature actually verifies against its own embedded issuer-key, that issuer-key is self-certifying (sha256(issuer-key.public-key) equals the claimed issuer device-id -- no shared secret needed to check this), the token is currently valid (not expired, not before not-before, not revoked by its own issuer), and -- recursively -- any parent delegation narrows rather than widens across all three axes of authority: the parent's bearer must be this token's issuer (the delegation chain is unbroken), this token's expiry must not exceed its parent's, and this token's scope must narrow its parent's (same kind; equal-or-descendant path when the parent carries one) with an identical capability verb (the capability-verb grammar has no sub-verb relation, so a different verb is a different authority, not a narrower one). Undecodable payload bytes return "malformed" and undecodable parent bytes return "parent_invalid" -- hostile input produces a verdict, never a throw.
|
|
207
|
-
*/
|
|
208
|
-
async function verifyCapabilityToken(token, options) {
|
|
209
|
-
const verdict = await verifyTokenChain(token, {
|
|
210
|
-
identity: options.identity,
|
|
211
|
-
clock: options.clock,
|
|
212
|
-
revocation: options.revocation,
|
|
213
|
-
...options.extraPredicateResolvers !== void 0 ? { extraPredicateResolvers: options.extraPredicateResolvers } : {}
|
|
214
|
-
});
|
|
215
|
-
if (!verdict.ok) return verdict;
|
|
216
|
-
if (options.expectedBearer !== void 0 && !bytesEqual(verdict.claims.bearer, options.expectedBearer)) return {
|
|
217
|
-
ok: false,
|
|
218
|
-
reason: "bearer_mismatch"
|
|
219
|
-
};
|
|
220
|
-
return verdict;
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* Does one recorded revocation-claims entry actually revoke targetClaims, per management.cddl's own additive obligation? Valid when EITHER the entry's own issuer equals the target token's own issuer (the original, unconditional rule -- only a token's own issuer may revoke it), OR the entry carries an `authorization` that independently verifies as an ordinary capability-token -- with `expectedBearer` set to THIS entry's own `issuer`, proving the authorization was actually granted to the party submitting this revocation, not merely referenced from someone else's -- whose own `capability` is `manage:revoke` (spec/registry/core-capabilities.md) and whose own `scope` narrows targetClaims' scope. An authorization that fails any part of this (wrong capability, scope doesn't narrow, fails ordinary verification -- expired, revoked, bad signature, bearer mismatch) makes the entry no more valid than if `authorization` were absent; it never falls back to weakening the issuer-match rule.
|
|
224
|
-
*/
|
|
225
|
-
async function revocationEntryGrantsRevoke(entry, targetClaims, options) {
|
|
226
|
-
if (bytesEqual(entry.issuer, targetClaims.issuer)) return true;
|
|
227
|
-
if (entry.authorization === void 0) return false;
|
|
228
|
-
let decodedAuthorization;
|
|
229
|
-
try {
|
|
230
|
-
decodedAuthorization = decode(entry.authorization, cdeDecodeOptions);
|
|
231
|
-
} catch {
|
|
232
|
-
return false;
|
|
233
|
-
}
|
|
234
|
-
const authorizationResult = capabilityTokenSchema.safeParse(decodedAuthorization);
|
|
235
|
-
if (!authorizationResult.success) return false;
|
|
236
|
-
const authorizationVerdict = await verifyCapabilityToken(authorizationResult.data, {
|
|
237
|
-
...options,
|
|
238
|
-
expectedBearer: entry.issuer
|
|
239
|
-
});
|
|
240
|
-
return authorizationVerdict.ok && authorizationVerdict.claims.capability === "manage:revoke" && scopeNarrows(authorizationVerdict.claims.scope, targetClaims.scope);
|
|
241
|
-
}
|
|
242
|
-
async function verifyTokenChain(token, options, subjectClaims, depth = 0, seen = /* @__PURE__ */ new Set()) {
|
|
243
|
-
const [protectedHeader, , payload, signature] = token;
|
|
244
|
-
if (payload === null) return {
|
|
245
|
-
ok: false,
|
|
246
|
-
reason: "malformed"
|
|
247
|
-
};
|
|
248
|
-
if (depth >= MAX_CHAIN_DEPTH) return {
|
|
249
|
-
ok: false,
|
|
250
|
-
reason: "chain_too_deep"
|
|
251
|
-
};
|
|
252
|
-
const payloadHex = bytesToHex(payload);
|
|
253
|
-
if (seen.has(payloadHex)) return {
|
|
254
|
-
ok: false,
|
|
255
|
-
reason: "chain_cycle"
|
|
256
|
-
};
|
|
257
|
-
const deeperSeen = new Set(seen).add(payloadHex);
|
|
258
|
-
let decodedClaims;
|
|
259
|
-
try {
|
|
260
|
-
decodedClaims = decode(payload, cdeDecodeOptions);
|
|
261
|
-
} catch {
|
|
262
|
-
return {
|
|
263
|
-
ok: false,
|
|
264
|
-
reason: "malformed"
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
const claimsResult = tokenClaimsSchema.safeParse(decodedClaims);
|
|
268
|
-
if (!claimsResult.success) return {
|
|
269
|
-
ok: false,
|
|
270
|
-
reason: "malformed"
|
|
271
|
-
};
|
|
272
|
-
const claims = claimsResult.data;
|
|
273
|
-
if (!await options.identity.verify(claims["issuer-key"], sig1ToBeSigned(protectedHeader, payload), signature)) return {
|
|
274
|
-
ok: false,
|
|
275
|
-
reason: "bad_signature"
|
|
276
|
-
};
|
|
277
|
-
const derivedIssuerId = await options.identity.deriveDeviceId(claims["issuer-key"]["public-key"]);
|
|
278
|
-
if (!bytesEqual(derivedIssuerId, claims.issuer)) return {
|
|
279
|
-
ok: false,
|
|
280
|
-
reason: "wrong_issuer"
|
|
281
|
-
};
|
|
282
|
-
const now = options.clock.now();
|
|
283
|
-
if (claims.expires <= now) return {
|
|
284
|
-
ok: false,
|
|
285
|
-
reason: "expired"
|
|
286
|
-
};
|
|
287
|
-
if (claims["not-before"] !== void 0 && claims["not-before"] > now) return {
|
|
288
|
-
ok: false,
|
|
289
|
-
reason: "not_yet_valid"
|
|
290
|
-
};
|
|
291
|
-
const validUntil = claims["valid-until"];
|
|
292
|
-
if (validUntil !== void 0 && validUntil <= now) return {
|
|
293
|
-
ok: false,
|
|
294
|
-
reason: "content_expired"
|
|
295
|
-
};
|
|
296
|
-
const revocationEntries = await options.revocation.entriesFor(claims["token-id"]);
|
|
297
|
-
for (const entry of revocationEntries) if (await revocationEntryGrantsRevoke(entry, claims, options)) return {
|
|
298
|
-
ok: false,
|
|
299
|
-
reason: "revoked"
|
|
300
|
-
};
|
|
301
|
-
if (claims.conditions !== void 0) {
|
|
302
|
-
let decodedConditions;
|
|
303
|
-
try {
|
|
304
|
-
decodedConditions = decode(claims.conditions, cdeDecodeOptions);
|
|
305
|
-
} catch {
|
|
306
|
-
return {
|
|
307
|
-
ok: false,
|
|
308
|
-
reason: "conditions_invalid"
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
const conditionsResult = conditionsListSchema.safeParse(decodedConditions);
|
|
312
|
-
if (!conditionsResult.success) return {
|
|
313
|
-
ok: false,
|
|
314
|
-
reason: "conditions_invalid"
|
|
315
|
-
};
|
|
316
|
-
if (!(await evaluateConditions(conditionsResult.data, claims, options.clock, options.extraPredicateResolvers, subjectClaims)).ok) return {
|
|
317
|
-
ok: false,
|
|
318
|
-
reason: "conditions_not_satisfied"
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
const parentBytes = claims.parent;
|
|
322
|
-
const authorisationBytes = claims["authorised-by"];
|
|
323
|
-
if (claims.capability === "manage:grant" && parentBytes !== void 0) return {
|
|
324
|
-
ok: false,
|
|
325
|
-
reason: "authorisation_invalid"
|
|
326
|
-
};
|
|
327
|
-
if (parentBytes !== void 0 && authorisationBytes !== void 0) return {
|
|
328
|
-
ok: false,
|
|
329
|
-
reason: "malformed"
|
|
330
|
-
};
|
|
331
|
-
if (parentBytes !== void 0) {
|
|
332
|
-
let decodedParent;
|
|
333
|
-
try {
|
|
334
|
-
decodedParent = decode(parentBytes, cdeDecodeOptions);
|
|
335
|
-
} catch {
|
|
336
|
-
return {
|
|
337
|
-
ok: false,
|
|
338
|
-
reason: "parent_invalid"
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
const parentResult = capabilityTokenSchema.safeParse(decodedParent);
|
|
342
|
-
if (!parentResult.success) return {
|
|
343
|
-
ok: false,
|
|
344
|
-
reason: "parent_invalid"
|
|
345
|
-
};
|
|
346
|
-
const parentVerdict = await verifyTokenChain(parentResult.data, options, void 0, depth + 1, deeperSeen);
|
|
347
|
-
if (!parentVerdict.ok) {
|
|
348
|
-
if (parentVerdict.reason === "chain_too_deep" || parentVerdict.reason === "chain_cycle") return {
|
|
349
|
-
ok: false,
|
|
350
|
-
reason: parentVerdict.reason
|
|
351
|
-
};
|
|
352
|
-
return {
|
|
353
|
-
ok: false,
|
|
354
|
-
reason: "parent_invalid"
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
const candidate = {
|
|
358
|
-
capability: claims.capability,
|
|
359
|
-
scope: claims.scope,
|
|
360
|
-
expires: claims.expires,
|
|
361
|
-
...claims["delegations-remaining"] !== void 0 ? { delegationsRemaining: claims["delegations-remaining"] } : {}
|
|
362
|
-
};
|
|
363
|
-
if (await evaluateNarrowing(parentVerdict.claims, claims.issuer, candidate) !== void 0) return {
|
|
364
|
-
ok: false,
|
|
365
|
-
reason: "delegation_exceeds_parent"
|
|
366
|
-
};
|
|
367
|
-
return {
|
|
368
|
-
ok: true,
|
|
369
|
-
claims,
|
|
370
|
-
rootIssuer: parentVerdict.rootIssuer,
|
|
371
|
-
rootIssuerKey: parentVerdict.rootIssuerKey,
|
|
372
|
-
depth: parentVerdict.depth + 1
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
if (authorisationBytes !== void 0) {
|
|
376
|
-
let decodedAuthoriser;
|
|
377
|
-
try {
|
|
378
|
-
decodedAuthoriser = decode(authorisationBytes, cdeDecodeOptions);
|
|
379
|
-
} catch {
|
|
380
|
-
return {
|
|
381
|
-
ok: false,
|
|
382
|
-
reason: "authorisation_invalid"
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
|
-
const authoriserResult = capabilityTokenSchema.safeParse(decodedAuthoriser);
|
|
386
|
-
if (!authoriserResult.success) return {
|
|
387
|
-
ok: false,
|
|
388
|
-
reason: "authorisation_invalid"
|
|
389
|
-
};
|
|
390
|
-
const authoriserVerdict = await verifyTokenChain(authoriserResult.data, options, claims, depth + 1, deeperSeen);
|
|
391
|
-
if (!authoriserVerdict.ok) {
|
|
392
|
-
if (authoriserVerdict.reason === "chain_too_deep" || authoriserVerdict.reason === "chain_cycle") return {
|
|
393
|
-
ok: false,
|
|
394
|
-
reason: authoriserVerdict.reason
|
|
395
|
-
};
|
|
396
|
-
return {
|
|
397
|
-
ok: false,
|
|
398
|
-
reason: "authorisation_invalid"
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
const authoriser = authoriserVerdict.claims;
|
|
402
|
-
const grantsCapability = authoriser["grants-capability"];
|
|
403
|
-
if (!(bytesEqual(authoriser.bearer, claims.issuer) && authoriser.capability === "manage:grant" && (grantsCapability === void 0 || grantsCapability === claims.capability) && scopeNarrows(authoriser.scope, claims.scope) && claims.expires <= authoriser.expires && (authoriser["delegations-remaining"] === void 0 || claims["delegations-remaining"] !== void 0 && claims["delegations-remaining"] < authoriser["delegations-remaining"]))) return {
|
|
404
|
-
ok: false,
|
|
405
|
-
reason: "authorisation_invalid"
|
|
406
|
-
};
|
|
407
|
-
return {
|
|
408
|
-
ok: true,
|
|
409
|
-
claims,
|
|
410
|
-
rootIssuer: authoriserVerdict.rootIssuer,
|
|
411
|
-
rootIssuerKey: authoriserVerdict.rootIssuerKey,
|
|
412
|
-
depth: authoriserVerdict.depth + 1
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
return {
|
|
416
|
-
ok: true,
|
|
417
|
-
claims,
|
|
418
|
-
rootIssuer: claims.issuer,
|
|
419
|
-
rootIssuerKey: claims["issuer-key"],
|
|
420
|
-
depth: 0
|
|
421
|
-
};
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
* Verifies one gossiped revocation-entry (management.cddl): a well-formed COSE_Sign1 whose signature verifies against its own embedded issuer-key, where that issuer-key is self-certifying (sha256(issuer-key.public-key) equals the claimed issuer device-id). A verifier that ingests a revocation-announce frame runs each entry through this before recording it in its revocation view; entries failing here are dropped, not stored. The issuer-match against a specific token's own issuer (only a token's own issuer may revoke it) is deliberately NOT checked here -- it happens at lookup time in RevocationCheck, against whichever token is being verified.
|
|
425
|
-
*/
|
|
426
|
-
async function verifyRevocationEntry(entry, options) {
|
|
427
|
-
const [protectedHeader, , payload, signature] = entry;
|
|
428
|
-
if (payload === null) return {
|
|
429
|
-
ok: false,
|
|
430
|
-
reason: "malformed"
|
|
431
|
-
};
|
|
432
|
-
let decodedClaims;
|
|
433
|
-
try {
|
|
434
|
-
decodedClaims = decode(payload, cdeDecodeOptions);
|
|
435
|
-
} catch {
|
|
436
|
-
return {
|
|
437
|
-
ok: false,
|
|
438
|
-
reason: "malformed"
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
const claimsResult = revocationClaimsSchema.safeParse(decodedClaims);
|
|
442
|
-
if (!claimsResult.success) return {
|
|
443
|
-
ok: false,
|
|
444
|
-
reason: "malformed"
|
|
445
|
-
};
|
|
446
|
-
const claims = claimsResult.data;
|
|
447
|
-
if (!await options.identity.verify(claims["issuer-key"], sig1ToBeSigned(protectedHeader, payload), signature)) return {
|
|
448
|
-
ok: false,
|
|
449
|
-
reason: "bad_signature"
|
|
450
|
-
};
|
|
451
|
-
const derivedIssuerId = await options.identity.deriveDeviceId(claims["issuer-key"]["public-key"]);
|
|
452
|
-
if (!bytesEqual(derivedIssuerId, claims.issuer)) return {
|
|
453
|
-
ok: false,
|
|
454
|
-
reason: "wrong_issuer"
|
|
455
|
-
};
|
|
456
|
-
return {
|
|
457
|
-
ok: true,
|
|
458
|
-
claims
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
/** Maps evaluateNarrowing's own NarrowingSystem (the first predicate op that did not hold) to mintCapabilityToken's five distinct refusal reasons -- verifyTokenChain collapses every narrowing failure to a single "delegation_exceeds_parent", but mint has always reported which specific rule was violated, since a caller building the candidate itself benefits from knowing exactly what to fix. */
|
|
462
|
-
const NARROWING_SYSTEM_TO_MINT_REFUSAL = {
|
|
463
|
-
"bearer-is": "parent_bearer_mismatch",
|
|
464
|
-
"expires-at": "expires_exceeds_parent",
|
|
465
|
-
"scope-narrows": "scope_does_not_narrow",
|
|
466
|
-
"capability-is": "capability_mismatch",
|
|
467
|
-
"depth-remaining": "delegation_exceeds_parent"
|
|
468
|
-
};
|
|
469
|
-
/** The narrowing arithmetic tokens.cddl's own delegation obligations require (bearer match, expiry within the parent's, scope narrows, same capability, delegations-remaining strictly less than the parent's) -- shared between mintCapabilityToken (which additionally builds and signs the resulting token) and canGrant (a pure query with no minting side effect at all), so the two can never silently drift into two different ideas of what "narrows" means. Delegates the actual check to evaluateNarrowing (token-predicates.ts), the same generic evaluator verifyTokenChain's own delegation-chain walk uses, so mint and verify share one implementation of each of the five checks rather than two hardcoded copies. Returns the specific refusal reason, or undefined when every rule is satisfied. */
|
|
470
|
-
async function checkNarrowing(parentClaims, granterDeviceId, candidate) {
|
|
471
|
-
const failedSystem = await evaluateNarrowing(parentClaims, granterDeviceId, candidate);
|
|
472
|
-
return failedSystem === void 0 ? void 0 : NARROWING_SYSTEM_TO_MINT_REFUSAL[failedSystem];
|
|
473
|
-
}
|
|
474
|
-
/** The authorised-by link's own arithmetic (wire-mesh#323), the analogue checkNarrowing already is for the parent link: everything tokens.cddl's authorised-by obligations require, checked against a not-yet-minted candidate, shared between mintCapabilityToken and canGrantVia so the two can never drift. The authoriser's conditions are evaluated in subject mode against the candidate, the same evaluation the verifier performs against the minted child. Returns the specific refusal reason, or undefined when the authorisation holds. */
|
|
475
|
-
async function checkAuthorisation(authorisingClaims, minterDeviceId, candidate) {
|
|
476
|
-
if (authorisingClaims.parent !== void 0) return "authorisation_parent_forbidden";
|
|
477
|
-
if (!bytesEqual(authorisingClaims.bearer, minterDeviceId)) return "authorisation_bearer_mismatch";
|
|
478
|
-
const grantsCapability = authorisingClaims["grants-capability"];
|
|
479
|
-
if (authorisingClaims.capability !== "manage:grant" || grantsCapability !== void 0 && grantsCapability !== candidate.capability) return "authorisation_capability_mismatch";
|
|
480
|
-
if (!scopeNarrows(authorisingClaims.scope, candidate.scope)) return "authorisation_scope_does_not_narrow";
|
|
481
|
-
if (candidate.expires > authorisingClaims.expires) return "authorisation_exceeds_authoriser";
|
|
482
|
-
const remaining = authorisingClaims["delegations-remaining"];
|
|
483
|
-
if (remaining !== void 0 && (candidate.delegationsRemaining === void 0 || candidate.delegationsRemaining >= remaining)) return "authorisation_delegation_exceeds";
|
|
484
|
-
if (authorisingClaims.conditions !== void 0) {
|
|
485
|
-
let decodedConditions;
|
|
486
|
-
try {
|
|
487
|
-
decodedConditions = decode(authorisingClaims.conditions, cdeDecodeOptions);
|
|
488
|
-
} catch {
|
|
489
|
-
return "authorisation_malformed";
|
|
490
|
-
}
|
|
491
|
-
const conditionsResult = conditionsListSchema.safeParse(decodedConditions);
|
|
492
|
-
if (!conditionsResult.success) return "authorisation_malformed";
|
|
493
|
-
if (!(await evaluateConditions(conditionsResult.data, authorisingClaims, { now: () => 0 }, {}, candidate)).ok) return "authorisation_conditions_not_satisfied";
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* A pure query: could deviceId, presenting heldToken as its own delegation authority," successfully mint a delegation matching candidate right now -- without attempting (and potentially failing) a real mint just to find out. Reuses mintCapabilityToken's own narrowing arithmetic via checkNarrowing, so the two can never silently drift into different ideas of what "narrows" means.
|
|
498
|
-
*
|
|
499
|
-
* Deliberately narrower than a full mint attempt in one respect: this checks only the narrowing rules tokens.cddl's own delegation obligations require (bearer match, expiry, scope, capability, delegations-remaining), the same scope mintCapabilityToken itself checks a *parent* against -- it does not verify heldToken's own signature or revocation status, exactly as mintCapabilityToken never re-verifies its own parent's signature either. A caller that also needs heldToken's cryptographic validity confirmed calls verifyCapabilityToken separately.
|
|
500
|
-
*
|
|
501
|
-
* Async as of the predicate-list evaluator (issue #85): checkNarrowing now routes through trilean's own evaluatePredicate, which is asynchronous throughout -- there is no synchronous path through a real evaluator call, so this is a genuine, deliberate breaking change to what was previously a synchronous pure function. No existing consumer of wire-mesh-core calls canGrant (confirmed against agent-comms, the only other repo depending on this package), so there is nothing to migrate.
|
|
502
|
-
*/
|
|
503
|
-
async function canGrant(heldToken, deviceId, candidate, now) {
|
|
504
|
-
if (candidate.expires <= now) return false;
|
|
505
|
-
const heldClaims = decodeTokenClaims(heldToken);
|
|
506
|
-
if (heldClaims === void 0) return false;
|
|
507
|
-
return await checkNarrowing(heldClaims, deviceId, candidate) === void 0;
|
|
508
|
-
}
|
|
509
|
-
/** A pure query: could deviceId, presenting heldGrantCapability as its minting authority, mint a token matching candidate right now (wire-mesh#323) -- canGrant's authorised-by analogue, with the same deliberately-narrower-than-verify caveat canGrant's own doc comment states: the held token's signature and revocation status are not checked here; a caller needing those confirmed calls verifyCapabilityToken separately. The candidate carries a bearer because the authoriser's subject-mode conditions read it (the no-self-grant bar). */
|
|
510
|
-
async function canGrantVia(heldGrantCapability, deviceId, candidate, now) {
|
|
511
|
-
if (candidate.expires <= now) return false;
|
|
512
|
-
const heldClaims = decodeTokenClaims(heldGrantCapability);
|
|
513
|
-
if (heldClaims === void 0) return false;
|
|
514
|
-
return await checkAuthorisation(heldClaims, deviceId, candidate) === void 0;
|
|
515
|
-
}
|
|
516
|
-
/**
|
|
517
|
-
* Mints one capability token: builds token-claims from the given fields, signs it as a COSE_Sign1 under `identity`'s own key, with a protected header matching what the frozen conformance vectors actually encode (`{1: alg, 4: issuer device-id}`, not the empty header a token merely needs to verify against itself).
|
|
518
|
-
*
|
|
519
|
-
* When `parent` is given, every one of `tokens.cddl`'s own narrowing obligations is enforced here, at issuance, rather than left for the far end to discover minutes or hours later as a bare `delegation_exceeds_parent` from `verifyCapabilityToken` -- the same "fail loudly, fail early" reasoning that governs every other boundary in this codebase. An issuer minting an invalid delegation is a bug in the caller; this function refuses rather than producing a token indistinguishable from a valid one until someone else verifies it.
|
|
520
|
-
*/
|
|
521
|
-
async function mintCapabilityToken(options) {
|
|
522
|
-
if (options.expires <= options.clock.now()) return {
|
|
523
|
-
ok: false,
|
|
524
|
-
reason: "already_expired"
|
|
525
|
-
};
|
|
526
|
-
if (options.parent !== void 0 && options.authorisedBy !== void 0) return {
|
|
527
|
-
ok: false,
|
|
528
|
-
reason: "links_exclusive"
|
|
529
|
-
};
|
|
530
|
-
if (options.capability === "manage:grant" && options.parent !== void 0) return {
|
|
531
|
-
ok: false,
|
|
532
|
-
reason: "authorisation_parent_forbidden"
|
|
533
|
-
};
|
|
534
|
-
let parentBytes;
|
|
535
|
-
if (options.parent !== void 0) {
|
|
536
|
-
const parentClaims = decodeTokenClaims(options.parent);
|
|
537
|
-
if (parentClaims === void 0) return {
|
|
538
|
-
ok: false,
|
|
539
|
-
reason: "parent_malformed"
|
|
540
|
-
};
|
|
541
|
-
const refusal = await checkNarrowing(parentClaims, options.identity.deviceId, {
|
|
542
|
-
capability: options.capability,
|
|
543
|
-
scope: options.scope,
|
|
544
|
-
expires: options.expires,
|
|
545
|
-
...options.delegationsRemaining !== void 0 ? { delegationsRemaining: options.delegationsRemaining } : {}
|
|
546
|
-
});
|
|
547
|
-
if (refusal !== void 0) return {
|
|
548
|
-
ok: false,
|
|
549
|
-
reason: refusal
|
|
550
|
-
};
|
|
551
|
-
parentBytes = encodeBuf(options.parent);
|
|
552
|
-
}
|
|
553
|
-
let authorisationBytes;
|
|
554
|
-
if (options.authorisedBy !== void 0) {
|
|
555
|
-
const authorisingClaims = decodeTokenClaims(options.authorisedBy);
|
|
556
|
-
if (authorisingClaims === void 0) return {
|
|
557
|
-
ok: false,
|
|
558
|
-
reason: "authorisation_malformed"
|
|
559
|
-
};
|
|
560
|
-
const refusal = await checkAuthorisation(authorisingClaims, options.identity.deviceId, {
|
|
561
|
-
capability: options.capability,
|
|
562
|
-
scope: options.scope,
|
|
563
|
-
bearer: options.bearer,
|
|
564
|
-
expires: options.expires,
|
|
565
|
-
...options.delegationsRemaining !== void 0 ? { delegationsRemaining: options.delegationsRemaining } : {}
|
|
566
|
-
});
|
|
567
|
-
if (refusal !== void 0) return {
|
|
568
|
-
ok: false,
|
|
569
|
-
reason: refusal
|
|
570
|
-
};
|
|
571
|
-
authorisationBytes = encodeBuf(options.authorisedBy);
|
|
572
|
-
}
|
|
573
|
-
const payload = encodeBuf({
|
|
574
|
-
"token-id": options.tokenId,
|
|
575
|
-
issuer: options.identity.deviceId,
|
|
576
|
-
"issuer-key": options.identity.identityKey,
|
|
577
|
-
bearer: options.bearer,
|
|
578
|
-
capability: options.capability,
|
|
579
|
-
scope: options.scope,
|
|
580
|
-
expires: options.expires,
|
|
581
|
-
...options.notBefore !== void 0 ? { "not-before": options.notBefore } : {},
|
|
582
|
-
...parentBytes !== void 0 ? { parent: parentBytes } : {},
|
|
583
|
-
...options.delegationsRemaining !== void 0 ? { "delegations-remaining": options.delegationsRemaining } : {},
|
|
584
|
-
...options.conditions !== void 0 ? { conditions: encodeBuf(options.conditions) } : {},
|
|
585
|
-
...authorisationBytes !== void 0 ? { "authorised-by": authorisationBytes } : {},
|
|
586
|
-
...options.grantsCapability !== void 0 ? { "grants-capability": options.grantsCapability } : {},
|
|
587
|
-
...options.requestsCapability !== void 0 ? { "requests-capability": options.requestsCapability } : {}
|
|
588
|
-
});
|
|
589
|
-
const protectedHeader = protectedHeaderFor(options.identity);
|
|
590
|
-
return {
|
|
591
|
-
ok: true,
|
|
592
|
-
token: [
|
|
593
|
-
protectedHeader,
|
|
594
|
-
{},
|
|
595
|
-
payload,
|
|
596
|
-
await options.identity.sign(sig1ToBeSigned(protectedHeader, payload))
|
|
597
|
-
]
|
|
598
|
-
};
|
|
599
|
-
}
|
|
600
|
-
/** Mints one revocation-entry (management.cddl): a COSE_Sign1 over revocation-claims, signed the same way mintCapabilityToken signs a token. No narrowing chain to check -- a revocation entry has no parent and cannot fail to be issuable the way a delegated token can, so this returns the entry directly rather than a verdict. */
|
|
601
|
-
async function mintRevocationEntry(options) {
|
|
602
|
-
const payload = encodeBuf({
|
|
603
|
-
"token-id": options.tokenId,
|
|
604
|
-
issuer: options.identity.deviceId,
|
|
605
|
-
"issuer-key": options.identity.identityKey,
|
|
606
|
-
"revoked-at": options.revokedAt
|
|
607
|
-
});
|
|
608
|
-
const protectedHeader = protectedHeaderFor(options.identity);
|
|
609
|
-
return [
|
|
610
|
-
protectedHeader,
|
|
611
|
-
{},
|
|
612
|
-
payload,
|
|
613
|
-
await options.identity.sign(sig1ToBeSigned(protectedHeader, payload))
|
|
614
|
-
];
|
|
615
|
-
}
|
|
616
|
-
//#endregion
|
|
617
|
-
export { mintRevocationEntry as a, verifyRevocationEntry as c, mintCapabilityToken as i, canGrant as n, sig1ToBeSigned as o, canGrantVia as r, verifyCapabilityToken as s, MANAGE_GRANT_CAPABILITY as t };
|