@storacha/encrypt-upload-client 1.1.57 → 1.1.58

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 +4 -3
  3. package/dist/config/env.d.ts +9 -6
  4. package/dist/config/service.d.ts +13 -13
  5. package/dist/core/client.d.ts +54 -41
  6. package/dist/core/client.js +68 -56
  7. package/dist/core/errors.d.ts +6 -6
  8. package/dist/core/metadata/encrypted-metadata.d.ts +13 -8
  9. package/dist/core/metadata/kms-metadata.d.ts +68 -36
  10. package/dist/core/metadata/lit-metadata.d.ts +63 -28
  11. package/dist/crypto/adapters/kms-crypto-adapter.d.ts +172 -137
  12. package/dist/crypto/adapters/lit-crypto-adapter.d.ts +107 -86
  13. package/dist/crypto/factories.browser.d.ts +9 -5
  14. package/dist/crypto/factories.browser.js +15 -7
  15. package/dist/crypto/factories.node.d.ts +13 -6
  16. package/dist/crypto/factories.node.js +19 -13
  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 +58 -54
  20. package/dist/crypto/symmetric/generic-aes-ctr-streaming-crypto.js +174 -146
  21. package/dist/crypto/symmetric/node-aes-cbc-crypto.d.ts +36 -32
  22. package/dist/crypto/symmetric/node-aes-cbc-crypto.js +101 -95
  23. package/dist/examples/decrypt-test.d.ts +2 -2
  24. package/dist/examples/decrypt-test.js +78 -69
  25. package/dist/examples/encrypt-test.d.ts +5 -3
  26. package/dist/examples/encrypt-test.js +58 -55
  27. package/dist/handlers/decrypt-handler.d.ts +19 -5
  28. package/dist/handlers/encrypt-handler.d.ts +9 -3
  29. package/dist/handlers/encrypt-handler.js +93 -57
  30. package/dist/index.d.ts +2 -2
  31. package/dist/index.js +2 -2
  32. package/dist/protocols/lit.d.ts +33 -9
  33. package/dist/protocols/lit.js +134 -98
  34. package/dist/test/cid-verification.spec.d.ts +2 -2
  35. package/dist/test/cid-verification.spec.js +341 -313
  36. package/dist/test/crypto-compatibility.spec.d.ts +2 -2
  37. package/dist/test/crypto-compatibility.spec.js +184 -120
  38. package/dist/test/crypto-counter-security.spec.d.ts +2 -2
  39. package/dist/test/crypto-counter-security.spec.js +177 -138
  40. package/dist/test/crypto-streaming.spec.d.ts +2 -2
  41. package/dist/test/crypto-streaming.spec.js +208 -126
  42. package/dist/test/encrypted-metadata.spec.d.ts +2 -2
  43. package/dist/test/encrypted-metadata.spec.js +89 -62
  44. package/dist/test/factories.spec.d.ts +2 -2
  45. package/dist/test/factories.spec.js +275 -139
  46. package/dist/test/file-metadata.spec.d.ts +2 -2
  47. package/dist/test/file-metadata.spec.js +472 -416
  48. package/dist/test/fixtures/test-fixtures.d.ts +25 -20
  49. package/dist/test/fixtures/test-fixtures.js +61 -53
  50. package/dist/test/helpers/test-file-utils.d.ts +19 -14
  51. package/dist/test/helpers/test-file-utils.js +78 -76
  52. package/dist/test/https-enforcement.spec.d.ts +2 -2
  53. package/dist/test/https-enforcement.spec.js +278 -124
  54. package/dist/test/kms-crypto-adapter.spec.d.ts +2 -2
  55. package/dist/test/kms-crypto-adapter.spec.js +473 -304
  56. package/dist/test/lit-crypto-adapter.spec.d.ts +2 -2
  57. package/dist/test/lit-crypto-adapter.spec.js +206 -118
  58. package/dist/test/memory-efficiency.spec.d.ts +2 -2
  59. package/dist/test/memory-efficiency.spec.js +100 -87
  60. package/dist/test/mocks/key-manager.d.ts +71 -38
  61. package/dist/test/mocks/key-manager.js +129 -113
  62. package/dist/test/node-crypto-adapter.spec.d.ts +2 -2
  63. package/dist/test/node-crypto-adapter.spec.js +155 -102
  64. package/dist/test/node-generic-crypto-adapter.spec.d.ts +2 -2
  65. package/dist/test/node-generic-crypto-adapter.spec.js +134 -94
  66. package/dist/test/setup.d.ts +2 -2
  67. package/dist/test/setup.js +8 -9
  68. package/dist/tsconfig.spec.tsbuildinfo +1 -1
  69. package/dist/types.d.ts +219 -181
  70. package/dist/utils/file-metadata.d.ts +19 -13
  71. package/dist/utils.d.ts +14 -5
  72. package/package.json +4 -4
@@ -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,10 +9,16 @@ 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(keyManagerServiceURL, keyManagerServiceDID) {
13
- const symmetricCrypto = new GenericAesCtrStreamingCrypto();
14
- return new KMSCryptoAdapter(symmetricCrypto, keyManagerServiceURL,
15
- /** @type {`did:${string}:${string}`} */ (keyManagerServiceDID));
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
+ )
16
22
  }
17
23
  /**
18
24
  * Create a Lit crypto adapter for Node.js using AES-CBC (legacy).
@@ -22,8 +28,8 @@ export function createGenericKMSAdapter(keyManagerServiceURL, keyManagerServiceD
22
28
  * @param {import('@lit-protocol/lit-node-client').LitNodeClient} litClient
23
29
  */
24
30
  export function createNodeLitAdapter(litClient) {
25
- const symmetricCrypto = new NodeAesCbcCrypto();
26
- return new LitCryptoAdapter(symmetricCrypto, litClient);
31
+ const symmetricCrypto = new NodeAesCbcCrypto()
32
+ return new LitCryptoAdapter(symmetricCrypto, litClient)
27
33
  }
28
34
  /**
29
35
  * Create a Lit crypto adapter for Node.js using the generic AES-CTR streaming crypto.
@@ -32,7 +38,7 @@ export function createNodeLitAdapter(litClient) {
32
38
  * @param {import('@lit-protocol/lit-node-client').LitNodeClient} litClient
33
39
  */
34
40
  export function createGenericLitAdapter(litClient) {
35
- const symmetricCrypto = new GenericAesCtrStreamingCrypto();
36
- return new LitCryptoAdapter(symmetricCrypto, litClient);
41
+ const symmetricCrypto = new GenericAesCtrStreamingCrypto()
42
+ return new LitCryptoAdapter(symmetricCrypto, litClient)
37
43
  }
38
- //# sourceMappingURL=factories.node.js.map
44
+ //# 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,58 +19,62 @@
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(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
- };
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
+ }
74
78
  }
75
- import * as Type from '../../types.js';
76
- //# sourceMappingURL=generic-aes-ctr-streaming-crypto.d.ts.map
79
+ import * as Type from '../../types.js'
80
+ //# 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,153 +24,181 @@ 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
- }
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
31
56
  }
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));
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
+ )
39
62
  }
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
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)
56
109
  }
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.');
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)
60
159
  }
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 };
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
+ )
102
178
  }
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);
179
+ if (iv.length !== IV_LENGTH) {
180
+ throw new Error(`AES-CTR IV must be ${IV_LENGTH} bytes, got ${iv.length}`)
140
181
  }
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]);
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
+ )
157
197
  }
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
- };
198
+ return {
199
+ key: combined.subarray(0, keyBytes),
200
+ iv: combined.subarray(keyBytes, keyBytes + IV_LENGTH),
174
201
  }
202
+ }
175
203
  }
176
- //# sourceMappingURL=generic-aes-ctr-streaming-crypto.js.map
204
+ //# sourceMappingURL=generic-aes-ctr-streaming-crypto.js.map
@@ -8,36 +8,40 @@
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(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
- };
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
+ }
41
45
  }
42
- import * as Type from '../../types.js';
43
- //# sourceMappingURL=node-aes-cbc-crypto.d.ts.map
46
+ import * as Type from '../../types.js'
47
+ //# sourceMappingURL=node-aes-cbc-crypto.d.ts.map