@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.
- package/dist/config/constants.d.ts +3 -3
- package/dist/config/constants.js +4 -3
- package/dist/config/env.d.ts +9 -6
- package/dist/config/service.d.ts +13 -13
- package/dist/core/client.d.ts +54 -41
- package/dist/core/client.js +68 -56
- package/dist/core/errors.d.ts +6 -6
- package/dist/core/metadata/encrypted-metadata.d.ts +13 -8
- package/dist/core/metadata/kms-metadata.d.ts +68 -36
- package/dist/core/metadata/lit-metadata.d.ts +63 -28
- package/dist/crypto/adapters/kms-crypto-adapter.d.ts +172 -137
- package/dist/crypto/adapters/lit-crypto-adapter.d.ts +107 -86
- package/dist/crypto/factories.browser.d.ts +9 -5
- package/dist/crypto/factories.browser.js +15 -7
- package/dist/crypto/factories.node.d.ts +13 -6
- package/dist/crypto/factories.node.js +19 -13
- package/dist/crypto/index.d.ts +5 -5
- package/dist/crypto/index.js +5 -5
- package/dist/crypto/symmetric/generic-aes-ctr-streaming-crypto.d.ts +58 -54
- package/dist/crypto/symmetric/generic-aes-ctr-streaming-crypto.js +174 -146
- package/dist/crypto/symmetric/node-aes-cbc-crypto.d.ts +36 -32
- package/dist/crypto/symmetric/node-aes-cbc-crypto.js +101 -95
- package/dist/examples/decrypt-test.d.ts +2 -2
- package/dist/examples/decrypt-test.js +78 -69
- package/dist/examples/encrypt-test.d.ts +5 -3
- package/dist/examples/encrypt-test.js +58 -55
- package/dist/handlers/decrypt-handler.d.ts +19 -5
- package/dist/handlers/encrypt-handler.d.ts +9 -3
- package/dist/handlers/encrypt-handler.js +93 -57
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/protocols/lit.d.ts +33 -9
- package/dist/protocols/lit.js +134 -98
- package/dist/test/cid-verification.spec.d.ts +2 -2
- package/dist/test/cid-verification.spec.js +341 -313
- package/dist/test/crypto-compatibility.spec.d.ts +2 -2
- package/dist/test/crypto-compatibility.spec.js +184 -120
- package/dist/test/crypto-counter-security.spec.d.ts +2 -2
- package/dist/test/crypto-counter-security.spec.js +177 -138
- package/dist/test/crypto-streaming.spec.d.ts +2 -2
- package/dist/test/crypto-streaming.spec.js +208 -126
- package/dist/test/encrypted-metadata.spec.d.ts +2 -2
- package/dist/test/encrypted-metadata.spec.js +89 -62
- package/dist/test/factories.spec.d.ts +2 -2
- package/dist/test/factories.spec.js +275 -139
- package/dist/test/file-metadata.spec.d.ts +2 -2
- package/dist/test/file-metadata.spec.js +472 -416
- package/dist/test/fixtures/test-fixtures.d.ts +25 -20
- package/dist/test/fixtures/test-fixtures.js +61 -53
- package/dist/test/helpers/test-file-utils.d.ts +19 -14
- package/dist/test/helpers/test-file-utils.js +78 -76
- package/dist/test/https-enforcement.spec.d.ts +2 -2
- package/dist/test/https-enforcement.spec.js +278 -124
- package/dist/test/kms-crypto-adapter.spec.d.ts +2 -2
- package/dist/test/kms-crypto-adapter.spec.js +473 -304
- package/dist/test/lit-crypto-adapter.spec.d.ts +2 -2
- package/dist/test/lit-crypto-adapter.spec.js +206 -118
- package/dist/test/memory-efficiency.spec.d.ts +2 -2
- package/dist/test/memory-efficiency.spec.js +100 -87
- package/dist/test/mocks/key-manager.d.ts +71 -38
- package/dist/test/mocks/key-manager.js +129 -113
- package/dist/test/node-crypto-adapter.spec.d.ts +2 -2
- package/dist/test/node-crypto-adapter.spec.js +155 -102
- package/dist/test/node-generic-crypto-adapter.spec.d.ts +2 -2
- package/dist/test/node-generic-crypto-adapter.spec.js +134 -94
- package/dist/test/setup.d.ts +2 -2
- package/dist/test/setup.js +8 -9
- package/dist/tsconfig.spec.tsbuildinfo +1 -1
- package/dist/types.d.ts +219 -181
- package/dist/utils/file-metadata.d.ts +19 -13
- package/dist/utils.d.ts +14 -5
- 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(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
26
|
-
|
|
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
|
-
|
|
36
|
-
|
|
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
|
package/dist/crypto/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { GenericAesCtrStreamingCrypto } from
|
|
2
|
-
export { NodeAesCbcCrypto } from
|
|
3
|
-
export { LitCryptoAdapter } from
|
|
4
|
-
export { KMSCryptoAdapter } from
|
|
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
|
package/dist/crypto/index.js
CHANGED
|
@@ -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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
|
4
|
-
const IV_LENGTH = 16
|
|
5
|
-
const COUNTER_LENGTH = 64
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|