@interop/was-client 0.17.0 → 0.19.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 +88 -0
- package/dist/Collection.d.ts +1 -46
- package/dist/Collection.d.ts.map +1 -1
- package/dist/Collection.js +77 -79
- package/dist/Collection.js.map +1 -1
- package/dist/Resource.d.ts +1 -51
- package/dist/Resource.d.ts.map +1 -1
- package/dist/Resource.js +61 -58
- package/dist/Resource.js.map +1 -1
- package/dist/Space.d.ts +1 -27
- package/dist/Space.d.ts.map +1 -1
- package/dist/Space.js +65 -65
- package/dist/Space.js.map +1 -1
- package/dist/WasClient.d.ts +1 -12
- package/dist/WasClient.d.ts.map +1 -1
- package/dist/WasClient.js +15 -15
- package/dist/WasClient.js.map +1 -1
- package/dist/codec.d.ts +15 -2
- package/dist/codec.d.ts.map +1 -1
- package/dist/edv/EdvCodec.d.ts +13 -95
- package/dist/edv/EdvCodec.d.ts.map +1 -1
- package/dist/edv/EdvCodec.js +221 -52
- package/dist/edv/EdvCodec.js.map +1 -1
- package/dist/edv/WasTransport.d.ts +1 -53
- package/dist/edv/WasTransport.d.ts.map +1 -1
- package/dist/edv/WasTransport.js +29 -29
- package/dist/edv/WasTransport.js.map +1 -1
- package/dist/edv/docCipher.d.ts +111 -0
- package/dist/edv/docCipher.d.ts.map +1 -0
- package/dist/edv/docCipher.js +236 -0
- package/dist/edv/docCipher.js.map +1 -0
- package/dist/edv/epochKeys.d.ts.map +1 -1
- package/dist/edv/epochKeys.js +46 -16
- package/dist/edv/epochKeys.js.map +1 -1
- package/dist/edv/epochMac.d.ts +32 -0
- package/dist/edv/epochMac.d.ts.map +1 -0
- package/dist/edv/epochMac.js +117 -0
- package/dist/edv/epochMac.js.map +1 -0
- package/dist/edv/index.d.ts +2 -0
- package/dist/edv/index.d.ts.map +1 -1
- package/dist/edv/index.js +1 -0
- package/dist/edv/index.js.map +1 -1
- package/dist/edv/recipients.d.ts.map +1 -1
- package/dist/edv/recipients.js +26 -3
- package/dist/edv/recipients.js.map +1 -1
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +28 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +1 -1
- 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 +1 -2
- package/dist/internal/codec.d.ts.map +1 -1
- package/dist/internal/codec.js +10 -10
- package/dist/internal/codec.js.map +1 -1
- package/dist/internal/features.d.ts +1 -12
- package/dist/internal/features.d.ts.map +1 -1
- package/dist/internal/features.js +8 -8
- package/dist/internal/features.js.map +1 -1
- package/dist/internal/pagination.d.ts +1 -1
- package/dist/internal/pagination.js +1 -1
- package/dist/sync/cid.d.ts +31 -0
- package/dist/sync/cid.d.ts.map +1 -0
- package/dist/sync/cid.js +55 -0
- package/dist/sync/cid.js.map +1 -0
- package/dist/sync/envelope.d.ts +20 -0
- package/dist/sync/envelope.d.ts.map +1 -0
- package/dist/sync/envelope.js +17 -0
- package/dist/sync/envelope.js.map +1 -0
- package/dist/sync/index.d.ts +29 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +28 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/plaintextCipher.d.ts +16 -0
- package/dist/sync/plaintextCipher.d.ts.map +1 -0
- package/dist/sync/plaintextCipher.js +36 -0
- package/dist/sync/plaintextCipher.js.map +1 -0
- package/dist/sync/port.d.ts +78 -0
- package/dist/sync/port.d.ts.map +1 -0
- package/dist/sync/port.js +204 -0
- package/dist/sync/port.js.map +1 -0
- package/dist/sync/provisioning.d.ts +41 -0
- package/dist/sync/provisioning.d.ts.map +1 -0
- package/dist/sync/provisioning.js +44 -0
- package/dist/sync/provisioning.js.map +1 -0
- package/dist/sync/types.d.ts +166 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/sync/types.js +2 -0
- package/dist/sync/types.js.map +1 -0
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +13 -5
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { KeyUnwrapError } from '../errors.js';
|
|
2
|
+
import { createEdvEncryption } from './EdvCodec.js';
|
|
3
|
+
import { epochKeyIdFor } from './epochCrypto.js';
|
|
4
|
+
// `isEncryptedEnvelope` and the `DocCipher` interface live in the crypto-free
|
|
5
|
+
// `../sync` module; re-exported here so an encrypted-collection consumer that
|
|
6
|
+
// imports this subpath gets both without a second import.
|
|
7
|
+
export { isEncryptedEnvelope } from '../sync/envelope.js';
|
|
8
|
+
/**
|
|
9
|
+
* Thrown by a {@link DocCipher.decrypt} when a stored envelope names a JWE
|
|
10
|
+
* recipient (`kid`) this cipher cannot route: neither the key-agreement key nor
|
|
11
|
+
* -- on an epoch-aware cipher -- any epoch this cipher knows about. It signals
|
|
12
|
+
* that the caller's cached Collection Description may be stale and should be
|
|
13
|
+
* re-read before retrying: an epoch rotation emits no change-feed entry, so a
|
|
14
|
+
* cipher built from a pre-rotation marker meets envelopes stamped with a newer
|
|
15
|
+
* epoch it has never seen. It also fires on a single-key cipher that meets an
|
|
16
|
+
* envelope encrypted to a different key-agreement key entirely.
|
|
17
|
+
*/
|
|
18
|
+
export class UnknownEpochError extends Error {
|
|
19
|
+
constructor({ collectionId, kids }) {
|
|
20
|
+
super(`Cannot decrypt a resource in collection "${collectionId}": its ` +
|
|
21
|
+
`envelope names recipient key id(s) [${kids.join(', ')}] that match ` +
|
|
22
|
+
'neither the key-agreement key nor any known key epoch. The cached ' +
|
|
23
|
+
'Collection Description may be stale (an epoch rotation emits no ' +
|
|
24
|
+
'change-feed entry); re-read it and rebuild the cipher.');
|
|
25
|
+
this.name = 'UnknownEpochError';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A wallet's own key-agreement key as a `RecipientPublicKey` -- the "recipient
|
|
30
|
+
* zero" entry a caller passes to `initRecipients` when it first makes a
|
|
31
|
+
* collection multi-recipient (the owner must be a recipient of every epoch, or it
|
|
32
|
+
* could write envelopes it cannot itself read). An `X25519KeyAgreementKey2020`
|
|
33
|
+
* carries a did:key-shaped `id` and a `publicKeyMultibase`, so its `kid`'s
|
|
34
|
+
* fragment resolves through the default did:key recipient resolver.
|
|
35
|
+
*
|
|
36
|
+
* @param options {object}
|
|
37
|
+
* @param options.keyAgreementKey {IKeyAgreementKey}
|
|
38
|
+
* @returns {RecipientPublicKey}
|
|
39
|
+
*/
|
|
40
|
+
export function ownerRecipient({ keyAgreementKey }) {
|
|
41
|
+
const { id } = keyAgreementKey;
|
|
42
|
+
const { publicKeyMultibase, type } = keyAgreementKey;
|
|
43
|
+
if (typeof id !== 'string' || typeof publicKeyMultibase !== 'string') {
|
|
44
|
+
throw new Error('Cannot build an owner recipient: the key-agreement key lacks an id or ' +
|
|
45
|
+
'publicKeyMultibase (a public X25519 key is required to wrap an epoch ' +
|
|
46
|
+
'key to it).');
|
|
47
|
+
}
|
|
48
|
+
return { id, publicKeyMultibase, type };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Extracts the JWE recipient key ids (`kid`) an EDV envelope names. An epoch
|
|
52
|
+
* envelope carries one kid (the epoch key id); a single-recipient envelope
|
|
53
|
+
* carries the key-agreement key id. Returns `[]` for a malformed envelope, so
|
|
54
|
+
* routing falls through to letting a codec surface its own error.
|
|
55
|
+
*/
|
|
56
|
+
function envelopeRecipientKids(envelope) {
|
|
57
|
+
if (envelope === null || typeof envelope !== 'object') {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
const jwe = envelope.jwe;
|
|
61
|
+
if (jwe === null || typeof jwe !== 'object') {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
const recipients = jwe.recipients;
|
|
65
|
+
if (!Array.isArray(recipients)) {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
const kids = [];
|
|
69
|
+
for (const recipient of recipients) {
|
|
70
|
+
const kid = recipient?.header?.kid;
|
|
71
|
+
if (typeof kid === 'string') {
|
|
72
|
+
kids.push(kid);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return kids;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Builds a {@link DocCipher} for one encrypted collection from a reader's key
|
|
79
|
+
* material (the key-agreement key + resolver). Keys are supplied directly (no
|
|
80
|
+
* keystore lookup).
|
|
81
|
+
*
|
|
82
|
+
* `idDerivation` selects the id model: `'content'` (default) makes every id a
|
|
83
|
+
* hash of the JWE ciphertext -- the stable, replica-independent primary key an
|
|
84
|
+
* immutable content-addressed collection needs; `'random'` mints a stable random
|
|
85
|
+
* id updated in place via `sequence` (the mutable head-document model, driven by
|
|
86
|
+
* `encryptUpdate`).
|
|
87
|
+
*
|
|
88
|
+
* With no `encryption` marker (or a marker with no epochs) the cipher is single-
|
|
89
|
+
* recipient: the key-agreement key encrypts and decrypts directly. With epochs on
|
|
90
|
+
* the marker the cipher becomes multi-recipient: it ALSO builds an epoch codec
|
|
91
|
+
* that encrypts every write under the marker's `currentEpoch` and decrypts any
|
|
92
|
+
* epoch this reader still holds a key for. The single-key codec stays built
|
|
93
|
+
* either way, so a pre-epoch envelope keeps decrypting -- a permanent tolerance,
|
|
94
|
+
* not a migration shim.
|
|
95
|
+
*
|
|
96
|
+
* The reader must be a recipient of every epoch on the marker (the owner is
|
|
97
|
+
* "recipient zero"). If it is a recipient of none, building the epoch codec
|
|
98
|
+
* throws {@link KeyUnwrapError}; this surfaces it with a clearer error rather
|
|
99
|
+
* than silently writing envelopes other recipients cannot read.
|
|
100
|
+
*
|
|
101
|
+
* @param options {object}
|
|
102
|
+
* @param options.keyAgreementKey {IKeyAgreementKey}
|
|
103
|
+
* @param options.keyResolver {IKeyResolver}
|
|
104
|
+
* @param options.collectionId {string} labels errors; the codec is agnostic
|
|
105
|
+
* @param [options.idDerivation] {'content' | 'random'} defaults to `'content'`
|
|
106
|
+
* @param [options.encryption] {CollectionEncryption} the collection's
|
|
107
|
+
* encryption marker; when it carries key epochs, the cipher becomes
|
|
108
|
+
* multi-recipient
|
|
109
|
+
* @returns {Promise<DocCipher>}
|
|
110
|
+
*/
|
|
111
|
+
export async function createEdvDocCipher({ keyAgreementKey, keyResolver, collectionId, idDerivation = 'content', encryption }) {
|
|
112
|
+
const provider = createEdvEncryption({
|
|
113
|
+
resolveKeys: async () => null,
|
|
114
|
+
idDerivation
|
|
115
|
+
});
|
|
116
|
+
// The direct (single-key) codec is always built: it decrypts pre-epoch
|
|
117
|
+
// envelopes and is the whole cipher for a single-recipient collection.
|
|
118
|
+
const directCodec = await provider.codecFor({
|
|
119
|
+
spaceId: 'local',
|
|
120
|
+
collectionId,
|
|
121
|
+
scheme: 'edv',
|
|
122
|
+
keys: { keyAgreementKey, keyResolver }
|
|
123
|
+
});
|
|
124
|
+
if (!directCodec) {
|
|
125
|
+
throw new Error(`Could not build the EDV cipher for collection "${collectionId}".`);
|
|
126
|
+
}
|
|
127
|
+
// On a multi-recipient collection, ALSO build the epoch codec: same provider
|
|
128
|
+
// and keys, but with the marker so `codecFor` resolves this reader's per-epoch
|
|
129
|
+
// keys. Writes go under the marker's `currentEpoch`; reads pick the epoch key
|
|
130
|
+
// matching the envelope's recipient kid.
|
|
131
|
+
const hasEpochs = encryption?.epochs !== undefined && encryption.epochs.length > 0;
|
|
132
|
+
let epochCodec;
|
|
133
|
+
if (hasEpochs) {
|
|
134
|
+
try {
|
|
135
|
+
epochCodec = await provider.codecFor({
|
|
136
|
+
spaceId: 'local',
|
|
137
|
+
collectionId,
|
|
138
|
+
scheme: 'edv',
|
|
139
|
+
encryption,
|
|
140
|
+
keys: { keyAgreementKey, keyResolver }
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
if (err instanceof KeyUnwrapError) {
|
|
145
|
+
throw new Error(`Cannot build the multi-recipient EDV cipher for collection ` +
|
|
146
|
+
`"${collectionId}": the key-agreement key is not a recipient of any ` +
|
|
147
|
+
'key epoch on this collection. The owner must be a recipient of ' +
|
|
148
|
+
'every epoch (recipient zero) before writing, or it would encrypt ' +
|
|
149
|
+
'envelopes it cannot itself read.', { cause: err });
|
|
150
|
+
}
|
|
151
|
+
throw err;
|
|
152
|
+
}
|
|
153
|
+
if (!epochCodec) {
|
|
154
|
+
throw new Error(`Could not build the multi-recipient EDV cipher for collection ` +
|
|
155
|
+
`"${collectionId}".`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const vaultKid = keyAgreementKey.id;
|
|
159
|
+
const knownEpochKids = new Set((encryption?.epochs ?? []).map(epoch => epochKeyIdFor(epoch.id)));
|
|
160
|
+
// Parses the codec's `EncodedWrite` (id + envelope body bytes) to the stored
|
|
161
|
+
// `{ id, envelope, epoch? }` shape. Shared by the create and update paths.
|
|
162
|
+
const readEncoded = (encoded) => {
|
|
163
|
+
if (typeof encoded.id !== 'string' ||
|
|
164
|
+
!(encoded.body instanceof Uint8Array)) {
|
|
165
|
+
throw new Error(`EDV encrypt for collection "${collectionId}" returned no id/envelope body.`);
|
|
166
|
+
}
|
|
167
|
+
const envelope = JSON.parse(new TextDecoder().decode(encoded.body));
|
|
168
|
+
return {
|
|
169
|
+
id: encoded.id,
|
|
170
|
+
envelope,
|
|
171
|
+
...(typeof encoded.epoch === 'string' && { epoch: encoded.epoch })
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
return {
|
|
175
|
+
async encrypt({ data }) {
|
|
176
|
+
// `encode` with no caller id is the add() path: encrypt, then either derive
|
|
177
|
+
// and stamp the content-hash id (`'content'`) or use the minted random id.
|
|
178
|
+
// Writes go under the current epoch on a multi-recipient cipher.
|
|
179
|
+
const codec = epochCodec ?? directCodec;
|
|
180
|
+
const encoded = await codec.encode({
|
|
181
|
+
data: data
|
|
182
|
+
});
|
|
183
|
+
return readEncoded(encoded);
|
|
184
|
+
},
|
|
185
|
+
async encryptUpdate({ id, data, current }) {
|
|
186
|
+
// The update path (mutable random-id head document): hand the codec the
|
|
187
|
+
// prior stored envelope so it advances `sequence` from it and re-encrypts
|
|
188
|
+
// under the same id.
|
|
189
|
+
const codec = epochCodec ?? directCodec;
|
|
190
|
+
const priorResponse = {
|
|
191
|
+
data: current,
|
|
192
|
+
json: async () => current,
|
|
193
|
+
headers: { get: () => null }
|
|
194
|
+
};
|
|
195
|
+
const encoded = await codec.encode({
|
|
196
|
+
id,
|
|
197
|
+
data: data,
|
|
198
|
+
current: priorResponse
|
|
199
|
+
});
|
|
200
|
+
const { id: encodedId, envelope } = readEncoded(encoded);
|
|
201
|
+
return { id: encodedId, envelope };
|
|
202
|
+
},
|
|
203
|
+
async decrypt({ envelope }) {
|
|
204
|
+
// Route by the envelope's JWE recipient kids:
|
|
205
|
+
// 1. any kid is the key-agreement key id -- a pre-epoch envelope, so the
|
|
206
|
+
// direct codec (permanent tolerance, not a migration shim);
|
|
207
|
+
// 2. else, on an epoch cipher, any kid names a known epoch -- the epoch
|
|
208
|
+
// codec;
|
|
209
|
+
// 3. else UnknownEpochError: the marker is likely stale, or a single-key
|
|
210
|
+
// cipher met an envelope encrypted to a different key entirely.
|
|
211
|
+
const kids = envelopeRecipientKids(envelope);
|
|
212
|
+
const codec = selectCodec();
|
|
213
|
+
const response = {
|
|
214
|
+
data: envelope,
|
|
215
|
+
json: async () => envelope
|
|
216
|
+
};
|
|
217
|
+
return (await codec.decode(response));
|
|
218
|
+
function selectCodec() {
|
|
219
|
+
if (kids.some(kid => kid === vaultKid)) {
|
|
220
|
+
return directCodec;
|
|
221
|
+
}
|
|
222
|
+
if (epochCodec && kids.some(kid => knownEpochKids.has(kid))) {
|
|
223
|
+
return epochCodec;
|
|
224
|
+
}
|
|
225
|
+
// A malformed/empty-kid envelope falls through to the direct codec so it
|
|
226
|
+
// can surface its own decrypt error; a non-empty set of unroutable kids
|
|
227
|
+
// is the stale-marker signal.
|
|
228
|
+
if (kids.length === 0) {
|
|
229
|
+
return directCodec;
|
|
230
|
+
}
|
|
231
|
+
throw new UnknownEpochError({ collectionId, kids });
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=docCipher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docCipher.js","sourceRoot":"","sources":["../../src/edv/docCipher.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"epochKeys.d.ts","sourceRoot":"","sources":["../../src/edv/epochKeys.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AAEpE,OAAO,KAAK,EACV,oBAAoB,EAErB,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"epochKeys.d.ts","sourceRoot":"","sources":["../../src/edv/epochKeys.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AAEpE,OAAO,KAAK,EACV,oBAAoB,EAErB,MAAM,aAAa,CAAA;AAQpB;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,QAAQ,EAAE,gBAAgB,CAAA;IAC1B;;;;OAIG;IACH,QAAQ,EAAE,gBAAgB,EAAE,CAAA;CAC7B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,gBAAgB,CAAC,EACrC,UAAU,EACV,eAAe,EAChB,EAAE;IACD,UAAU,EAAE,oBAAoB,CAAA;IAChC,eAAe,EAAE,gBAAgB,CAAA;CAClC,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CA+FpC"}
|
package/dist/edv/epochKeys.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { KeyUnwrapError } from '../errors.js';
|
|
1
|
+
import { IntegrityError, KeyUnwrapError } from '../errors.js';
|
|
2
2
|
import { epochKeyIdFor, reconstructEpochKeyPair, unwrapEpochSecret } from './epochCrypto.js';
|
|
3
|
+
import { verifyEpochsMac } from './epochMac.js';
|
|
3
4
|
/**
|
|
4
5
|
* Resolves the reader's epoch keys from a marker. Returns `null` when the marker
|
|
5
6
|
* declares no epochs (a single-key collection -- the caller keeps its existing
|
|
@@ -33,7 +34,7 @@ export async function resolveEpochKeys({ encryption, keyAgreementKey }) {
|
|
|
33
34
|
// the newest epoch it holds -- defined deterministically as the LAST epoch in
|
|
34
35
|
// the marker's canonical `epochs` order that names this reader, never the
|
|
35
36
|
// incidental order in which secrets happened to unwrap. A reader that is not a
|
|
36
|
-
// recipient of `currentEpoch` is a removed/
|
|
37
|
+
// recipient of `currentEpoch` is a removed/archive reader whose writes the
|
|
37
38
|
// server rejects via its revoked zcap anyway; selecting a deterministic
|
|
38
39
|
// fallback here only keeps the local `writeEpoch`/`writeKey` well-defined
|
|
39
40
|
// instead of assuming the `epochs` array is append-ordered newest-last.
|
|
@@ -42,23 +43,49 @@ export async function resolveEpochKeys({ encryption, keyAgreementKey }) {
|
|
|
42
43
|
namedEpochs.find(epoch => epoch.id === currentEpoch)) ||
|
|
43
44
|
namedEpochs[namedEpochs.length - 1];
|
|
44
45
|
// The write epoch is unwrapped eagerly: `writeKey` must be a full key pair the
|
|
45
|
-
// EDV cipher can name recipients with and encrypt under right away.
|
|
46
|
-
|
|
46
|
+
// EDV cipher can name recipients with and encrypt under right away. Its raw
|
|
47
|
+
// secret is surfaced alongside so the epoch-configuration MAC can be verified.
|
|
48
|
+
const writeUnwrapped = await unwrapEpochKey({
|
|
47
49
|
epoch: writeEpochEntry,
|
|
48
50
|
keyAgreementKey
|
|
49
51
|
});
|
|
50
|
-
if (!
|
|
52
|
+
if (!writeUnwrapped) {
|
|
51
53
|
throw new KeyUnwrapError(`This reader's recipient entry for the write epoch ` +
|
|
52
54
|
`"${writeEpochEntry.id}" did not unwrap (a corrupt entry). Re-add ` +
|
|
53
55
|
'this reader with addRecipient, or supply the correct key-agreement ' +
|
|
54
56
|
'key.');
|
|
55
57
|
}
|
|
58
|
+
const writeKey = writeUnwrapped.key;
|
|
59
|
+
// Authenticate the epoch configuration when this reader holds the marker's
|
|
60
|
+
// `currentEpoch` (its write epoch IS `currentEpoch`) and the marker carries a
|
|
61
|
+
// MAC. A reader whose write epoch is an older held epoch cannot key the MAC
|
|
62
|
+
// (its secret is not the current epoch's), so it skips verification -- its
|
|
63
|
+
// writes are rejected server-side via its revoked zcap anyway. An absent MAC
|
|
64
|
+
// is a legacy marker, accepted.
|
|
65
|
+
if (writeEpochEntry.id === encryption.currentEpoch &&
|
|
66
|
+
encryption.epochsMac !== undefined) {
|
|
67
|
+
const { v, alg } = encryption.epochsMac;
|
|
68
|
+
if (v !== 1 || alg !== 'HS256') {
|
|
69
|
+
throw new IntegrityError('The epoch configuration failed to authenticate: its MAC declares an ' +
|
|
70
|
+
`unsupported construction (v=${v}, alg="${alg}"). This is a ` +
|
|
71
|
+
'server-side rollback or tamper.');
|
|
72
|
+
}
|
|
73
|
+
const authentic = await verifyEpochsMac({
|
|
74
|
+
marker: encryption,
|
|
75
|
+
epochSecret: writeUnwrapped.secret
|
|
76
|
+
});
|
|
77
|
+
if (!authentic) {
|
|
78
|
+
throw new IntegrityError('The epoch configuration failed to authenticate; a server-side ' +
|
|
79
|
+
'rollback or tamper of the encryption marker was detected (the ' +
|
|
80
|
+
'MAC over its epoch configuration does not verify).');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
56
83
|
// Read keys: the eagerly-unwrapped write key, plus a LAZY key per other named
|
|
57
84
|
// epoch. Each lazy key knows its `id` up front (derived from the epoch id, so
|
|
58
85
|
// the codec's kid-match filter needs no secret) and unwraps + reconstructs its
|
|
59
86
|
// epoch secret only on first decrypt naming it, caching the result. So a
|
|
60
87
|
// write-only handle -- or a reader that only ever touches current-epoch
|
|
61
|
-
// resources -- never pays the ECDH + KDF + key-unwrap for
|
|
88
|
+
// resources -- never pays the ECDH + KDF + key-unwrap for archive epochs it
|
|
62
89
|
// does not read.
|
|
63
90
|
const readKeys = [writeKey];
|
|
64
91
|
for (const epoch of namedEpochs) {
|
|
@@ -71,12 +98,15 @@ export async function resolveEpochKeys({ encryption, keyAgreementKey }) {
|
|
|
71
98
|
/**
|
|
72
99
|
* Unwraps and reconstructs a single epoch's key pair for this reader, or returns
|
|
73
100
|
* `null` when the reader is not a recipient of the epoch or its entry does not
|
|
74
|
-
* unwrap (a corrupt entry -- never treat `null` as a key).
|
|
101
|
+
* unwrap (a corrupt entry -- never treat `null` as a key). Returns the raw
|
|
102
|
+
* 32-byte epoch secret alongside the reconstructed key pair, so a caller that
|
|
103
|
+
* must authenticate the epoch configuration (the `epochsMac`) can key the MAC
|
|
104
|
+
* without re-unwrapping.
|
|
75
105
|
*
|
|
76
106
|
* @param options {object}
|
|
77
107
|
* @param options.epoch {CollectionEncryptionEpoch} the epoch to unwrap
|
|
78
108
|
* @param options.keyAgreementKey {IKeyAgreementKey} the reader's own KAK
|
|
79
|
-
* @returns {Promise<IKeyAgreementKey | null>}
|
|
109
|
+
* @returns {Promise<{ key: IKeyAgreementKey; secret: Uint8Array } | null>}
|
|
80
110
|
*/
|
|
81
111
|
async function unwrapEpochKey({ epoch, keyAgreementKey }) {
|
|
82
112
|
const entry = epoch.recipients.find(recipient => recipient.header.kid === keyAgreementKey.id);
|
|
@@ -87,7 +117,7 @@ async function unwrapEpochKey({ epoch, keyAgreementKey }) {
|
|
|
87
117
|
if (!secret) {
|
|
88
118
|
return null;
|
|
89
119
|
}
|
|
90
|
-
return reconstructEpochKeyPair({ epochId: epoch.id, secret });
|
|
120
|
+
return { key: reconstructEpochKeyPair({ epochId: epoch.id, secret }), secret };
|
|
91
121
|
}
|
|
92
122
|
/**
|
|
93
123
|
* Builds a lazily-unwrapping read key for a named epoch: an `IKeyAgreementKey`
|
|
@@ -107,16 +137,16 @@ function lazyEpochKey({ epoch, keyAgreementKey }) {
|
|
|
107
137
|
let pending;
|
|
108
138
|
const resolve = () => {
|
|
109
139
|
if (pending === undefined) {
|
|
110
|
-
const promise = unwrapEpochKey({ epoch, keyAgreementKey }).then(
|
|
111
|
-
if (!
|
|
112
|
-
// The reader was named in this epoch (else no lazy key was built)
|
|
113
|
-
// its entry did not unwrap: a corrupt entry. The codec's
|
|
114
|
-
// catches this and tries the next candidate before
|
|
115
|
-
// typed failure.
|
|
140
|
+
const promise = unwrapEpochKey({ epoch, keyAgreementKey }).then(unwrapped => {
|
|
141
|
+
if (!unwrapped) {
|
|
142
|
+
// The reader was named in this epoch (else no lazy key was built)
|
|
143
|
+
// but its entry did not unwrap: a corrupt entry. The codec's
|
|
144
|
+
// `_decrypt` catches this and tries the next candidate before
|
|
145
|
+
// surfacing its own typed failure.
|
|
116
146
|
throw new KeyUnwrapError(`This reader's recipient entry for epoch "${epoch.id}" did not ` +
|
|
117
147
|
'unwrap (a corrupt entry).');
|
|
118
148
|
}
|
|
119
|
-
return key;
|
|
149
|
+
return unwrapped.key;
|
|
120
150
|
});
|
|
121
151
|
// Cache only a successful unwrap: drop a rejected promise so the next
|
|
122
152
|
// read re-attempts (the failure may have been transient), rather than
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"epochKeys.js","sourceRoot":"","sources":["../../src/edv/epochKeys.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"epochKeys.js","sourceRoot":"","sources":["../../src/edv/epochKeys.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { CollectionEncryption, CollectionEncryptionEpochsMac } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Computes the `epochsMac` field for a marker under the current epoch's secret.
|
|
4
|
+
* Compute this over the exact marker state being written (member fields already
|
|
5
|
+
* stamped), since a compare-and-swap retry re-reads the marker.
|
|
6
|
+
*
|
|
7
|
+
* @param options {object}
|
|
8
|
+
* @param options.marker {CollectionEncryption} the marker being written
|
|
9
|
+
* @param options.epochSecret {Uint8Array} the 32-byte current epoch secret
|
|
10
|
+
* @returns {Promise<CollectionEncryptionEpochsMac>}
|
|
11
|
+
*/
|
|
12
|
+
export declare function computeEpochsMac({ marker, epochSecret }: {
|
|
13
|
+
marker: CollectionEncryption;
|
|
14
|
+
epochSecret: Uint8Array;
|
|
15
|
+
}): Promise<CollectionEncryptionEpochsMac>;
|
|
16
|
+
/**
|
|
17
|
+
* Verifies a marker's `epochsMac` against a recomputation from the current
|
|
18
|
+
* epoch's secret. Returns `false` on any mismatch (or a malformed `mac`); the
|
|
19
|
+
* caller decides the error semantics. The MAC construction's own version/alg
|
|
20
|
+
* (`v` / `alg`) is validated by the caller before this is called.
|
|
21
|
+
*
|
|
22
|
+
* @param options {object}
|
|
23
|
+
* @param options.marker {CollectionEncryption} the marker to verify (its
|
|
24
|
+
* `epochsMac.mac` is the tag under test)
|
|
25
|
+
* @param options.epochSecret {Uint8Array} the 32-byte current epoch secret
|
|
26
|
+
* @returns {Promise<boolean>}
|
|
27
|
+
*/
|
|
28
|
+
export declare function verifyEpochsMac({ marker, epochSecret }: {
|
|
29
|
+
marker: CollectionEncryption;
|
|
30
|
+
epochSecret: Uint8Array;
|
|
31
|
+
}): Promise<boolean>;
|
|
32
|
+
//# sourceMappingURL=epochMac.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"epochMac.d.ts","sourceRoot":"","sources":["../../src/edv/epochMac.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EACV,oBAAoB,EACpB,6BAA6B,EAC9B,MAAM,aAAa,CAAA;AAwEpB;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,EACrC,MAAM,EACN,WAAW,EACZ,EAAE;IACD,MAAM,EAAE,oBAAoB,CAAA;IAC5B,WAAW,EAAE,UAAU,CAAA;CACxB,GAAG,OAAO,CAAC,6BAA6B,CAAC,CAQzC;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CAAC,EACpC,MAAM,EACN,WAAW,EACZ,EAAE;IACD,MAAM,EAAE,oBAAoB,CAAA;IAC5B,WAAW,EAAE,UAAU,CAAA;CACxB,GAAG,OAAO,CAAC,OAAO,CAAC,CAkBnB"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Authenticated epoch configuration for multi-recipient encrypted Collections:
|
|
6
|
+
* a MAC over a Collection `encryption` marker's epoch configuration, keyed from
|
|
7
|
+
* the current epoch's secret (which the server never holds). It lets a reader
|
|
8
|
+
* detect a server-side rollback of `currentEpoch` or a fabricated epoch list --
|
|
9
|
+
* a malicious server cannot forge a valid MAC without the epoch secret.
|
|
10
|
+
*
|
|
11
|
+
* The MAC covers only `scheme`, `version`, `currentEpoch`, and the ordered list
|
|
12
|
+
* of epoch ids -- deliberately NOT the recipient entries, so adding a recipient
|
|
13
|
+
* (which cannot be forged without the epoch secret anyway) does not invalidate
|
|
14
|
+
* the MAC. Covering `version` makes stripping the marker's scheme version
|
|
15
|
+
* MAC-detectable.
|
|
16
|
+
*
|
|
17
|
+
* Documented limitation (mirrors Cryptomator's versionMac): a server can still
|
|
18
|
+
* replay an ENTIRE prior consistent configuration (an old epoch list with its
|
|
19
|
+
* matching old MAC). Detecting that needs client-side monotonic state, which is
|
|
20
|
+
* out of scope here.
|
|
21
|
+
*/
|
|
22
|
+
import { base64urlnopad } from '@scure/base';
|
|
23
|
+
const TEXT_ENCODER = new TextEncoder();
|
|
24
|
+
/**
|
|
25
|
+
* The HKDF `info` string binding the derived key to this MAC construction.
|
|
26
|
+
*/
|
|
27
|
+
const MAC_KEY_INFO = TEXT_ENCODER.encode('was-epoch-config-mac/v1');
|
|
28
|
+
/**
|
|
29
|
+
* The domain-separation prefix prepended to the JSON payload before it is MACed.
|
|
30
|
+
*/
|
|
31
|
+
const MAC_PAYLOAD_PREFIX = 'was-epoch-config/v1.';
|
|
32
|
+
/**
|
|
33
|
+
* Derives the HMAC-SHA256 key from a 32-byte epoch secret:
|
|
34
|
+
* HKDF-SHA256(ikm = the epoch secret, salt = empty, info =
|
|
35
|
+
* "was-epoch-config-mac/v1", length 32 bytes). Uses WebCrypto (`crypto.subtle`)
|
|
36
|
+
* so it works unchanged in Node 24 and browsers.
|
|
37
|
+
*
|
|
38
|
+
* @param epochSecret {Uint8Array} the 32-byte current epoch secret
|
|
39
|
+
* @returns {Promise<CryptoKey>} the derived HMAC key (sign + verify)
|
|
40
|
+
*/
|
|
41
|
+
async function deriveMacKey(epochSecret) {
|
|
42
|
+
const ikm = await crypto.subtle.importKey('raw', epochSecret, 'HKDF', false, ['deriveKey']);
|
|
43
|
+
return crypto.subtle.deriveKey({
|
|
44
|
+
name: 'HKDF',
|
|
45
|
+
hash: 'SHA-256',
|
|
46
|
+
salt: new Uint8Array(0),
|
|
47
|
+
info: MAC_KEY_INFO
|
|
48
|
+
}, ikm, { name: 'HMAC', hash: 'SHA-256', length: 256 }, false, ['sign', 'verify']);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Builds the MACed payload bytes for a marker's epoch configuration:
|
|
52
|
+
* `UTF8("was-epoch-config/v1." + JSON.stringify({ scheme, version,
|
|
53
|
+
* currentEpoch, epochs }))`, with `version` normalized to `null` when absent
|
|
54
|
+
* and `epochs` the ordered list of epoch id strings. The object member order is
|
|
55
|
+
* fixed so both sides serialize identically.
|
|
56
|
+
*
|
|
57
|
+
* @param marker {CollectionEncryption} the marker whose epoch configuration is
|
|
58
|
+
* being authenticated
|
|
59
|
+
* @returns {Uint8Array}
|
|
60
|
+
*/
|
|
61
|
+
function macPayload(marker) {
|
|
62
|
+
const payload = {
|
|
63
|
+
scheme: marker.scheme,
|
|
64
|
+
version: marker.version ?? null,
|
|
65
|
+
currentEpoch: marker.currentEpoch,
|
|
66
|
+
epochs: (marker.epochs ?? []).map(epoch => epoch.id)
|
|
67
|
+
};
|
|
68
|
+
const bytes = TEXT_ENCODER.encode(MAC_PAYLOAD_PREFIX + JSON.stringify(payload));
|
|
69
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Computes the `epochsMac` field for a marker under the current epoch's secret.
|
|
73
|
+
* Compute this over the exact marker state being written (member fields already
|
|
74
|
+
* stamped), since a compare-and-swap retry re-reads the marker.
|
|
75
|
+
*
|
|
76
|
+
* @param options {object}
|
|
77
|
+
* @param options.marker {CollectionEncryption} the marker being written
|
|
78
|
+
* @param options.epochSecret {Uint8Array} the 32-byte current epoch secret
|
|
79
|
+
* @returns {Promise<CollectionEncryptionEpochsMac>}
|
|
80
|
+
*/
|
|
81
|
+
export async function computeEpochsMac({ marker, epochSecret }) {
|
|
82
|
+
const key = await deriveMacKey(epochSecret);
|
|
83
|
+
const signature = await crypto.subtle.sign('HMAC', key, macPayload(marker));
|
|
84
|
+
return {
|
|
85
|
+
v: 1,
|
|
86
|
+
alg: 'HS256',
|
|
87
|
+
mac: base64urlnopad.encode(new Uint8Array(signature))
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Verifies a marker's `epochsMac` against a recomputation from the current
|
|
92
|
+
* epoch's secret. Returns `false` on any mismatch (or a malformed `mac`); the
|
|
93
|
+
* caller decides the error semantics. The MAC construction's own version/alg
|
|
94
|
+
* (`v` / `alg`) is validated by the caller before this is called.
|
|
95
|
+
*
|
|
96
|
+
* @param options {object}
|
|
97
|
+
* @param options.marker {CollectionEncryption} the marker to verify (its
|
|
98
|
+
* `epochsMac.mac` is the tag under test)
|
|
99
|
+
* @param options.epochSecret {Uint8Array} the 32-byte current epoch secret
|
|
100
|
+
* @returns {Promise<boolean>}
|
|
101
|
+
*/
|
|
102
|
+
export async function verifyEpochsMac({ marker, epochSecret }) {
|
|
103
|
+
const epochsMac = marker.epochsMac;
|
|
104
|
+
if (!epochsMac) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
let mac;
|
|
108
|
+
try {
|
|
109
|
+
mac = base64urlnopad.decode(epochsMac.mac);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
const key = await deriveMacKey(epochSecret);
|
|
115
|
+
return crypto.subtle.verify('HMAC', key, mac, macPayload(marker));
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=epochMac.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"epochMac.js","sourceRoot":"","sources":["../../src/edv/epochMac.ts"],"names":[],"mappings":"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"}
|
package/dist/edv/index.d.ts
CHANGED
|
@@ -27,4 +27,6 @@ export { WasTransport, JOSE_CONTENT_TYPE } from './WasTransport.js';
|
|
|
27
27
|
export { initRecipients, addRecipient, removeRecipient } from './recipients.js';
|
|
28
28
|
export type { OwnerKey, RecipientPublicKey } from './recipients.js';
|
|
29
29
|
export { mintEpoch, epochKeyIdFor } from './epochCrypto.js';
|
|
30
|
+
export { createEdvDocCipher, ownerRecipient, UnknownEpochError, isEncryptedEnvelope } from './docCipher.js';
|
|
31
|
+
export type { DocCipher } from './docCipher.js';
|
|
30
32
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/edv/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/edv/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7D,YAAY,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACnE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAC/E,YAAY,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACnE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/edv/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7D,YAAY,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACnE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAC/E,YAAY,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACnE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAC3D,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,gBAAgB,CAAA;AACvB,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"}
|
package/dist/edv/index.js
CHANGED
|
@@ -25,4 +25,5 @@ export { createEdvEncryption, EdvCodec } from './EdvCodec.js';
|
|
|
25
25
|
export { WasTransport, JOSE_CONTENT_TYPE } from './WasTransport.js';
|
|
26
26
|
export { initRecipients, addRecipient, removeRecipient } from './recipients.js';
|
|
27
27
|
export { mintEpoch, epochKeyIdFor } from './epochCrypto.js';
|
|
28
|
+
export { createEdvDocCipher, ownerRecipient, UnknownEpochError, isEncryptedEnvelope } from './docCipher.js';
|
|
28
29
|
//# sourceMappingURL=index.js.map
|
package/dist/edv/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/edv/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE7D,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACnE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAE/E,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/edv/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE7D,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACnE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAE/E,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAC3D,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,gBAAgB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recipients.d.ts","sourceRoot":"","sources":["../../src/edv/recipients.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AACpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAGxC,OAAO,KAAK,EACV,oBAAoB,EAEpB,cAAc,EACf,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"recipients.d.ts","sourceRoot":"","sources":["../../src/edv/recipients.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AACpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAGxC,OAAO,KAAK,EACV,oBAAoB,EAEpB,cAAc,EACf,MAAM,aAAa,CAAA;AAQpB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE1D,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE1D;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,eAAe,EAAE,gBAAgB,CAAA;CAClC;AAQD;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAAC,EACnC,UAAU,EACV,UAAU,EACX,EAAE;IACD,UAAU,EAAE,UAAU,CAAA;IACtB,UAAU,EAAE,kBAAkB,EAAE,CAAA;CACjC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAyChC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,YAAY,CAAC,EACjC,UAAU,EACV,SAAS,EACT,KAAK,EACN,EAAE;IACD,UAAU,EAAE,UAAU,CAAA;IACtB,SAAS,EAAE,kBAAkB,CAAA;IAC7B,KAAK,EAAE,QAAQ,CAAA;CAChB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAuDhC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAsB,eAAe,CAAC,EACpC,UAAU,EACV,KAAK,EACL,WAAW,EACX,MAAM,EACN,mBAAgD,EACjD,EAAE;IACD,UAAU,EAAE,UAAU,CAAA;IACtB,KAAK,EAAE,KAAK,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,cAAc,GAAG,cAAc,EAAE,CAAA;IACzC,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAA;CACnE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA+FhC"}
|