@interop/was-client 0.13.3 → 0.14.1
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 +33 -0
- package/dist/Collection.d.ts +58 -0
- package/dist/Collection.d.ts.map +1 -1
- package/dist/Collection.js +106 -13
- package/dist/Collection.js.map +1 -1
- package/dist/Resource.d.ts +8 -0
- package/dist/Resource.d.ts.map +1 -1
- package/dist/Resource.js +52 -34
- package/dist/Resource.js.map +1 -1
- package/dist/Space.d.ts +42 -1
- package/dist/Space.d.ts.map +1 -1
- package/dist/Space.js +55 -2
- package/dist/Space.js.map +1 -1
- package/dist/WasClient.d.ts +15 -0
- package/dist/WasClient.d.ts.map +1 -1
- package/dist/WasClient.js +52 -21
- package/dist/WasClient.js.map +1 -1
- package/dist/codec.d.ts +14 -11
- package/dist/codec.d.ts.map +1 -1
- package/dist/edv/EdvCodec.d.ts +34 -56
- package/dist/edv/EdvCodec.d.ts.map +1 -1
- package/dist/edv/EdvCodec.js +155 -52
- package/dist/edv/EdvCodec.js.map +1 -1
- package/dist/edv/WasTransport.d.ts +84 -19
- package/dist/edv/WasTransport.d.ts.map +1 -1
- package/dist/edv/WasTransport.js +198 -44
- package/dist/edv/WasTransport.js.map +1 -1
- package/dist/edv/epochCrypto.d.ts +101 -0
- package/dist/edv/epochCrypto.d.ts.map +1 -0
- package/dist/edv/epochCrypto.js +211 -0
- package/dist/edv/epochCrypto.js.map +1 -0
- package/dist/edv/epochKeys.d.ts +57 -0
- package/dist/edv/epochKeys.d.ts.map +1 -0
- package/dist/edv/epochKeys.js +132 -0
- package/dist/edv/epochKeys.js.map +1 -0
- package/dist/edv/index.d.ts +8 -0
- package/dist/edv/index.d.ts.map +1 -1
- package/dist/edv/index.js +7 -0
- package/dist/edv/index.js.map +1 -1
- package/dist/edv/recipients.d.ts +122 -0
- package/dist/edv/recipients.d.ts.map +1 -0
- package/dist/edv/recipients.js +265 -0
- package/dist/edv/recipients.js.map +1 -0
- package/dist/errors.d.ts +31 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +37 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/codec.d.ts.map +1 -1
- package/dist/internal/codec.js +12 -4
- package/dist/internal/codec.js.map +1 -1
- package/dist/internal/conditional.d.ts +6 -1
- package/dist/internal/conditional.d.ts.map +1 -1
- package/dist/internal/conditional.js +8 -1
- package/dist/internal/conditional.js.map +1 -1
- package/dist/internal/content.d.ts +2 -2
- package/dist/internal/content.d.ts.map +1 -1
- package/dist/internal/content.js +16 -6
- package/dist/internal/content.js.map +1 -1
- package/dist/internal/grant.d.ts +11 -1
- package/dist/internal/grant.d.ts.map +1 -1
- package/dist/internal/grant.js +32 -3
- package/dist/internal/grant.js.map +1 -1
- package/dist/internal/paths.d.ts +28 -0
- package/dist/internal/paths.d.ts.map +1 -1
- package/dist/internal/paths.js +52 -1
- package/dist/internal/paths.js.map +1 -1
- package/dist/internal/revoke.d.ts +34 -0
- package/dist/internal/revoke.d.ts.map +1 -0
- package/dist/internal/revoke.js +106 -0
- package/dist/internal/revoke.js.map +1 -0
- package/dist/internal/write.d.ts.map +1 -1
- package/dist/internal/write.js +5 -1
- package/dist/internal/write.js.map +1 -1
- package/dist/types.d.ts +77 -5
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -5
package/dist/edv/WasTransport.js
CHANGED
|
@@ -26,11 +26,16 @@
|
|
|
26
26
|
* server that does not will reject it with 415). Pass `contentType:
|
|
27
27
|
* JOSE_CONTENT_TYPE` to opt into it where the server supports it.
|
|
28
28
|
*
|
|
29
|
-
* Scope: documents
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
29
|
+
* Scope: documents (`insert` / `update` / `get`) plus blinded-index content
|
|
30
|
+
* query (`find`, the `blinded-index` profile of the reserved Collection
|
|
31
|
+
* `POST .../query` endpoint -- the server's `blinded-index-query` backend
|
|
32
|
+
* feature). `updateIndex` throws: in this profile the `indexed` array rides
|
|
33
|
+
* inside the stored document envelope, so `update()` IS the re-index
|
|
34
|
+
* operation and no separate index endpoint exists. Chunked streams
|
|
35
|
+
* (`storeChunk` / `getChunk`) still require the server's `chunked-streams`
|
|
36
|
+
* affordance (the reserved `/{id}/chunks/{n}` sub-segment), which neither
|
|
37
|
+
* reference backend provides, so they throw.
|
|
38
|
+
* `insert` uses an atomic `If-None-Match: *` create when the backend
|
|
34
39
|
* advertises the optional `conditional-writes` feature; otherwise (and for
|
|
35
40
|
* `update`) writes are advisory -- the EDV `sequence` is not enforced
|
|
36
41
|
* (last-writer-wins on `update`).
|
|
@@ -38,9 +43,20 @@
|
|
|
38
43
|
import { Transport } from '@interop/edv-client';
|
|
39
44
|
import { httpStatus } from '../errors.js';
|
|
40
45
|
import { readJsonData } from '../internal/content.js';
|
|
41
|
-
import { collectionBackend, resourcePath } from '../internal/paths.js';
|
|
46
|
+
import { collectionBackend, collectionQuery, resourcePath } from '../internal/paths.js';
|
|
42
47
|
import { DEFAULT_CONTENT_TYPE, envelopeBytes, JOSE_CONTENT_TYPE } from './constants.js';
|
|
43
48
|
export { JOSE_CONTENT_TYPE };
|
|
49
|
+
/**
|
|
50
|
+
* HTTP statuses that mean the backend-descriptor endpoint is legitimately
|
|
51
|
+
* absent (or explicitly unimplemented), as opposed to transiently failing:
|
|
52
|
+
* `404` (no such endpoint), `405` (endpoint does not answer `GET`), `501`
|
|
53
|
+
* (not implemented). These are a definitive "this server advertises no
|
|
54
|
+
* backend features" answer and are safe to cache -- every affordance gate
|
|
55
|
+
* then falls closed against a server that has no backend descriptors. Any
|
|
56
|
+
* other failure (network error, timeout, `401`, `429`, other `5xx`) is
|
|
57
|
+
* transient/ambiguous and is re-probed instead of cached.
|
|
58
|
+
*/
|
|
59
|
+
const DESCRIPTOR_ABSENT_STATUSES = new Set([404, 405, 501]);
|
|
44
60
|
/**
|
|
45
61
|
* Builds an `Error` carrying the `name` that `EdvClientCore` (and the reference
|
|
46
62
|
* `HttpsTransport`) dispatch on -- `DuplicateError`, `InvalidStateError`,
|
|
@@ -65,7 +81,7 @@ export class WasTransport extends Transport {
|
|
|
65
81
|
collectionId;
|
|
66
82
|
contentType;
|
|
67
83
|
_was;
|
|
68
|
-
|
|
84
|
+
_backendFeaturesPromise;
|
|
69
85
|
/**
|
|
70
86
|
* @param options {object}
|
|
71
87
|
* @param options.was {WasClient} a WAS client holding the signer
|
|
@@ -116,30 +132,66 @@ export class WasTransport extends Transport {
|
|
|
116
132
|
});
|
|
117
133
|
}
|
|
118
134
|
/**
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
* `
|
|
135
|
+
* The feature tokens the collection's backend advertises in its "Collection
|
|
136
|
+
* Backend Selected" descriptor (e.g. `conditional-writes`,
|
|
137
|
+
* `blinded-index-query`). Memoized once it produces a definitive answer: a
|
|
138
|
+
* successful read (including one that lists no features) and a definitive
|
|
139
|
+
* "endpoint absent" (`404` / `405` / `501`) both resolve to a cached feature
|
|
140
|
+
* list, so every affordance gate falls closed against a server that has no
|
|
141
|
+
* backend descriptors.
|
|
124
142
|
*
|
|
125
|
-
*
|
|
143
|
+
* A transient/ambiguous failure (network error, timeout, `401`, `429`, other
|
|
144
|
+
* `5xx`) is NOT cached: the memo is cleared so the next call re-probes, and
|
|
145
|
+
* the error is rethrown so the caller fails loud rather than silently
|
|
146
|
+
* degrading atomicity against a server that may well be capable. (A single
|
|
147
|
+
* transient failure must not poison the transport for its lifetime.)
|
|
148
|
+
*
|
|
149
|
+
* @returns {Promise<string[]>}
|
|
126
150
|
*/
|
|
127
|
-
|
|
128
|
-
this.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
151
|
+
_backendFeatures() {
|
|
152
|
+
this._backendFeaturesPromise ??= this._probeBackendFeatures();
|
|
153
|
+
return this._backendFeaturesPromise;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Reads and parses the backend descriptor once. On a definitive answer
|
|
157
|
+
* (success, or a `404` / `405` / `501` that means the endpoint is legitimately
|
|
158
|
+
* absent) resolves the feature list, which `_backendFeatures` then caches. On
|
|
159
|
+
* a transient failure, clears the memo (so the next call re-probes) and
|
|
160
|
+
* rethrows.
|
|
161
|
+
*
|
|
162
|
+
* @returns {Promise<string[]>}
|
|
163
|
+
*/
|
|
164
|
+
async _probeBackendFeatures() {
|
|
165
|
+
try {
|
|
166
|
+
const response = await this._was.request({
|
|
167
|
+
path: collectionBackend(this.spaceId, this.collectionId),
|
|
168
|
+
method: 'GET'
|
|
169
|
+
});
|
|
170
|
+
const descriptor = (await readJsonData(response));
|
|
171
|
+
return Array.isArray(descriptor?.features)
|
|
172
|
+
? descriptor.features.filter((feature) => typeof feature === 'string')
|
|
173
|
+
: [];
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
const status = httpStatus(err);
|
|
177
|
+
if (status !== undefined && DESCRIPTOR_ABSENT_STATUSES.has(status)) {
|
|
178
|
+
return [];
|
|
140
179
|
}
|
|
141
|
-
|
|
142
|
-
|
|
180
|
+
// Transient/ambiguous: do not cache this failure -- drop the memo so the
|
|
181
|
+
// next call re-probes -- and rethrow.
|
|
182
|
+
this._backendFeaturesPromise = undefined;
|
|
183
|
+
throw err;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Whether the collection's backend advertises the optional
|
|
188
|
+
* `conditional-writes` feature, so `insert` can use an atomic
|
|
189
|
+
* `If-None-Match: *` create instead of the advisory existence-check path.
|
|
190
|
+
*
|
|
191
|
+
* @returns {Promise<boolean>}
|
|
192
|
+
*/
|
|
193
|
+
async _conditionalWrites() {
|
|
194
|
+
return (await this._backendFeatures()).includes('conditional-writes');
|
|
143
195
|
}
|
|
144
196
|
/**
|
|
145
197
|
* @inheritdoc
|
|
@@ -150,7 +202,9 @@ export class WasTransport extends Transport {
|
|
|
150
202
|
* `PUT` with `If-None-Match: *`, and the server's 412 maps to
|
|
151
203
|
* `DuplicateError`. Otherwise it degrades to a bodiless existence check
|
|
152
204
|
* (`HEAD`) before the `PUT` -- advisory and non-atomic, but no longer
|
|
153
|
-
* downloading the whole stored envelope just to discard it.
|
|
205
|
+
* downloading the whole stored envelope just to discard it. In either path a
|
|
206
|
+
* 409 (a `unique: true` blinded attribute already held by another document)
|
|
207
|
+
* likewise maps to `DuplicateError`.
|
|
154
208
|
*
|
|
155
209
|
* @param options {object}
|
|
156
210
|
* @param options.encrypted {IEncryptedDocument} the document to insert
|
|
@@ -172,6 +226,14 @@ export class WasTransport extends Transport {
|
|
|
172
226
|
cause: err
|
|
173
227
|
});
|
|
174
228
|
}
|
|
229
|
+
if (httpStatus(err) === 409) {
|
|
230
|
+
throw namedError({
|
|
231
|
+
name: 'DuplicateError',
|
|
232
|
+
message: 'A unique indexed attribute value is already held by another ' +
|
|
233
|
+
'document in this collection.',
|
|
234
|
+
cause: err
|
|
235
|
+
});
|
|
236
|
+
}
|
|
175
237
|
throw err;
|
|
176
238
|
}
|
|
177
239
|
return;
|
|
@@ -182,7 +244,20 @@ export class WasTransport extends Transport {
|
|
|
182
244
|
message: `A document with id "${encrypted.id}" already exists.`
|
|
183
245
|
});
|
|
184
246
|
}
|
|
185
|
-
|
|
247
|
+
try {
|
|
248
|
+
await this._put(encrypted.id, encrypted);
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
if (httpStatus(err) === 409) {
|
|
252
|
+
throw namedError({
|
|
253
|
+
name: 'DuplicateError',
|
|
254
|
+
message: 'A unique indexed attribute value is already held by another ' +
|
|
255
|
+
'document in this collection.',
|
|
256
|
+
cause: err
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
throw err;
|
|
260
|
+
}
|
|
186
261
|
}
|
|
187
262
|
/**
|
|
188
263
|
* @inheritdoc
|
|
@@ -191,6 +266,14 @@ export class WasTransport extends Transport {
|
|
|
191
266
|
* here -- without server-side conditional writes, a stale write is not
|
|
192
267
|
* rejected (last-writer-wins).
|
|
193
268
|
*
|
|
269
|
+
* Two write-time conflicts are mapped to the names `EdvClientCore` dispatches
|
|
270
|
+
* on. A server enforcing conditional writes rejects a stale/sequence
|
|
271
|
+
* conflict with 412 (precondition-failed), which surfaces as
|
|
272
|
+
* `InvalidStateError` -- the recoverable case, by re-fetching the current
|
|
273
|
+
* document and retrying. A 409 is the EDV unique-attribute collision (a
|
|
274
|
+
* `unique: true` blinded attribute already held by another document), which
|
|
275
|
+
* is NOT recoverable by re-fetch-and-retry; it surfaces as `DuplicateError`.
|
|
276
|
+
*
|
|
194
277
|
* @param options {object}
|
|
195
278
|
* @param options.encrypted {IEncryptedDocument} the document to update
|
|
196
279
|
* @returns {Promise<void>}
|
|
@@ -203,12 +286,19 @@ export class WasTransport extends Transport {
|
|
|
203
286
|
await this._put(encrypted.id, encrypted);
|
|
204
287
|
}
|
|
205
288
|
catch (err) {
|
|
206
|
-
|
|
207
|
-
// with 409; surface it the way `EdvClientCore` expects.
|
|
208
|
-
if (httpStatus(err) === 409) {
|
|
289
|
+
if (httpStatus(err) === 412) {
|
|
209
290
|
throw namedError({
|
|
210
291
|
name: 'InvalidStateError',
|
|
211
|
-
message: '
|
|
292
|
+
message: 'Document update conflict: the stored document changed since it ' +
|
|
293
|
+
'was read. Re-fetch the current document and retry.',
|
|
294
|
+
cause: err
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
if (httpStatus(err) === 409) {
|
|
298
|
+
throw namedError({
|
|
299
|
+
name: 'DuplicateError',
|
|
300
|
+
message: 'A unique indexed attribute value is already held by another ' +
|
|
301
|
+
'document in this collection.',
|
|
212
302
|
cause: err
|
|
213
303
|
});
|
|
214
304
|
}
|
|
@@ -271,19 +361,81 @@ export class WasTransport extends Transport {
|
|
|
271
361
|
/**
|
|
272
362
|
* @inheritdoc
|
|
273
363
|
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
364
|
+
* Runs a blinded-index content query: a signed `POST` of
|
|
365
|
+
* `{ profile: 'blinded-index', ...query }` to the Collection's reserved
|
|
366
|
+
* `/query` endpoint. Requires the backend's `blinded-index-query` affordance
|
|
367
|
+
* (throws `NotSupportedError` when it is absent). The server evaluates the
|
|
368
|
+
* blinded `equals` / `has` filters against the `indexed` entries of stored
|
|
369
|
+
* documents (opaque string comparison -- it does no crypto) and returns
|
|
370
|
+
* `{ documents, hasMore, cursor? }` (the encrypted envelopes verbatim, in
|
|
371
|
+
* ascending resource-id order, with `cursor` present iff `hasMore`), or a
|
|
372
|
+
* bare `{ count }` when `query.count` is `true`. The body is returned
|
|
373
|
+
* untouched: `EdvClientCore.find` decrypts `documents` and passes
|
|
374
|
+
* `hasMore` / `cursor` through.
|
|
375
|
+
*
|
|
376
|
+
* @param options {object}
|
|
377
|
+
* @param options.query {IEDVQuery} the blinded query (`index` plus one of
|
|
378
|
+
* `equals` / `has`, and optional `count` / `limit` / `cursor`), as built
|
|
379
|
+
* by `EdvClientCore`'s `IndexHelper.buildQuery`
|
|
380
|
+
* @returns {Promise<object>} the server's response body verbatim
|
|
276
381
|
*/
|
|
277
|
-
async find() {
|
|
278
|
-
|
|
382
|
+
async find({ query } = {}) {
|
|
383
|
+
if (!query) {
|
|
384
|
+
throw new TypeError('"query" is required.');
|
|
385
|
+
}
|
|
386
|
+
if (!(await this._backendFeatures()).includes('blinded-index-query')) {
|
|
387
|
+
throw namedError({
|
|
388
|
+
name: 'NotSupportedError',
|
|
389
|
+
message: "Blinded-index query is not supported: the collection's backend " +
|
|
390
|
+
'does not advertise the "blinded-index-query" affordance.'
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
// `returnDocuments` is a first-class `IEDVQuery` field, but the WAS profile
|
|
394
|
+
// has no ids-only mode, so it is dropped (whatever its value) and full
|
|
395
|
+
// documents come back -- the best-effort degradation `EdvClientCore.find`
|
|
396
|
+
// documents for this option.
|
|
397
|
+
const { returnDocuments: _returnDocuments, ...blindedQuery } = query;
|
|
398
|
+
let response;
|
|
399
|
+
try {
|
|
400
|
+
response = await this._was.request({
|
|
401
|
+
path: collectionQuery(this.spaceId, this.collectionId),
|
|
402
|
+
method: 'POST',
|
|
403
|
+
json: { profile: 'blinded-index', ...blindedQuery }
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
catch (err) {
|
|
407
|
+
if (httpStatus(err) === 404) {
|
|
408
|
+
throw namedError({
|
|
409
|
+
name: 'NotFoundError',
|
|
410
|
+
message: 'Collection not found.',
|
|
411
|
+
cause: err
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
throw err;
|
|
415
|
+
}
|
|
416
|
+
const result = await readJsonData(response);
|
|
417
|
+
if (result === null || typeof result !== 'object') {
|
|
418
|
+
throw new Error('Malformed blinded-index query response.');
|
|
419
|
+
}
|
|
420
|
+
return result;
|
|
279
421
|
}
|
|
280
422
|
/**
|
|
281
423
|
* @inheritdoc
|
|
282
424
|
*
|
|
283
|
-
*
|
|
425
|
+
* Not supported, deliberately: in the EDV-over-WAS profile, index entries
|
|
426
|
+
* are not a separate server-side resource -- the `indexed` array rides
|
|
427
|
+
* inside the stored document envelope, and every `insert` / `update`
|
|
428
|
+
* already carries it. Re-indexing a document is therefore an ordinary
|
|
429
|
+
* `update()` of the full envelope; there is no `/{id}/index` endpoint to
|
|
430
|
+
* bind this to.
|
|
284
431
|
*/
|
|
285
432
|
async updateIndex() {
|
|
286
|
-
|
|
433
|
+
throw namedError({
|
|
434
|
+
name: 'NotSupportedError',
|
|
435
|
+
message: '"updateIndex" is not supported by the EDV-over-WAS profile: index ' +
|
|
436
|
+
'entries ride inside the stored document envelope, so re-index a ' +
|
|
437
|
+
'document with an ordinary "update()" of the full document.'
|
|
438
|
+
});
|
|
287
439
|
}
|
|
288
440
|
/**
|
|
289
441
|
* @inheritdoc
|
|
@@ -302,8 +454,10 @@ export class WasTransport extends Transport {
|
|
|
302
454
|
return this._unsupported('getChunk (chunked streams)');
|
|
303
455
|
}
|
|
304
456
|
/**
|
|
305
|
-
* Throws a uniform "not supported in this profile" error for
|
|
306
|
-
*
|
|
457
|
+
* Throws a uniform "not supported in this profile" error for the chunked-
|
|
458
|
+
* stream operations, which depend on a server-side affordance (the reserved
|
|
459
|
+
* `/{id}/chunks/{n}` sub-segment, the `chunked-streams` backend feature)
|
|
460
|
+
* that neither reference backend provides yet.
|
|
307
461
|
*
|
|
308
462
|
* @param operation {string}
|
|
309
463
|
* @returns {never}
|
|
@@ -311,8 +465,8 @@ export class WasTransport extends Transport {
|
|
|
311
465
|
_unsupported(operation) {
|
|
312
466
|
throw namedError({
|
|
313
467
|
name: 'NotSupportedError',
|
|
314
|
-
message: `"${operation}" is not supported by the
|
|
315
|
-
'
|
|
468
|
+
message: `"${operation}" is not supported by the EDV-over-WAS profile ` +
|
|
469
|
+
'(requires the server\'s "chunked-streams" affordance).'
|
|
316
470
|
});
|
|
317
471
|
}
|
|
318
472
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WasTransport.js","sourceRoot":"","sources":["../../src/edv/WasTransport.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH
|
|
1
|
+
{"version":3,"file":"WasTransport.js","sourceRoot":"","sources":["../../src/edv/WasTransport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { IKeyAgreementKey } from '@interop/data-integrity-core';
|
|
2
|
+
import type { CollectionEncryptionRecipient } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The JOSE key-management algorithm for every epoch/recipient wrap.
|
|
5
|
+
*/
|
|
6
|
+
export declare const KEY_WRAP_ALG = "ECDH-ES+A256KW";
|
|
7
|
+
/**
|
|
8
|
+
* A reader's public key-agreement key, as needed to wrap an epoch key to it:
|
|
9
|
+
* the recipient `id` (`kid`, which the reader's own key-agreement key must also
|
|
10
|
+
* report) and its `publicKeyMultibase`.
|
|
11
|
+
*/
|
|
12
|
+
export interface RecipientPublicKey {
|
|
13
|
+
id: string;
|
|
14
|
+
publicKeyMultibase: string;
|
|
15
|
+
type?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Wraps a 32-byte epoch secret to one recipient's X25519 key-agreement key,
|
|
19
|
+
* producing the JWE `recipients` entry stored on the marker. Generates a fresh
|
|
20
|
+
* ephemeral key per call (ECDH-ES), so each wrap carries its own `epk`.
|
|
21
|
+
*
|
|
22
|
+
* @param options {object}
|
|
23
|
+
* @param options.epochSecret {Uint8Array} the 32-byte epoch key to wrap
|
|
24
|
+
* @param options.recipient {RecipientPublicKey} the reader's public KAK
|
|
25
|
+
* @returns {Promise<CollectionEncryptionRecipient>}
|
|
26
|
+
*/
|
|
27
|
+
export declare function wrapEpochSecret({ epochSecret, recipient }: {
|
|
28
|
+
epochSecret: Uint8Array;
|
|
29
|
+
recipient: RecipientPublicKey;
|
|
30
|
+
}): Promise<CollectionEncryptionRecipient>;
|
|
31
|
+
/**
|
|
32
|
+
* Unwraps an epoch secret from a marker `recipients` entry using the reader's
|
|
33
|
+
* own key-agreement key. Returns `null` when this key does not match the entry
|
|
34
|
+
* (the wrong recipient, or a corrupt entry) -- never treat `null` as a key; try
|
|
35
|
+
* the next candidate entry or epoch, and fail with a typed error when nothing
|
|
36
|
+
* unwraps.
|
|
37
|
+
*
|
|
38
|
+
* @param options {object}
|
|
39
|
+
* @param options.entry {CollectionEncryptionRecipient} the marker entry
|
|
40
|
+
* @param options.keyAgreementKey {IKeyAgreementKey} the reader's own KAK
|
|
41
|
+
* (its `id` must equal `entry.header.kid` for the derivation to match)
|
|
42
|
+
* @returns {Promise<Uint8Array | null>}
|
|
43
|
+
*/
|
|
44
|
+
export declare function unwrapEpochSecret({ entry, keyAgreementKey }: {
|
|
45
|
+
entry: CollectionEncryptionRecipient;
|
|
46
|
+
keyAgreementKey: IKeyAgreementKey;
|
|
47
|
+
}): Promise<Uint8Array | null>;
|
|
48
|
+
/**
|
|
49
|
+
* Mints a fresh key epoch: a new random X25519 key pair whose `did:key` is the
|
|
50
|
+
* epoch id. Returns the id and the raw 32-byte secret (to wrap to recipients);
|
|
51
|
+
* `resolveEpochKeys` reconstructs the epoch key pair from the id and secret when
|
|
52
|
+
* a resource must be encrypted or decrypted under the epoch.
|
|
53
|
+
*
|
|
54
|
+
* @returns {Promise<{ epochId: string, secret: Uint8Array }>}
|
|
55
|
+
*/
|
|
56
|
+
export declare function mintEpoch(): Promise<{
|
|
57
|
+
epochId: string;
|
|
58
|
+
secret: Uint8Array;
|
|
59
|
+
}>;
|
|
60
|
+
/**
|
|
61
|
+
* Reconstructs an epoch key pair from its id (a `did:key`, which carries the
|
|
62
|
+
* public key) and the unwrapped 32-byte secret, ready to hand to the EDV
|
|
63
|
+
* `documentCipher` as its `keyAgreementKey`.
|
|
64
|
+
*
|
|
65
|
+
* @param options {object}
|
|
66
|
+
* @param options.epochId {string} the epoch's `did:key`
|
|
67
|
+
* @param options.secret {Uint8Array} the unwrapped 32-byte epoch secret
|
|
68
|
+
* @returns {X25519KeyAgreementKey2020}
|
|
69
|
+
*/
|
|
70
|
+
export declare function reconstructEpochKeyPair({ epochId, secret }: {
|
|
71
|
+
epochId: string;
|
|
72
|
+
secret: Uint8Array;
|
|
73
|
+
}): IKeyAgreementKey;
|
|
74
|
+
/**
|
|
75
|
+
* The verification-method id of an epoch key: `<did:key>#<fingerprint>`. This is
|
|
76
|
+
* the `kid` the EDV `documentCipher` stamps on a resource encrypted under the
|
|
77
|
+
* epoch; a reader maps it back to the epoch by taking the `did:key` portion
|
|
78
|
+
* before the `#` fragment.
|
|
79
|
+
*
|
|
80
|
+
* @param epochId {string} the epoch's `did:key`
|
|
81
|
+
* @returns {string}
|
|
82
|
+
*/
|
|
83
|
+
export declare function epochKeyIdFor(epochId: string): string;
|
|
84
|
+
/**
|
|
85
|
+
* A `did:key` key resolver for X25519 key-agreement keys: resolves a
|
|
86
|
+
* `did:key:z...#z...` id (an epoch key, or any self-describing X25519 key) to
|
|
87
|
+
* its public verification method. The public key is the fragment, so no network
|
|
88
|
+
* or registry lookup is needed.
|
|
89
|
+
*
|
|
90
|
+
* @param options {object}
|
|
91
|
+
* @param options.id {string} the key id to resolve
|
|
92
|
+
* @returns {Promise<{ id: string, type: string, publicKeyMultibase: string }>}
|
|
93
|
+
*/
|
|
94
|
+
export declare function didKeyResolver({ id }: {
|
|
95
|
+
id?: string;
|
|
96
|
+
}): Promise<{
|
|
97
|
+
id: string;
|
|
98
|
+
type: string;
|
|
99
|
+
publicKeyMultibase: string;
|
|
100
|
+
}>;
|
|
101
|
+
//# sourceMappingURL=epochCrypto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"epochCrypto.d.ts","sourceRoot":"","sources":["../../src/edv/epochCrypto.ts"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AACpE,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAA;AAEhE;;GAEG;AACH,eAAO,MAAM,YAAY,mBAAmB,CAAA;AAS5C;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,kBAAkB,EAAE,MAAM,CAAA;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CAAC,EACpC,WAAW,EACX,SAAS,EACV,EAAE;IACD,WAAW,EAAE,UAAU,CAAA;IACvB,SAAS,EAAE,kBAAkB,CAAA;CAC9B,GAAG,OAAO,CAAC,6BAA6B,CAAC,CA4BzC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CAAC,EACtC,KAAK,EACL,eAAe,EAChB,EAAE;IACD,KAAK,EAAE,6BAA6B,CAAA;IACpC,eAAe,EAAE,gBAAgB,CAAA;CAClC,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAyB7B;AAED;;;;;;;GAOG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC;IACzC,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,UAAU,CAAA;CACnB,CAAC,CAQD;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,OAAO,EACP,MAAM,EACP,EAAE;IACD,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,UAAU,CAAA;CACnB,GAAG,gBAAgB,CASnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAErD;AAkBD;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAAC,EACnC,EAAE,EACH,EAAE;IACD,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,CAAC,CAoBpE"}
|