@enbox/dwn-sdk-js 0.4.9 → 0.4.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.mjs +1 -1
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/generated/precompiled-validators.js +1262 -3888
- package/dist/esm/generated/precompiled-validators.js.map +1 -1
- package/dist/esm/src/core/constants.js +0 -26
- package/dist/esm/src/core/constants.js.map +1 -1
- package/dist/esm/src/core/encryption-control.js +0 -10
- package/dist/esm/src/core/encryption-control.js.map +1 -1
- package/dist/esm/src/core/replication-apply.js.map +1 -1
- package/dist/esm/src/handlers/records-query.js +9 -11
- package/dist/esm/src/handlers/records-query.js.map +1 -1
- package/dist/esm/src/handlers/records-read.js +15 -6
- package/dist/esm/src/handlers/records-read.js.map +1 -1
- package/dist/esm/src/handlers/records-subscribe.js +8 -11
- package/dist/esm/src/handlers/records-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/records-write.js +6 -12
- package/dist/esm/src/handlers/records-write.js.map +1 -1
- package/dist/esm/src/index.js +1 -1
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/interfaces/records-write-query.js +3 -0
- package/dist/esm/src/interfaces/records-write-query.js.map +1 -1
- package/dist/esm/src/interfaces/records-write.js +0 -9
- package/dist/esm/src/interfaces/records-write.js.map +1 -1
- package/dist/esm/src/store/message-store-level.js +104 -3
- package/dist/esm/src/store/message-store-level.js.map +1 -1
- package/dist/esm/src/store/storage-controller.js +44 -33
- package/dist/esm/src/store/storage-controller.js.map +1 -1
- package/dist/esm/src/utils/encryption.js +2 -7
- package/dist/esm/src/utils/encryption.js.map +1 -1
- package/dist/esm/src/utils/hd-key.js +0 -27
- package/dist/esm/src/utils/hd-key.js.map +1 -1
- package/dist/esm/src/utils/initial-write-attachment.js +48 -0
- package/dist/esm/src/utils/initial-write-attachment.js.map +1 -0
- package/dist/esm/src/utils/protocols.js +23 -51
- package/dist/esm/src/utils/protocols.js.map +1 -1
- package/dist/esm/src/utils/records.js +17 -31
- package/dist/esm/src/utils/records.js.map +1 -1
- package/dist/esm/tests/core/constants.spec.js +1 -11
- package/dist/esm/tests/core/constants.spec.js.map +1 -1
- package/dist/esm/tests/core/validation-read-closure.spec.js +1 -1
- package/dist/esm/tests/core/validation-read-closure.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-composition.spec.js +15 -60
- package/dist/esm/tests/features/protocol-composition.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-delete.spec.js +1 -1
- package/dist/esm/tests/handlers/records-delete.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-query.spec.js +82 -0
- package/dist/esm/tests/handlers/records-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-read.spec.js +40 -13
- package/dist/esm/tests/handlers/records-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-subscribe.spec.js +60 -1
- package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-write.spec.js +10 -10
- package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/protocols-configure.spec.js +5 -5
- package/dist/esm/tests/interfaces/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-write.spec.js +5 -51
- package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js +2 -2
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js.map +1 -1
- package/dist/esm/tests/store/message-store.spec.js +105 -0
- package/dist/esm/tests/store/message-store.spec.js.map +1 -1
- package/dist/esm/tests/utils/encryption-callbacks.spec.js +18 -30
- package/dist/esm/tests/utils/encryption-callbacks.spec.js.map +1 -1
- package/dist/esm/tests/utils/encryption-control-test-utils.js +1 -1
- package/dist/esm/tests/utils/encryption-control-test-utils.js.map +1 -1
- package/dist/esm/tests/utils/encryption.spec.js +30 -33
- package/dist/esm/tests/utils/encryption.spec.js.map +1 -1
- package/dist/esm/tests/utils/test-data-generator.js +76 -24
- package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/protocols/protocols-configure.spec.js +1 -1
- package/dist/esm/tests/validation/json-schemas/protocols/protocols-configure.spec.js.map +1 -1
- package/dist/types/generated/precompiled-validators.d.ts +88 -120
- package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
- package/dist/types/src/core/constants.d.ts +0 -13
- package/dist/types/src/core/constants.d.ts.map +1 -1
- package/dist/types/src/core/encryption-control.d.ts +0 -1
- package/dist/types/src/core/encryption-control.d.ts.map +1 -1
- package/dist/types/src/core/replication-apply.d.ts +0 -7
- package/dist/types/src/core/replication-apply.d.ts.map +1 -1
- package/dist/types/src/handlers/records-query.d.ts.map +1 -1
- package/dist/types/src/handlers/records-read.d.ts.map +1 -1
- package/dist/types/src/handlers/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +2 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-write-query.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-write.d.ts +0 -4
- package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
- package/dist/types/src/store/message-store-level.d.ts +9 -1
- package/dist/types/src/store/message-store-level.d.ts.map +1 -1
- package/dist/types/src/store/storage-controller.d.ts +18 -8
- package/dist/types/src/store/storage-controller.d.ts.map +1 -1
- package/dist/types/src/types/message-store.d.ts +36 -0
- package/dist/types/src/types/message-store.d.ts.map +1 -1
- package/dist/types/src/utils/encryption.d.ts +0 -3
- package/dist/types/src/utils/encryption.d.ts.map +1 -1
- package/dist/types/src/utils/hd-key.d.ts +1 -11
- package/dist/types/src/utils/hd-key.d.ts.map +1 -1
- package/dist/types/src/utils/initial-write-attachment.d.ts +17 -0
- package/dist/types/src/utils/initial-write-attachment.d.ts.map +1 -0
- package/dist/types/src/utils/protocols.d.ts +2 -7
- package/dist/types/src/utils/protocols.d.ts.map +1 -1
- package/dist/types/src/utils/records.d.ts +1 -8
- package/dist/types/src/utils/records.d.ts.map +1 -1
- package/dist/types/tests/features/protocol-composition.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-query.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-read.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/end-to-end-tests.spec.d.ts.map +1 -1
- package/dist/types/tests/store/message-store.spec.d.ts.map +1 -1
- package/dist/types/tests/utils/test-data-generator.d.ts +17 -1
- package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/core/constants.ts +0 -30
- package/src/core/encryption-control.ts +1 -17
- package/src/core/replication-apply.ts +0 -8
- package/src/handlers/records-query.ts +21 -22
- package/src/handlers/records-read.ts +22 -9
- package/src/handlers/records-subscribe.ts +9 -15
- package/src/handlers/records-write.ts +15 -15
- package/src/index.ts +1 -2
- package/src/interfaces/records-write-query.ts +3 -0
- package/src/interfaces/records-write.ts +0 -13
- package/src/store/message-store-level.ts +160 -5
- package/src/store/storage-controller.ts +57 -43
- package/src/types/message-store.ts +37 -0
- package/src/utils/encryption.ts +2 -15
- package/src/utils/hd-key.ts +1 -32
- package/src/utils/initial-write-attachment.ts +64 -0
- package/src/utils/protocols.ts +15 -78
- package/src/utils/records.ts +15 -57
- package/dist/esm/tests/validation/json-schemas/encryption/delivery.spec.js +0 -47
- package/dist/esm/tests/validation/json-schemas/encryption/delivery.spec.js.map +0 -1
- package/dist/esm/tests/validation/json-schemas/encryption/grant-key.spec.js +0 -72
- package/dist/esm/tests/validation/json-schemas/encryption/grant-key.spec.js.map +0 -1
- package/dist/types/tests/validation/json-schemas/encryption/delivery.spec.d.ts +0 -2
- package/dist/types/tests/validation/json-schemas/encryption/delivery.spec.d.ts.map +0 -1
- package/dist/types/tests/validation/json-schemas/encryption/grant-key.spec.d.ts +0 -2
- package/dist/types/tests/validation/json-schemas/encryption/grant-key.spec.d.ts.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { DataStore } from '../types/data-store.js';
|
|
2
|
-
import type { Filter } from '../types/query-types.js';
|
|
3
2
|
import type { GenericMessage } from '../types/message-types.js';
|
|
4
|
-
import type { MessageStore } from '../types/message-store.js';
|
|
5
3
|
import type { ProgressToken } from '../types/subscriptions.js';
|
|
4
|
+
import type { Filter, KeyValues } from '../types/query-types.js';
|
|
5
|
+
import type { MessageStore, MessageStorePutResult } from '../types/message-store.js';
|
|
6
6
|
import type { RecordsDeleteMessage, RecordsWriteMessage } from '../types/records-types.js';
|
|
7
7
|
|
|
8
8
|
import { DwnConstant } from '../core/dwn-constant.js';
|
|
@@ -80,7 +80,12 @@ export class StorageController {
|
|
|
80
80
|
// tombstones carry no visibility facts in their descriptors, so the retained write is the
|
|
81
81
|
// durable source of truth for later prune/replay/replication index reconstruction.
|
|
82
82
|
const indexes = recordsDelete.constructIndexes(initialWrite, newestPreDeleteWrite);
|
|
83
|
-
|
|
83
|
+
|
|
84
|
+
// store the tombstone and displace every other message for this record in one atomic commit,
|
|
85
|
+
// retaining the writes needed for replay and future tombstone visibility
|
|
86
|
+
const { position } = await StorageController.commitLatestStateTransition(
|
|
87
|
+
tenant, existingMessages, { message, indexes }, [newestPreDeleteWrite], this.messageStore, this.dataStore
|
|
88
|
+
);
|
|
84
89
|
|
|
85
90
|
if (message.descriptor.prune) {
|
|
86
91
|
// purge/hard-delete all descendant records. Cascade is intentionally protocol-agnostic:
|
|
@@ -92,11 +97,6 @@ export class StorageController {
|
|
|
92
97
|
);
|
|
93
98
|
}
|
|
94
99
|
|
|
95
|
-
// displace every other message for this record, retaining the writes needed for replay and future tombstone visibility
|
|
96
|
-
await StorageController.deleteDisplacedMessagesAndRetainWrites(
|
|
97
|
-
tenant, existingMessages, message, this.messageStore, this.dataStore, [newestPreDeleteWrite]
|
|
98
|
-
);
|
|
99
|
-
|
|
100
100
|
return { position };
|
|
101
101
|
}
|
|
102
102
|
|
|
@@ -285,56 +285,70 @@ export class StorageController {
|
|
|
285
285
|
}
|
|
286
286
|
|
|
287
287
|
/**
|
|
288
|
-
*
|
|
289
|
-
* the initial write and the caller-supplied writes
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
288
|
+
* Stores the new latest message and displaces every other message in `existingMessages` as ONE
|
|
289
|
+
* atomic message-store commit: the initial write and the caller-supplied writes are retained as
|
|
290
|
+
* non-latest state for future replay and tombstone visibility reconstruction, and the remaining
|
|
291
|
+
* displaced messages are deleted. Readers can never observe an intermediate state where both the
|
|
292
|
+
* new message and a displaced message carry latest-state indexes.
|
|
293
|
+
*
|
|
294
|
+
* Displacement is deliberately NOT a timestamp comparison: a RecordsDelete displaces even a
|
|
295
|
+
* newer RecordsWrite (delete-wins convergence), and on resumable-task replay the new message
|
|
296
|
+
* itself is back in `existingMessages` and must survive — so membership is decided by CID.
|
|
297
|
+
*
|
|
298
|
+
* Displaced messages' unreferenced data is deleted from the data store only AFTER the commit:
|
|
299
|
+
* a crash in between leaves at most an orphaned data blob, never a live row whose data is gone.
|
|
294
300
|
*/
|
|
295
|
-
public static async
|
|
301
|
+
public static async commitLatestStateTransition(
|
|
296
302
|
tenant: string,
|
|
297
303
|
existingMessages: GenericMessage[],
|
|
298
|
-
|
|
304
|
+
newMessage: { message: GenericMessage, indexes: KeyValues },
|
|
305
|
+
additionalRetainedRecordsWrites: RecordsWriteMessage[],
|
|
299
306
|
messageStore: MessageStore,
|
|
300
307
|
dataStore: DataStore,
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const retainedMessageCid = await Message.getCid(retainedMessage);
|
|
308
|
+
): Promise<MessageStorePutResult> {
|
|
309
|
+
const newMessageCid = await Message.getCid(newMessage.message);
|
|
304
310
|
const additionalRetainedRecordsWriteCids = new Set<string>();
|
|
305
311
|
for (const retainedRecordsWrite of additionalRetainedRecordsWrites) {
|
|
306
312
|
additionalRetainedRecordsWriteCids.add(await Message.getCid(retainedRecordsWrite));
|
|
307
313
|
}
|
|
308
314
|
|
|
309
315
|
// NOTE: under normal operation, there should only be at most two existing records per `recordId` (initial + a potential subsequent write/delete),
|
|
310
|
-
// but the
|
|
316
|
+
// but the plan-then-commit shape below heals any lingering rows a previous partial application left behind.
|
|
317
|
+
const retains: { messageCid: string, message: GenericMessage, indexes: KeyValues }[] = [];
|
|
318
|
+
const deletes: string[] = [];
|
|
319
|
+
const displacedMessages: GenericMessage[] = [];
|
|
311
320
|
for (const message of existingMessages) {
|
|
312
321
|
const messageCid = await Message.getCid(message);
|
|
313
|
-
const messageIsDisplaced = messageCid !==
|
|
314
|
-
if (messageIsDisplaced) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
// delete message from message store
|
|
334
|
-
await messageStore.delete(tenant, messageCid);
|
|
335
|
-
}
|
|
322
|
+
const messageIsDisplaced = messageCid !== newMessageCid;
|
|
323
|
+
if (!messageIsDisplaced) {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
displacedMessages.push(message);
|
|
328
|
+
|
|
329
|
+
// Retained writes must stay in the message store and state index so future deletes and
|
|
330
|
+
// replicas can reconstruct tombstone visibility, but they must no longer be latest state.
|
|
331
|
+
const shouldKeepAsNonLatestWrite =
|
|
332
|
+
await RecordsWrite.isInitialWrite(message) ||
|
|
333
|
+
additionalRetainedRecordsWriteCids.has(messageCid);
|
|
334
|
+
if (shouldKeepAsNonLatestWrite) {
|
|
335
|
+
const retainedRecordsWriteMessage = StorageController.stripInlineData(message as RecordsWriteMessage);
|
|
336
|
+
const existingRecordsWrite = await RecordsWrite.parse(retainedRecordsWriteMessage);
|
|
337
|
+
const isLatestBaseState = false;
|
|
338
|
+
const indexes = await existingRecordsWrite.constructIndexes(isLatestBaseState);
|
|
339
|
+
retains.push({ messageCid, message: retainedRecordsWriteMessage, indexes });
|
|
340
|
+
} else {
|
|
341
|
+
deletes.push(messageCid);
|
|
336
342
|
}
|
|
337
343
|
}
|
|
344
|
+
|
|
345
|
+
const putResult = await messageStore.commitLatestState(tenant, { put: newMessage, retains, deletes });
|
|
346
|
+
|
|
347
|
+
for (const message of displacedMessages) {
|
|
348
|
+
await StorageController.deleteFromDataStoreIfNeeded(dataStore, tenant, message, newMessage.message);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return putResult;
|
|
338
352
|
}
|
|
339
353
|
|
|
340
354
|
private static stripInlineData(message: RecordsWriteMessage): RecordsWriteMessage {
|
|
@@ -24,6 +24,28 @@ export type MessageStorePutResult = {
|
|
|
24
24
|
position?: ProgressToken;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* A latest-state transition applied by {@link MessageStore.commitLatestState}: the insert of a new
|
|
29
|
+
* message together with the displacement of the messages it supersedes.
|
|
30
|
+
*/
|
|
31
|
+
export type MessageStoreLatestStateTransition = {
|
|
32
|
+
/**
|
|
33
|
+
* The new message to insert, with its insert-time indexes.
|
|
34
|
+
*/
|
|
35
|
+
put: { message: GenericMessage; indexes: KeyValues };
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Displaced writes retained as non-latest state: same-CID in-place replacements
|
|
39
|
+
* (same row, same log sequence) applied with the insert.
|
|
40
|
+
*/
|
|
41
|
+
retains?: { messageCid: string; message: GenericMessage; indexes: KeyValues }[];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Message CIDs of displaced messages that are not retained, deleted with the insert.
|
|
45
|
+
*/
|
|
46
|
+
deletes?: string[];
|
|
47
|
+
};
|
|
48
|
+
|
|
27
49
|
export interface MessageStore {
|
|
28
50
|
/**
|
|
29
51
|
* opens a connection to the underlying store
|
|
@@ -46,6 +68,21 @@ export interface MessageStore {
|
|
|
46
68
|
options?: MessageStoreOptions
|
|
47
69
|
): Promise<MessageStorePutResult>;
|
|
48
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Atomically inserts a new message and displaces the messages it supersedes: retained writes are
|
|
73
|
+
* replaced in place with their demoted state, and the remaining displaced rows are deleted, all
|
|
74
|
+
* in one commit. Readers can never observe an intermediate state where both the new message and
|
|
75
|
+
* a displaced message carry latest-state indexes.
|
|
76
|
+
*
|
|
77
|
+
* When the new message already exists, returns `duplicate` and still applies the retains and
|
|
78
|
+
* deletes, so replaying a transition heals one that was only partially planned before a crash.
|
|
79
|
+
*/
|
|
80
|
+
commitLatestState(
|
|
81
|
+
tenant: string,
|
|
82
|
+
transition: MessageStoreLatestStateTransition,
|
|
83
|
+
options?: MessageStoreOptions
|
|
84
|
+
): Promise<MessageStorePutResult>;
|
|
85
|
+
|
|
49
86
|
/**
|
|
50
87
|
* Fetches a single message by `cid` from the underlying store.
|
|
51
88
|
* Returns `undefined` no message was found.
|
package/src/utils/encryption.ts
CHANGED
|
@@ -107,13 +107,11 @@ export type KeyUnwrapPayload = {
|
|
|
107
107
|
export type X25519KeyWrapInput = {
|
|
108
108
|
cek: Uint8Array;
|
|
109
109
|
keyInput: X25519KeyEncryptionInput;
|
|
110
|
-
ephemeralPrivateKey?: Jwk;
|
|
111
110
|
};
|
|
112
111
|
|
|
113
112
|
export type SealWrapInput = {
|
|
114
113
|
privateKeyBytes: Uint8Array;
|
|
115
114
|
keyInput: SealKeyWrapInput;
|
|
116
|
-
ephemeralPrivateKey?: Jwk;
|
|
117
115
|
};
|
|
118
116
|
|
|
119
117
|
export type SealUnwrapInput = {
|
|
@@ -180,17 +178,6 @@ export class Encryption {
|
|
|
180
178
|
return Encryption.toStream(ciphertext);
|
|
181
179
|
}
|
|
182
180
|
|
|
183
|
-
public static async decryptStream(
|
|
184
|
-
algorithm: ContentEncryptionAlgorithm,
|
|
185
|
-
keyBytes: Uint8Array,
|
|
186
|
-
initializationVector: Uint8Array,
|
|
187
|
-
ciphertextStream: ReadableStream<Uint8Array>,
|
|
188
|
-
): Promise<ReadableStream<Uint8Array>> {
|
|
189
|
-
const ciphertext = await Encryption.readStream(ciphertextStream);
|
|
190
|
-
const plaintext = await Encryption.decrypt(algorithm, keyBytes, initializationVector, ciphertext);
|
|
191
|
-
return Encryption.toStream(plaintext);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
181
|
public static async wrapKey(input: X25519KeyWrapInput): Promise<{ encryptedKey: Uint8Array; ephemeralPublicKey: PublicKeyJwk }> {
|
|
195
182
|
if (Encryption.isX25519KeyAgreementAlgorithm(input.keyInput.algorithm)) {
|
|
196
183
|
return Encryption.wrapX25519Key(input);
|
|
@@ -206,7 +193,7 @@ export class Encryption {
|
|
|
206
193
|
|
|
207
194
|
Encryption.validateContentEncryptionKey(input.privateKeyBytes);
|
|
208
195
|
|
|
209
|
-
const privateKey =
|
|
196
|
+
const privateKey = await X25519.generateKey();
|
|
210
197
|
const publicKey = await X25519.getPublicKey({ key: privateKey }) as PublicKeyJwk;
|
|
211
198
|
const sharedSecret = await X25519.sharedSecret({
|
|
212
199
|
privateKeyA : privateKey,
|
|
@@ -231,7 +218,7 @@ export class Encryption {
|
|
|
231
218
|
private static async wrapX25519Key(input: X25519KeyWrapInput): Promise<{ encryptedKey: Uint8Array; ephemeralPublicKey: PublicKeyJwk }> {
|
|
232
219
|
Encryption.validateContentEncryptionKey(input.cek);
|
|
233
220
|
|
|
234
|
-
const privateKey =
|
|
221
|
+
const privateKey = await X25519.generateKey();
|
|
235
222
|
const publicKey = await X25519.getPublicKey({ key: privateKey }) as PublicKeyJwk;
|
|
236
223
|
const sharedSecret = await X25519.sharedSecret({
|
|
237
224
|
privateKeyA : privateKey,
|
package/src/utils/hd-key.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { PrivateKeyJwk
|
|
1
|
+
import type { PrivateKeyJwk } from '../types/jose-types.js';
|
|
2
2
|
|
|
3
3
|
import { Encoder } from './encoder.js';
|
|
4
|
-
import { X25519 } from '@enbox/crypto';
|
|
5
4
|
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
|
|
6
5
|
|
|
7
6
|
export enum KeyDerivationScheme {
|
|
@@ -20,36 +19,6 @@ export type DerivedPrivateJwk = {
|
|
|
20
19
|
* Class containing hierarchical deterministic key related utility methods used by the DWN.
|
|
21
20
|
*/
|
|
22
21
|
export class HdKey {
|
|
23
|
-
/**
|
|
24
|
-
* Derives a descendant private key.
|
|
25
|
-
* Uses X25519 keys for encryption key derivation.
|
|
26
|
-
*/
|
|
27
|
-
public static async derivePrivateKey(ancestorKey: DerivedPrivateJwk, subDerivationPath: string[]): Promise<DerivedPrivateJwk> {
|
|
28
|
-
const ancestorPrivateKey = await X25519.privateKeyToBytes({ privateKey: ancestorKey.derivedPrivateKey });
|
|
29
|
-
const ancestorPrivateKeyDerivationPath = ancestorKey.derivationPath ?? [];
|
|
30
|
-
const derivedPrivateKeyBytes = await HdKey.derivePrivateKeyBytes(ancestorPrivateKey, subDerivationPath);
|
|
31
|
-
const derivedPrivateKeyJwk = await X25519.bytesToPrivateKey({ privateKeyBytes: derivedPrivateKeyBytes });
|
|
32
|
-
const derivedDescendantPrivateKey: DerivedPrivateJwk = {
|
|
33
|
-
rootKeyId : ancestorKey.rootKeyId,
|
|
34
|
-
derivationScheme : ancestorKey.derivationScheme,
|
|
35
|
-
derivationPath : [...ancestorPrivateKeyDerivationPath, ...subDerivationPath],
|
|
36
|
-
derivedPrivateKey : derivedPrivateKeyJwk as PrivateKeyJwk
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
return derivedDescendantPrivateKey;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Derives a descendant public key from an ancestor private key.
|
|
44
|
-
* Uses X25519 keys for encryption key derivation.
|
|
45
|
-
*/
|
|
46
|
-
public static async derivePublicKey(ancestorKey: DerivedPrivateJwk, subDerivationPath: string[]): Promise<PublicKeyJwk> {
|
|
47
|
-
const derivedDescendantPrivateKey = await HdKey.derivePrivateKey(ancestorKey, subDerivationPath);
|
|
48
|
-
const derivedDescendantPublicKey = await X25519.getPublicKey({ key: derivedDescendantPrivateKey.derivedPrivateKey });
|
|
49
|
-
|
|
50
|
-
return derivedDescendantPublicKey as PublicKeyJwk;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
22
|
/**
|
|
54
23
|
* Derives a hardened hierarchical deterministic private key.
|
|
55
24
|
*/
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { MessageStore } from '../types/message-store.js';
|
|
2
|
+
import type { RecordsQueryReplyEntry } from '../types/records-types.js';
|
|
3
|
+
|
|
4
|
+
import { DwnErrorCode } from '../core/dwn-error.js';
|
|
5
|
+
import { Messages } from './messages.js';
|
|
6
|
+
import { RecordsWrite } from '../interfaces/records-write.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Attaches each returned update's retained initial write, resolved for the whole result page in
|
|
10
|
+
* one batched lookup by the stable identity `entryId === recordId` — unlike the mutable
|
|
11
|
+
* latest-state index, that identity holds at every point of the record's lifecycle.
|
|
12
|
+
*
|
|
13
|
+
* An update whose initial write is genuinely missing (store corruption) is omitted with a
|
|
14
|
+
* warning rather than failing the whole reply.
|
|
15
|
+
*/
|
|
16
|
+
export async function attachInitialWrites(input: {
|
|
17
|
+
messageStore: MessageStore;
|
|
18
|
+
tenant: string;
|
|
19
|
+
recordsWrites: RecordsQueryReplyEntry[];
|
|
20
|
+
operationName: 'RecordsQuery' | 'RecordsSubscribe';
|
|
21
|
+
}): Promise<RecordsQueryReplyEntry[]> {
|
|
22
|
+
const updateRecordIds = new Set<string>();
|
|
23
|
+
const initialWriteState = new Map<RecordsQueryReplyEntry, boolean>();
|
|
24
|
+
for (const recordsWrite of input.recordsWrites) {
|
|
25
|
+
const isInitialWrite = await RecordsWrite.isInitialWrite(recordsWrite);
|
|
26
|
+
initialWriteState.set(recordsWrite, isInitialWrite);
|
|
27
|
+
if (!isInitialWrite) {
|
|
28
|
+
updateRecordIds.add(recordsWrite.recordId);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (updateRecordIds.size === 0) {
|
|
33
|
+
return input.recordsWrites;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const initialWriteByRecordId = new Map<string, RecordsQueryReplyEntry>();
|
|
37
|
+
const { messages } = await input.messageStore.query(input.tenant, [{ entryId: [...updateRecordIds] }]);
|
|
38
|
+
for (const message of messages) {
|
|
39
|
+
const initialWrite = message as RecordsQueryReplyEntry;
|
|
40
|
+
initialWriteByRecordId.set(initialWrite.recordId, initialWrite);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const completeRecordsWrites: RecordsQueryReplyEntry[] = [];
|
|
44
|
+
for (const recordsWrite of input.recordsWrites) {
|
|
45
|
+
if (initialWriteState.get(recordsWrite) === true) {
|
|
46
|
+
completeRecordsWrites.push(recordsWrite);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const storedInitialWrite = initialWriteByRecordId.get(recordsWrite.recordId);
|
|
51
|
+
if (storedInitialWrite === undefined) {
|
|
52
|
+
console.warn(
|
|
53
|
+
`${DwnErrorCode.RecordsWriteGetInitialWriteNotFound}: ` +
|
|
54
|
+
`${input.operationName} skipped record ${recordsWrite.recordId}`,
|
|
55
|
+
);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const { message: initialWrite } = Messages.detachEncodedData(storedInitialWrite);
|
|
60
|
+
completeRecordsWrites.push({ ...recordsWrite, initialWrite: initialWrite as RecordsQueryReplyEntry });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return completeRecordsWrites;
|
|
64
|
+
}
|
package/src/utils/protocols.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import type { DerivedPrivateJwk } from '../utils/hd-key.js';
|
|
2
1
|
import type { EncryptionKeyDeriver } from '../types/encryption-types.js';
|
|
3
|
-
import type { PrivateKeyJwk, PublicKeyJwk } from '../types/jose-types.js';
|
|
4
2
|
import type { ProtocolDefinition, ProtocolRuleSet } from '../types/protocols-types.js';
|
|
5
3
|
|
|
6
|
-
import {
|
|
7
|
-
import { HdKey, KeyDerivationScheme } from '../utils/hd-key.js';
|
|
4
|
+
import { KeyDerivationScheme } from '../utils/hd-key.js';
|
|
8
5
|
|
|
9
6
|
/**
|
|
10
7
|
* Result of parsing a cross-protocol reference in `alias:path` format.
|
|
@@ -116,104 +113,44 @@ export class Protocols {
|
|
|
116
113
|
* because their records belong to the referenced protocol, whose own encryption keys govern them.
|
|
117
114
|
* Children of `$ref` nodes are still processed because they belong to the composing protocol.
|
|
118
115
|
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
116
|
+
* Accepts an EncryptionKeyDeriver that performs key derivation internally.
|
|
117
|
+
* The private key never leaves the caller's boundary.
|
|
121
118
|
*/
|
|
122
119
|
public static async deriveAndInjectPublicEncryptionKeys(
|
|
123
120
|
protocolDefinition: ProtocolDefinition,
|
|
124
121
|
keyDeriver: EncryptionKeyDeriver,
|
|
125
|
-
): Promise<ProtocolDefinition>;
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Overload 2 (raw-key): Takes rootKeyId and raw PrivateKeyJwk directly.
|
|
129
|
-
*/
|
|
130
|
-
public static async deriveAndInjectPublicEncryptionKeys(
|
|
131
|
-
protocolDefinition: ProtocolDefinition,
|
|
132
|
-
rootKeyId: string,
|
|
133
|
-
privateJwk: PrivateKeyJwk,
|
|
134
|
-
): Promise<ProtocolDefinition>;
|
|
135
|
-
|
|
136
|
-
// Implementation dispatches based on argument type.
|
|
137
|
-
public static async deriveAndInjectPublicEncryptionKeys(
|
|
138
|
-
protocolDefinition: ProtocolDefinition,
|
|
139
|
-
rootKeyIdOrKeyDeriver: string | EncryptionKeyDeriver,
|
|
140
|
-
privateJwk?: PrivateKeyJwk,
|
|
141
122
|
): Promise<ProtocolDefinition> {
|
|
142
123
|
// clone before modify
|
|
143
124
|
const clone = JSON.parse(JSON.stringify(protocolDefinition)) as ProtocolDefinition;
|
|
144
125
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
clone.$keyAgreement = {
|
|
150
|
-
publicKeyJwk: await keyDeriver.derivePublicKey(basePath),
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
async function injectKeysViaCallback(
|
|
154
|
-
ruleSet: ProtocolRuleSet, parentPath: string[],
|
|
155
|
-
): Promise<void> {
|
|
156
|
-
for (const key in ruleSet) {
|
|
157
|
-
if (!key.startsWith('$')) {
|
|
158
|
-
const currentPath = [...parentPath, key];
|
|
159
|
-
const childRuleSet = ruleSet[key] as ProtocolRuleSet;
|
|
160
|
-
|
|
161
|
-
// Skip $ref nodes — they are governed by the referenced protocol's encryption keys.
|
|
162
|
-
// Still recurse into children, which belong to the composing protocol.
|
|
163
|
-
if (childRuleSet.$ref !== undefined) {
|
|
164
|
-
await injectKeysViaCallback(childRuleSet, currentPath);
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const publicKeyJwk = await keyDeriver.derivePublicKey(currentPath);
|
|
169
|
-
childRuleSet.$keyAgreement = { publicKeyJwk };
|
|
170
|
-
await injectKeysViaCallback(childRuleSet, currentPath);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
await injectKeysViaCallback(clone.structure as ProtocolRuleSet, basePath);
|
|
176
|
-
return clone;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Raw-key path
|
|
180
|
-
const rootKeyId = rootKeyIdOrKeyDeriver;
|
|
126
|
+
const basePath = [KeyDerivationScheme.ProtocolPath, protocolDefinition.protocol];
|
|
127
|
+
clone.$keyAgreement = {
|
|
128
|
+
publicKeyJwk: await keyDeriver.derivePublicKey(basePath),
|
|
129
|
+
};
|
|
181
130
|
|
|
182
|
-
|
|
183
|
-
|
|
131
|
+
async function injectKeys(
|
|
132
|
+
ruleSet: ProtocolRuleSet, parentPath: string[],
|
|
133
|
+
): Promise<void> {
|
|
184
134
|
for (const key in ruleSet) {
|
|
185
|
-
// if we encounter a nested rule set (a property name that doesn't begin with '$'), recursively inject the `$keyAgreement` property
|
|
186
135
|
if (!key.startsWith('$')) {
|
|
187
|
-
const
|
|
136
|
+
const currentPath = [...parentPath, key];
|
|
188
137
|
const childRuleSet = ruleSet[key] as ProtocolRuleSet;
|
|
189
138
|
|
|
190
139
|
// Skip $ref nodes — they are governed by the referenced protocol's encryption keys.
|
|
191
140
|
// Still recurse into children, which belong to the composing protocol.
|
|
192
141
|
if (childRuleSet.$ref !== undefined) {
|
|
193
|
-
await
|
|
142
|
+
await injectKeys(childRuleSet, currentPath);
|
|
194
143
|
continue;
|
|
195
144
|
}
|
|
196
145
|
|
|
197
|
-
const publicKeyJwk = await
|
|
198
|
-
|
|
146
|
+
const publicKeyJwk = await keyDeriver.derivePublicKey(currentPath);
|
|
199
147
|
childRuleSet.$keyAgreement = { publicKeyJwk };
|
|
200
|
-
await
|
|
148
|
+
await injectKeys(childRuleSet, currentPath);
|
|
201
149
|
}
|
|
202
150
|
}
|
|
203
151
|
}
|
|
204
152
|
|
|
205
|
-
|
|
206
|
-
const rootKey: DerivedPrivateJwk = {
|
|
207
|
-
derivationScheme : KeyDerivationScheme.ProtocolPath,
|
|
208
|
-
derivedPrivateKey : privateJwk!,
|
|
209
|
-
rootKeyId
|
|
210
|
-
};
|
|
211
|
-
const protocolLevelDerivedKey = await HdKey.derivePrivateKey(rootKey, [KeyDerivationScheme.ProtocolPath, protocolDefinition.protocol]);
|
|
212
|
-
clone.$keyAgreement = {
|
|
213
|
-
publicKeyJwk: await X25519.getPublicKey({ key: protocolLevelDerivedKey.derivedPrivateKey }) as PublicKeyJwk,
|
|
214
|
-
};
|
|
215
|
-
await addEncryptionProperty(clone.structure as ProtocolRuleSet, protocolLevelDerivedKey);
|
|
216
|
-
|
|
153
|
+
await injectKeys(clone.structure as ProtocolRuleSet, basePath);
|
|
217
154
|
return clone;
|
|
218
155
|
}
|
|
219
156
|
}
|
package/src/utils/records.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { DerivedPrivateJwk } from './hd-key.js';
|
|
2
|
-
import type { Jwk } from '@enbox/crypto';
|
|
3
2
|
import type { KeyDecrypter } from '../types/encryption-types.js';
|
|
4
|
-
import type { PublicKeyJwk } from '../types/jose-types.js';
|
|
5
3
|
import type { X25519KeyEncryption } from './encryption.js';
|
|
6
4
|
import type { Filter, KeyValues, StartsWithFilter } from '../types/query-types.js';
|
|
7
5
|
import type { GenericMessage, GenericSignaturePayload, MessageSort } from '../types/message-types.js';
|
|
@@ -63,31 +61,12 @@ export class Records {
|
|
|
63
61
|
|
|
64
62
|
/**
|
|
65
63
|
* Decrypts the encrypted data in a message reply.
|
|
66
|
-
*
|
|
67
|
-
* Overload 1 (callback-based): Accepts a KeyDecrypter that performs
|
|
68
|
-
* X25519-HKDF key agreement + AES Key Unwrap internally.
|
|
64
|
+
* Accepts a KeyDecrypter that performs X25519-HKDF key agreement + AES Key Unwrap internally.
|
|
69
65
|
*/
|
|
70
66
|
public static async decrypt(
|
|
71
67
|
recordsWrite: RecordsWriteMessage,
|
|
72
68
|
keyDecrypter: KeyDecrypter,
|
|
73
69
|
cipherStream: ReadableStream<Uint8Array>,
|
|
74
|
-
): Promise<ReadableStream<Uint8Array>>;
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Overload 2 (raw-key): Takes DerivedPrivateJwk directly.
|
|
78
|
-
* @param ancestorPrivateKey Any ancestor private key in the key derivation path.
|
|
79
|
-
*/
|
|
80
|
-
public static async decrypt(
|
|
81
|
-
recordsWrite: RecordsWriteMessage,
|
|
82
|
-
ancestorPrivateKey: DerivedPrivateJwk,
|
|
83
|
-
cipherStream: ReadableStream<Uint8Array>,
|
|
84
|
-
): Promise<ReadableStream<Uint8Array>>;
|
|
85
|
-
|
|
86
|
-
// Implementation dispatches based on argument type.
|
|
87
|
-
public static async decrypt(
|
|
88
|
-
recordsWrite: RecordsWriteMessage,
|
|
89
|
-
keyOrDecrypter: DerivedPrivateJwk | KeyDecrypter,
|
|
90
|
-
cipherStream: ReadableStream<Uint8Array>,
|
|
91
70
|
): Promise<ReadableStream<Uint8Array>> {
|
|
92
71
|
const { encryption } = recordsWrite;
|
|
93
72
|
if (encryption === undefined) {
|
|
@@ -101,27 +80,21 @@ export class Records {
|
|
|
101
80
|
const actualDataCid = await Cid.computeDagPbCidFromBytes(ciphertext);
|
|
102
81
|
Records.validateEncryptedDataIntegrity(recordsWrite, actualDataCid, ciphertext.byteLength);
|
|
103
82
|
|
|
104
|
-
const { fullDerivationPath,
|
|
105
|
-
await Records.findMatchingKeyEncryption(recordsWrite,
|
|
83
|
+
const { fullDerivationPath, matchingKeyEncryption } =
|
|
84
|
+
await Records.findMatchingKeyEncryption(recordsWrite, keyDecrypter);
|
|
106
85
|
|
|
107
86
|
if (matchingKeyEncryption === undefined) {
|
|
108
87
|
throw new DwnError(
|
|
109
88
|
DwnErrorCode.RecordsDecryptNoMatchingKeyEncryptedFound,
|
|
110
|
-
`Unable to find a matching key encryption entry for '${
|
|
89
|
+
`Unable to find a matching key encryption entry for '${keyDecrypter.rootKeyId}' and '${keyDecrypter.derivationScheme}'.`
|
|
111
90
|
);
|
|
112
91
|
}
|
|
113
92
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
ephemeralPublicKey : matchingKeyEncryption.ephemeralPublicKey,
|
|
120
|
-
keyEncryption : matchingKeyEncryption,
|
|
121
|
-
});
|
|
122
|
-
} else {
|
|
123
|
-
cek = await Encryption.unwrapKey(leafPrivateKey!, matchingKeyEncryption);
|
|
124
|
-
}
|
|
93
|
+
const cek = await keyDecrypter.decrypt(fullDerivationPath, {
|
|
94
|
+
encryptedKey : Encoder.base64UrlToBytes(matchingKeyEncryption.encryptedKey),
|
|
95
|
+
ephemeralPublicKey : matchingKeyEncryption.ephemeralPublicKey,
|
|
96
|
+
keyEncryption : matchingKeyEncryption,
|
|
97
|
+
});
|
|
125
98
|
|
|
126
99
|
const iv = Encoder.base64UrlToBytes(encryption.initializationVector);
|
|
127
100
|
const plaintext = await Encryption.decrypt(encryption.algorithm, cek, iv, ciphertext);
|
|
@@ -132,33 +105,18 @@ export class Records {
|
|
|
132
105
|
|
|
133
106
|
private static async findMatchingKeyEncryption(
|
|
134
107
|
recordsWrite: RecordsWriteMessage,
|
|
135
|
-
|
|
136
|
-
): Promise<{ fullDerivationPath: string[];
|
|
108
|
+
keyDecrypter: KeyDecrypter,
|
|
109
|
+
): Promise<{ fullDerivationPath: string[]; matchingKeyEncryption?: X25519KeyEncryption }> {
|
|
137
110
|
const { encryption } = recordsWrite;
|
|
138
|
-
const fullDerivationPath = Records.constructKeyDerivationPath(
|
|
139
|
-
|
|
140
|
-
if ('decrypt' in keyOrDecrypter) {
|
|
141
|
-
const publicKey = await keyOrDecrypter.derivePublicKey(fullDerivationPath);
|
|
142
|
-
const keyId = await Encryption.getKeyId(publicKey);
|
|
143
|
-
return {
|
|
144
|
-
fullDerivationPath,
|
|
145
|
-
matchingKeyEncryption: encryption!.keyEncryption.find((entry): boolean =>
|
|
146
|
-
entry.derivationScheme === keyOrDecrypter.derivationScheme && entry.keyId === keyId
|
|
147
|
-
),
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const leafPrivateKeyBytes = await Records.derivePrivateKey(keyOrDecrypter, fullDerivationPath);
|
|
152
|
-
const leafPrivateKey = await X25519.bytesToPrivateKey({ privateKeyBytes: leafPrivateKeyBytes });
|
|
111
|
+
const fullDerivationPath = Records.constructKeyDerivationPath(keyDecrypter.derivationScheme, recordsWrite);
|
|
153
112
|
|
|
154
|
-
const publicKey = await
|
|
155
|
-
const keyId =
|
|
113
|
+
const publicKey = await keyDecrypter.derivePublicKey(fullDerivationPath);
|
|
114
|
+
const keyId = await Encryption.getKeyId(publicKey);
|
|
156
115
|
|
|
157
116
|
return {
|
|
158
117
|
fullDerivationPath,
|
|
159
|
-
leafPrivateKey,
|
|
160
118
|
matchingKeyEncryption: encryption!.keyEncryption.find((entry): boolean =>
|
|
161
|
-
entry.derivationScheme ===
|
|
119
|
+
entry.derivationScheme === keyDecrypter.derivationScheme && entry.keyId === keyId
|
|
162
120
|
),
|
|
163
121
|
};
|
|
164
122
|
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { validateJsonSchema } from '../../../../src/schema-validator.js';
|
|
2
|
-
import { describe, expect, it } from 'bun:test';
|
|
3
|
-
const delivery = {
|
|
4
|
-
contextId: 'thread-1',
|
|
5
|
-
keyId: 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG',
|
|
6
|
-
keyMaterial: {
|
|
7
|
-
algorithm: 'X25519-HKDF-SHA256+A256KW',
|
|
8
|
-
derivationScheme: 'roleAudience',
|
|
9
|
-
keyId: 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG',
|
|
10
|
-
privateKeyJwk: {
|
|
11
|
-
crv: 'X25519',
|
|
12
|
-
d: 'private-key',
|
|
13
|
-
kty: 'OKP',
|
|
14
|
-
x: 'public-key',
|
|
15
|
-
},
|
|
16
|
-
publicKeyJwk: {
|
|
17
|
-
crv: 'X25519',
|
|
18
|
-
kty: 'OKP',
|
|
19
|
-
x: 'public-key',
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
protocol: 'https://example.com/protocol',
|
|
23
|
-
rolePath: 'thread/participant',
|
|
24
|
-
};
|
|
25
|
-
describe('Delivery Schema', () => {
|
|
26
|
-
it('should accept a roleAudience key delivery payload', () => {
|
|
27
|
-
expect(() => validateJsonSchema('Delivery', delivery)).not.toThrow();
|
|
28
|
-
});
|
|
29
|
-
it('should reject legacy epoch fields', () => {
|
|
30
|
-
const invalidDelivery = {
|
|
31
|
-
...structuredClone(delivery),
|
|
32
|
-
epoch: 1,
|
|
33
|
-
};
|
|
34
|
-
expect(() => validateJsonSchema('Delivery', invalidDelivery)).toThrow();
|
|
35
|
-
});
|
|
36
|
-
it('should reject non-roleAudience key material', () => {
|
|
37
|
-
const invalidDelivery = structuredClone(delivery);
|
|
38
|
-
invalidDelivery.keyMaterial.derivationScheme = 'protocolPath';
|
|
39
|
-
expect(() => validateJsonSchema('Delivery', invalidDelivery)).toThrow();
|
|
40
|
-
});
|
|
41
|
-
it('should reject key IDs that are not base64url thumbprints', () => {
|
|
42
|
-
const invalidDelivery = structuredClone(delivery);
|
|
43
|
-
invalidDelivery.keyMaterial.keyId = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEF!';
|
|
44
|
-
expect(() => validateJsonSchema('Delivery', invalidDelivery)).toThrow();
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
//# sourceMappingURL=delivery.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"delivery.spec.js","sourceRoot":"","sources":["../../../../../../tests/validation/json-schemas/encryption/delivery.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAEhD,MAAM,QAAQ,GAAG;IACf,SAAS,EAAK,UAAU;IACxB,KAAK,EAAS,6CAA6C;IAC3D,WAAW,EAAG;QACZ,SAAS,EAAU,2BAA2B;QAC9C,gBAAgB,EAAG,cAAc;QACjC,KAAK,EAAc,6CAA6C;QAChE,aAAa,EAAM;YACjB,GAAG,EAAG,QAAQ;YACd,CAAC,EAAK,aAAa;YACnB,GAAG,EAAG,KAAK;YACX,CAAC,EAAK,YAAY;SACnB;QACD,YAAY,EAAE;YACZ,GAAG,EAAG,QAAQ;YACd,GAAG,EAAG,KAAK;YACX,CAAC,EAAK,YAAY;SACnB;KACF;IACD,QAAQ,EAAG,8BAA8B;IACzC,QAAQ,EAAG,oBAAoB;CAChC,CAAC;AAEF,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,CACJ,GAAG,EAAE,CAAC,kBAAkB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAC/C,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,eAAe,GAAG;YACtB,GAAG,eAAe,CAAC,QAAQ,CAAC;YAC5B,KAAK,EAAE,CAAC;SACT,CAAC;QAEF,MAAM,CACJ,GAAG,EAAE,CAAC,kBAAkB,CAAC,UAAU,EAAE,eAAe,CAAC,CACtD,CAAC,OAAO,EAAE,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,eAAe,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QAClD,eAAe,CAAC,WAAW,CAAC,gBAAgB,GAAG,cAAc,CAAC;QAE9D,MAAM,CACJ,GAAG,EAAE,CAAC,kBAAkB,CAAC,UAAU,EAAE,eAAe,CAAC,CACtD,CAAC,OAAO,EAAE,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,eAAe,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QAClD,eAAe,CAAC,WAAW,CAAC,KAAK,GAAG,6CAA6C,CAAC;QAElF,MAAM,CACJ,GAAG,EAAE,CAAC,kBAAkB,CAAC,UAAU,EAAE,eAAe,CAAC,CACtD,CAAC,OAAO,EAAE,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|