@interop/zcap 10.1.0 → 11.0.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/README.md +21 -3
- package/dist/CapabilityDelegation.d.ts +173 -0
- package/dist/CapabilityDelegation.d.ts.map +1 -0
- package/dist/CapabilityDelegation.js +372 -0
- package/dist/CapabilityDelegation.js.map +1 -0
- package/dist/CapabilityInvocation.d.ts +151 -0
- package/dist/CapabilityInvocation.d.ts.map +1 -0
- package/dist/CapabilityInvocation.js +365 -0
- package/dist/CapabilityInvocation.js.map +1 -0
- package/dist/CapabilityProofPurpose.d.ts +203 -0
- package/dist/CapabilityProofPurpose.d.ts.map +1 -0
- package/dist/CapabilityProofPurpose.js +531 -0
- package/dist/CapabilityProofPurpose.js.map +1 -0
- package/dist/constants.d.ts +11 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +23 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +224 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +250 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +591 -0
- package/dist/utils.js.map +1 -0
- package/package.json +47 -34
- package/lib/CapabilityDelegation.js +0 -312
- package/lib/CapabilityInvocation.js +0 -343
- package/lib/CapabilityProofPurpose.js +0 -538
- package/lib/constants.js +0 -32
- package/lib/index.js +0 -55
- package/lib/utils.js +0 -673
- package/types/lib/CapabilityDelegation.d.ts +0 -101
- package/types/lib/CapabilityDelegation.d.ts.map +0 -1
- package/types/lib/CapabilityInvocation.d.ts +0 -100
- package/types/lib/CapabilityInvocation.d.ts.map +0 -1
- package/types/lib/CapabilityProofPurpose.d.ts +0 -126
- package/types/lib/CapabilityProofPurpose.d.ts.map +0 -1
- package/types/lib/constants.d.ts +0 -15
- package/types/lib/constants.d.ts.map +0 -1
- package/types/lib/index.d.ts +0 -42
- package/types/lib/index.d.ts.map +0 -1
- package/types/lib/utils.d.ts +0 -308
- package/types/lib/utils.d.ts.map +0 -1
|
@@ -1,538 +0,0 @@
|
|
|
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
|
-
}
|
package/lib/constants.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
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').InspectCapabilityChain} InspectCapabilityChain
|
|
15
|
-
* @typedef {import('./utils.js').InspectResult} InspectResult
|
|
16
|
-
* @typedef {import('./utils.js').CapabilityChainDetails} CapabilityChainDetails
|
|
17
|
-
* @typedef {import('./utils.js').CapabilityMeta} CapabilityMeta
|
|
18
|
-
* @typedef {import('./utils.js').VerifyResult} VerifyResult
|
|
19
|
-
* @typedef {import('./utils.js').VerifyProofResult} VerifyProofResult
|
|
20
|
-
* @typedef {import('./utils.js').VerifyProofPurposeResult} VerifyProofPurposeResult
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Wraps an existing document loader so that it also serves the zcap JSON-LD
|
|
25
|
-
* context. The wrapped loader is called for all other URLs.
|
|
26
|
-
*
|
|
27
|
-
* @param {Function} documentLoader - An existing JSON-LD document loader to
|
|
28
|
-
* extend.
|
|
29
|
-
*
|
|
30
|
-
* @returns {Function} A new document loader that handles the zcap context URL
|
|
31
|
-
* and delegates all other URLs to the wrapped loader.
|
|
32
|
-
*/
|
|
33
|
-
export function extendDocumentLoader(documentLoader) {
|
|
34
|
-
return async function loadZcapContexts(url) {
|
|
35
|
-
if(url === constants.ZCAP_CONTEXT_URL) {
|
|
36
|
-
return {
|
|
37
|
-
contextUrl: null,
|
|
38
|
-
documentUrl: url,
|
|
39
|
-
document: constants.ZCAP_CONTEXT,
|
|
40
|
-
tag: 'static'
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
return documentLoader(url);
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* A default JSON-LD document loader that serves only the zcap and
|
|
49
|
-
* jsonld-signatures contexts. Suitable for use when no other contexts are
|
|
50
|
-
* needed. Extend it with {@link extendDocumentLoader} if additional contexts
|
|
51
|
-
* are required.
|
|
52
|
-
*
|
|
53
|
-
* @type {Function}
|
|
54
|
-
*/
|
|
55
|
-
export const documentLoader = extendDocumentLoader(jsigs.strictDocumentLoader);
|