@interop/zcap 9.0.3

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.
@@ -0,0 +1,538 @@
1
+ /*!
2
+ * Copyright (c) 2018-2024 Digital Bazaar, Inc. All rights reserved.
3
+ */
4
+ import * as utils from './utils.js';
5
+ import jsigs from '@interop/jsonld-signatures';
6
+ const {ControllerProofPurpose} = jsigs.purposes;
7
+
8
+ /* Note: This class is just an abstract base class for the
9
+ `CapabilityInvocation` and `CapabilityDelegation` proof purposes. */
10
+
11
+ /**
12
+ * @typedef {import('./utils.js').InspectCapabilityChain} InspectCapabilityChain
13
+ * @typedef {import('./utils.js').CapabilityMeta} CapabilityMeta
14
+ */
15
+
16
+ export class CapabilityProofPurpose extends ControllerProofPurpose {
17
+ /**
18
+ * @param {object} options - The options.
19
+ * @param {boolean} [options.allowTargetAttenuation=false] - Allow the
20
+ * invocationTarget of a delegation chain to be increasingly restrictive
21
+ * based on a hierarchical RESTful URL structure.
22
+ * @param {object} [options.controller] - The description of the controller,
23
+ * if it is not to be dereferenced via a `documentLoader`.
24
+ * @param {string|Date|number} [options.date] - Used during proof
25
+ * verification as the expected date for the creation of the proof
26
+ * (within a maximum timestamp delta) and for checking to see if a
27
+ * capability has expired; if not passed the current date will be used.
28
+ * @param {string|string[]} [options.expectedRootCapability] - The expected
29
+ * root capability for the delegation chain (a single root capability ID
30
+ * string, or an array of acceptable root capability ID strings).
31
+ * @param {InspectCapabilityChain} [options.inspectCapabilityChain] - An
32
+ * async function that can be used to check for revocations related to any
33
+ * of verified capabilities.
34
+ * @param {number} [options.maxChainLength=10] - The maximum length of the
35
+ * capability delegation chain.
36
+ * @param {number} [options.maxClockSkew=300] - A maximum number of seconds
37
+ * that clocks may be skewed checking capability expiration date-times
38
+ * against `date` and when comparing invocation proof creation time against
39
+ * delegation proof creation time.
40
+ * @param {number} [options.maxDelegationTtl=Infinity] - The maximum
41
+ * milliseconds to live for a delegated zcap as measured by the time
42
+ * difference between `expires` and `created` on the delegation proof.
43
+ * @param {number} [options.maxTimestampDelta=Infinity] - A maximum number
44
+ * of seconds that a capability invocation proof (only used by this proof
45
+ * type) "created" date can deviate from `date`, defaults to `Infinity`.
46
+ * @param {object|object[]} [options.suite] - The jsonld-signature suite(s) to
47
+ * use to verify the capability chain. Required only when verifying a proof;
48
+ * unused (and omitted) when creating a delegation proof.
49
+ * @param {string} options.term - The term `capabilityInvocation` or
50
+ * `capabilityDelegation` to look for in an LD proof.
51
+ */
52
+ constructor({
53
+ // proof verification params (and common to all derived classes)
54
+ allowTargetAttenuation = false,
55
+ controller,
56
+ date,
57
+ expectedRootCapability,
58
+ inspectCapabilityChain,
59
+ maxChainLength,
60
+ maxDelegationTtl = Infinity,
61
+ maxTimestampDelta = Infinity,
62
+ maxClockSkew = 300,
63
+ suite,
64
+ term
65
+ } = {}) {
66
+ super({term, controller, date, maxTimestampDelta});
67
+
68
+ // params used to verify a proof
69
+ const hasVerifyProofParams = controller || date ||
70
+ expectedRootCapability || inspectCapabilityChain || suite;
71
+ if(hasVerifyProofParams) {
72
+ if(!(typeof expectedRootCapability === 'string' ||
73
+ Array.isArray(expectedRootCapability))) {
74
+ throw new TypeError(
75
+ '"expectedRootCapability" must be a string or array.');
76
+ }
77
+
78
+ // expected root capability values must be absolute URIs
79
+ const expectedRootCapabilities = Array.isArray(expectedRootCapability) ?
80
+ expectedRootCapability : [expectedRootCapability];
81
+ for(const erc of expectedRootCapabilities) {
82
+ if(!(typeof erc === 'string' && erc.includes(':'))) {
83
+ throw new Error(
84
+ '"expectedRootCapability" values must be absolute URI strings.');
85
+ }
86
+ }
87
+
88
+ if(typeof maxClockSkew !== 'number') {
89
+ throw new TypeError('"maxClockSkew" must be a number.');
90
+ }
91
+
92
+ this.allowTargetAttenuation = allowTargetAttenuation;
93
+ this.expectedRootCapability = expectedRootCapability;
94
+ this.inspectCapabilityChain = inspectCapabilityChain;
95
+ this.maxChainLength = maxChainLength;
96
+ this.maxClockSkew = maxClockSkew;
97
+ this.maxDelegationTtl = maxDelegationTtl;
98
+ this.suite = suite;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Validates a capability proof by verifying its capability delegation chain
104
+ * from the root outward. Overrides
105
+ * {@link jsigs.ControllerProofPurpose#validate} and is structurally
106
+ * compatible with it.
107
+ *
108
+ * @param {object} proof - The proof to validate.
109
+ * @param {object} validateOptions - The validation options (passed through
110
+ * from `jsigs`), including `document` and `documentLoader`.
111
+ *
112
+ * @returns {Promise<import('@interop/jsonld-signatures').
113
+ * ProofValidateResult>} Resolves to `{valid, error?}` (plus an internal
114
+ * `dereferencedChain` on success).
115
+ */
116
+ async validate(proof, validateOptions) {
117
+ /* Note: Trust begins at the root zcap, so we start chain validation at
118
+ the root and move forward toward the tail from there. This also helps
119
+ prevent an attacker from wasting time when they submit long zcap chains
120
+ that are extensions of otherwise valid chains.
121
+
122
+ So, each parent zcap must be verified before its child is. This also means
123
+ that we can't simply recursively unwind the chain in reverse; therefore,
124
+ the code is a bit more complex.
125
+
126
+ Note that if a chain is being checked without an invocation, i.e., without
127
+ invoking the tail capability, then the tail's capability delegation *proof*
128
+ will have been cryptographically verified prior to this call. Otherwise,
129
+ it will need to be cryptographically verified. There is a signal described
130
+ below to indicate whether this verification needs to occur. Regardless, the
131
+ tail has not yet been validated as a tail for the chain and won't be until
132
+ the rest of the chain, starting at the root, is validated.
133
+
134
+ The validation process is:
135
+
136
+ 0. Run a short-circuit check to ensure that we only verify the capability
137
+ chain once; that is, we only start checking the chain when we haven't
138
+ verified any parent zcaps yet. Whether we've started checking the chain
139
+ yet or not is handled by a derived class that implements
140
+ `_shortCircuitValidate`, returning the short-circuit validation result
141
+ if the chain check has already started and `undefined` if it hasn't.
142
+ 1. If we haven't been short-circuited, then dereference the capability
143
+ chain referenced in the tail proof to get all zcaps in the chain.
144
+ 2. Run any proof-purpose specific checks prior to checking the rest of
145
+ the chain. This allows shortcuts when checking a capability invocation
146
+ proof, e.g., if an invocation is immediately invalid for some reason,
147
+ there is no need to check that the delegation rules were followed along
148
+ the entire chain. This method also returns the `capabilityChainMeta`
149
+ array to use to hold the capability delegation proof verify results. If
150
+ a capability delegation proof for the tail has already been verified,
151
+ this array will have a placeholder for its full proof validation result
152
+ as a signal to avoid duplicating this work later.
153
+ 3. Verify the chain from root => tail by calling `verifyCapabilityChain`
154
+ just once -- when validating the tail. The short-circuit check above
155
+ ensures we don't call this more than once. Additionally, the
156
+ `capabilityChainMeta` array signals whether we need to cryptographically
157
+ verify the capability delegation proof on the tail or if we must skip
158
+ this to avoid duplicating that work.
159
+ 4. Run any purpose-specific checks after chain verification. This allows
160
+ capability delegation proof checks to be run on the tail against the now
161
+ verified parent, allowing its proof validation result to be fully
162
+ constructed and updated in the `capabilityChainMeta` array (as well
163
+ as the return value for this function).
164
+ 5. Run the `inspectCapabilityChain` hook, if given, to allow for custom
165
+ implementations to check for revoked zcaps in databases or whatever other
166
+ behavior is desired. */
167
+
168
+ try {
169
+ // ensure proof has expected context (even though this is called in
170
+ // `match`, it is possible to call `validate` separately without calling
171
+ // `match`, so check here too)
172
+ utils.checkProofContext({proof});
173
+
174
+ const {document, documentLoader} = validateOptions;
175
+
176
+ // 0. Run any proof-purpose-specific short-circuit check.
177
+ const shortcircuit = await this._shortCircuitValidate({
178
+ proof, validateOptions
179
+ });
180
+ if(shortcircuit) {
181
+ return shortcircuit;
182
+ }
183
+
184
+ /* 1. Dereference the capability chain. This involves finding all
185
+ embedded delegated zcaps, using a verifier-trusted hook to dereference
186
+ the root zcap, and putting the full zcaps in order (root => tail) in an
187
+ array. The `tail` is the zcap that was invoked. */
188
+ const {dereferencedChain} = await this._dereferenceChain({
189
+ document, documentLoader, proof
190
+ });
191
+
192
+ /* 2. Run any proof-purpose-specific early checks prior to chain
193
+ verification. */
194
+ const {
195
+ capabilityChainMeta
196
+ } = await this._runChecksBeforeChainVerification({
197
+ dereferencedChain, proof, validateOptions
198
+ });
199
+
200
+ /* 3. Verify the capability delegation chain. This will make sure that
201
+ the root zcap in the chain is as expected (for the endpoint where the
202
+ invocation occurred) and that every other zcap in the chain (including
203
+ the invoked one), has been properly delegated. */
204
+ const {
205
+ verified, error
206
+ } = await this._verifyCapabilityChain({
207
+ // required to avoid circular dependencies
208
+ CapabilityDelegation: this._getCapabilityDelegationClass(),
209
+ capabilityChainMeta,
210
+ dereferencedChain,
211
+ documentLoader
212
+ });
213
+ if(!verified) {
214
+ throw error;
215
+ }
216
+
217
+ /* 4. Run any proof-purpose-specific checks after chain verification
218
+ to get the proof validation result. */
219
+ const validateResult = await this._runChecksAfterChainVerification({
220
+ capabilityChainMeta, dereferencedChain, proof, validateOptions
221
+ });
222
+
223
+ // 5. Run `inspectCapabilityChain` hook.
224
+ const {inspectCapabilityChain} = this;
225
+ if(inspectCapabilityChain) {
226
+ const {valid, error} = await inspectCapabilityChain({
227
+ // full chain, including root zcap
228
+ capabilityChain: dereferencedChain,
229
+ // capability chain meta including `null` for root zcap
230
+ capabilityChainMeta: [{verifyResult: null}, ...capabilityChainMeta]
231
+ });
232
+ if(!valid) {
233
+ throw error;
234
+ }
235
+ }
236
+
237
+ // include dereferenced chain result
238
+ validateResult.dereferencedChain = dereferencedChain;
239
+
240
+ return validateResult;
241
+ } catch(error) {
242
+ return {valid: false, error};
243
+ }
244
+ }
245
+
246
+ async _dereferenceChain({document, documentLoader, proof}) {
247
+ const {expectedRootCapability, maxChainLength} = this;
248
+ const {capability} = this._getTailCapability({document, proof});
249
+ const {dereferencedChain} = await utils.dereferenceCapabilityChain({
250
+ capability,
251
+ async getRootCapability({id}) {
252
+ // ensure root zcap in chain is as expected
253
+ let match;
254
+ if(typeof expectedRootCapability === 'string') {
255
+ match = expectedRootCapability === id;
256
+ } else {
257
+ match = expectedRootCapability.includes(id);
258
+ }
259
+ if(!match) {
260
+ const error = new Error(
261
+ `Actual root capability (${id}) does not match expected root ` +
262
+ `capability (${expectedRootCapability}).`);
263
+ error.details = {
264
+ actual: id,
265
+ expected: expectedRootCapability,
266
+ };
267
+ throw error;
268
+ }
269
+
270
+ // load root zcap
271
+ const {document} = await documentLoader(id);
272
+ return {rootCapability: document};
273
+ },
274
+ maxChainLength
275
+ });
276
+ return {dereferencedChain};
277
+ }
278
+
279
+ _getCapabilityDelegationClass() {
280
+ throw new Error('Not implemented.');
281
+ }
282
+
283
+ async _getTailCapability(/*{document, proof}*/) {
284
+ throw new Error('Not implemented.');
285
+ }
286
+
287
+ // no-op by default
288
+ async _runChecksBeforeChainVerification() {}
289
+
290
+ // no-op by default
291
+ async _runChecksAfterChainVerification() {}
292
+
293
+ async _runBaseProofValidation({proof, validateOptions}) {
294
+ // run super class's validation checks
295
+ const result = await super.validate(proof, validateOptions);
296
+ if(!result.valid) {
297
+ throw result.error;
298
+ }
299
+ return result;
300
+ }
301
+
302
+ // no-op by default
303
+ async _shortCircuitValidate() {}
304
+
305
+ /**
306
+ * Verifies the given dereferenced capability chain. This involves ensuring
307
+ * that the root zcap in the chain is as expected (for the endpoint where an
308
+ * invocation or a simple chain chain is occurring) and that every other zcap
309
+ * in the chain (including any invoked one), has been properly delegated.
310
+ *
311
+ * @param {object} options - The options.
312
+ * @param {Function} options.CapabilityDelegation - The CapabilityDelegation
313
+ * class; this must be passed to avoid circular references in this module.
314
+ * @param {CapabilityMeta[]} options.capabilityChainMeta - The array of
315
+ * results for inspecting the capability chain; if this has a value when
316
+ * passed, then it is presumed to be the verify result for the tail
317
+ * capability and that tail capability will not be verified internally by
318
+ * this function to avoid duplicating work; all verification results
319
+ * (including the tail's -- either computed locally or reused from what
320
+ * was passed) will be added to this array in order from root => tail.
321
+ * @param {Array} options.dereferencedChain - The dereferenced capability
322
+ * chain for `capability`, starting at the root capability and ending at
323
+ * `capability`.
324
+ * @param {Function} options.documentLoader - A configured jsonld
325
+ * documentLoader.
326
+ *
327
+ * @returns {object} An object with `{verified, error}`.
328
+ */
329
+ async _verifyCapabilityChain({
330
+ CapabilityDelegation,
331
+ capabilityChainMeta,
332
+ dereferencedChain,
333
+ documentLoader
334
+ }) {
335
+ /* Note: We start verifying a capability chain at its root of trust (the
336
+ root capability) and then move toward the tail. To prevent recursively
337
+ repeating checks, we pass a `verifiedParentCapability` each time we start
338
+ verifying another capability delegation proof in the capability chain.
339
+
340
+ Verification process is:
341
+
342
+ 1. If the chain only as the root capability, exit early.
343
+ 2. For each capability `zcap` in the chain, verify the capability delegation
344
+ proof on `zcap` (if `capabilityChainMeta` has no precomputed result) and
345
+ that all of the delegation rules have been followed. */
346
+
347
+ try {
348
+ // 1. If the chain only has the root, exit early.
349
+ if(dereferencedChain.length === 1) {
350
+ return {verified: true};
351
+ }
352
+
353
+ // 2. For each capability `zcap` in the chain, verify the capability
354
+ // delegation proof on `zcap` and that the delegation rules have been
355
+ // followed.
356
+ let parentAllowedAction;
357
+ let parentDelegationTime;
358
+ let parentExpirationTime;
359
+ const [root] = dereferencedChain;
360
+ let {invocationTarget: parentInvocationTarget} = root;
361
+
362
+ // track whether `capabilityChainMeta` needs its first result shifted to
363
+ // the end (if a result was present, it is for the last or "tail" zcap,
364
+ // so we set a flag to remember to move it to the end when we're done
365
+ // checking zcaps below)
366
+ const mustShift = capabilityChainMeta.length > 0;
367
+
368
+ // get all delegated capabilities (no root zcap since it has no delegation
369
+ // proof to check)
370
+ const delegatedCapabilities = dereferencedChain.slice(1);
371
+ const {
372
+ allowTargetAttenuation,
373
+ expectedRootCapability,
374
+ date,
375
+ maxClockSkew,
376
+ maxDelegationTtl,
377
+ suite
378
+ } = this;
379
+ const currentDate = (date && new Date(date)) || new Date();
380
+ for(let i = 0; i < delegatedCapabilities.length; ++i) {
381
+ const zcap = delegatedCapabilities[i];
382
+ /* Note: Passing `_verifiedParentCapability` will prevent repetitive
383
+ checking of the same segments of the chain (once a parent is verified,
384
+ its chain is not checked again when checking its children). */
385
+ const _verifiedParentCapability = delegatedCapabilities[i - 1] || root;
386
+
387
+ // verify proof on zcap if no result has been computed yet (one
388
+ // verify result will be present in `capabilityChainMeta` per
389
+ // delegated capability)
390
+ if(capabilityChainMeta.length < delegatedCapabilities.length) {
391
+ const verifyResult = await jsigs.verify(zcap, {
392
+ suite,
393
+ purpose: new CapabilityDelegation({
394
+ allowTargetAttenuation,
395
+ date: currentDate,
396
+ expectedRootCapability,
397
+ maxDelegationTtl,
398
+ _verifiedParentCapability
399
+ }),
400
+ documentLoader
401
+ });
402
+ if(!verifyResult.verified) {
403
+ throw verifyResult.error;
404
+ }
405
+ // delegation proof verified; save meta data for later inspection
406
+ capabilityChainMeta.push({verifyResult});
407
+ }
408
+
409
+ // ensure `allowedAction` is valid (compared against parent)
410
+ const {allowedAction} = zcap;
411
+ if(!utils.hasValidAllowedAction({allowedAction, parentAllowedAction})) {
412
+ throw new Error(
413
+ 'The "allowedAction" in a delegated capability ' +
414
+ 'must not be less restrictive than its parent.');
415
+ }
416
+
417
+ // ensure `invocationTarget` delegation is acceptable
418
+ const invocationTarget = utils.getTarget({capability: zcap});
419
+ if(!utils.isValidTarget({
420
+ invocationTarget,
421
+ baseInvocationTarget: parentInvocationTarget,
422
+ allowTargetAttenuation
423
+ })) {
424
+ if(allowTargetAttenuation) {
425
+ throw new Error(
426
+ `The "invocationTarget" in a delegated capability must not be ` +
427
+ 'less restrictive than its parent.');
428
+ } else {
429
+ throw new Error(
430
+ 'The "invocationTarget" in a delegated capability ' +
431
+ 'must be equivalent to its parent.');
432
+ }
433
+ }
434
+
435
+ // verify expiration dates
436
+ // expires date has been previously validated, so just parse it
437
+ const currentCapabilityExpirationTime = Date.parse(zcap.expires);
438
+
439
+ // if the parent does not specify an expiration date, then any more
440
+ // restrictive expiration date is acceptable
441
+ if(parentExpirationTime !== undefined) {
442
+ // handle case where `expires` is set in the parent, but the child
443
+ // has an expiration date greater than the parent
444
+ if(currentCapabilityExpirationTime > parentExpirationTime) {
445
+ // `utils.compareTime` intentionally not used; the delegator MUST
446
+ // not use an `expires` value later than what is in the parent,
447
+ // which they have access to (not a decentralized clock problem)
448
+ throw new Error(
449
+ 'The `expires` property in a delegated capability must not ' +
450
+ 'be less restrictive than its parent.');
451
+ }
452
+ // use `utils.compareTime` to allow for allow for clock drift because
453
+ // we are comparing against `currentDate`
454
+ if(utils.compareTime({
455
+ t1: currentDate.getTime(),
456
+ t2: parentExpirationTime,
457
+ maxClockSkew
458
+ }) > 0) {
459
+ throw new Error(
460
+ 'A capability in the delegation chain has expired.');
461
+ }
462
+ }
463
+
464
+ // get delegated date-time
465
+ // note: there can be only one proof here and this has already been
466
+ // validated to be the case during `dereferenceCapabilityChain`
467
+ const [proof] = utils.getDelegationProofs({capability: zcap});
468
+ const currentCapabilityDelegationTime = Date.parse(proof.created);
469
+
470
+ // verify parent capability was not delegated after child
471
+ if(parentDelegationTime !== undefined &&
472
+ parentDelegationTime > currentCapabilityDelegationTime) {
473
+ throw new Error(
474
+ 'A capability in the delegation chain was delegated before ' +
475
+ 'its parent.');
476
+ }
477
+
478
+ // some systems may require historical verification of zcaps, so
479
+ // allow `maxDelegationTtl` of `Infinity`
480
+ if(maxDelegationTtl < Infinity) {
481
+ /* Note: Here we ensure zcap has a time-to-live (TTL) that is
482
+ sufficiently short. This is to prevent the use of zcaps that, when
483
+ revoked, will have to be stored for long periods of time. We have to
484
+ ensure:
485
+
486
+ 1. The zcap's delegation date is not in the future (this also ensures
487
+ that the zcap's expiration date is not before its delegation date
488
+ as it would have triggered an expiration error in a previous check).
489
+ 2. The zcap's current TTL is <= `maxDelegationTtl`
490
+ 3. The zcap's TTL was never > `maxDelegationTtl`. */
491
+
492
+ // use `utils.compareTime` to allow for allow for clock drift because
493
+ // we are comparing against `currentDate`
494
+ if(utils.compareTime({
495
+ t1: currentCapabilityDelegationTime,
496
+ t2: currentDate.getTime(),
497
+ maxClockSkew
498
+ }) > 0) {
499
+ throw new Error(
500
+ 'A delegated capability in the delegation chain was delegated ' +
501
+ 'in the future.');
502
+ }
503
+ const currentTtl = currentCapabilityExpirationTime -
504
+ currentDate.getTime();
505
+ const maxTtl = currentCapabilityExpirationTime -
506
+ currentCapabilityDelegationTime;
507
+ // use `utils.compareTime` to allow for allow for clock drift because
508
+ // we are comparing against `currentDate`
509
+ const currentTtlComparison = utils.compareTime({
510
+ t1: currentTtl,
511
+ t2: maxDelegationTtl,
512
+ maxClockSkew
513
+ });
514
+ if(currentTtlComparison > 0 || maxTtl > maxDelegationTtl) {
515
+ throw new Error(
516
+ 'A delegated capability in the delegation chain has a time to ' +
517
+ 'live that is too long.');
518
+ }
519
+ }
520
+
521
+ parentAllowedAction = allowedAction;
522
+ parentExpirationTime = currentCapabilityExpirationTime;
523
+ parentDelegationTime = currentCapabilityDelegationTime;
524
+ parentInvocationTarget = invocationTarget;
525
+ }
526
+
527
+ // shift zcap verify result for last zcap to the end of meta array if
528
+ // necessary
529
+ if(mustShift) {
530
+ capabilityChainMeta.push(capabilityChainMeta.shift());
531
+ }
532
+
533
+ return {verified: true};
534
+ } catch(error) {
535
+ return {verified: false, error};
536
+ }
537
+ }
538
+ }
@@ -0,0 +1,32 @@
1
+ /*!
2
+ * Copyright (c) 2018-2022 Digital Bazaar, Inc. All rights reserved.
3
+ */
4
+ import {CONTEXT, CONTEXT_URL} from '@digitalbazaar/zcap-context';
5
+
6
+ // Re-exported through local typed bindings (rather than a direct
7
+ // `export ... from`) so the generated `.d.ts` carries explicit types instead of
8
+ // a re-export that points at `@digitalbazaar/zcap-context`, which ships no
9
+ // types. This lets downstream consumers resolve `ZCAP_CONTEXT`/
10
+ // `ZCAP_CONTEXT_URL` without needing types for that package. The values are
11
+ // `const` in zcap-context, so the snapshot binding is equivalent to a live one.
12
+
13
+ /** @type {object} The zcap JSON-LD context document. */
14
+ export const ZCAP_CONTEXT = CONTEXT;
15
+
16
+ /** @type {string} The zcap JSON-LD context URL (`https://w3id.org/zcap/v1`). */
17
+ export const ZCAP_CONTEXT_URL = CONTEXT_URL;
18
+
19
+ /** @type {string} The base URL for the zcap security vocabulary. */
20
+ export const CAPABILITY_VOCAB_URL = 'https://w3id.org/security#';
21
+
22
+ /** @type {string} URI prefix for root capability IDs (`urn:zcap:root:`). */
23
+ export const ZCAP_ROOT_PREFIX = 'urn:zcap:root:';
24
+
25
+ /**
26
+ * Default maximum capability delegation chain length (inclusive of the tail).
27
+ *
28
+ * @type {number}
29
+ */
30
+ // 6 is probably more reasonable for Kevin Bacon reasons? but picking a
31
+ // power of 10
32
+ export const MAX_CHAIN_LENGTH = 10;
package/lib/index.js ADDED
@@ -0,0 +1,60 @@
1
+ /*!
2
+ * Copyright (c) 2018-2022 Digital Bazaar, Inc. All rights reserved.
3
+ */
4
+ import jsigs from '@interop/jsonld-signatures';
5
+
6
+ /* Core API */
7
+ export {CapabilityInvocation} from './CapabilityInvocation.js';
8
+ export {CapabilityDelegation} from './CapabilityDelegation.js';
9
+ export {createRootCapability} from './utils.js';
10
+ import * as constants from './constants.js';
11
+ export {constants};
12
+
13
+ /**
14
+ * @typedef {import('./utils.js').RootZcap} RootZcap
15
+ * @typedef {import('./utils.js').DelegatedZcap} DelegatedZcap
16
+ * @typedef {import('./utils.js').Zcap} Zcap
17
+ * @typedef {import('./utils.js').CapabilityDelegationProof} CapabilityDelegationProof
18
+ * @typedef {import('./utils.js').InspectCapabilityChain} InspectCapabilityChain
19
+ * @typedef {import('./utils.js').InspectResult} InspectResult
20
+ * @typedef {import('./utils.js').CapabilityChainDetails} CapabilityChainDetails
21
+ * @typedef {import('./utils.js').CapabilityMeta} CapabilityMeta
22
+ * @typedef {import('./utils.js').VerifyResult} VerifyResult
23
+ * @typedef {import('./utils.js').VerifyProofResult} VerifyProofResult
24
+ * @typedef {import('./utils.js').VerifyProofPurposeResult} VerifyProofPurposeResult
25
+ */
26
+
27
+ /**
28
+ * Wraps an existing document loader so that it also serves the zcap JSON-LD
29
+ * context. The wrapped loader is called for all other URLs.
30
+ *
31
+ * @param {Function} documentLoader - An existing JSON-LD document loader to
32
+ * extend.
33
+ *
34
+ * @returns {Function} A new document loader that handles the zcap context URL
35
+ * and delegates all other URLs to the wrapped loader.
36
+ */
37
+ export function extendDocumentLoader(documentLoader) {
38
+ return async function loadZcapContexts(url) {
39
+ if(url === constants.ZCAP_CONTEXT_URL) {
40
+ return {
41
+ contextUrl: null,
42
+ documentUrl: url,
43
+ document: constants.ZCAP_CONTEXT,
44
+ tag: 'static'
45
+ };
46
+ }
47
+ return documentLoader(url);
48
+ };
49
+ }
50
+
51
+ /**
52
+ * A default JSON-LD document loader that serves only the zcap and
53
+ * jsonld-signatures contexts. Suitable for use when no other contexts are
54
+ * needed. Extend it with {@link extendDocumentLoader} if additional contexts
55
+ * are required.
56
+ *
57
+ * @type {Function}
58
+ */
59
+ export const documentLoader = extendDocumentLoader(
60
+ jsigs.strictDocumentLoader);