@enbox/dwn-sdk-js 0.4.4 → 0.4.6
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 +15 -15
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/generated/precompiled-validators.js +5406 -2391
- package/dist/esm/generated/precompiled-validators.js.map +1 -1
- package/dist/esm/json-schemas/definitions.json +18 -0
- package/dist/esm/src/core/constants.js +3 -3
- package/dist/esm/src/core/constants.js.map +1 -1
- package/dist/esm/src/core/dwn-error.js +29 -1
- package/dist/esm/src/core/dwn-error.js.map +1 -1
- package/dist/esm/src/core/messages-grant-authorization.js +20 -0
- package/dist/esm/src/core/messages-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization-validation.js +80 -3
- package/dist/esm/src/core/protocol-authorization-validation.js.map +1 -1
- package/dist/esm/src/core/recording-validation-state-reader.js +5 -0
- package/dist/esm/src/core/recording-validation-state-reader.js.map +1 -1
- package/dist/esm/src/core/replication-apply.js +163 -7
- package/dist/esm/src/core/replication-apply.js.map +1 -1
- package/dist/esm/src/core/validation-state-reader.js +20 -0
- package/dist/esm/src/core/validation-state-reader.js.map +1 -1
- package/dist/esm/src/dwn.js +19 -2
- package/dist/esm/src/dwn.js.map +1 -1
- package/dist/esm/src/handlers/messages-query.js +3 -5
- package/dist/esm/src/handlers/messages-query.js.map +1 -1
- package/dist/esm/src/handlers/protocols-configure.js +10 -4
- package/dist/esm/src/handlers/protocols-configure.js.map +1 -1
- package/dist/esm/src/handlers/records-write.js +1 -1
- package/dist/esm/src/handlers/records-write.js.map +1 -1
- package/dist/esm/src/index.js +2 -1
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/interfaces/protocols-configure.js +23 -10
- package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
- package/dist/esm/src/interfaces/records-write-signing.js +2 -8
- package/dist/esm/src/interfaces/records-write-signing.js.map +1 -1
- package/dist/esm/src/interfaces/records-write.js +43 -65
- package/dist/esm/src/interfaces/records-write.js.map +1 -1
- package/dist/esm/src/protocols/encryption.js +411 -0
- package/dist/esm/src/protocols/encryption.js.map +1 -0
- package/dist/esm/src/protocols/permission-grant.js +0 -5
- package/dist/esm/src/protocols/permission-grant.js.map +1 -1
- package/dist/esm/src/types/message-types.js.map +1 -1
- package/dist/esm/src/types/permission-types.js.map +1 -1
- package/dist/esm/src/types/protocols-types.js.map +1 -1
- package/dist/esm/src/utils/encryption.js +156 -188
- package/dist/esm/src/utils/encryption.js.map +1 -1
- package/dist/esm/src/utils/hd-key.js +0 -1
- package/dist/esm/src/utils/hd-key.js.map +1 -1
- package/dist/esm/src/utils/permission-scope.js +9 -6
- package/dist/esm/src/utils/permission-scope.js.map +1 -1
- package/dist/esm/src/utils/protocols.js +12 -9
- package/dist/esm/src/utils/protocols.js.map +1 -1
- package/dist/esm/src/utils/records.js +54 -56
- package/dist/esm/src/utils/records.js.map +1 -1
- package/dist/esm/src/utils/replication.js +27 -4
- package/dist/esm/src/utils/replication.js.map +1 -1
- package/dist/esm/tests/core/replication-apply.spec.js +141 -1
- package/dist/esm/tests/core/replication-apply.spec.js.map +1 -1
- package/dist/esm/tests/core/replication-replay-property.spec.js +1 -0
- package/dist/esm/tests/core/replication-replay-property.spec.js.map +1 -1
- package/dist/esm/tests/core/validation-read-closure.spec.js +111 -0
- package/dist/esm/tests/core/validation-read-closure.spec.js.map +1 -1
- package/dist/esm/tests/core/validation-state-reader.spec.js +89 -0
- package/dist/esm/tests/core/validation-state-reader.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-composition.spec.js +30 -31
- package/dist/esm/tests/features/protocol-composition.spec.js.map +1 -1
- package/dist/esm/tests/fuzz/encryption.fuzz.spec.js +29 -121
- package/dist/esm/tests/fuzz/encryption.fuzz.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-query.spec.js +71 -3
- package/dist/esm/tests/handlers/messages-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-read.spec.js +2 -2
- package/dist/esm/tests/handlers/messages-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-read.spec.js +9 -135
- package/dist/esm/tests/handlers/records-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-write.spec.js +279 -19
- package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/protocols-configure.spec.js +17 -22
- package/dist/esm/tests/interfaces/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-write.spec.js +170 -150
- package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
- package/dist/esm/tests/protocols/encryption.spec.js +914 -0
- package/dist/esm/tests/protocols/encryption.spec.js.map +1 -0
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js +100 -100
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js.map +1 -1
- package/dist/esm/tests/store/message-store-level.spec.js +20 -2
- package/dist/esm/tests/store/message-store-level.spec.js.map +1 -1
- package/dist/esm/tests/utils/encryption-callbacks.spec.js +88 -125
- package/dist/esm/tests/utils/encryption-callbacks.spec.js.map +1 -1
- package/dist/esm/tests/utils/encryption.spec.js +121 -271
- package/dist/esm/tests/utils/encryption.spec.js.map +1 -1
- package/dist/esm/tests/utils/permission-scope.spec.js +5 -1
- package/dist/esm/tests/utils/permission-scope.spec.js.map +1 -1
- package/dist/esm/tests/utils/records.spec.js +15 -5
- package/dist/esm/tests/utils/records.spec.js.map +1 -1
- package/dist/esm/tests/utils/test-data-generator.js +7 -44
- package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/encryption/audience-key.spec.js +40 -0
- package/dist/esm/tests/validation/json-schemas/encryption/audience-key.spec.js.map +1 -0
- package/dist/esm/tests/validation/json-schemas/encryption/grant-key.spec.js +72 -0
- package/dist/esm/tests/validation/json-schemas/encryption/grant-key.spec.js.map +1 -0
- package/dist/types/generated/precompiled-validators.d.ts +140 -92
- package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
- package/dist/types/src/core/constants.d.ts +2 -2
- package/dist/types/src/core/constants.d.ts.map +1 -1
- package/dist/types/src/core/core-protocol.d.ts +1 -1
- package/dist/types/src/core/core-protocol.d.ts.map +1 -1
- package/dist/types/src/core/dwn-error.d.ts +29 -1
- package/dist/types/src/core/dwn-error.d.ts.map +1 -1
- package/dist/types/src/core/messages-grant-authorization.d.ts +2 -0
- package/dist/types/src/core/messages-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization-validation.d.ts +2 -2
- package/dist/types/src/core/protocol-authorization-validation.d.ts.map +1 -1
- package/dist/types/src/core/recording-validation-state-reader.d.ts +9 -0
- package/dist/types/src/core/recording-validation-state-reader.d.ts.map +1 -1
- package/dist/types/src/core/replication-apply.d.ts +4 -3
- package/dist/types/src/core/replication-apply.d.ts.map +1 -1
- package/dist/types/src/core/validation-state-reader.d.ts +9 -0
- package/dist/types/src/core/validation-state-reader.d.ts.map +1 -1
- package/dist/types/src/dwn.d.ts +1 -0
- package/dist/types/src/dwn.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-query.d.ts.map +1 -1
- package/dist/types/src/handlers/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +6 -5
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/interfaces/protocols-configure.d.ts +1 -1
- package/dist/types/src/interfaces/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-write-signing.d.ts +4 -8
- package/dist/types/src/interfaces/records-write-signing.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-write.d.ts +10 -26
- package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
- package/dist/types/src/protocols/encryption.d.ts +43 -0
- package/dist/types/src/protocols/encryption.d.ts.map +1 -0
- package/dist/types/src/protocols/permission-grant.d.ts +0 -7
- package/dist/types/src/protocols/permission-grant.d.ts.map +1 -1
- package/dist/types/src/types/encryption-types.d.ts +5 -38
- package/dist/types/src/types/encryption-types.d.ts.map +1 -1
- package/dist/types/src/types/message-types.d.ts +0 -13
- package/dist/types/src/types/message-types.d.ts.map +1 -1
- package/dist/types/src/types/permission-types.d.ts +0 -9
- package/dist/types/src/types/permission-types.d.ts.map +1 -1
- package/dist/types/src/types/protocols-types.d.ts +9 -14
- package/dist/types/src/types/protocols-types.d.ts.map +1 -1
- package/dist/types/src/types/records-types.d.ts +3 -3
- package/dist/types/src/types/records-types.d.ts.map +1 -1
- package/dist/types/src/types/validation-state-reader.d.ts +12 -0
- package/dist/types/src/types/validation-state-reader.d.ts.map +1 -1
- package/dist/types/src/utils/encryption.d.ts +61 -145
- package/dist/types/src/utils/encryption.d.ts.map +1 -1
- package/dist/types/src/utils/hd-key.d.ts +1 -1
- package/dist/types/src/utils/hd-key.d.ts.map +1 -1
- package/dist/types/src/utils/permission-scope.d.ts +4 -3
- package/dist/types/src/utils/permission-scope.d.ts.map +1 -1
- package/dist/types/src/utils/protocols.d.ts +3 -5
- package/dist/types/src/utils/protocols.d.ts.map +1 -1
- package/dist/types/src/utils/records.d.ts +5 -13
- package/dist/types/src/utils/records.d.ts.map +1 -1
- package/dist/types/src/utils/replication.d.ts +3 -0
- package/dist/types/src/utils/replication.d.ts.map +1 -1
- package/dist/types/tests/features/protocol-composition.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-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-write.spec.d.ts.map +1 -1
- package/dist/types/tests/protocols/encryption.spec.d.ts +2 -0
- package/dist/types/tests/protocols/encryption.spec.d.ts.map +1 -0
- package/dist/types/tests/scenarios/end-to-end-tests.spec.d.ts.map +1 -1
- package/dist/types/tests/utils/test-data-generator.d.ts +0 -5
- package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
- package/dist/types/tests/validation/json-schemas/encryption/audience-key.spec.d.ts +2 -0
- package/dist/types/tests/validation/json-schemas/encryption/audience-key.spec.d.ts.map +1 -0
- package/dist/types/tests/validation/json-schemas/encryption/grant-key.spec.d.ts +2 -0
- package/dist/types/tests/validation/json-schemas/encryption/grant-key.spec.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/core/constants.ts +3 -3
- package/src/core/core-protocol.ts +1 -1
- package/src/core/dwn-error.ts +29 -1
- package/src/core/messages-grant-authorization.ts +27 -0
- package/src/core/protocol-authorization-validation.ts +145 -6
- package/src/core/recording-validation-state-reader.ts +13 -0
- package/src/core/replication-apply.ts +222 -8
- package/src/core/validation-state-reader.ts +30 -0
- package/src/dwn.ts +24 -2
- package/src/handlers/messages-query.ts +6 -5
- package/src/handlers/protocols-configure.ts +19 -3
- package/src/handlers/records-write.ts +1 -1
- package/src/index.ts +15 -5
- package/src/interfaces/protocols-configure.ts +39 -12
- package/src/interfaces/records-write-signing.ts +5 -12
- package/src/interfaces/records-write.ts +75 -92
- package/src/protocols/encryption.ts +649 -0
- package/src/protocols/permission-grant.ts +0 -6
- package/src/types/encryption-types.ts +9 -38
- package/src/types/message-types.ts +0 -13
- package/src/types/permission-types.ts +0 -10
- package/src/types/protocols-types.ts +9 -16
- package/src/types/records-types.ts +3 -3
- package/src/types/validation-state-reader.ts +13 -0
- package/src/utils/encryption.ts +258 -280
- package/src/utils/hd-key.ts +1 -1
- package/src/utils/permission-scope.ts +10 -6
- package/src/utils/protocols.ts +16 -15
- package/src/utils/records.ts +86 -71
- package/src/utils/replication.ts +30 -4
- package/dist/esm/tests/vectors/protocol-definitions/chat.json +0 -70
package/src/utils/encryption.ts
CHANGED
|
@@ -2,387 +2,365 @@ import type { Jwk } from '@enbox/crypto';
|
|
|
2
2
|
import type { PublicKeyJwk } from '../types/jose-types.js';
|
|
3
3
|
|
|
4
4
|
import { concatBytes } from '@noble/ciphers/utils';
|
|
5
|
+
import { AesCtr, AesKw, computeJwkThumbprint, Hkdf, X25519 } from '@enbox/crypto';
|
|
6
|
+
|
|
5
7
|
import { Encoder } from './encoder.js';
|
|
6
8
|
import { KeyDerivationScheme } from './hd-key.js';
|
|
7
|
-
import {
|
|
9
|
+
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
|
|
8
10
|
|
|
9
|
-
/**
|
|
10
|
-
* Content encryption algorithms supported by the DWN.
|
|
11
|
-
* Both are AEAD (Authenticated Encryption with Associated Data) ciphers.
|
|
12
|
-
*/
|
|
13
11
|
export enum ContentEncryptionAlgorithm {
|
|
14
|
-
|
|
15
|
-
A256GCM = 'A256GCM',
|
|
16
|
-
/** XChaCha20-Poly1305. 192-bit nonce (safe to randomize). Constant-time. */
|
|
17
|
-
XC20P = 'XC20P',
|
|
12
|
+
A256CTR = 'A256CTR',
|
|
18
13
|
}
|
|
19
14
|
|
|
20
|
-
/**
|
|
21
|
-
* Key agreement algorithm used by the DWN.
|
|
22
|
-
* ECDH-ES with X25519 key agreement and AES-256 Key Wrap.
|
|
23
|
-
*/
|
|
24
15
|
export enum KeyAgreementAlgorithm {
|
|
25
|
-
|
|
16
|
+
X25519HkdfSha256A256Kw = 'X25519-HKDF-SHA256+A256KW',
|
|
26
17
|
}
|
|
27
18
|
|
|
28
|
-
|
|
29
|
-
const AES_GCM_TAG_LENGTH_BYTES = 16;
|
|
19
|
+
export const ROLE_AUDIENCE_DERIVATION_SCHEME = 'roleAudience';
|
|
30
20
|
|
|
31
|
-
|
|
32
|
-
const
|
|
21
|
+
const AES_256_KEY_LENGTH_BYTES = 32;
|
|
22
|
+
const AES_CTR_COUNTER_LENGTH_BYTES = 16;
|
|
23
|
+
const AES_CTR_COUNTER_LENGTH_BITS = 128;
|
|
24
|
+
const KEK_INFO_PREFIX = KeyAgreementAlgorithm.X25519HkdfSha256A256Kw;
|
|
33
25
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
26
|
+
export type ProtocolPathKeyEncryption = {
|
|
27
|
+
algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw;
|
|
28
|
+
keyId: string;
|
|
29
|
+
derivationScheme: KeyDerivationScheme.ProtocolPath;
|
|
30
|
+
ephemeralPublicKey: PublicKeyJwk;
|
|
31
|
+
encryptedKey: string;
|
|
40
32
|
};
|
|
41
33
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
derivationScheme: KeyDerivationScheme;
|
|
52
|
-
/** Derived public key. Present when derivationScheme is 'protocolContext'. */
|
|
53
|
-
derivedPublicKey?: PublicKeyJwk;
|
|
34
|
+
export type RoleAudienceKeyEncryption = {
|
|
35
|
+
algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw;
|
|
36
|
+
keyId: string;
|
|
37
|
+
derivationScheme: typeof ROLE_AUDIENCE_DERIVATION_SCHEME;
|
|
38
|
+
protocol: string;
|
|
39
|
+
role: string;
|
|
40
|
+
epoch: number;
|
|
41
|
+
ephemeralPublicKey: PublicKeyJwk;
|
|
42
|
+
encryptedKey: string;
|
|
54
43
|
};
|
|
55
44
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
45
|
+
export type KeyEncryption = ProtocolPathKeyEncryption | RoleAudienceKeyEncryption;
|
|
46
|
+
|
|
47
|
+
export type X25519KeyEncryption = ProtocolPathKeyEncryption | RoleAudienceKeyEncryption;
|
|
48
|
+
|
|
49
|
+
export type DwnEncryption = {
|
|
50
|
+
algorithm: ContentEncryptionAlgorithm.A256CTR;
|
|
51
|
+
initializationVector: string;
|
|
52
|
+
keyEncryption: KeyEncryption[];
|
|
62
53
|
};
|
|
63
54
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
* the `ciphertext` is NOT included here because the encrypted record data is stored separately
|
|
69
|
-
* in the DataStore (or as inline `encodedData`). Only the key wrapping metadata, IV, and
|
|
70
|
-
* authentication tag are stored in this structure.
|
|
71
|
-
*/
|
|
72
|
-
export type JweEncryption = {
|
|
73
|
-
/** Base64url-encoded JWE Protected Header. */
|
|
74
|
-
protected: string;
|
|
75
|
-
/** Base64url-encoded initialization vector for content encryption. */
|
|
76
|
-
iv: string;
|
|
77
|
-
/** Base64url-encoded authentication tag from the AEAD cipher. */
|
|
78
|
-
tag: string;
|
|
79
|
-
/** Array of recipient entries, one per recipient or derivation path. */
|
|
80
|
-
recipients: JweRecipient[];
|
|
55
|
+
export type ProtocolPathKeyEncryptionInput = {
|
|
56
|
+
keyId: string;
|
|
57
|
+
publicKey: PublicKeyJwk;
|
|
58
|
+
derivationScheme: KeyDerivationScheme.ProtocolPath;
|
|
81
59
|
};
|
|
82
60
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
*/
|
|
86
|
-
export type KeyEncryptionInput = {
|
|
87
|
-
/** Fully qualified key ID of the recipient's root encryption key. */
|
|
88
|
-
publicKeyId: string;
|
|
89
|
-
/** The recipient's derived X25519 public key. */
|
|
61
|
+
export type RoleAudienceKeyEncryptionInput = {
|
|
62
|
+
keyId: string;
|
|
90
63
|
publicKey: PublicKeyJwk;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
64
|
+
derivationScheme: typeof ROLE_AUDIENCE_DERIVATION_SCHEME;
|
|
65
|
+
protocol: string;
|
|
66
|
+
role: string;
|
|
67
|
+
epoch: number;
|
|
95
68
|
};
|
|
96
69
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
70
|
+
export type KeyEncryptionInput = ProtocolPathKeyEncryptionInput | RoleAudienceKeyEncryptionInput;
|
|
71
|
+
|
|
72
|
+
export type X25519KeyEncryptionInput = ProtocolPathKeyEncryptionInput | RoleAudienceKeyEncryptionInput;
|
|
73
|
+
|
|
100
74
|
export type EncryptionInput = {
|
|
101
|
-
/** Content encryption algorithm. Defaults to A256GCM. */
|
|
102
75
|
algorithm?: ContentEncryptionAlgorithm;
|
|
103
|
-
/** The Content Encryption Key (CEK). Must be 32 bytes (256-bit). */
|
|
104
76
|
key: Uint8Array;
|
|
105
|
-
/** Initialization vector. 12 bytes for A256GCM, 24 bytes for XC20P. */
|
|
106
77
|
initializationVector: Uint8Array;
|
|
107
|
-
/** Authentication tag from the AEAD encryption of the record data. */
|
|
108
|
-
authenticationTag: Uint8Array;
|
|
109
|
-
/** Recipient key encryption inputs. */
|
|
110
78
|
keyEncryptionInputs: KeyEncryptionInput[];
|
|
111
79
|
};
|
|
112
80
|
|
|
113
|
-
|
|
114
|
-
* Payload passed to a KeyDecrypter callback for JWE-based key unwrapping.
|
|
115
|
-
*/
|
|
116
|
-
export type JweKeyUnwrapPayload = {
|
|
117
|
-
/** The wrapped CEK bytes. */
|
|
81
|
+
export type KeyUnwrapPayload = {
|
|
118
82
|
encryptedKey: Uint8Array;
|
|
119
|
-
/** The ephemeral X25519 public key used for ECDH. */
|
|
120
83
|
ephemeralPublicKey: PublicKeyJwk;
|
|
84
|
+
keyEncryption: KeyEncryption;
|
|
121
85
|
};
|
|
122
86
|
|
|
123
|
-
/**
|
|
124
|
-
* Utility class for DWN encryption operations using JWE (RFC 7516).
|
|
125
|
-
* Uses ECDH-ES+A256KW key agreement with X25519 and either AES-256-GCM or XChaCha20-Poly1305
|
|
126
|
-
* for authenticated content encryption.
|
|
127
|
-
*/
|
|
128
87
|
export class Encryption {
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Encrypts data using an AEAD cipher (A256GCM or XC20P).
|
|
132
|
-
* Returns ciphertext with the authentication tag appended.
|
|
133
|
-
*/
|
|
134
|
-
public static async aeadEncrypt(
|
|
88
|
+
public static async encrypt(
|
|
135
89
|
algorithm: ContentEncryptionAlgorithm,
|
|
136
90
|
keyBytes: Uint8Array,
|
|
137
|
-
|
|
91
|
+
initializationVector: Uint8Array,
|
|
138
92
|
plaintext: Uint8Array,
|
|
139
|
-
): Promise<
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
} else if (algorithm === ContentEncryptionAlgorithm.XC20P) {
|
|
149
|
-
// @noble/ciphers XChaCha20-Poly1305 returns ciphertext || tag
|
|
150
|
-
const combined = await XChaCha20Poly1305.encryptRaw({ data: plaintext, keyBytes, nonce: iv });
|
|
151
|
-
const ciphertext = combined.slice(0, combined.length - POLY1305_TAG_LENGTH_BYTES);
|
|
152
|
-
const tag = combined.slice(combined.length - POLY1305_TAG_LENGTH_BYTES);
|
|
153
|
-
return { ciphertext, tag };
|
|
154
|
-
|
|
155
|
-
} else {
|
|
156
|
-
throw new Error(`Unsupported content encryption algorithm: ${algorithm as string}`);
|
|
157
|
-
}
|
|
93
|
+
): Promise<Uint8Array> {
|
|
94
|
+
Encryption.validateContentEncryptionParameters(algorithm, keyBytes, initializationVector);
|
|
95
|
+
return AesCtr.encrypt({
|
|
96
|
+
counter : initializationVector,
|
|
97
|
+
data : plaintext,
|
|
98
|
+
key : Encryption.toA256CtrJwk(keyBytes),
|
|
99
|
+
length : AES_CTR_COUNTER_LENGTH_BITS,
|
|
100
|
+
});
|
|
158
101
|
}
|
|
159
102
|
|
|
160
|
-
|
|
161
|
-
* Decrypts data using an AEAD cipher (A256GCM or XC20P).
|
|
162
|
-
* Expects ciphertext and tag as separate inputs.
|
|
163
|
-
*/
|
|
164
|
-
public static async aeadDecrypt(
|
|
103
|
+
public static async decrypt(
|
|
165
104
|
algorithm: ContentEncryptionAlgorithm,
|
|
166
105
|
keyBytes: Uint8Array,
|
|
167
|
-
|
|
106
|
+
initializationVector: Uint8Array,
|
|
168
107
|
ciphertext: Uint8Array,
|
|
169
|
-
tag: Uint8Array,
|
|
170
108
|
): Promise<Uint8Array> {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
} else if (algorithm === ContentEncryptionAlgorithm.XC20P) {
|
|
179
|
-
return XChaCha20Poly1305.decryptRaw({ data: combined, keyBytes, nonce: iv });
|
|
180
|
-
|
|
181
|
-
} else {
|
|
182
|
-
throw new Error(`Unsupported content encryption algorithm: ${algorithm as string}`);
|
|
183
|
-
}
|
|
109
|
+
Encryption.validateContentEncryptionParameters(algorithm, keyBytes, initializationVector);
|
|
110
|
+
return AesCtr.decrypt({
|
|
111
|
+
counter : initializationVector,
|
|
112
|
+
data : ciphertext,
|
|
113
|
+
key : Encryption.toA256CtrJwk(keyBytes),
|
|
114
|
+
length : AES_CTR_COUNTER_LENGTH_BITS,
|
|
115
|
+
});
|
|
184
116
|
}
|
|
185
117
|
|
|
186
|
-
|
|
187
|
-
* Encrypts data as a ReadableStream using an AEAD cipher.
|
|
188
|
-
* Collects all chunks, encrypts, and returns a new stream of ciphertext || tag.
|
|
189
|
-
* The iv and tag are NOT embedded in the stream — they are stored in the JWE structure.
|
|
190
|
-
*/
|
|
191
|
-
public static async aeadEncryptStream(
|
|
118
|
+
public static async encryptStream(
|
|
192
119
|
algorithm: ContentEncryptionAlgorithm,
|
|
193
120
|
keyBytes: Uint8Array,
|
|
194
|
-
|
|
121
|
+
initializationVector: Uint8Array,
|
|
195
122
|
plaintextStream: ReadableStream<Uint8Array>,
|
|
196
|
-
): Promise<
|
|
123
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
197
124
|
const plaintext = await Encryption.readStream(plaintextStream);
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
start(controller): void {
|
|
201
|
-
controller.enqueue(ciphertext);
|
|
202
|
-
controller.close();
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
return { ciphertextStream, tag };
|
|
125
|
+
const ciphertext = await Encryption.encrypt(algorithm, keyBytes, initializationVector, plaintext);
|
|
126
|
+
return Encryption.toStream(ciphertext);
|
|
206
127
|
}
|
|
207
128
|
|
|
208
|
-
|
|
209
|
-
* Decrypts a ciphertext stream using an AEAD cipher.
|
|
210
|
-
* Returns a ReadableStream of plaintext.
|
|
211
|
-
*/
|
|
212
|
-
public static async aeadDecryptStream(
|
|
129
|
+
public static async decryptStream(
|
|
213
130
|
algorithm: ContentEncryptionAlgorithm,
|
|
214
131
|
keyBytes: Uint8Array,
|
|
215
|
-
|
|
132
|
+
initializationVector: Uint8Array,
|
|
216
133
|
ciphertextStream: ReadableStream<Uint8Array>,
|
|
217
|
-
tag: Uint8Array,
|
|
218
134
|
): Promise<ReadableStream<Uint8Array>> {
|
|
219
135
|
const ciphertext = await Encryption.readStream(ciphertextStream);
|
|
220
|
-
const plaintext = await Encryption.
|
|
221
|
-
return
|
|
222
|
-
start(controller): void {
|
|
223
|
-
controller.enqueue(plaintext);
|
|
224
|
-
controller.close();
|
|
225
|
-
}
|
|
226
|
-
});
|
|
136
|
+
const plaintext = await Encryption.decrypt(algorithm, keyBytes, initializationVector, ciphertext);
|
|
137
|
+
return Encryption.toStream(plaintext);
|
|
227
138
|
}
|
|
228
139
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
*
|
|
232
|
-
* @param ephemeralPrivateKey - Ephemeral X25519 private key (JWK).
|
|
233
|
-
* @param recipientPublicKey - Recipient's X25519 public key (JWK).
|
|
234
|
-
* @param cek - The Content Encryption Key to wrap.
|
|
235
|
-
* @returns The wrapped CEK bytes.
|
|
236
|
-
*/
|
|
237
|
-
public static async ecdhEsWrapKey(
|
|
238
|
-
ephemeralPrivateKey: Jwk,
|
|
239
|
-
recipientPublicKey: Jwk,
|
|
140
|
+
public static async wrapKey(
|
|
141
|
+
recipientPublicKey: PublicKeyJwk,
|
|
240
142
|
cek: Uint8Array,
|
|
241
|
-
|
|
242
|
-
|
|
143
|
+
keyInput: KeyEncryptionInput,
|
|
144
|
+
ephemeralPrivateKey?: Jwk,
|
|
145
|
+
): Promise<{ encryptedKey: Uint8Array; ephemeralPublicKey: PublicKeyJwk }> {
|
|
146
|
+
return Encryption.wrapX25519Key(recipientPublicKey, cek, keyInput, ephemeralPrivateKey);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private static async wrapX25519Key(
|
|
150
|
+
recipientPublicKey: PublicKeyJwk,
|
|
151
|
+
cek: Uint8Array,
|
|
152
|
+
keyInput: X25519KeyEncryptionInput,
|
|
153
|
+
ephemeralPrivateKey?: Jwk,
|
|
154
|
+
): Promise<{ encryptedKey: Uint8Array; ephemeralPublicKey: PublicKeyJwk }> {
|
|
155
|
+
Encryption.validateContentEncryptionKey(cek);
|
|
156
|
+
|
|
157
|
+
const privateKey = ephemeralPrivateKey ?? await X25519.generateKey();
|
|
158
|
+
const publicKey = await X25519.getPublicKey({ key: privateKey }) as PublicKeyJwk;
|
|
243
159
|
const sharedSecret = await X25519.sharedSecret({
|
|
244
|
-
privateKeyA :
|
|
245
|
-
publicKeyB : recipientPublicKey,
|
|
160
|
+
privateKeyA : privateKey,
|
|
161
|
+
publicKeyB : recipientPublicKey as Jwk,
|
|
246
162
|
});
|
|
163
|
+
const kek = await Encryption.deriveKek(sharedSecret, keyInput);
|
|
164
|
+
const cekJwk = Encryption.toA256CtrJwk(cek);
|
|
165
|
+
const kekJwk: Jwk = { alg: 'A256KW', k: Encoder.bytesToBase64Url(kek), kty: 'oct' };
|
|
166
|
+
const encryptedKey = await AesKw.wrapKey({ encryptionKey: kekJwk, unwrappedKey: cekJwk });
|
|
247
167
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
sharedSecret,
|
|
251
|
-
keyDataLen : 256,
|
|
252
|
-
fixedInfo : {
|
|
253
|
-
algorithmId : 'A256KW',
|
|
254
|
-
partyUInfo : '',
|
|
255
|
-
partyVInfo : '',
|
|
256
|
-
suppPubInfo : 256,
|
|
257
|
-
},
|
|
258
|
-
});
|
|
168
|
+
return { encryptedKey, ephemeralPublicKey: publicKey };
|
|
169
|
+
}
|
|
259
170
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
171
|
+
public static async unwrapKey(
|
|
172
|
+
recipientPrivateKey: Jwk,
|
|
173
|
+
keyEncryption: KeyEncryption,
|
|
174
|
+
): Promise<Uint8Array> {
|
|
175
|
+
if (keyEncryption.algorithm === KeyAgreementAlgorithm.X25519HkdfSha256A256Kw) {
|
|
176
|
+
return Encryption.unwrapX25519Key(recipientPrivateKey, keyEncryption);
|
|
177
|
+
}
|
|
264
178
|
|
|
265
|
-
|
|
179
|
+
throw new Error(`Unsupported key agreement algorithm: ${(keyEncryption as KeyEncryption).algorithm}`);
|
|
266
180
|
}
|
|
267
181
|
|
|
268
|
-
|
|
269
|
-
* Performs ECDH-ES key agreement with X25519 and unwraps the CEK using AES-256 Key Unwrap.
|
|
270
|
-
*
|
|
271
|
-
* @param recipientPrivateKey - Recipient's X25519 private key (JWK).
|
|
272
|
-
* @param ephemeralPublicKey - Ephemeral X25519 public key from the JWE recipient header (JWK).
|
|
273
|
-
* @param wrappedKey - The wrapped CEK bytes.
|
|
274
|
-
* @returns The unwrapped CEK bytes.
|
|
275
|
-
*/
|
|
276
|
-
public static async ecdhEsUnwrapKey(
|
|
182
|
+
private static async unwrapX25519Key(
|
|
277
183
|
recipientPrivateKey: Jwk,
|
|
278
|
-
|
|
279
|
-
wrappedKey: Uint8Array,
|
|
184
|
+
keyEncryption: X25519KeyEncryption,
|
|
280
185
|
): Promise<Uint8Array> {
|
|
281
|
-
// 1. ECDH shared secret
|
|
282
186
|
const sharedSecret = await X25519.sharedSecret({
|
|
283
187
|
privateKeyA : recipientPrivateKey,
|
|
284
|
-
publicKeyB : ephemeralPublicKey,
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
// 2. Derive KEK via Concat KDF
|
|
288
|
-
const kek = await ConcatKdf.deriveKey({
|
|
289
|
-
sharedSecret,
|
|
290
|
-
keyDataLen : 256,
|
|
291
|
-
fixedInfo : {
|
|
292
|
-
algorithmId : 'A256KW',
|
|
293
|
-
partyUInfo : '',
|
|
294
|
-
partyVInfo : '',
|
|
295
|
-
suppPubInfo : 256,
|
|
296
|
-
},
|
|
188
|
+
publicKeyB : keyEncryption.ephemeralPublicKey as Jwk,
|
|
297
189
|
});
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
const kekJwk: Jwk = { kty: 'oct', k: Encoder.bytesToBase64Url(kek), alg: 'A256KW' };
|
|
190
|
+
const kek = await Encryption.deriveKek(sharedSecret, keyEncryption);
|
|
191
|
+
const kekJwk: Jwk = { alg: 'A256KW', k: Encoder.bytesToBase64Url(kek), kty: 'oct' };
|
|
301
192
|
const unwrappedJwk = await AesKw.unwrapKey({
|
|
302
|
-
wrappedKeyBytes : wrappedKey,
|
|
303
|
-
wrappedKeyAlgorithm : 'A256GCM',
|
|
304
193
|
decryptionKey : kekJwk,
|
|
194
|
+
wrappedKeyAlgorithm : ContentEncryptionAlgorithm.A256CTR,
|
|
195
|
+
wrappedKeyBytes : Encoder.base64UrlToBytes(keyEncryption.encryptedKey),
|
|
305
196
|
});
|
|
306
197
|
|
|
307
198
|
return Encoder.base64UrlToBytes(unwrappedJwk.k!);
|
|
308
199
|
}
|
|
309
200
|
|
|
310
|
-
|
|
311
|
-
* Builds a JWE encryption property structure from encryption input.
|
|
312
|
-
* The ciphertext (encrypted record data) is stored separately in the DataStore,
|
|
313
|
-
* so only the key wrapping metadata, IV, and authentication tag are included here.
|
|
314
|
-
*
|
|
315
|
-
* @param encryptionInput - Describes the CEK, IV, and recipient key encryption inputs.
|
|
316
|
-
* @param tag - The authentication tag produced by the AEAD cipher during data encryption.
|
|
317
|
-
*/
|
|
318
|
-
public static async buildJwe(
|
|
201
|
+
public static async buildEncryptionProperty(
|
|
319
202
|
encryptionInput: EncryptionInput,
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
203
|
+
): Promise<DwnEncryption> {
|
|
204
|
+
const algorithm = encryptionInput.algorithm ?? ContentEncryptionAlgorithm.A256CTR;
|
|
205
|
+
Encryption.validateContentEncryptionParameters(algorithm, encryptionInput.key, encryptionInput.initializationVector);
|
|
206
|
+
|
|
207
|
+
const keyEncryption: KeyEncryption[] = [];
|
|
208
|
+
for (const keyInput of encryptionInput.keyEncryptionInputs) {
|
|
209
|
+
keyEncryption.push(await Encryption.buildKeyEncryptionEntry(encryptionInput.key, keyInput));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
algorithm,
|
|
214
|
+
initializationVector: Encoder.bytesToBase64Url(encryptionInput.initializationVector),
|
|
215
|
+
keyEncryption,
|
|
326
216
|
};
|
|
217
|
+
}
|
|
327
218
|
|
|
328
|
-
|
|
219
|
+
public static async getKeyId(publicKeyJwk: PublicKeyJwk): Promise<string> {
|
|
220
|
+
return computeJwkThumbprint({ jwk: publicKeyJwk });
|
|
221
|
+
}
|
|
329
222
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
// Wrap the CEK
|
|
337
|
-
const wrappedKey = await Encryption.ecdhEsWrapKey(
|
|
338
|
-
ephemeralPrivateKey,
|
|
339
|
-
keyInput.publicKey as Jwk,
|
|
340
|
-
encryptionInput.key,
|
|
223
|
+
public static validateEncryptionProperty(encryption: DwnEncryption): void {
|
|
224
|
+
const initializationVector = Encoder.base64UrlToBytes(encryption.initializationVector);
|
|
225
|
+
if (initializationVector.byteLength !== AES_CTR_COUNTER_LENGTH_BYTES) {
|
|
226
|
+
throw new DwnError(
|
|
227
|
+
DwnErrorCode.RecordsWriteValidateIntegrityEncryptionInitializationVectorInvalid,
|
|
228
|
+
`A256CTR initializationVector must decode to ${AES_CTR_COUNTER_LENGTH_BYTES} bytes.`
|
|
341
229
|
);
|
|
230
|
+
}
|
|
342
231
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
};
|
|
232
|
+
for (const entry of encryption.keyEncryption) {
|
|
233
|
+
Encryption.validateKeyEncryptionEntry(entry);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
348
236
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
237
|
+
private static async buildKeyEncryptionEntry(
|
|
238
|
+
cek: Uint8Array,
|
|
239
|
+
keyInput: KeyEncryptionInput,
|
|
240
|
+
): Promise<KeyEncryption> {
|
|
241
|
+
const { encryptedKey, ephemeralPublicKey } = await Encryption.wrapKey(
|
|
242
|
+
keyInput.publicKey,
|
|
243
|
+
cek,
|
|
244
|
+
keyInput,
|
|
245
|
+
);
|
|
246
|
+
const common = {
|
|
247
|
+
algorithm : KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
|
|
248
|
+
encryptedKey : Encoder.bytesToBase64Url(encryptedKey),
|
|
249
|
+
ephemeralPublicKey,
|
|
250
|
+
keyId : keyInput.keyId,
|
|
251
|
+
};
|
|
353
252
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
253
|
+
if (keyInput.derivationScheme === KeyDerivationScheme.ProtocolPath) {
|
|
254
|
+
return {
|
|
255
|
+
...common,
|
|
256
|
+
derivationScheme: KeyDerivationScheme.ProtocolPath,
|
|
257
|
+
};
|
|
358
258
|
}
|
|
359
259
|
|
|
360
260
|
return {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
261
|
+
...common,
|
|
262
|
+
derivationScheme : ROLE_AUDIENCE_DERIVATION_SCHEME,
|
|
263
|
+
epoch : keyInput.epoch,
|
|
264
|
+
protocol : keyInput.protocol,
|
|
265
|
+
role : keyInput.role,
|
|
365
266
|
};
|
|
366
267
|
}
|
|
367
268
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
269
|
+
private static validateKeyEncryptionEntry(entry: KeyEncryption): void {
|
|
270
|
+
if (entry.algorithm === KeyAgreementAlgorithm.X25519HkdfSha256A256Kw) {
|
|
271
|
+
Encryption.validateX25519KeyEncryptionEntry(entry);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
throw new DwnError(
|
|
276
|
+
DwnErrorCode.RecordsWriteValidateIntegrityEncryptionEphemeralPublicKeyInvalid,
|
|
277
|
+
`Unsupported key agreement algorithm: ${(entry as KeyEncryption).algorithm}`
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private static validateX25519KeyEncryptionEntry(entry: X25519KeyEncryption): void {
|
|
282
|
+
if (entry.ephemeralPublicKey.kty !== 'OKP' || entry.ephemeralPublicKey.crv !== 'X25519') {
|
|
283
|
+
throw new DwnError(
|
|
284
|
+
DwnErrorCode.RecordsWriteValidateIntegrityEncryptionEphemeralPublicKeyInvalid,
|
|
285
|
+
'ephemeralPublicKey must be an OKP X25519 public key.'
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private static async deriveKek(
|
|
291
|
+
sharedSecret: Uint8Array,
|
|
292
|
+
keyEncryption: X25519KeyEncryptionInput | X25519KeyEncryption,
|
|
293
|
+
): Promise<Uint8Array> {
|
|
294
|
+
Encryption.validateSharedSecret(sharedSecret);
|
|
295
|
+
const info = Encryption.getKekInfo(keyEncryption);
|
|
296
|
+
return Hkdf.deriveKeyBytes({
|
|
297
|
+
baseKeyBytes : sharedSecret,
|
|
298
|
+
hash : 'SHA-256',
|
|
299
|
+
info,
|
|
300
|
+
length : 256,
|
|
301
|
+
salt : new Uint8Array(),
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
private static getKekInfo(keyEncryption: X25519KeyEncryptionInput | X25519KeyEncryption): string {
|
|
306
|
+
if (keyEncryption.derivationScheme === ROLE_AUDIENCE_DERIVATION_SCHEME) {
|
|
307
|
+
return `${KEK_INFO_PREFIX}|roleAudience|${keyEncryption.protocol}|${keyEncryption.role}|${keyEncryption.epoch}|${keyEncryption.keyId}`;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return `${KEK_INFO_PREFIX}|protocolPath|${keyEncryption.keyId}`;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
private static validateContentEncryptionParameters(
|
|
314
|
+
algorithm: ContentEncryptionAlgorithm,
|
|
315
|
+
keyBytes: Uint8Array,
|
|
316
|
+
initializationVector: Uint8Array,
|
|
317
|
+
): void {
|
|
318
|
+
if (algorithm !== ContentEncryptionAlgorithm.A256CTR) {
|
|
319
|
+
throw new Error(`Unsupported content encryption algorithm: ${algorithm as string}`);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
Encryption.validateContentEncryptionKey(keyBytes);
|
|
323
|
+
|
|
324
|
+
if (initializationVector.byteLength !== AES_CTR_COUNTER_LENGTH_BYTES) {
|
|
325
|
+
throw new Error(`A256CTR initializationVector must be ${AES_CTR_COUNTER_LENGTH_BYTES} bytes.`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private static validateContentEncryptionKey(keyBytes: Uint8Array): void {
|
|
330
|
+
if (keyBytes.byteLength !== AES_256_KEY_LENGTH_BYTES) {
|
|
331
|
+
throw new Error(`A256CTR content encryption key must be ${AES_256_KEY_LENGTH_BYTES} bytes.`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
private static validateSharedSecret(sharedSecret: Uint8Array): void {
|
|
336
|
+
if (sharedSecret.every((byte): boolean => byte === 0)) {
|
|
337
|
+
throw new Error('X25519 shared secret MUST NOT be all zeros.');
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
private static toA256CtrJwk(keyBytes: Uint8Array): Jwk {
|
|
342
|
+
return { alg: ContentEncryptionAlgorithm.A256CTR, k: Encoder.bytesToBase64Url(keyBytes), kty: 'oct' };
|
|
373
343
|
}
|
|
374
344
|
|
|
375
|
-
/**
|
|
376
|
-
* Reads a ReadableStream to completion and returns all bytes concatenated.
|
|
377
|
-
*/
|
|
378
345
|
private static async readStream(stream: ReadableStream<Uint8Array>): Promise<Uint8Array> {
|
|
379
346
|
const reader = stream.getReader();
|
|
380
347
|
const chunks: Uint8Array[] = [];
|
|
381
348
|
for (;;) {
|
|
382
349
|
const { done, value } = await reader.read();
|
|
383
|
-
if (done) {
|
|
350
|
+
if (done) {
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
384
353
|
chunks.push(value);
|
|
385
354
|
}
|
|
386
355
|
return concatBytes(...chunks);
|
|
387
356
|
}
|
|
357
|
+
|
|
358
|
+
private static toStream(data: Uint8Array): ReadableStream<Uint8Array> {
|
|
359
|
+
return new ReadableStream<Uint8Array>({
|
|
360
|
+
start(controller): void {
|
|
361
|
+
controller.enqueue(data);
|
|
362
|
+
controller.close();
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
}
|
|
388
366
|
}
|
package/src/utils/hd-key.ts
CHANGED
|
@@ -6,12 +6,12 @@ import { X25519 } from '@enbox/crypto';
|
|
|
6
6
|
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
|
|
7
7
|
|
|
8
8
|
export enum KeyDerivationScheme {
|
|
9
|
-
ProtocolContext = 'protocolContext',
|
|
10
9
|
ProtocolPath = 'protocolPath',
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
export type DerivedPrivateJwk = {
|
|
14
13
|
rootKeyId: string,
|
|
14
|
+
keyId?: string,
|
|
15
15
|
derivationScheme: KeyDerivationScheme;
|
|
16
16
|
derivationPath?: string[];
|
|
17
17
|
derivedPrivateKey: PrivateKeyJwk,
|