@storacha/encrypt-upload-client 1.1.81 → 1.1.82
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/crypto/adapters/kms-crypto-adapter.d.ts +4 -10
- package/dist/crypto/adapters/kms-crypto-adapter.d.ts.map +1 -1
- package/dist/crypto/adapters/kms-crypto-adapter.js +4 -4
- package/dist/examples/decrypt-test.d.ts +2 -0
- package/dist/examples/decrypt-test.d.ts.map +1 -0
- package/dist/examples/decrypt-test.js +102 -0
- package/dist/examples/encrypt-test.d.ts +2 -0
- package/dist/examples/encrypt-test.d.ts.map +1 -0
- package/dist/examples/encrypt-test.js +93 -0
- package/dist/test/cid-verification.spec.d.ts +2 -0
- package/dist/test/cid-verification.spec.d.ts.map +1 -0
- package/dist/test/cid-verification.spec.js +314 -0
- package/dist/test/crypto-compatibility.spec.d.ts +2 -0
- package/dist/test/crypto-compatibility.spec.d.ts.map +1 -0
- package/dist/test/crypto-compatibility.spec.js +124 -0
- package/dist/test/crypto-counter-security.spec.d.ts +2 -0
- package/dist/test/crypto-counter-security.spec.d.ts.map +1 -0
- package/dist/test/crypto-counter-security.spec.js +147 -0
- package/dist/test/crypto-streaming.spec.d.ts +2 -0
- package/dist/test/crypto-streaming.spec.d.ts.map +1 -0
- package/dist/test/crypto-streaming.spec.js +129 -0
- package/dist/test/encrypted-metadata.spec.d.ts +2 -0
- package/dist/test/encrypted-metadata.spec.d.ts.map +1 -0
- package/dist/test/encrypted-metadata.spec.js +68 -0
- package/dist/test/factories.spec.d.ts +2 -0
- package/dist/test/factories.spec.d.ts.map +1 -0
- package/dist/test/factories.spec.js +129 -0
- package/dist/test/file-metadata.spec.d.ts +2 -0
- package/dist/test/file-metadata.spec.d.ts.map +1 -0
- package/dist/test/file-metadata.spec.js +433 -0
- package/dist/test/fixtures/test-fixtures.d.ts +28 -0
- package/dist/test/fixtures/test-fixtures.d.ts.map +1 -0
- package/dist/test/fixtures/test-fixtures.js +63 -0
- package/dist/test/helpers/test-file-utils.d.ts +60 -0
- package/dist/test/helpers/test-file-utils.d.ts.map +1 -0
- package/dist/test/helpers/test-file-utils.js +139 -0
- package/dist/test/https-enforcement.spec.d.ts +2 -0
- package/dist/test/https-enforcement.spec.d.ts.map +1 -0
- package/dist/test/https-enforcement.spec.js +125 -0
- package/dist/test/kms-crypto-adapter.spec.d.ts +2 -0
- package/dist/test/kms-crypto-adapter.spec.d.ts.map +1 -0
- package/dist/test/kms-crypto-adapter.spec.js +305 -0
- package/dist/test/lit-crypto-adapter.spec.d.ts +2 -0
- package/dist/test/lit-crypto-adapter.spec.d.ts.map +1 -0
- package/dist/test/lit-crypto-adapter.spec.js +76 -0
- package/dist/test/memory-efficiency.spec.d.ts +2 -0
- package/dist/test/memory-efficiency.spec.d.ts.map +1 -0
- package/dist/test/memory-efficiency.spec.js +93 -0
- package/dist/test/mocks/key-manager.d.ts +58 -0
- package/dist/test/mocks/key-manager.d.ts.map +1 -0
- package/dist/test/mocks/key-manager.js +137 -0
- package/dist/test/node-crypto-adapter.spec.d.ts +2 -0
- package/dist/test/node-crypto-adapter.spec.d.ts.map +1 -0
- package/dist/test/node-crypto-adapter.spec.js +103 -0
- package/dist/test/node-generic-crypto-adapter.spec.d.ts +2 -0
- package/dist/test/node-generic-crypto-adapter.spec.d.ts.map +1 -0
- package/dist/test/node-generic-crypto-adapter.spec.js +95 -0
- package/dist/test/setup.d.ts +2 -0
- package/dist/test/setup.d.ts.map +1 -0
- package/dist/test/setup.js +12 -0
- package/dist/tsconfig.spec.tsbuildinfo +1 -0
- package/dist/types.d.ts +1 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import './setup.js';
|
|
2
|
+
import { test, describe } from 'node:test';
|
|
3
|
+
import assert from 'node:assert';
|
|
4
|
+
import { GenericAesCtrStreamingCrypto } from '../src/crypto/symmetric/generic-aes-ctr-streaming-crypto.js';
|
|
5
|
+
import { NodeAesCbcCrypto } from '../src/crypto/symmetric/node-aes-cbc-crypto.js';
|
|
6
|
+
import { createTestFile, streamToUint8Array, } from './helpers/test-file-utils.js';
|
|
7
|
+
/**
|
|
8
|
+
* These tests verify cross-environment compatibility and consistency of crypto implementations.
|
|
9
|
+
*/
|
|
10
|
+
await describe('Cross-Environment Crypto Compatibility', async () => {
|
|
11
|
+
await test('should work consistently across multiple instances', async () => {
|
|
12
|
+
const crypto1 = new GenericAesCtrStreamingCrypto();
|
|
13
|
+
const crypto2 = new GenericAesCtrStreamingCrypto();
|
|
14
|
+
const testFile = createTestFile(0.01); // 10KB test (ultra-small for memory safety)
|
|
15
|
+
console.log('Testing consistency across instances...');
|
|
16
|
+
// Use SAME key and IV for both instances
|
|
17
|
+
const sharedKey = globalThis.crypto.getRandomValues(new Uint8Array(32));
|
|
18
|
+
const sharedIV = globalThis.crypto.getRandomValues(new Uint8Array(16));
|
|
19
|
+
// Override generateKey to return our shared key
|
|
20
|
+
crypto1.generateKey = async () => sharedKey;
|
|
21
|
+
crypto2.generateKey = async () => sharedKey;
|
|
22
|
+
// Override IV generation to return our shared IV
|
|
23
|
+
const originalRandomValues = globalThis.crypto.getRandomValues;
|
|
24
|
+
let ivCallCount = 0;
|
|
25
|
+
// @ts-ignore - Overriding for test purposes
|
|
26
|
+
globalThis.crypto.getRandomValues = (array) => {
|
|
27
|
+
// @ts-ignore - TypeScript is confused about the array type
|
|
28
|
+
if (array && array.length === 16 && ivCallCount < 2) {
|
|
29
|
+
ivCallCount++;
|
|
30
|
+
// @ts-ignore - TypeScript is confused about the array type
|
|
31
|
+
array.set(sharedIV);
|
|
32
|
+
return array;
|
|
33
|
+
}
|
|
34
|
+
return originalRandomValues.call(globalThis.crypto, array);
|
|
35
|
+
};
|
|
36
|
+
try {
|
|
37
|
+
// Get encryption results from both instances
|
|
38
|
+
const result1 = await crypto1.encryptStream(testFile);
|
|
39
|
+
const result2 = await crypto2.encryptStream(testFile);
|
|
40
|
+
// Verify keys and IVs are identical
|
|
41
|
+
assert.deepStrictEqual(result1.key, result2.key, 'Keys should be identical');
|
|
42
|
+
assert.deepStrictEqual(result1.iv, result2.iv, 'IVs should be identical');
|
|
43
|
+
// Convert streams to bytes for comparison
|
|
44
|
+
const encrypted1 = await streamToUint8Array(result1.encryptedStream);
|
|
45
|
+
const encrypted2 = await streamToUint8Array(result2.encryptedStream);
|
|
46
|
+
// Verify encrypted outputs are byte-for-byte identical
|
|
47
|
+
assert.strictEqual(encrypted1.length, encrypted2.length, 'Encrypted lengths should match');
|
|
48
|
+
assert.deepStrictEqual(encrypted1, encrypted2, 'Encrypted data should be identical');
|
|
49
|
+
console.log('Multiple instances produce identical results');
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
// Restore original methods
|
|
53
|
+
globalThis.crypto.getRandomValues = originalRandomValues;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
await test('should support cross-instance decrypt', async () => {
|
|
57
|
+
const crypto1 = new GenericAesCtrStreamingCrypto();
|
|
58
|
+
const crypto2 = new GenericAesCtrStreamingCrypto();
|
|
59
|
+
const testFile = createTestFile(0.01); // 10KB test (ultra-small for memory safety)
|
|
60
|
+
console.log('Testing cross-instance decryption...');
|
|
61
|
+
// Encrypt with first instance
|
|
62
|
+
const { key, iv, encryptedStream } = await crypto1.encryptStream(testFile);
|
|
63
|
+
const encryptedBytes = await streamToUint8Array(encryptedStream);
|
|
64
|
+
// Decrypt with second instance
|
|
65
|
+
const encryptedForDecrypt = new ReadableStream({
|
|
66
|
+
start(controller) {
|
|
67
|
+
controller.enqueue(encryptedBytes);
|
|
68
|
+
controller.close();
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
const decryptedStream = await crypto2.decryptStream(encryptedForDecrypt, key, iv);
|
|
72
|
+
const decryptedBytes = await streamToUint8Array(decryptedStream);
|
|
73
|
+
// Verify decrypted data matches original
|
|
74
|
+
const originalBytes = new Uint8Array(await testFile.arrayBuffer());
|
|
75
|
+
assert.deepStrictEqual(decryptedBytes, originalBytes, 'Cross-instance decryption should work');
|
|
76
|
+
console.log('Cross-instance decryption verified');
|
|
77
|
+
});
|
|
78
|
+
await test('should handle edge cases consistently', async () => {
|
|
79
|
+
const crypto = new GenericAesCtrStreamingCrypto();
|
|
80
|
+
console.log('Testing edge case consistency...');
|
|
81
|
+
// Test empty file
|
|
82
|
+
const emptyFile = new Blob([]);
|
|
83
|
+
const emptyResult = await crypto.encryptStream(emptyFile);
|
|
84
|
+
const emptyEncrypted = await streamToUint8Array(emptyResult.encryptedStream);
|
|
85
|
+
assert.strictEqual(emptyEncrypted.length, 0, 'Empty file should encrypt to 0 bytes');
|
|
86
|
+
// Test single byte file
|
|
87
|
+
const singleByteFile = new Blob([new Uint8Array([42])]);
|
|
88
|
+
const singleResult = await crypto.encryptStream(singleByteFile);
|
|
89
|
+
const singleEncrypted = await streamToUint8Array(singleResult.encryptedStream);
|
|
90
|
+
assert.strictEqual(singleEncrypted.length, 1, 'Single byte file should encrypt to 1 byte');
|
|
91
|
+
// Test round-trip of single byte
|
|
92
|
+
const singleDecryptStream = new ReadableStream({
|
|
93
|
+
start(controller) {
|
|
94
|
+
controller.enqueue(singleEncrypted);
|
|
95
|
+
controller.close();
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
const singleDecrypted = await crypto.decryptStream(singleDecryptStream, singleResult.key, singleResult.iv);
|
|
99
|
+
const singleDecryptedBytes = await streamToUint8Array(singleDecrypted);
|
|
100
|
+
assert.deepStrictEqual(singleDecryptedBytes, new Uint8Array([42]), 'Single byte round-trip should work');
|
|
101
|
+
console.log('Edge cases handled consistently');
|
|
102
|
+
});
|
|
103
|
+
await test('should demonstrate algorithm differences with NodeAesCbcCrypto', async () => {
|
|
104
|
+
const genericCrypto = new GenericAesCtrStreamingCrypto();
|
|
105
|
+
const nodeCrypto = new NodeAesCbcCrypto();
|
|
106
|
+
const testFile = createTestFile(0.01); // 10KB test (ultra-small for memory safety)
|
|
107
|
+
console.log('Testing algorithm differences...');
|
|
108
|
+
// Encrypt with both implementations
|
|
109
|
+
const genericResult = await genericCrypto.encryptStream(testFile);
|
|
110
|
+
const nodeResult = await nodeCrypto.encryptStream(testFile);
|
|
111
|
+
// Convert streams to bytes
|
|
112
|
+
const genericEncrypted = await streamToUint8Array(genericResult.encryptedStream);
|
|
113
|
+
const nodeEncrypted = await streamToUint8Array(nodeResult.encryptedStream);
|
|
114
|
+
// Verify they produce different results (different algorithms)
|
|
115
|
+
assert.notDeepEqual(genericEncrypted, nodeEncrypted, 'AES-CTR and AES-CBC should produce different results');
|
|
116
|
+
// Verify they have different key/IV formats
|
|
117
|
+
assert.strictEqual(genericResult.key.length, 32, 'Generic crypto should use 32-byte keys');
|
|
118
|
+
assert.strictEqual(genericResult.iv.length, 16, 'Generic crypto should use 16-byte IVs');
|
|
119
|
+
assert.strictEqual(nodeResult.key.length, 32, 'Node crypto should use 32-byte keys');
|
|
120
|
+
assert.strictEqual(nodeResult.iv.length, 16, 'Node crypto should use 16-byte IVs');
|
|
121
|
+
console.log('Algorithm differences confirmed - use consistent crypto for encrypt/decrypt');
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
//# sourceMappingURL=crypto-compatibility.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto-counter-security.spec.d.ts","sourceRoot":"","sources":["../../test/crypto-counter-security.spec.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import './setup.js';
|
|
2
|
+
import { test, describe } from 'node:test';
|
|
3
|
+
import assert from 'node:assert';
|
|
4
|
+
import { GenericAesCtrStreamingCrypto } from '../src/crypto/symmetric/generic-aes-ctr-streaming-crypto.js';
|
|
5
|
+
/**
|
|
6
|
+
* Security tests for AES-CTR counter management
|
|
7
|
+
*
|
|
8
|
+
* These tests verify that the block-based counter implementation prevents keystream reuse,
|
|
9
|
+
* which is critical for AES-CTR security.
|
|
10
|
+
*/
|
|
11
|
+
await describe('AES-CTR Counter Security', async () => {
|
|
12
|
+
await test('should increment counter by blocks, not chunks', async () => {
|
|
13
|
+
const crypto = new GenericAesCtrStreamingCrypto();
|
|
14
|
+
// Test the incrementCounter function directly
|
|
15
|
+
const baseCounter = new Uint8Array(16).fill(0);
|
|
16
|
+
// Test counter increments for block counts
|
|
17
|
+
const counter1 = crypto.incrementCounter(baseCounter, 0); // First block
|
|
18
|
+
const counter2 = crypto.incrementCounter(baseCounter, 1); // Second block
|
|
19
|
+
const counter3 = crypto.incrementCounter(baseCounter, 2); // Third block
|
|
20
|
+
const counter4 = crypto.incrementCounter(baseCounter, 7); // Eighth block
|
|
21
|
+
// Verify each counter is unique
|
|
22
|
+
assert.notDeepEqual(counter1, counter2, 'Block 1 and 2 should have different counters');
|
|
23
|
+
assert.notDeepEqual(counter2, counter3, 'Block 2 and 3 should have different counters');
|
|
24
|
+
assert.notDeepEqual(counter3, counter4, 'Block 3 and 8 should have different counters');
|
|
25
|
+
// Verify counter progression is correct
|
|
26
|
+
assert.strictEqual(counter1[15], 0, 'First counter should be 0');
|
|
27
|
+
assert.strictEqual(counter2[15], 1, 'Second counter should be 1');
|
|
28
|
+
assert.strictEqual(counter3[15], 2, 'Third counter should be 2');
|
|
29
|
+
assert.strictEqual(counter4[15], 7, 'Eighth counter should be 7');
|
|
30
|
+
console.log('Counter increments by blocks correctly');
|
|
31
|
+
});
|
|
32
|
+
await test('should handle chunk sizes correctly', async () => {
|
|
33
|
+
const crypto = new GenericAesCtrStreamingCrypto();
|
|
34
|
+
// Test different chunk sizes and verify block calculations
|
|
35
|
+
const testData = new Uint8Array(100).fill(0xaa); // 7 blocks (100 bytes = ceil(100/16) = 7)
|
|
36
|
+
const blob = new Blob([testData]);
|
|
37
|
+
const { key, iv, encryptedStream } = await crypto.encryptStream(blob);
|
|
38
|
+
// Read encrypted data
|
|
39
|
+
const reader = encryptedStream.getReader();
|
|
40
|
+
let encryptedChunks = [];
|
|
41
|
+
let done = false;
|
|
42
|
+
while (!done) {
|
|
43
|
+
const result = await reader.read();
|
|
44
|
+
done = result.done;
|
|
45
|
+
if (result.value) {
|
|
46
|
+
encryptedChunks.push(result.value);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Verify we got data
|
|
50
|
+
assert(encryptedChunks.length > 0, 'Should have encrypted chunks');
|
|
51
|
+
// Decrypt to verify correctness
|
|
52
|
+
const combinedEncrypted = new Uint8Array(encryptedChunks.reduce((sum, chunk) => sum + chunk.length, 0));
|
|
53
|
+
let offset = 0;
|
|
54
|
+
for (const chunk of encryptedChunks) {
|
|
55
|
+
combinedEncrypted.set(chunk, offset);
|
|
56
|
+
offset += chunk.length;
|
|
57
|
+
}
|
|
58
|
+
const encryptedStream2 = new ReadableStream({
|
|
59
|
+
start(controller) {
|
|
60
|
+
controller.enqueue(combinedEncrypted);
|
|
61
|
+
controller.close();
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
const decryptedStream = await crypto.decryptStream(encryptedStream2, key, iv);
|
|
65
|
+
const decryptedReader = decryptedStream.getReader();
|
|
66
|
+
let decryptedChunks = [];
|
|
67
|
+
done = false;
|
|
68
|
+
while (!done) {
|
|
69
|
+
const result = await decryptedReader.read();
|
|
70
|
+
done = result.done;
|
|
71
|
+
if (result.value) {
|
|
72
|
+
decryptedChunks.push(result.value);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Combine decrypted chunks
|
|
76
|
+
const combinedDecrypted = new Uint8Array(decryptedChunks.reduce((sum, chunk) => sum + chunk.length, 0));
|
|
77
|
+
offset = 0;
|
|
78
|
+
for (const chunk of decryptedChunks) {
|
|
79
|
+
combinedDecrypted.set(chunk, offset);
|
|
80
|
+
offset += chunk.length;
|
|
81
|
+
}
|
|
82
|
+
// Verify perfect round-trip
|
|
83
|
+
assert.deepStrictEqual(combinedDecrypted, testData, 'Decrypt must produce exact original data');
|
|
84
|
+
console.log('Chunk handling with block-based counters works correctly');
|
|
85
|
+
});
|
|
86
|
+
await test('should prevent keystream reuse in different chunk sizes', async () => {
|
|
87
|
+
const crypto = new GenericAesCtrStreamingCrypto();
|
|
88
|
+
// Test with different chunk sizes that would have caused reuse with old implementation
|
|
89
|
+
const testSizes = [15, 16, 17, 32, 33, 64, 65];
|
|
90
|
+
for (const size of testSizes) {
|
|
91
|
+
const testData = new Uint8Array(size).fill(0xbb);
|
|
92
|
+
const blob = new Blob([testData]);
|
|
93
|
+
const { key, iv, encryptedStream } = await crypto.encryptStream(blob);
|
|
94
|
+
// Read encrypted data
|
|
95
|
+
const reader = encryptedStream.getReader();
|
|
96
|
+
let encryptedData = new Uint8Array(0);
|
|
97
|
+
let done = false;
|
|
98
|
+
while (!done) {
|
|
99
|
+
const result = await reader.read();
|
|
100
|
+
done = result.done;
|
|
101
|
+
if (result.value) {
|
|
102
|
+
const combined = new Uint8Array(encryptedData.length + result.value.length);
|
|
103
|
+
combined.set(encryptedData);
|
|
104
|
+
combined.set(result.value, encryptedData.length);
|
|
105
|
+
encryptedData = combined;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Decrypt and verify
|
|
109
|
+
const encryptedStream2 = new ReadableStream({
|
|
110
|
+
start(controller) {
|
|
111
|
+
controller.enqueue(encryptedData);
|
|
112
|
+
controller.close();
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
const decryptedStream = await crypto.decryptStream(encryptedStream2, key, iv);
|
|
116
|
+
const decryptedReader = decryptedStream.getReader();
|
|
117
|
+
let decryptedData = new Uint8Array(0);
|
|
118
|
+
done = false;
|
|
119
|
+
while (!done) {
|
|
120
|
+
const result = await decryptedReader.read();
|
|
121
|
+
done = result.done;
|
|
122
|
+
if (result.value) {
|
|
123
|
+
const combined = new Uint8Array(decryptedData.length + result.value.length);
|
|
124
|
+
combined.set(decryptedData);
|
|
125
|
+
combined.set(result.value, decryptedData.length);
|
|
126
|
+
decryptedData = combined;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
assert.deepStrictEqual(decryptedData, testData, `Encryption/decryption failed for ${size}-byte chunk`);
|
|
130
|
+
}
|
|
131
|
+
console.log('No keystream reuse detected across different chunk sizes');
|
|
132
|
+
});
|
|
133
|
+
await test('should throw a clear error if Web Crypto API is not available', async () => {
|
|
134
|
+
// Save and remove globalThis.crypto
|
|
135
|
+
const originalCrypto = globalThis.crypto;
|
|
136
|
+
try {
|
|
137
|
+
// @ts-expect-error
|
|
138
|
+
delete globalThis.crypto;
|
|
139
|
+
assert.throws(() => new GenericAesCtrStreamingCrypto(), /Web Crypto API|crypto( is)? not available/i, 'Should throw if Web Crypto API is missing');
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
// Restore globalThis.crypto
|
|
143
|
+
globalThis.crypto = originalCrypto;
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
//# sourceMappingURL=crypto-counter-security.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto-streaming.spec.d.ts","sourceRoot":"","sources":["../../test/crypto-streaming.spec.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import './setup.js';
|
|
2
|
+
import { test, describe } from 'node:test';
|
|
3
|
+
import assert from 'node:assert';
|
|
4
|
+
import { GenericAesCtrStreamingCrypto } from '../src/crypto/symmetric/generic-aes-ctr-streaming-crypto.js';
|
|
5
|
+
import { createTestFile, streamToUint8Array, } from './helpers/test-file-utils.js';
|
|
6
|
+
// FIXME (fforbeck) - Remove this skip when ready to run streaming crypto tests. It is failing on CI.
|
|
7
|
+
await describe.skip('Streaming Crypto - Core Functionality', async () => {
|
|
8
|
+
await test('should encrypt and decrypt small files correctly', async () => {
|
|
9
|
+
const crypto = new GenericAesCtrStreamingCrypto();
|
|
10
|
+
const testFile = createTestFile(0.01); // 10KB (ultra-small for memory safety)
|
|
11
|
+
// Encrypt
|
|
12
|
+
const { key, iv, encryptedStream } = await crypto.encryptStream(testFile);
|
|
13
|
+
assert(key instanceof Uint8Array, 'Key should be Uint8Array');
|
|
14
|
+
assert.strictEqual(key.length, 32, 'Key should be 32 bytes');
|
|
15
|
+
assert(iv instanceof Uint8Array, 'IV should be Uint8Array');
|
|
16
|
+
assert.strictEqual(iv.length, 16, 'IV should be 16 bytes');
|
|
17
|
+
// Convert stream to bytes
|
|
18
|
+
const encryptedBytes = await streamToUint8Array(encryptedStream);
|
|
19
|
+
assert.strictEqual(encryptedBytes.length, testFile.size, 'Encrypted size should match original');
|
|
20
|
+
// Decrypt
|
|
21
|
+
const encryptedForDecrypt = new ReadableStream({
|
|
22
|
+
start(controller) {
|
|
23
|
+
controller.enqueue(encryptedBytes);
|
|
24
|
+
controller.close();
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
const decryptedStream = await crypto.decryptStream(encryptedForDecrypt, key, iv);
|
|
28
|
+
const decryptedBytes = await streamToUint8Array(decryptedStream);
|
|
29
|
+
// Verify round-trip
|
|
30
|
+
const originalBytes = new Uint8Array(await testFile.arrayBuffer());
|
|
31
|
+
assert.deepStrictEqual(decryptedBytes, originalBytes, 'Decrypted should match original');
|
|
32
|
+
console.log(`✓ Successfully encrypted/decrypted ${testFile.size} bytes`);
|
|
33
|
+
});
|
|
34
|
+
await test('should handle edge cases', async () => {
|
|
35
|
+
const crypto = new GenericAesCtrStreamingCrypto();
|
|
36
|
+
// Empty file
|
|
37
|
+
const emptyFile = new Blob([]);
|
|
38
|
+
const { encryptedStream: emptyEncrypted } = await crypto.encryptStream(emptyFile);
|
|
39
|
+
const emptyBytes = await streamToUint8Array(emptyEncrypted);
|
|
40
|
+
assert.strictEqual(emptyBytes.length, 0, 'Empty file should produce empty encrypted data');
|
|
41
|
+
// Single byte
|
|
42
|
+
const singleByteFile = new Blob([new Uint8Array([42])]);
|
|
43
|
+
const { key, iv, encryptedStream } = await crypto.encryptStream(singleByteFile);
|
|
44
|
+
const encryptedBytes = await streamToUint8Array(encryptedStream);
|
|
45
|
+
assert.strictEqual(encryptedBytes.length, 1, 'Single byte should produce single encrypted byte');
|
|
46
|
+
// Decrypt single byte
|
|
47
|
+
const encryptedForDecrypt = new ReadableStream({
|
|
48
|
+
start(controller) {
|
|
49
|
+
controller.enqueue(encryptedBytes);
|
|
50
|
+
controller.close();
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
const decryptedStream = await crypto.decryptStream(encryptedForDecrypt, key, iv);
|
|
54
|
+
const decryptedBytes = await streamToUint8Array(decryptedStream);
|
|
55
|
+
assert.deepStrictEqual(decryptedBytes, new Uint8Array([42]), 'Should decrypt to original byte');
|
|
56
|
+
console.log('✓ Edge cases handled correctly');
|
|
57
|
+
});
|
|
58
|
+
await test('should handle medium-sized files without issues', async () => {
|
|
59
|
+
try {
|
|
60
|
+
const crypto = new GenericAesCtrStreamingCrypto();
|
|
61
|
+
// Test with progressively larger files to show streaming works consistently
|
|
62
|
+
// Reduced sizes for CI stability while still testing streaming functionality
|
|
63
|
+
const testSizes = [0.5, 1, 2]; // MB
|
|
64
|
+
for (const sizeMB of testSizes) {
|
|
65
|
+
console.log(`Testing ${sizeMB}MB file...`);
|
|
66
|
+
const testFile = createTestFile(sizeMB);
|
|
67
|
+
const { key, iv, encryptedStream } = await crypto.encryptStream(testFile);
|
|
68
|
+
// Convert encrypted stream to bytes first
|
|
69
|
+
const encryptedBytes = await streamToUint8Array(encryptedStream);
|
|
70
|
+
// Create a new stream from the encrypted bytes for decryption
|
|
71
|
+
const encryptedForDecrypt = new ReadableStream({
|
|
72
|
+
start(controller) {
|
|
73
|
+
controller.enqueue(encryptedBytes);
|
|
74
|
+
controller.close();
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
const decryptedStream = await crypto.decryptStream(encryptedForDecrypt, key, iv);
|
|
78
|
+
const decryptedBytes = await streamToUint8Array(decryptedStream);
|
|
79
|
+
assert.strictEqual(decryptedBytes.length, sizeMB * 1024 * 1024, `Decrypted file size mismatch for ${sizeMB}MB`);
|
|
80
|
+
console.log(`✓ ${sizeMB}MB file encrypted and decrypted successfully`);
|
|
81
|
+
}
|
|
82
|
+
console.log('✓ Medium-sized files handled without issues');
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
console.error('Test error (type):', typeof err, err && err.constructor && err.constructor.name);
|
|
86
|
+
console.error('Test error (keys):', err && Object.keys(err));
|
|
87
|
+
console.error('Test error (string):', String(err));
|
|
88
|
+
console.log(err);
|
|
89
|
+
assert.fail('Unable to test medium-sized files');
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
await test('should use proper counter arithmetic', async () => {
|
|
93
|
+
const crypto = new GenericAesCtrStreamingCrypto();
|
|
94
|
+
// Test counter increment function directly
|
|
95
|
+
const baseCounter = new Uint8Array([
|
|
96
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
97
|
+
]);
|
|
98
|
+
// Increment by 1
|
|
99
|
+
const counter1 = crypto.incrementCounter(baseCounter, 1);
|
|
100
|
+
assert.deepStrictEqual(counter1, new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]), 'Should increment last byte by 1');
|
|
101
|
+
// Increment by 255
|
|
102
|
+
const counter255 = crypto.incrementCounter(baseCounter, 255);
|
|
103
|
+
assert.deepStrictEqual(counter255, new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255]), 'Should increment last byte by 255');
|
|
104
|
+
// Increment by 256 (should carry)
|
|
105
|
+
const counter256 = crypto.incrementCounter(baseCounter, 256);
|
|
106
|
+
assert.deepStrictEqual(counter256, new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]), 'Should carry to second-to-last byte');
|
|
107
|
+
console.log('✓ Counter arithmetic works correctly');
|
|
108
|
+
});
|
|
109
|
+
await test('should implement complete interface', async () => {
|
|
110
|
+
const crypto = new GenericAesCtrStreamingCrypto();
|
|
111
|
+
// Check all required methods exist
|
|
112
|
+
assert(typeof crypto.generateKey === 'function', 'Should have generateKey method');
|
|
113
|
+
assert(typeof crypto.encryptStream === 'function', 'Should have encryptStream method');
|
|
114
|
+
assert(typeof crypto.decryptStream === 'function', 'Should have decryptStream method');
|
|
115
|
+
assert(typeof crypto.combineKeyAndIV === 'function', 'Should have combineKeyAndIV method');
|
|
116
|
+
assert(typeof crypto.splitKeyAndIV === 'function', 'Should have splitKeyAndIV method');
|
|
117
|
+
assert(typeof crypto.incrementCounter === 'function', 'Should have incrementCounter method');
|
|
118
|
+
// Test combine/split methods
|
|
119
|
+
const key = await crypto.generateKey();
|
|
120
|
+
const iv = globalThis.crypto.getRandomValues(new Uint8Array(16));
|
|
121
|
+
const combined = crypto.combineKeyAndIV(key, iv);
|
|
122
|
+
assert.strictEqual(combined.length, 48, 'Combined should be 48 bytes (32 key + 16 IV)');
|
|
123
|
+
const { key: splitKey, iv: splitIV } = crypto.splitKeyAndIV(combined);
|
|
124
|
+
assert.deepStrictEqual(splitKey, key, 'Split key should match original');
|
|
125
|
+
assert.deepStrictEqual(splitIV, iv, 'Split IV should match original');
|
|
126
|
+
console.log('Complete interface implemented correctly');
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
//# sourceMappingURL=crypto-streaming.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encrypted-metadata.spec.d.ts","sourceRoot":"","sources":["../../test/encrypted-metadata.spec.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
import * as Result from '@storacha/client/result';
|
|
4
|
+
import * as Types from '../src/types.js';
|
|
5
|
+
import { create, extract } from '../src/core/metadata/encrypted-metadata.js';
|
|
6
|
+
import { CID } from 'multiformats';
|
|
7
|
+
/** @type {Types.LitMetadataInput} */
|
|
8
|
+
const encryptedMetadataInput = {
|
|
9
|
+
encryptedDataCID: 'bafkreids275u5ex6xfw7d4k67afej43c6rhm2kzdox2z6or4jxrndgevae',
|
|
10
|
+
identityBoundCiphertext: 'mF3OPa9dQ0wO4B1/XylmAV/eaHhLtM3JUPIbS175bvmqGaJUYroyDbsytV29q0cLD4XCpCRfCinntASNg9s730FIM7f4Mw2hVWeJ5g4akFA6BoZoaKgDC5Ln6MOQK5Ymb1y6No7um7Bn4uIIJTYNuUukDQvVxzY8LcRBc2ySR1Md+VSGzmyEgyvHtAI=',
|
|
11
|
+
plaintextKeyHash: '15f39e9a977cca43f16f3cd25237d711cd8130ff9763197b29df52a198607206',
|
|
12
|
+
accessControlConditions: [
|
|
13
|
+
{
|
|
14
|
+
contractAddress: '',
|
|
15
|
+
standardContractType: '',
|
|
16
|
+
chain: 'ethereum',
|
|
17
|
+
method: '',
|
|
18
|
+
parameters: [
|
|
19
|
+
':currentActionIpfsId',
|
|
20
|
+
'did:key:z6MktfnQz8Kcz5nsC65oyXWFXhbbAZQavjg6LYuHgv4YbxzN',
|
|
21
|
+
],
|
|
22
|
+
returnValueTest: {
|
|
23
|
+
comparator: '=',
|
|
24
|
+
value: 'QmPFrQGo5RAtdSTZ4bkaeDHVGrmy2TeEUwTu4LuVAPHiMd',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
/** @type {Types.KMSMetadata} */
|
|
30
|
+
const kmsMetadataInput = {
|
|
31
|
+
encryptedDataCID: CID.parse('bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku'),
|
|
32
|
+
encryptedSymmetricKey: 'bXlLTVNLZXlCeXRlcw==', // 'myKMSKeyBytes' in base64 for example
|
|
33
|
+
space: 'did:key:z6MktfnQz8Kcz5nsC65oyXWFXhbbAZQavjg6LYuHgv4YbxzN',
|
|
34
|
+
kms: {
|
|
35
|
+
provider: 'google-kms',
|
|
36
|
+
keyId: 'test-key',
|
|
37
|
+
algorithm: 'RSA-OAEP-2048-SHA256',
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
await describe('LitEncrypted Metadata', async () => {
|
|
41
|
+
await it('should create a valid block content', async () => {
|
|
42
|
+
const encryptedMetadata = create('lit', encryptedMetadataInput);
|
|
43
|
+
const block = await encryptedMetadata.archiveBlock();
|
|
44
|
+
// Encode the block into a CAR file
|
|
45
|
+
const car = await import('@ucanto/core').then((m) => m.CAR.encode({ roots: [block] }));
|
|
46
|
+
// Use the extract function to get the JSON object
|
|
47
|
+
const extractedData = Result.unwrap(extract(car));
|
|
48
|
+
const extractedDataJson = extractedData.toJSON();
|
|
49
|
+
assert.equal(extractedDataJson.identityBoundCiphertext, encryptedMetadataInput.identityBoundCiphertext);
|
|
50
|
+
assert.equal(extractedDataJson.plaintextKeyHash, encryptedMetadataInput.plaintextKeyHash);
|
|
51
|
+
assert.equal(extractedDataJson.encryptedDataCID, encryptedMetadataInput.encryptedDataCID);
|
|
52
|
+
assert.deepEqual(extractedDataJson.accessControlConditions, encryptedMetadataInput.accessControlConditions);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
await describe('KMS Encrypted Metadata', async () => {
|
|
56
|
+
await it('should create a valid block content', async () => {
|
|
57
|
+
const kmsMetadata = create('kms', kmsMetadataInput);
|
|
58
|
+
const block = await kmsMetadata.archiveBlock();
|
|
59
|
+
const car = await import('@ucanto/core').then((m) => m.CAR.encode({ roots: [block] }));
|
|
60
|
+
const extractedData = Result.unwrap(extract(car));
|
|
61
|
+
const extractedDataJson = extractedData.toJSON();
|
|
62
|
+
assert.equal(extractedDataJson.encryptedSymmetricKey, kmsMetadataInput.encryptedSymmetricKey);
|
|
63
|
+
assert.equal(extractedDataJson.encryptedDataCID, kmsMetadataInput.encryptedDataCID);
|
|
64
|
+
assert.equal(extractedDataJson.space, kmsMetadataInput.space);
|
|
65
|
+
assert.deepEqual(extractedDataJson.kms, kmsMetadataInput.kms);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=encrypted-metadata.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factories.spec.d.ts","sourceRoot":"","sources":["../../test/factories.spec.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import './setup.js';
|
|
2
|
+
import { test, describe } from 'node:test';
|
|
3
|
+
import assert from 'node:assert';
|
|
4
|
+
import { createGenericKMSAdapter, createGenericLitAdapter, } from '../src/crypto/factories.node.js';
|
|
5
|
+
import { GenericAesCtrStreamingCrypto } from '../src/crypto/symmetric/generic-aes-ctr-streaming-crypto.js';
|
|
6
|
+
import { LitCryptoAdapter } from '../src/crypto/adapters/lit-crypto-adapter.js';
|
|
7
|
+
import { KMSCryptoAdapter } from '../src/crypto/adapters/kms-crypto-adapter.js';
|
|
8
|
+
// Mock Lit client for testing
|
|
9
|
+
const mockLitClient = /** @type {any} */ ({
|
|
10
|
+
connect: () => Promise.resolve(),
|
|
11
|
+
disconnect: () => Promise.resolve(),
|
|
12
|
+
});
|
|
13
|
+
const mockAuthManager = /** @type {any} */ ({
|
|
14
|
+
// Add mock methods as needed
|
|
15
|
+
});
|
|
16
|
+
await describe('Crypto Factory Functions', async () => {
|
|
17
|
+
await describe('createBrowserLitAdapter', async () => {
|
|
18
|
+
await test('should create LitCryptoAdapter with streaming crypto', async () => {
|
|
19
|
+
const adapter = createGenericLitAdapter(mockLitClient, mockAuthManager);
|
|
20
|
+
// Verify adapter type
|
|
21
|
+
assert(adapter instanceof LitCryptoAdapter, 'Should create LitCryptoAdapter instance');
|
|
22
|
+
// Verify symmetric crypto implementation
|
|
23
|
+
assert(adapter.symmetricCrypto instanceof GenericAesCtrStreamingCrypto, 'Should use GenericAesCtrStreamingCrypto for browser environment');
|
|
24
|
+
// Verify lit client is passed through
|
|
25
|
+
assert.strictEqual(adapter.litClient, mockLitClient, 'Should pass through the lit client');
|
|
26
|
+
});
|
|
27
|
+
await test('should create adapter with required interface methods', async () => {
|
|
28
|
+
const adapter = createGenericLitAdapter(mockLitClient, mockAuthManager);
|
|
29
|
+
// Verify adapter has all required methods
|
|
30
|
+
assert(typeof adapter.encryptStream === 'function', 'Should have encryptStream method');
|
|
31
|
+
assert(typeof adapter.decryptStream === 'function', 'Should have decryptStream method');
|
|
32
|
+
assert(typeof adapter.encryptSymmetricKey === 'function', 'Should have encryptSymmetricKey method');
|
|
33
|
+
assert(typeof adapter.decryptSymmetricKey === 'function', 'Should have decryptSymmetricKey method');
|
|
34
|
+
assert(typeof adapter.extractEncryptedMetadata === 'function', 'Should have extractEncryptedMetadata method');
|
|
35
|
+
assert(typeof adapter.getEncryptedKey === 'function', 'Should have getEncryptedKey method');
|
|
36
|
+
});
|
|
37
|
+
await test('should handle null or undefined lit client gracefully', async () => {
|
|
38
|
+
// This should still create the adapter (validation happens at runtime)
|
|
39
|
+
const adapter = createGenericLitAdapter(
|
|
40
|
+
/** @type {any} */ (null), mockAuthManager);
|
|
41
|
+
assert(adapter instanceof LitCryptoAdapter, 'Should create adapter even with null client');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
await describe('createBrowserKMSAdapter', async () => {
|
|
45
|
+
await test('should create KMSCryptoAdapter with streaming crypto', async () => {
|
|
46
|
+
const keyManagerServiceURL = 'https://gateway.example.com';
|
|
47
|
+
const keyManagerServiceDID = 'did:web:gateway.example.com';
|
|
48
|
+
const adapter = createGenericKMSAdapter(keyManagerServiceURL, keyManagerServiceDID);
|
|
49
|
+
// Verify adapter type
|
|
50
|
+
assert(adapter instanceof KMSCryptoAdapter, 'Should create KMSCryptoAdapter instance');
|
|
51
|
+
// Verify symmetric crypto implementation
|
|
52
|
+
assert(adapter.symmetricCrypto instanceof GenericAesCtrStreamingCrypto, 'Should use GenericAesCtrStreamingCrypto for browser environment');
|
|
53
|
+
// Verify configuration is passed through
|
|
54
|
+
assert.strictEqual(adapter.keyManagerServiceURL.toString(), keyManagerServiceURL + '/', 'Should set the key manager service URL');
|
|
55
|
+
assert.strictEqual(adapter.keyManagerServiceDID.did(), keyManagerServiceDID, 'Should set the key manager service DID');
|
|
56
|
+
});
|
|
57
|
+
await test('should accept URL object for gateway URL', async () => {
|
|
58
|
+
const keyManagerServiceURL = new URL('https://gateway.example.com');
|
|
59
|
+
const keyManagerServiceDID = 'did:web:gateway.example.com';
|
|
60
|
+
const adapter = createGenericKMSAdapter(keyManagerServiceURL, keyManagerServiceDID);
|
|
61
|
+
assert(adapter instanceof KMSCryptoAdapter, 'Should create KMSCryptoAdapter with URL object');
|
|
62
|
+
assert.strictEqual(adapter.keyManagerServiceURL.toString(), keyManagerServiceURL.toString(), 'Should handle URL object input');
|
|
63
|
+
});
|
|
64
|
+
await test('should enforce HTTPS for security', async () => {
|
|
65
|
+
const httpKeyManagerServiceURL = 'http://insecure.example.com';
|
|
66
|
+
const keyManagerServiceDID = 'did:web:example.com';
|
|
67
|
+
assert.throws(() => createGenericKMSAdapter(httpKeyManagerServiceURL, keyManagerServiceDID), /Key manager service must use HTTPS protocol for security/, 'Should reject HTTP URLs for security');
|
|
68
|
+
});
|
|
69
|
+
await test('should allow HTTP with explicit insecure option', async () => {
|
|
70
|
+
// Note: The current implementation doesn't expose options in the factory
|
|
71
|
+
// but we can test this through direct adapter construction
|
|
72
|
+
const httpKeyManagerServiceURL = 'http://localhost:3000';
|
|
73
|
+
const keyManagerServiceDID = 'did:web:localhost';
|
|
74
|
+
assert.throws(() => createGenericKMSAdapter(httpKeyManagerServiceURL, keyManagerServiceDID), /Key manager service must use HTTPS protocol for security/, 'Should reject HTTP URLs even for localhost by default');
|
|
75
|
+
});
|
|
76
|
+
await test('should have all required KMS adapter methods', async () => {
|
|
77
|
+
const adapter = createGenericKMSAdapter('https://gateway.example.com', 'did:web:gateway.example.com');
|
|
78
|
+
// Verify adapter has all required methods
|
|
79
|
+
assert(typeof adapter.encryptStream === 'function', 'Should have encryptStream method');
|
|
80
|
+
assert(typeof adapter.decryptStream === 'function', 'Should have decryptStream method');
|
|
81
|
+
assert(typeof adapter.encryptSymmetricKey === 'function', 'Should have encryptSymmetricKey method');
|
|
82
|
+
assert(typeof adapter.decryptSymmetricKey === 'function', 'Should have decryptSymmetricKey method');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
await describe('Factory Function Consistency', async () => {
|
|
86
|
+
await test('browser factories should use streaming crypto', async () => {
|
|
87
|
+
const litAdapter = createGenericLitAdapter(mockLitClient, mockAuthManager);
|
|
88
|
+
const kmsAdapter = createGenericKMSAdapter('https://gateway.example.com', 'did:web:gateway.example.com');
|
|
89
|
+
assert(litAdapter.symmetricCrypto.constructor.name ===
|
|
90
|
+
'GenericAesCtrStreamingCrypto', 'Browser Lit adapter should use streaming crypto');
|
|
91
|
+
assert(kmsAdapter.symmetricCrypto.constructor.name ===
|
|
92
|
+
'GenericAesCtrStreamingCrypto', 'Browser KMS adapter should use streaming crypto');
|
|
93
|
+
});
|
|
94
|
+
await test('all adapters should implement the same interface', async () => {
|
|
95
|
+
const adapters = [
|
|
96
|
+
createGenericLitAdapter(mockLitClient, mockAuthManager),
|
|
97
|
+
createGenericKMSAdapter('https://gateway.example.com', 'did:web:gateway.example.com'),
|
|
98
|
+
createGenericKMSAdapter('https://gateway.example.com', 'did:web:gateway.example.com'),
|
|
99
|
+
];
|
|
100
|
+
const requiredMethods = [
|
|
101
|
+
'encryptStream',
|
|
102
|
+
'decryptStream',
|
|
103
|
+
'encryptSymmetricKey',
|
|
104
|
+
'decryptSymmetricKey',
|
|
105
|
+
'extractEncryptedMetadata',
|
|
106
|
+
'getEncryptedKey',
|
|
107
|
+
];
|
|
108
|
+
for (const adapter of adapters) {
|
|
109
|
+
for (const method of requiredMethods) {
|
|
110
|
+
assert(typeof ( /** @type {any} */(adapter)[method]) === 'function', `${adapter.constructor.name} should have ${method} method`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
await describe('Memory Usage Verification', async () => {
|
|
116
|
+
await test('browser adapters should use memory-efficient streaming crypto', async () => {
|
|
117
|
+
const litAdapter = createGenericLitAdapter(mockLitClient, mockAuthManager);
|
|
118
|
+
const kmsAdapter = createGenericKMSAdapter('https://gateway.example.com', 'did:web:gateway.example.com');
|
|
119
|
+
// Verify both use the streaming implementation
|
|
120
|
+
assert(litAdapter.symmetricCrypto instanceof GenericAesCtrStreamingCrypto, 'Lit adapter should use streaming crypto for memory efficiency');
|
|
121
|
+
assert(kmsAdapter.symmetricCrypto instanceof GenericAesCtrStreamingCrypto, 'KMS adapter should use streaming crypto for memory efficiency');
|
|
122
|
+
// Verify they have the streaming characteristics
|
|
123
|
+
const testBlob = new Blob([new Uint8Array(1024)]); // 1KB test
|
|
124
|
+
const litResult = await litAdapter.encryptStream(testBlob);
|
|
125
|
+
assert(litResult.encryptedStream instanceof ReadableStream, 'Should return ReadableStream for streaming');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
//# sourceMappingURL=factories.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-metadata.spec.d.ts","sourceRoot":"","sources":["../../test/file-metadata.spec.js"],"names":[],"mappings":""}
|