@storacha/encrypt-upload-client 1.1.58 → 1.1.60

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.
Files changed (72) hide show
  1. package/dist/config/constants.d.ts +3 -3
  2. package/dist/config/constants.js +3 -4
  3. package/dist/config/env.d.ts +6 -9
  4. package/dist/config/service.d.ts +13 -13
  5. package/dist/core/client.d.ts +41 -54
  6. package/dist/core/client.js +56 -68
  7. package/dist/core/errors.d.ts +6 -6
  8. package/dist/core/metadata/encrypted-metadata.d.ts +8 -13
  9. package/dist/core/metadata/kms-metadata.d.ts +36 -68
  10. package/dist/core/metadata/lit-metadata.d.ts +28 -63
  11. package/dist/crypto/adapters/kms-crypto-adapter.d.ts +137 -172
  12. package/dist/crypto/adapters/lit-crypto-adapter.d.ts +86 -107
  13. package/dist/crypto/factories.browser.d.ts +5 -9
  14. package/dist/crypto/factories.browser.js +7 -15
  15. package/dist/crypto/factories.node.d.ts +6 -13
  16. package/dist/crypto/factories.node.js +13 -19
  17. package/dist/crypto/index.d.ts +5 -5
  18. package/dist/crypto/index.js +5 -5
  19. package/dist/crypto/symmetric/generic-aes-ctr-streaming-crypto.d.ts +54 -58
  20. package/dist/crypto/symmetric/generic-aes-ctr-streaming-crypto.js +146 -174
  21. package/dist/crypto/symmetric/node-aes-cbc-crypto.d.ts +32 -36
  22. package/dist/crypto/symmetric/node-aes-cbc-crypto.js +95 -101
  23. package/dist/examples/decrypt-test.d.ts +2 -2
  24. package/dist/examples/decrypt-test.js +69 -78
  25. package/dist/examples/encrypt-test.d.ts +3 -5
  26. package/dist/examples/encrypt-test.js +55 -58
  27. package/dist/handlers/decrypt-handler.d.ts +5 -19
  28. package/dist/handlers/encrypt-handler.d.ts +3 -9
  29. package/dist/handlers/encrypt-handler.js +57 -93
  30. package/dist/index.d.ts +2 -2
  31. package/dist/index.js +2 -2
  32. package/dist/protocols/lit.d.ts +9 -33
  33. package/dist/protocols/lit.js +98 -134
  34. package/dist/test/cid-verification.spec.d.ts +2 -2
  35. package/dist/test/cid-verification.spec.js +313 -341
  36. package/dist/test/crypto-compatibility.spec.d.ts +2 -2
  37. package/dist/test/crypto-compatibility.spec.js +120 -184
  38. package/dist/test/crypto-counter-security.spec.d.ts +2 -2
  39. package/dist/test/crypto-counter-security.spec.js +138 -177
  40. package/dist/test/crypto-streaming.spec.d.ts +2 -2
  41. package/dist/test/crypto-streaming.spec.js +126 -208
  42. package/dist/test/encrypted-metadata.spec.d.ts +2 -2
  43. package/dist/test/encrypted-metadata.spec.js +62 -89
  44. package/dist/test/factories.spec.d.ts +2 -2
  45. package/dist/test/factories.spec.js +139 -275
  46. package/dist/test/file-metadata.spec.d.ts +2 -2
  47. package/dist/test/file-metadata.spec.js +416 -472
  48. package/dist/test/fixtures/test-fixtures.d.ts +20 -25
  49. package/dist/test/fixtures/test-fixtures.js +53 -61
  50. package/dist/test/helpers/test-file-utils.d.ts +14 -19
  51. package/dist/test/helpers/test-file-utils.js +76 -78
  52. package/dist/test/https-enforcement.spec.d.ts +2 -2
  53. package/dist/test/https-enforcement.spec.js +124 -278
  54. package/dist/test/kms-crypto-adapter.spec.d.ts +2 -2
  55. package/dist/test/kms-crypto-adapter.spec.js +304 -473
  56. package/dist/test/lit-crypto-adapter.spec.d.ts +2 -2
  57. package/dist/test/lit-crypto-adapter.spec.js +118 -206
  58. package/dist/test/memory-efficiency.spec.d.ts +2 -2
  59. package/dist/test/memory-efficiency.spec.js +87 -100
  60. package/dist/test/mocks/key-manager.d.ts +38 -71
  61. package/dist/test/mocks/key-manager.js +113 -129
  62. package/dist/test/node-crypto-adapter.spec.d.ts +2 -2
  63. package/dist/test/node-crypto-adapter.spec.js +102 -155
  64. package/dist/test/node-generic-crypto-adapter.spec.d.ts +2 -2
  65. package/dist/test/node-generic-crypto-adapter.spec.js +94 -134
  66. package/dist/test/setup.d.ts +2 -2
  67. package/dist/test/setup.js +9 -8
  68. package/dist/tsconfig.spec.tsbuildinfo +1 -1
  69. package/dist/types.d.ts +181 -219
  70. package/dist/utils/file-metadata.d.ts +13 -19
  71. package/dist/utils.d.ts +5 -14
  72. package/package.json +2 -2
@@ -1,7 +1,7 @@
1
- import { GenericAesCtrStreamingCrypto } from './symmetric/generic-aes-ctr-streaming-crypto.js'
2
- import { NodeAesCbcCrypto } from './symmetric/node-aes-cbc-crypto.js'
3
- import { LitCryptoAdapter } from './adapters/lit-crypto-adapter.js'
4
- import { KMSCryptoAdapter } from './adapters/kms-crypto-adapter.js'
1
+ import { GenericAesCtrStreamingCrypto } from './symmetric/generic-aes-ctr-streaming-crypto.js';
2
+ import { NodeAesCbcCrypto } from './symmetric/node-aes-cbc-crypto.js';
3
+ import { LitCryptoAdapter } from './adapters/lit-crypto-adapter.js';
4
+ import { KMSCryptoAdapter } from './adapters/kms-crypto-adapter.js';
5
5
  /**
6
6
  * Create a KMS crypto adapter for Node.js using the generic AES-CTR streaming crypto.
7
7
  * Works in Node.js & browser environments.
@@ -9,16 +9,10 @@ import { KMSCryptoAdapter } from './adapters/kms-crypto-adapter.js'
9
9
  * @param {URL|string} keyManagerServiceURL
10
10
  * @param {string} keyManagerServiceDID
11
11
  */
12
- export function createGenericKMSAdapter(
13
- keyManagerServiceURL,
14
- keyManagerServiceDID
15
- ) {
16
- const symmetricCrypto = new GenericAesCtrStreamingCrypto()
17
- return new KMSCryptoAdapter(
18
- symmetricCrypto,
19
- keyManagerServiceURL,
20
- /** @type {`did:${string}:${string}`} */ (keyManagerServiceDID)
21
- )
12
+ export function createGenericKMSAdapter(keyManagerServiceURL, keyManagerServiceDID) {
13
+ const symmetricCrypto = new GenericAesCtrStreamingCrypto();
14
+ return new KMSCryptoAdapter(symmetricCrypto, keyManagerServiceURL,
15
+ /** @type {`did:${string}:${string}`} */ (keyManagerServiceDID));
22
16
  }
23
17
  /**
24
18
  * Create a Lit crypto adapter for Node.js using AES-CBC (legacy).
@@ -28,8 +22,8 @@ export function createGenericKMSAdapter(
28
22
  * @param {import('@lit-protocol/lit-node-client').LitNodeClient} litClient
29
23
  */
30
24
  export function createNodeLitAdapter(litClient) {
31
- const symmetricCrypto = new NodeAesCbcCrypto()
32
- return new LitCryptoAdapter(symmetricCrypto, litClient)
25
+ const symmetricCrypto = new NodeAesCbcCrypto();
26
+ return new LitCryptoAdapter(symmetricCrypto, litClient);
33
27
  }
34
28
  /**
35
29
  * Create a Lit crypto adapter for Node.js using the generic AES-CTR streaming crypto.
@@ -38,7 +32,7 @@ export function createNodeLitAdapter(litClient) {
38
32
  * @param {import('@lit-protocol/lit-node-client').LitNodeClient} litClient
39
33
  */
40
34
  export function createGenericLitAdapter(litClient) {
41
- const symmetricCrypto = new GenericAesCtrStreamingCrypto()
42
- return new LitCryptoAdapter(symmetricCrypto, litClient)
35
+ const symmetricCrypto = new GenericAesCtrStreamingCrypto();
36
+ return new LitCryptoAdapter(symmetricCrypto, litClient);
43
37
  }
44
- //# sourceMappingURL=factories.node.js.map
38
+ //# sourceMappingURL=factories.node.js.map
@@ -1,5 +1,5 @@
1
- export { GenericAesCtrStreamingCrypto } from './symmetric/generic-aes-ctr-streaming-crypto.js'
2
- export { NodeAesCbcCrypto } from './symmetric/node-aes-cbc-crypto.js'
3
- export { LitCryptoAdapter } from './adapters/lit-crypto-adapter.js'
4
- export { KMSCryptoAdapter } from './adapters/kms-crypto-adapter.js'
5
- //# sourceMappingURL=index.d.ts.map
1
+ export { GenericAesCtrStreamingCrypto } from "./symmetric/generic-aes-ctr-streaming-crypto.js";
2
+ export { NodeAesCbcCrypto } from "./symmetric/node-aes-cbc-crypto.js";
3
+ export { LitCryptoAdapter } from "./adapters/lit-crypto-adapter.js";
4
+ export { KMSCryptoAdapter } from "./adapters/kms-crypto-adapter.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -1,7 +1,7 @@
1
1
  // Symmetric crypto implementations (algorithm-specific)
2
- export { GenericAesCtrStreamingCrypto } from './symmetric/generic-aes-ctr-streaming-crypto.js'
3
- export { NodeAesCbcCrypto } from './symmetric/node-aes-cbc-crypto.js'
2
+ export { GenericAesCtrStreamingCrypto } from './symmetric/generic-aes-ctr-streaming-crypto.js';
3
+ export { NodeAesCbcCrypto } from './symmetric/node-aes-cbc-crypto.js';
4
4
  // Strategy adapters (composition-based)
5
- export { LitCryptoAdapter } from './adapters/lit-crypto-adapter.js'
6
- export { KMSCryptoAdapter } from './adapters/kms-crypto-adapter.js'
7
- //# sourceMappingURL=index.js.map
5
+ export { LitCryptoAdapter } from './adapters/lit-crypto-adapter.js';
6
+ export { KMSCryptoAdapter } from './adapters/kms-crypto-adapter.js';
7
+ //# sourceMappingURL=index.js.map
@@ -19,62 +19,58 @@
19
19
  * @implements {Type.SymmetricCrypto}
20
20
  */
21
21
  export class GenericAesCtrStreamingCrypto implements Type.SymmetricCrypto {
22
- /**
23
- * Generate a random AES key
24
- *
25
- * @returns {Promise<Uint8Array>} A random AES key
26
- */
27
- generateKey(): Promise<Uint8Array>
28
- /**
29
- * Properly increment AES-CTR counter with 128-bit arithmetic
30
- *
31
- * @param {Uint8Array} counter - The base counter (16 bytes)
32
- * @param {number} increment - The value to add
33
- * @returns {Uint8Array} - New counter with proper carry propagation
34
- */
35
- incrementCounter(counter: Uint8Array, increment: number): Uint8Array
36
- /**
37
- * Encrypt a stream of data using AES-CTR with TRUE streaming (no buffering).
38
- *
39
- * @param {Blob} data The data to encrypt.
40
- * @returns {Promise<{ key: Uint8Array, iv: Uint8Array, encryptedStream: ReadableStream }>}
41
- */
42
- encryptStream(data: Blob): Promise<{
43
- key: Uint8Array
44
- iv: Uint8Array
45
- encryptedStream: ReadableStream
46
- }>
47
- /**
48
- * Decrypt a stream of data using AES-CTR with TRUE streaming (no buffering).
49
- *
50
- * @param {ReadableStream} encryptedData The encrypted data stream.
51
- * @param {Uint8Array} key The encryption key.
52
- * @param {Uint8Array} iv The initialization vector (counter).
53
- * @returns {Promise<ReadableStream>} A stream of decrypted data.
54
- */
55
- decryptStream(
56
- encryptedData: ReadableStream,
57
- key: Uint8Array,
58
- iv: Uint8Array
59
- ): Promise<ReadableStream>
60
- /**
61
- * Combine key and IV into a single array for AES-CTR
62
- *
63
- * @param {Uint8Array} key - The AES key (KEY_LENGTH/8 bytes)
64
- * @param {Uint8Array} iv - The AES-CTR IV (IV_LENGTH bytes)
65
- * @returns {Uint8Array} Combined key and IV (KEY_LENGTH/8 + IV_LENGTH bytes)
66
- */
67
- combineKeyAndIV(key: Uint8Array, iv: Uint8Array): Uint8Array
68
- /**
69
- * Split combined key and IV for AES-CTR
70
- *
71
- * @param {Uint8Array} combined - Combined key and IV (KEY_LENGTH/8 + IV_LENGTH bytes)
72
- * @returns {{ key: Uint8Array, iv: Uint8Array }} Separated key and IV
73
- */
74
- splitKeyAndIV(combined: Uint8Array): {
75
- key: Uint8Array
76
- iv: Uint8Array
77
- }
22
+ /**
23
+ * Generate a random AES key
24
+ *
25
+ * @returns {Promise<Uint8Array>} A random AES key
26
+ */
27
+ generateKey(): Promise<Uint8Array>;
28
+ /**
29
+ * Properly increment AES-CTR counter with 128-bit arithmetic
30
+ *
31
+ * @param {Uint8Array} counter - The base counter (16 bytes)
32
+ * @param {number} increment - The value to add
33
+ * @returns {Uint8Array} - New counter with proper carry propagation
34
+ */
35
+ incrementCounter(counter: Uint8Array, increment: number): Uint8Array;
36
+ /**
37
+ * Encrypt a stream of data using AES-CTR with TRUE streaming (no buffering).
38
+ *
39
+ * @param {Blob} data The data to encrypt.
40
+ * @returns {Promise<{ key: Uint8Array, iv: Uint8Array, encryptedStream: ReadableStream }>}
41
+ */
42
+ encryptStream(data: Blob): Promise<{
43
+ key: Uint8Array;
44
+ iv: Uint8Array;
45
+ encryptedStream: ReadableStream;
46
+ }>;
47
+ /**
48
+ * Decrypt a stream of data using AES-CTR with TRUE streaming (no buffering).
49
+ *
50
+ * @param {ReadableStream} encryptedData The encrypted data stream.
51
+ * @param {Uint8Array} key The encryption key.
52
+ * @param {Uint8Array} iv The initialization vector (counter).
53
+ * @returns {Promise<ReadableStream>} A stream of decrypted data.
54
+ */
55
+ decryptStream(encryptedData: ReadableStream, key: Uint8Array, iv: Uint8Array): Promise<ReadableStream>;
56
+ /**
57
+ * Combine key and IV into a single array for AES-CTR
58
+ *
59
+ * @param {Uint8Array} key - The AES key (KEY_LENGTH/8 bytes)
60
+ * @param {Uint8Array} iv - The AES-CTR IV (IV_LENGTH bytes)
61
+ * @returns {Uint8Array} Combined key and IV (KEY_LENGTH/8 + IV_LENGTH bytes)
62
+ */
63
+ combineKeyAndIV(key: Uint8Array, iv: Uint8Array): Uint8Array;
64
+ /**
65
+ * Split combined key and IV for AES-CTR
66
+ *
67
+ * @param {Uint8Array} combined - Combined key and IV (KEY_LENGTH/8 + IV_LENGTH bytes)
68
+ * @returns {{ key: Uint8Array, iv: Uint8Array }} Separated key and IV
69
+ */
70
+ splitKeyAndIV(combined: Uint8Array): {
71
+ key: Uint8Array;
72
+ iv: Uint8Array;
73
+ };
78
74
  }
79
- import * as Type from '../../types.js'
80
- //# sourceMappingURL=generic-aes-ctr-streaming-crypto.d.ts.map
75
+ import * as Type from '../../types.js';
76
+ //# sourceMappingURL=generic-aes-ctr-streaming-crypto.d.ts.map
@@ -1,8 +1,8 @@
1
- import * as Type from '../../types.js'
2
- const ENCRYPTION_ALGORITHM = 'AES-CTR'
3
- const KEY_LENGTH = 256 // bits
4
- const IV_LENGTH = 16 // bytes (128 bits, used as counter)
5
- const COUNTER_LENGTH = 64 // bits (Web Crypto API default for AES-CTR)
1
+ import * as Type from '../../types.js';
2
+ const ENCRYPTION_ALGORITHM = 'AES-CTR';
3
+ const KEY_LENGTH = 256; // bits
4
+ const IV_LENGTH = 16; // bytes (128 bits, used as counter)
5
+ const COUNTER_LENGTH = 64; // bits (Web Crypto API default for AES-CTR)
6
6
  /**
7
7
  * GenericAesCtrStreamingCrypto implements TRUE streaming AES-CTR encryption for any JavaScript environment.
8
8
  *
@@ -24,181 +24,153 @@ const COUNTER_LENGTH = 64 // bits (Web Crypto API default for AES-CTR)
24
24
  * @implements {Type.SymmetricCrypto}
25
25
  */
26
26
  export class GenericAesCtrStreamingCrypto {
27
- constructor() {
28
- if (typeof globalThis.crypto === 'undefined') {
29
- throw new Error('Web Crypto API is not available.')
30
- }
31
- }
32
- /**
33
- * Generate a random AES key
34
- *
35
- * @returns {Promise<Uint8Array>} A random AES key
36
- */
37
- async generateKey() {
38
- return globalThis.crypto.getRandomValues(new Uint8Array(KEY_LENGTH / 8))
39
- }
40
- /**
41
- * Properly increment AES-CTR counter with 128-bit arithmetic
42
- *
43
- * @param {Uint8Array} counter - The base counter (16 bytes)
44
- * @param {number} increment - The value to add
45
- * @returns {Uint8Array} - New counter with proper carry propagation
46
- */
47
- incrementCounter(counter, increment) {
48
- const result = new Uint8Array(counter)
49
- let carry = increment
50
- // Implement proper 128-bit arithmetic with carry propagation
51
- // Start from the least significant byte (rightmost) and propagate carry
52
- for (let i = result.length - 1; i >= 0 && carry > 0; i--) {
53
- const sum = result[i] + carry
54
- result[i] = sum & 0xff // Keep only the low 8 bits
55
- carry = sum >> 8 // Carry the high bits to next position
27
+ constructor() {
28
+ if (typeof globalThis.crypto === 'undefined') {
29
+ throw new Error('Web Crypto API is not available.');
30
+ }
56
31
  }
57
- // Check for counter overflow (extremely unlikely with 128-bit counter)
58
- if (carry > 0) {
59
- throw new Error(
60
- 'Counter overflow: exceeded 128-bit limit. This should never happen in practice.'
61
- )
32
+ /**
33
+ * Generate a random AES key
34
+ *
35
+ * @returns {Promise<Uint8Array>} A random AES key
36
+ */
37
+ async generateKey() {
38
+ return globalThis.crypto.getRandomValues(new Uint8Array(KEY_LENGTH / 8));
62
39
  }
63
- return result
64
- }
65
- /**
66
- * Encrypt a stream of data using AES-CTR with TRUE streaming (no buffering).
67
- *
68
- * @param {Blob} data The data to encrypt.
69
- * @returns {Promise<{ key: Uint8Array, iv: Uint8Array, encryptedStream: ReadableStream }>}
70
- */
71
- async encryptStream(data) {
72
- const key = await this.generateKey()
73
- const iv = globalThis.crypto.getRandomValues(new Uint8Array(IV_LENGTH))
74
- // Pre-import the crypto key for reuse across chunks
75
- const cryptoKey = await globalThis.crypto.subtle.importKey(
76
- 'raw',
77
- key,
78
- { name: ENCRYPTION_ALGORITHM },
79
- false,
80
- ['encrypt', 'decrypt']
81
- )
82
- // State for AES-CTR counter management
83
- let counter = new Uint8Array(iv) // Copy the IV for counter
84
- let totalBlocks = 0 // Track total blocks processed
85
- // Create TransformStream (inspired by Node.js approach)
86
- const encryptTransform = new TransformStream({
87
- transform: async (chunk, controller) => {
88
- try {
89
- // SECURITY: Calculate counter based on total blocks, not chunks
90
- const chunkCounter = this.incrementCounter(counter, totalBlocks)
91
- // SECURITY: Increment by blocks in this chunk (16 bytes per block)
92
- const blocksInChunk = Math.ceil(chunk.length / 16)
93
- totalBlocks += blocksInChunk
94
- // Encrypt chunk using Web Crypto API
95
- const encrypted = new Uint8Array(
96
- await globalThis.crypto.subtle.encrypt(
97
- {
98
- name: ENCRYPTION_ALGORITHM,
99
- counter: chunkCounter,
100
- length: COUNTER_LENGTH,
101
- },
102
- cryptoKey,
103
- chunk
104
- )
105
- )
106
- controller.enqueue(encrypted)
107
- } catch (error) {
108
- controller.error(error)
40
+ /**
41
+ * Properly increment AES-CTR counter with 128-bit arithmetic
42
+ *
43
+ * @param {Uint8Array} counter - The base counter (16 bytes)
44
+ * @param {number} increment - The value to add
45
+ * @returns {Uint8Array} - New counter with proper carry propagation
46
+ */
47
+ incrementCounter(counter, increment) {
48
+ const result = new Uint8Array(counter);
49
+ let carry = increment;
50
+ // Implement proper 128-bit arithmetic with carry propagation
51
+ // Start from the least significant byte (rightmost) and propagate carry
52
+ for (let i = result.length - 1; i >= 0 && carry > 0; i--) {
53
+ const sum = result[i] + carry;
54
+ result[i] = sum & 0xff; // Keep only the low 8 bits
55
+ carry = sum >> 8; // Carry the high bits to next position
109
56
  }
110
- },
111
- // Note: No flush needed for AES-CTR (unlike CBC which needs final padding)
112
- })
113
- const encryptedStream = data.stream().pipeThrough(encryptTransform)
114
- return { key, iv, encryptedStream }
115
- }
116
- /**
117
- * Decrypt a stream of data using AES-CTR with TRUE streaming (no buffering).
118
- *
119
- * @param {ReadableStream} encryptedData The encrypted data stream.
120
- * @param {Uint8Array} key The encryption key.
121
- * @param {Uint8Array} iv The initialization vector (counter).
122
- * @returns {Promise<ReadableStream>} A stream of decrypted data.
123
- */
124
- async decryptStream(encryptedData, key, iv) {
125
- // Pre-import the crypto key for reuse across chunks
126
- const cryptoKey = await globalThis.crypto.subtle.importKey(
127
- 'raw',
128
- key,
129
- { name: ENCRYPTION_ALGORITHM },
130
- false,
131
- ['encrypt', 'decrypt']
132
- )
133
- // State for AES-CTR counter management
134
- let counter = new Uint8Array(iv)
135
- let totalBlocks = 0 // Track total blocks processed (CRITICAL for security)
136
- const decryptTransform = new TransformStream({
137
- transform: async (chunk, controller) => {
138
- try {
139
- // SECURITY: Calculate counter based on total blocks, not chunks
140
- const chunkCounter = this.incrementCounter(counter, totalBlocks)
141
- // SECURITY: Increment by blocks in this chunk (16 bytes per block)
142
- const blocksInChunk = Math.ceil(chunk.length / 16)
143
- totalBlocks += blocksInChunk
144
- // Decrypt chunk using Web Crypto API
145
- const decrypted = new Uint8Array(
146
- await globalThis.crypto.subtle.decrypt(
147
- {
148
- name: ENCRYPTION_ALGORITHM,
149
- counter: chunkCounter,
150
- length: COUNTER_LENGTH,
151
- },
152
- cryptoKey,
153
- chunk
154
- )
155
- )
156
- controller.enqueue(decrypted)
157
- } catch (error) {
158
- controller.error(error)
57
+ // Check for counter overflow (extremely unlikely with 128-bit counter)
58
+ if (carry > 0) {
59
+ throw new Error('Counter overflow: exceeded 128-bit limit. This should never happen in practice.');
159
60
  }
160
- },
161
- // Note: No flush needed for AES-CTR (unlike CBC which needs final padding)
162
- })
163
- return encryptedData.pipeThrough(decryptTransform)
164
- }
165
- /**
166
- * Combine key and IV into a single array for AES-CTR
167
- *
168
- * @param {Uint8Array} key - The AES key (KEY_LENGTH/8 bytes)
169
- * @param {Uint8Array} iv - The AES-CTR IV (IV_LENGTH bytes)
170
- * @returns {Uint8Array} Combined key and IV (KEY_LENGTH/8 + IV_LENGTH bytes)
171
- */
172
- combineKeyAndIV(key, iv) {
173
- const keyBytes = KEY_LENGTH / 8
174
- if (key.length !== keyBytes) {
175
- throw new Error(
176
- `AES-${KEY_LENGTH} key must be ${keyBytes} bytes, got ${key.length}`
177
- )
61
+ return result;
62
+ }
63
+ /**
64
+ * Encrypt a stream of data using AES-CTR with TRUE streaming (no buffering).
65
+ *
66
+ * @param {Blob} data The data to encrypt.
67
+ * @returns {Promise<{ key: Uint8Array, iv: Uint8Array, encryptedStream: ReadableStream }>}
68
+ */
69
+ async encryptStream(data) {
70
+ const key = await this.generateKey();
71
+ const iv = globalThis.crypto.getRandomValues(new Uint8Array(IV_LENGTH));
72
+ // Pre-import the crypto key for reuse across chunks
73
+ const cryptoKey = await globalThis.crypto.subtle.importKey('raw', key, { name: ENCRYPTION_ALGORITHM }, false, ['encrypt', 'decrypt']);
74
+ // State for AES-CTR counter management
75
+ let counter = new Uint8Array(iv); // Copy the IV for counter
76
+ let totalBlocks = 0; // Track total blocks processed
77
+ // Create TransformStream (inspired by Node.js approach)
78
+ const encryptTransform = new TransformStream({
79
+ transform: async (chunk, controller) => {
80
+ try {
81
+ // SECURITY: Calculate counter based on total blocks, not chunks
82
+ const chunkCounter = this.incrementCounter(counter, totalBlocks);
83
+ // SECURITY: Increment by blocks in this chunk (16 bytes per block)
84
+ const blocksInChunk = Math.ceil(chunk.length / 16);
85
+ totalBlocks += blocksInChunk;
86
+ // Encrypt chunk using Web Crypto API
87
+ const encrypted = new Uint8Array(await globalThis.crypto.subtle.encrypt({
88
+ name: ENCRYPTION_ALGORITHM,
89
+ counter: chunkCounter,
90
+ length: COUNTER_LENGTH,
91
+ }, cryptoKey, chunk));
92
+ controller.enqueue(encrypted);
93
+ }
94
+ catch (error) {
95
+ controller.error(error);
96
+ }
97
+ },
98
+ // Note: No flush needed for AES-CTR (unlike CBC which needs final padding)
99
+ });
100
+ const encryptedStream = data.stream().pipeThrough(encryptTransform);
101
+ return { key, iv, encryptedStream };
178
102
  }
179
- if (iv.length !== IV_LENGTH) {
180
- throw new Error(`AES-CTR IV must be ${IV_LENGTH} bytes, got ${iv.length}`)
103
+ /**
104
+ * Decrypt a stream of data using AES-CTR with TRUE streaming (no buffering).
105
+ *
106
+ * @param {ReadableStream} encryptedData The encrypted data stream.
107
+ * @param {Uint8Array} key The encryption key.
108
+ * @param {Uint8Array} iv The initialization vector (counter).
109
+ * @returns {Promise<ReadableStream>} A stream of decrypted data.
110
+ */
111
+ async decryptStream(encryptedData, key, iv) {
112
+ // Pre-import the crypto key for reuse across chunks
113
+ const cryptoKey = await globalThis.crypto.subtle.importKey('raw', key, { name: ENCRYPTION_ALGORITHM }, false, ['encrypt', 'decrypt']);
114
+ // State for AES-CTR counter management
115
+ let counter = new Uint8Array(iv);
116
+ let totalBlocks = 0; // Track total blocks processed (CRITICAL for security)
117
+ const decryptTransform = new TransformStream({
118
+ transform: async (chunk, controller) => {
119
+ try {
120
+ // SECURITY: Calculate counter based on total blocks, not chunks
121
+ const chunkCounter = this.incrementCounter(counter, totalBlocks);
122
+ // SECURITY: Increment by blocks in this chunk (16 bytes per block)
123
+ const blocksInChunk = Math.ceil(chunk.length / 16);
124
+ totalBlocks += blocksInChunk;
125
+ // Decrypt chunk using Web Crypto API
126
+ const decrypted = new Uint8Array(await globalThis.crypto.subtle.decrypt({
127
+ name: ENCRYPTION_ALGORITHM,
128
+ counter: chunkCounter,
129
+ length: COUNTER_LENGTH,
130
+ }, cryptoKey, chunk));
131
+ controller.enqueue(decrypted);
132
+ }
133
+ catch (error) {
134
+ controller.error(error);
135
+ }
136
+ },
137
+ // Note: No flush needed for AES-CTR (unlike CBC which needs final padding)
138
+ });
139
+ return encryptedData.pipeThrough(decryptTransform);
181
140
  }
182
- return new Uint8Array([...key, ...iv])
183
- }
184
- /**
185
- * Split combined key and IV for AES-CTR
186
- *
187
- * @param {Uint8Array} combined - Combined key and IV (KEY_LENGTH/8 + IV_LENGTH bytes)
188
- * @returns {{ key: Uint8Array, iv: Uint8Array }} Separated key and IV
189
- */
190
- splitKeyAndIV(combined) {
191
- const keyBytes = KEY_LENGTH / 8
192
- const expectedLength = keyBytes + IV_LENGTH
193
- if (combined.length !== expectedLength) {
194
- throw new Error(
195
- `AES-${KEY_LENGTH}-CTR combined key+IV must be ${expectedLength} bytes, got ${combined.length}`
196
- )
141
+ /**
142
+ * Combine key and IV into a single array for AES-CTR
143
+ *
144
+ * @param {Uint8Array} key - The AES key (KEY_LENGTH/8 bytes)
145
+ * @param {Uint8Array} iv - The AES-CTR IV (IV_LENGTH bytes)
146
+ * @returns {Uint8Array} Combined key and IV (KEY_LENGTH/8 + IV_LENGTH bytes)
147
+ */
148
+ combineKeyAndIV(key, iv) {
149
+ const keyBytes = KEY_LENGTH / 8;
150
+ if (key.length !== keyBytes) {
151
+ throw new Error(`AES-${KEY_LENGTH} key must be ${keyBytes} bytes, got ${key.length}`);
152
+ }
153
+ if (iv.length !== IV_LENGTH) {
154
+ throw new Error(`AES-CTR IV must be ${IV_LENGTH} bytes, got ${iv.length}`);
155
+ }
156
+ return new Uint8Array([...key, ...iv]);
197
157
  }
198
- return {
199
- key: combined.subarray(0, keyBytes),
200
- iv: combined.subarray(keyBytes, keyBytes + IV_LENGTH),
158
+ /**
159
+ * Split combined key and IV for AES-CTR
160
+ *
161
+ * @param {Uint8Array} combined - Combined key and IV (KEY_LENGTH/8 + IV_LENGTH bytes)
162
+ * @returns {{ key: Uint8Array, iv: Uint8Array }} Separated key and IV
163
+ */
164
+ splitKeyAndIV(combined) {
165
+ const keyBytes = KEY_LENGTH / 8;
166
+ const expectedLength = keyBytes + IV_LENGTH;
167
+ if (combined.length !== expectedLength) {
168
+ throw new Error(`AES-${KEY_LENGTH}-CTR combined key+IV must be ${expectedLength} bytes, got ${combined.length}`);
169
+ }
170
+ return {
171
+ key: combined.subarray(0, keyBytes),
172
+ iv: combined.subarray(keyBytes, keyBytes + IV_LENGTH),
173
+ };
201
174
  }
202
- }
203
175
  }
204
- //# sourceMappingURL=generic-aes-ctr-streaming-crypto.js.map
176
+ //# sourceMappingURL=generic-aes-ctr-streaming-crypto.js.map
@@ -8,40 +8,36 @@
8
8
  * @implements {Type.SymmetricCrypto}
9
9
  */
10
10
  export class NodeAesCbcCrypto implements Type.SymmetricCrypto {
11
- /** @param {Type.BlobLike} data */
12
- encryptStream(data: Type.BlobLike): Promise<{
13
- key: Buffer<ArrayBufferLike>
14
- iv: Buffer<ArrayBufferLike>
15
- encryptedStream: ReadableStream<any>
16
- }>
17
- /**
18
- * @param {ReadableStream} encryptedData
19
- * @param {Uint8Array} key
20
- * @param {Uint8Array} iv
21
- */
22
- decryptStream(
23
- encryptedData: ReadableStream,
24
- key: Uint8Array,
25
- iv: Uint8Array
26
- ): Promise<ReadableStream<any>>
27
- /**
28
- * Combine key and IV into a single array for AES-CBC
29
- *
30
- * @param {Uint8Array} key - The AES key (KEY_LENGTH/8 bytes)
31
- * @param {Uint8Array} iv - The AES-CBC IV (IV_LENGTH bytes)
32
- * @returns {Uint8Array} Combined key and IV (KEY_LENGTH/8 + IV_LENGTH bytes)
33
- */
34
- combineKeyAndIV(key: Uint8Array, iv: Uint8Array): Uint8Array
35
- /**
36
- * Split combined key and IV for AES-CBC
37
- *
38
- * @param {Uint8Array} combined - Combined key and IV (KEY_LENGTH/8 + IV_LENGTH bytes)
39
- * @returns {{ key: Uint8Array, iv: Uint8Array }} Separated key and IV
40
- */
41
- splitKeyAndIV(combined: Uint8Array): {
42
- key: Uint8Array
43
- iv: Uint8Array
44
- }
11
+ /** @param {Type.BlobLike} data */
12
+ encryptStream(data: Type.BlobLike): Promise<{
13
+ key: Buffer<ArrayBufferLike>;
14
+ iv: Buffer<ArrayBufferLike>;
15
+ encryptedStream: ReadableStream<any>;
16
+ }>;
17
+ /**
18
+ * @param {ReadableStream} encryptedData
19
+ * @param {Uint8Array} key
20
+ * @param {Uint8Array} iv
21
+ */
22
+ decryptStream(encryptedData: ReadableStream, key: Uint8Array, iv: Uint8Array): Promise<ReadableStream<any>>;
23
+ /**
24
+ * Combine key and IV into a single array for AES-CBC
25
+ *
26
+ * @param {Uint8Array} key - The AES key (KEY_LENGTH/8 bytes)
27
+ * @param {Uint8Array} iv - The AES-CBC IV (IV_LENGTH bytes)
28
+ * @returns {Uint8Array} Combined key and IV (KEY_LENGTH/8 + IV_LENGTH bytes)
29
+ */
30
+ combineKeyAndIV(key: Uint8Array, iv: Uint8Array): Uint8Array;
31
+ /**
32
+ * Split combined key and IV for AES-CBC
33
+ *
34
+ * @param {Uint8Array} combined - Combined key and IV (KEY_LENGTH/8 + IV_LENGTH bytes)
35
+ * @returns {{ key: Uint8Array, iv: Uint8Array }} Separated key and IV
36
+ */
37
+ splitKeyAndIV(combined: Uint8Array): {
38
+ key: Uint8Array;
39
+ iv: Uint8Array;
40
+ };
45
41
  }
46
- import * as Type from '../../types.js'
47
- //# sourceMappingURL=node-aes-cbc-crypto.d.ts.map
42
+ import * as Type from '../../types.js';
43
+ //# sourceMappingURL=node-aes-cbc-crypto.d.ts.map