@storacha/encrypt-upload-client 1.1.59 → 1.1.61
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/package.json +4 -4
- package/dist/examples/decrypt-test.d.ts +0 -2
- package/dist/examples/decrypt-test.d.ts.map +0 -1
- package/dist/examples/decrypt-test.js +0 -73
- package/dist/examples/encrypt-test.d.ts +0 -4
- package/dist/examples/encrypt-test.d.ts.map +0 -1
- package/dist/examples/encrypt-test.js +0 -61
- package/dist/test/cid-verification.spec.d.ts +0 -2
- package/dist/test/cid-verification.spec.d.ts.map +0 -1
- package/dist/test/cid-verification.spec.js +0 -314
- package/dist/test/crypto-compatibility.spec.d.ts +0 -2
- package/dist/test/crypto-compatibility.spec.d.ts.map +0 -1
- package/dist/test/crypto-compatibility.spec.js +0 -124
- package/dist/test/crypto-counter-security.spec.d.ts +0 -2
- package/dist/test/crypto-counter-security.spec.d.ts.map +0 -1
- package/dist/test/crypto-counter-security.spec.js +0 -147
- package/dist/test/crypto-streaming.spec.d.ts +0 -2
- package/dist/test/crypto-streaming.spec.d.ts.map +0 -1
- package/dist/test/crypto-streaming.spec.js +0 -129
- package/dist/test/encrypted-metadata.spec.d.ts +0 -2
- package/dist/test/encrypted-metadata.spec.d.ts.map +0 -1
- package/dist/test/encrypted-metadata.spec.js +0 -68
- package/dist/test/factories.spec.d.ts +0 -2
- package/dist/test/factories.spec.d.ts.map +0 -1
- package/dist/test/factories.spec.js +0 -142
- package/dist/test/file-metadata.spec.d.ts +0 -2
- package/dist/test/file-metadata.spec.d.ts.map +0 -1
- package/dist/test/file-metadata.spec.js +0 -433
- package/dist/test/fixtures/test-fixtures.d.ts +0 -28
- package/dist/test/fixtures/test-fixtures.d.ts.map +0 -1
- package/dist/test/fixtures/test-fixtures.js +0 -63
- package/dist/test/helpers/test-file-utils.d.ts +0 -60
- package/dist/test/helpers/test-file-utils.d.ts.map +0 -1
- package/dist/test/helpers/test-file-utils.js +0 -139
- package/dist/test/https-enforcement.spec.d.ts +0 -2
- package/dist/test/https-enforcement.spec.d.ts.map +0 -1
- package/dist/test/https-enforcement.spec.js +0 -125
- package/dist/test/kms-crypto-adapter.spec.d.ts +0 -2
- package/dist/test/kms-crypto-adapter.spec.d.ts.map +0 -1
- package/dist/test/kms-crypto-adapter.spec.js +0 -305
- package/dist/test/lit-crypto-adapter.spec.d.ts +0 -2
- package/dist/test/lit-crypto-adapter.spec.d.ts.map +0 -1
- package/dist/test/lit-crypto-adapter.spec.js +0 -120
- package/dist/test/memory-efficiency.spec.d.ts +0 -2
- package/dist/test/memory-efficiency.spec.d.ts.map +0 -1
- package/dist/test/memory-efficiency.spec.js +0 -93
- package/dist/test/mocks/key-manager.d.ts +0 -58
- package/dist/test/mocks/key-manager.d.ts.map +0 -1
- package/dist/test/mocks/key-manager.js +0 -137
- package/dist/test/node-crypto-adapter.spec.d.ts +0 -2
- package/dist/test/node-crypto-adapter.spec.d.ts.map +0 -1
- package/dist/test/node-crypto-adapter.spec.js +0 -103
- package/dist/test/node-generic-crypto-adapter.spec.d.ts +0 -2
- package/dist/test/node-generic-crypto-adapter.spec.d.ts.map +0 -1
- package/dist/test/node-generic-crypto-adapter.spec.js +0 -95
- package/dist/test/setup.d.ts +0 -2
- package/dist/test/setup.d.ts.map +0 -1
- package/dist/test/setup.js +0 -12
- package/dist/tsconfig.spec.tsbuildinfo +0 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storacha/encrypt-upload-client",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.61",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
6
|
"description": "Client for upload and download encrypted files",
|
|
7
7
|
"author": "Storacha",
|
|
@@ -80,9 +80,9 @@
|
|
|
80
80
|
"ethers": "5.7.1",
|
|
81
81
|
"ipfs-unixfs-exporter": "^10.0.0",
|
|
82
82
|
"multiformats": "^13.3.6",
|
|
83
|
-
"@storacha/client": "^1.8.
|
|
84
|
-
"@storacha/
|
|
85
|
-
"@storacha/
|
|
83
|
+
"@storacha/client": "^1.8.18",
|
|
84
|
+
"@storacha/upload-client": "^1.3.6",
|
|
85
|
+
"@storacha/capabilities": "^1.12.0"
|
|
86
86
|
},
|
|
87
87
|
"devDependencies": {
|
|
88
88
|
"@lit-protocol/types": "^7.0.8",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"decrypt-test.d.ts","sourceRoot":"","sources":["../../examples/decrypt-test.js"],"names":[],"mappings":""}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import dotenv from 'dotenv';
|
|
3
|
-
import { CID } from 'multiformats';
|
|
4
|
-
import * as Client from '@storacha/client';
|
|
5
|
-
import * as Signer from '@ucanto/principal/ed25519';
|
|
6
|
-
import { StoreMemory } from '@storacha/client/stores/memory';
|
|
7
|
-
import { create } from '../src/index.js';
|
|
8
|
-
import { Wallet } from 'ethers';
|
|
9
|
-
import { serviceConf, receiptsEndpoint } from '../src/config/service.js';
|
|
10
|
-
import { createNodeLitAdapter } from '../src/crypto/factories.node.js';
|
|
11
|
-
import { LitNodeClient } from '@lit-protocol/lit-node-client';
|
|
12
|
-
import { extract } from '@ucanto/core/delegation';
|
|
13
|
-
dotenv.config();
|
|
14
|
-
async function main() {
|
|
15
|
-
// set up storacha client with a new agent
|
|
16
|
-
const cid = CID.parse('bafyreifhwqmspdjsy6rgcmcizgodv7bwskgiehjhdx7wukax3z5r7tz5ji');
|
|
17
|
-
const delegationCarBuffer = fs.readFileSync('delegation.car');
|
|
18
|
-
const wallet = new Wallet(process.env.WALLET_PK || '');
|
|
19
|
-
const principal = Signer.parse(process.env.DELEGATEE_AGENT_PK || '');
|
|
20
|
-
const store = new StoreMemory();
|
|
21
|
-
const client = await Client.create({
|
|
22
|
-
principal,
|
|
23
|
-
store,
|
|
24
|
-
serviceConf,
|
|
25
|
-
receiptsEndpoint,
|
|
26
|
-
});
|
|
27
|
-
// Set up Lit client
|
|
28
|
-
const litClient = new LitNodeClient({
|
|
29
|
-
litNetwork: 'datil-dev',
|
|
30
|
-
});
|
|
31
|
-
await litClient.connect();
|
|
32
|
-
const encryptedClient = await create({
|
|
33
|
-
storachaClient: client,
|
|
34
|
-
cryptoAdapter: createNodeLitAdapter(litClient),
|
|
35
|
-
});
|
|
36
|
-
const res = await extract(delegationCarBuffer);
|
|
37
|
-
if (res.error) {
|
|
38
|
-
throw new Error(`Failed to extract delegation: ${res.error.message}`);
|
|
39
|
-
}
|
|
40
|
-
const decryptDelegation = res.ok;
|
|
41
|
-
const decryptionCapability = decryptDelegation.capabilities.find((c) => c.can === 'space/content/decrypt');
|
|
42
|
-
if (!decryptionCapability) {
|
|
43
|
-
throw new Error('Failed to find decryption capability');
|
|
44
|
-
}
|
|
45
|
-
const spaceDID = /** @type {`did:key:${string}`} */ (decryptionCapability.with);
|
|
46
|
-
const decryptionConfig = {
|
|
47
|
-
wallet,
|
|
48
|
-
decryptDelegation,
|
|
49
|
-
spaceDID,
|
|
50
|
-
};
|
|
51
|
-
const decryptedContent = await encryptedClient.retrieveAndDecryptFile(cid, decryptionConfig);
|
|
52
|
-
const reader = decryptedContent.stream.getReader();
|
|
53
|
-
const decoder = new TextDecoder();
|
|
54
|
-
let result = '';
|
|
55
|
-
let done = false;
|
|
56
|
-
while (!done) {
|
|
57
|
-
const { value, done: isDone } = await reader.read();
|
|
58
|
-
done = isDone;
|
|
59
|
-
if (value) {
|
|
60
|
-
result += decoder.decode(value, { stream: true });
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
console.log('================ RESULT ===================');
|
|
64
|
-
console.log(result);
|
|
65
|
-
console.log('===========================================');
|
|
66
|
-
}
|
|
67
|
-
main()
|
|
68
|
-
.then(() => process.exit(0))
|
|
69
|
-
.catch((err) => {
|
|
70
|
-
console.error(err);
|
|
71
|
-
process.exit(1);
|
|
72
|
-
});
|
|
73
|
-
//# sourceMappingURL=decrypt-test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"encrypt-test.d.ts","sourceRoot":"","sources":["../../examples/encrypt-test.js"],"names":[],"mappings":"AAeA,mDAAmD;AACnD,iCADY,MAAM,0DAQjB;wBAlBuB,2BAA2B"}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import dotenv from 'dotenv';
|
|
3
|
-
import { CarReader } from '@ipld/car';
|
|
4
|
-
import * as Client from '@storacha/client';
|
|
5
|
-
import { importDAG } from '@ucanto/core/delegation';
|
|
6
|
-
import * as Signer from '@ucanto/principal/ed25519';
|
|
7
|
-
import { StoreMemory } from '@storacha/client/stores/memory';
|
|
8
|
-
import * as EncryptClient from '../src/index.js';
|
|
9
|
-
import { serviceConf, receiptsEndpoint } from '../src/config/service.js';
|
|
10
|
-
import { createNodeLitAdapter } from '../src/crypto/factories.node.js';
|
|
11
|
-
import { LitNodeClient } from '@lit-protocol/lit-node-client';
|
|
12
|
-
dotenv.config();
|
|
13
|
-
/** @param {string} data Base64 encoded CAR file */
|
|
14
|
-
export async function parseProof(data) {
|
|
15
|
-
const blocks = [];
|
|
16
|
-
const reader = await CarReader.fromBytes(Buffer.from(data, 'base64'));
|
|
17
|
-
for await (const block of reader.blocks()) {
|
|
18
|
-
blocks.push(block);
|
|
19
|
-
}
|
|
20
|
-
return importDAG(blocks);
|
|
21
|
-
}
|
|
22
|
-
async function main() {
|
|
23
|
-
// set up storacha client with a new agent
|
|
24
|
-
const principal = Signer.parse(process.env.AGENT_PK || '');
|
|
25
|
-
const store = new StoreMemory();
|
|
26
|
-
const client = await Client.create({
|
|
27
|
-
principal,
|
|
28
|
-
store,
|
|
29
|
-
serviceConf,
|
|
30
|
-
receiptsEndpoint,
|
|
31
|
-
});
|
|
32
|
-
// now give Agent the delegation from the Space
|
|
33
|
-
const proof = await parseProof(process.env.PROOF || '');
|
|
34
|
-
const space = await client.addSpace(proof);
|
|
35
|
-
await client.setCurrentSpace(space.did());
|
|
36
|
-
// Set up Lit client
|
|
37
|
-
const litClient = new LitNodeClient({
|
|
38
|
-
litNetwork: 'datil-dev',
|
|
39
|
-
});
|
|
40
|
-
await litClient.connect();
|
|
41
|
-
const encryptedClient = await EncryptClient.create({
|
|
42
|
-
storachaClient: client,
|
|
43
|
-
cryptoAdapter: createNodeLitAdapter(litClient),
|
|
44
|
-
});
|
|
45
|
-
const fileContent = await fs.promises.readFile('./README.md');
|
|
46
|
-
const blob = new Blob([fileContent]);
|
|
47
|
-
// Create encryption config
|
|
48
|
-
const encryptionConfig = {
|
|
49
|
-
issuer: principal,
|
|
50
|
-
spaceDID: space.did(),
|
|
51
|
-
};
|
|
52
|
-
const link = await encryptedClient.encryptAndUploadFile(blob, encryptionConfig);
|
|
53
|
-
console.log(link);
|
|
54
|
-
}
|
|
55
|
-
main()
|
|
56
|
-
.then(() => process.exit(0))
|
|
57
|
-
.catch((err) => {
|
|
58
|
-
console.error(err);
|
|
59
|
-
process.exit(1);
|
|
60
|
-
});
|
|
61
|
-
//# sourceMappingURL=encrypt-test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cid-verification.spec.d.ts","sourceRoot":"","sources":["../../test/cid-verification.spec.js"],"names":[],"mappings":""}
|
|
@@ -1,314 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert';
|
|
2
|
-
import { describe, test } from 'node:test';
|
|
3
|
-
import { getCarFileFromPublicGateway } from '../src/handlers/decrypt-handler.js';
|
|
4
|
-
import { createTestCar } from './helpers/test-file-utils.js';
|
|
5
|
-
await describe('CID Verification', async () => {
|
|
6
|
-
await describe('getCarFileFromPublicGateway', async () => {
|
|
7
|
-
await test('should construct correct gateway URL format', async () => {
|
|
8
|
-
// This is a basic test to verify the function exists and can be called
|
|
9
|
-
// Integration tests with real network calls would test full functionality
|
|
10
|
-
const gatewayURL = new URL('https://example.com');
|
|
11
|
-
const testCID = 'bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7z3aldwdht2oqadq';
|
|
12
|
-
// Mock fetch to avoid network calls in unit tests
|
|
13
|
-
const originalFetch = globalThis.fetch;
|
|
14
|
-
let capturedURL = '';
|
|
15
|
-
// @ts-ignore - Mock fetch for testing
|
|
16
|
-
globalThis.fetch = async (url) => {
|
|
17
|
-
capturedURL = url.toString();
|
|
18
|
-
// Return a mock response that looks like a failed fetch
|
|
19
|
-
return {
|
|
20
|
-
ok: false,
|
|
21
|
-
status: 404,
|
|
22
|
-
statusText: 'Not Found',
|
|
23
|
-
arrayBuffer: async () => {
|
|
24
|
-
throw new Error('Mock 404');
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
try {
|
|
29
|
-
await getCarFileFromPublicGateway(gatewayURL, testCID);
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
// We expect this to fail with our mock, that's fine
|
|
33
|
-
}
|
|
34
|
-
finally {
|
|
35
|
-
globalThis.fetch = originalFetch;
|
|
36
|
-
}
|
|
37
|
-
// Verify the URL was constructed correctly
|
|
38
|
-
const expectedURL = `https://example.com/ipfs/${testCID}?format=car`;
|
|
39
|
-
assert.strictEqual(capturedURL, expectedURL, 'Should construct correct gateway URL');
|
|
40
|
-
});
|
|
41
|
-
await test('should be exported and callable', async () => {
|
|
42
|
-
// Basic smoke test
|
|
43
|
-
assert.strictEqual(typeof getCarFileFromPublicGateway, 'function', 'Should be a function');
|
|
44
|
-
// Verify it requires proper parameters
|
|
45
|
-
try {
|
|
46
|
-
// @ts-ignore - intentionally testing invalid input
|
|
47
|
-
await getCarFileFromPublicGateway(null, 'test');
|
|
48
|
-
assert.fail('Should throw error for null gateway URL');
|
|
49
|
-
}
|
|
50
|
-
catch (error) {
|
|
51
|
-
// Expected to fail - good
|
|
52
|
-
assert(error instanceof Error, 'Should throw an Error');
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
await test('should validate CID format', async () => {
|
|
56
|
-
const gatewayURL = new URL('https://example.com');
|
|
57
|
-
// Mock fetch to return valid CAR response
|
|
58
|
-
const originalFetch = globalThis.fetch;
|
|
59
|
-
// @ts-ignore - Mock fetch for testing
|
|
60
|
-
globalThis.fetch = async (url) => ({
|
|
61
|
-
ok: true,
|
|
62
|
-
status: 200,
|
|
63
|
-
statusText: 'OK',
|
|
64
|
-
arrayBuffer: async () => new ArrayBuffer(100), // Mock CAR data
|
|
65
|
-
});
|
|
66
|
-
try {
|
|
67
|
-
await getCarFileFromPublicGateway(gatewayURL, 'invalid-cid');
|
|
68
|
-
assert.fail('Should throw error for invalid CID');
|
|
69
|
-
}
|
|
70
|
-
catch (error) {
|
|
71
|
-
// Expected to fail due to invalid CID format
|
|
72
|
-
assert(error instanceof Error, 'Should throw an Error for invalid CID');
|
|
73
|
-
}
|
|
74
|
-
finally {
|
|
75
|
-
globalThis.fetch = originalFetch;
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
await test('should accept valid CAR file with matching root CID', async () => {
|
|
79
|
-
const gatewayURL = new URL('https://example.com');
|
|
80
|
-
// Create a valid KMS metadata CAR file
|
|
81
|
-
const testContent = {
|
|
82
|
-
encryptedDataCID: 'bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7z3aldwdht2oqadq',
|
|
83
|
-
encryptedSymmetricKey: 'test-encrypted-key',
|
|
84
|
-
space: 'did:key:z6MkwDK3M4PxU1FqcSt6quBH1xRBSGnPRdQYP9B13h3Wq5X1',
|
|
85
|
-
kms: {
|
|
86
|
-
provider: 'google-kms',
|
|
87
|
-
keyId: 'test-key-id',
|
|
88
|
-
algorithm: 'RSA-OAEP-2048-SHA256',
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
const { car, actualRootCID } = await createTestCar(testContent);
|
|
92
|
-
// Mock fetch to return the valid CAR
|
|
93
|
-
const originalFetch = globalThis.fetch;
|
|
94
|
-
// @ts-ignore - Mock fetch for testing
|
|
95
|
-
globalThis.fetch = async (url) => ({
|
|
96
|
-
ok: true,
|
|
97
|
-
status: 200,
|
|
98
|
-
statusText: 'OK',
|
|
99
|
-
arrayBuffer: async () => car.buffer,
|
|
100
|
-
});
|
|
101
|
-
try {
|
|
102
|
-
const result = await getCarFileFromPublicGateway(gatewayURL, actualRootCID.toString());
|
|
103
|
-
assert(result instanceof Uint8Array, 'Should return Uint8Array');
|
|
104
|
-
assert.deepStrictEqual(result, car, 'Should return the exact CAR file');
|
|
105
|
-
}
|
|
106
|
-
finally {
|
|
107
|
-
globalThis.fetch = originalFetch;
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
await test('should reject CAR file with wrong root CID (tampering detection)', async () => {
|
|
111
|
-
const gatewayURL = new URL('https://example.com');
|
|
112
|
-
// Create a CAR file with content A
|
|
113
|
-
const originalContent = {
|
|
114
|
-
encryptedDataCID: 'bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7z3aldwdht2oqadq',
|
|
115
|
-
encryptedSymmetricKey: 'original-encrypted-key',
|
|
116
|
-
space: 'did:key:z6MkwDK3M4PxU1FqcSt6quBH1xRBSGnPRdQYP9B13h3Wq5X1',
|
|
117
|
-
kms: {
|
|
118
|
-
provider: 'google-kms',
|
|
119
|
-
keyId: 'original-key-id',
|
|
120
|
-
algorithm: 'RSA-OAEP-2048-SHA256',
|
|
121
|
-
},
|
|
122
|
-
};
|
|
123
|
-
const { actualRootCID: originalCID } = await createTestCar(originalContent);
|
|
124
|
-
// Create a different CAR file with content B
|
|
125
|
-
const tamperedContent = {
|
|
126
|
-
encryptedDataCID: 'bafkreidb6v6sjfnpnf6lqkh7p4w7zfzqfuzn2lqhp5x6zkojfuzwzlhpny',
|
|
127
|
-
encryptedSymmetricKey: 'tampered-encrypted-key',
|
|
128
|
-
space: 'did:key:z6MkMaliciousSpaceDIDThatShouldNotBeAccepted',
|
|
129
|
-
kms: {
|
|
130
|
-
provider: 'google-kms',
|
|
131
|
-
keyId: 'tampered-key-id',
|
|
132
|
-
algorithm: 'RSA-OAEP-2048-SHA256',
|
|
133
|
-
},
|
|
134
|
-
};
|
|
135
|
-
const { car: tamperedCar } = await createTestCar(tamperedContent);
|
|
136
|
-
// Mock fetch to return the tampered CAR when requesting the original CID
|
|
137
|
-
const originalFetch = globalThis.fetch;
|
|
138
|
-
// @ts-ignore - Mock fetch for testing
|
|
139
|
-
globalThis.fetch = async (url) => ({
|
|
140
|
-
ok: true,
|
|
141
|
-
status: 200,
|
|
142
|
-
statusText: 'OK',
|
|
143
|
-
arrayBuffer: async () => tamperedCar.buffer, // Return wrong CAR!
|
|
144
|
-
});
|
|
145
|
-
try {
|
|
146
|
-
await getCarFileFromPublicGateway(gatewayURL, originalCID.toString());
|
|
147
|
-
assert.fail('Should throw error for CID verification failure');
|
|
148
|
-
}
|
|
149
|
-
catch (error) {
|
|
150
|
-
assert(error instanceof Error, 'Should throw an Error');
|
|
151
|
-
assert(error.message.includes('CID verification failed'), `Should mention CID verification failure. Got: ${error.message}`);
|
|
152
|
-
}
|
|
153
|
-
finally {
|
|
154
|
-
globalThis.fetch = originalFetch;
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
await test('should detect when malicious gateway serves completely different CAR', async () => {
|
|
158
|
-
const gatewayURL = new URL('https://example.com');
|
|
159
|
-
// CID we're requesting
|
|
160
|
-
const requestedCID = 'bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7z3aldwdht2oqadq';
|
|
161
|
-
// Create a completely different CAR file
|
|
162
|
-
const maliciousContent = {
|
|
163
|
-
encryptedDataCID: 'bafkreig6h5fimhfvj3wmlsf4fzj2d2ndqxd7qnugcgcmkn6dcqzxcq5zdu',
|
|
164
|
-
encryptedSymmetricKey: 'malicious-encrypted-key',
|
|
165
|
-
space: 'did:key:z6MkMaliciousSpaceDIDControlledByAttacker',
|
|
166
|
-
kms: {
|
|
167
|
-
provider: 'google-kms',
|
|
168
|
-
keyId: 'attacker-controlled-key',
|
|
169
|
-
algorithm: 'RSA-OAEP-2048-SHA256',
|
|
170
|
-
},
|
|
171
|
-
};
|
|
172
|
-
const { car: maliciousCar } = await createTestCar(maliciousContent);
|
|
173
|
-
// Mock fetch to return the malicious CAR
|
|
174
|
-
const originalFetch = globalThis.fetch;
|
|
175
|
-
// @ts-ignore - Mock fetch for testing
|
|
176
|
-
globalThis.fetch = async (url) => ({
|
|
177
|
-
ok: true,
|
|
178
|
-
status: 200,
|
|
179
|
-
statusText: 'OK',
|
|
180
|
-
arrayBuffer: async () => maliciousCar.buffer,
|
|
181
|
-
});
|
|
182
|
-
try {
|
|
183
|
-
await getCarFileFromPublicGateway(gatewayURL, requestedCID);
|
|
184
|
-
assert.fail('Should throw error when gateway serves wrong CAR');
|
|
185
|
-
}
|
|
186
|
-
catch (error) {
|
|
187
|
-
assert(error instanceof Error, 'Should throw an Error');
|
|
188
|
-
assert(error.message.includes('CID verification failed'), `Should mention CID verification failure. Got: ${error.message}`);
|
|
189
|
-
}
|
|
190
|
-
finally {
|
|
191
|
-
globalThis.fetch = originalFetch;
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
await test('should handle network errors gracefully', async () => {
|
|
195
|
-
const gatewayURL = new URL('https://example.com');
|
|
196
|
-
const testCID = 'bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7z3aldwdht2oqadq';
|
|
197
|
-
// Mock fetch to return network error
|
|
198
|
-
const originalFetch = globalThis.fetch;
|
|
199
|
-
// @ts-ignore - Mock fetch for testing
|
|
200
|
-
globalThis.fetch = async (url) => ({
|
|
201
|
-
ok: false,
|
|
202
|
-
status: 500,
|
|
203
|
-
statusText: 'Internal Server Error',
|
|
204
|
-
arrayBuffer: async () => {
|
|
205
|
-
throw new Error('Network error');
|
|
206
|
-
},
|
|
207
|
-
});
|
|
208
|
-
try {
|
|
209
|
-
await getCarFileFromPublicGateway(gatewayURL, testCID);
|
|
210
|
-
assert.fail('Should throw error for network errors');
|
|
211
|
-
}
|
|
212
|
-
catch (error) {
|
|
213
|
-
assert(error instanceof Error, 'Should throw an Error');
|
|
214
|
-
assert(error.message.includes('Failed to fetch'), `Should mention fetch failure. Got: ${error.message}`);
|
|
215
|
-
}
|
|
216
|
-
finally {
|
|
217
|
-
globalThis.fetch = originalFetch;
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
await describe('Metadata Tampering Detection', async () => {
|
|
222
|
-
await test('should prevent modification of space DID in metadata', async () => {
|
|
223
|
-
const gatewayURL = new URL('https://example.com');
|
|
224
|
-
// Original metadata with legitimate space DID
|
|
225
|
-
const originalMetadata = {
|
|
226
|
-
encryptedDataCID: 'bafkreie2hvzhqzj3ixnmjh7h3nkhdyp6qxhqltkq6qxf3wxq7hqxd6nzde',
|
|
227
|
-
encryptedSymmetricKey: 'encrypted-key-data',
|
|
228
|
-
space: 'did:key:z6MkwDK3M4PxU1FqcSt6quBH1xRBSGnPRdQYP9B13h3Wq5X1', // Original space
|
|
229
|
-
kms: {
|
|
230
|
-
provider: 'google-kms',
|
|
231
|
-
keyId: 'test-key-id',
|
|
232
|
-
algorithm: 'RSA-OAEP-2048-SHA256',
|
|
233
|
-
},
|
|
234
|
-
};
|
|
235
|
-
// Tampered metadata with different space DID
|
|
236
|
-
const tamperedMetadata = {
|
|
237
|
-
...originalMetadata,
|
|
238
|
-
space: 'did:key:z6MkMaliciousSpaceDIDThatShouldNotBeAccepted', // Malicious space!
|
|
239
|
-
};
|
|
240
|
-
// Create CAR files for both
|
|
241
|
-
const { actualRootCID: originalCID } = await createTestCar(originalMetadata);
|
|
242
|
-
const { car: tamperedCar } = await createTestCar(tamperedMetadata);
|
|
243
|
-
// Mock fetch to return tampered CAR when requesting original CID
|
|
244
|
-
const originalFetch = globalThis.fetch;
|
|
245
|
-
// @ts-ignore - Mock fetch for testing
|
|
246
|
-
globalThis.fetch = async (url) => ({
|
|
247
|
-
ok: true,
|
|
248
|
-
status: 200,
|
|
249
|
-
statusText: 'OK',
|
|
250
|
-
arrayBuffer: async () => tamperedCar.buffer, // Returns tampered metadata!
|
|
251
|
-
});
|
|
252
|
-
try {
|
|
253
|
-
await getCarFileFromPublicGateway(gatewayURL, originalCID.toString());
|
|
254
|
-
assert.fail('Should detect space DID tampering via CID verification');
|
|
255
|
-
}
|
|
256
|
-
catch (error) {
|
|
257
|
-
assert(error instanceof Error, 'Should throw an Error');
|
|
258
|
-
assert(error.message.includes('CID verification failed'), `Should catch tampering via CID verification. Got: ${error.message}`);
|
|
259
|
-
}
|
|
260
|
-
finally {
|
|
261
|
-
globalThis.fetch = originalFetch;
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
await test('should prevent complete metadata substitution attacks', async () => {
|
|
265
|
-
const gatewayURL = new URL('https://example.com');
|
|
266
|
-
// Original legitimate metadata
|
|
267
|
-
const originalMetadata = {
|
|
268
|
-
encryptedDataCID: 'bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7z3aldwdht2oqadq',
|
|
269
|
-
encryptedSymmetricKey: 'original-encrypted-key',
|
|
270
|
-
space: 'did:key:z6MkwDK3M4PxU1FqcSt6quBH1xRBSGnPRdQYP9B13h3Wq5X1',
|
|
271
|
-
kms: {
|
|
272
|
-
provider: 'google-kms',
|
|
273
|
-
keyId: 'legitimate-key-id',
|
|
274
|
-
algorithm: 'RSA-OAEP-2048-SHA256',
|
|
275
|
-
},
|
|
276
|
-
};
|
|
277
|
-
// Completely different malicious metadata (using same CID but different content)
|
|
278
|
-
const maliciousMetadata = {
|
|
279
|
-
encryptedDataCID: 'bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7z3aldwdht2oqadq',
|
|
280
|
-
encryptedSymmetricKey: 'malicious-encrypted-key',
|
|
281
|
-
space: 'did:key:z6MkMaliciousSpaceDIDControlledByAttacker',
|
|
282
|
-
kms: {
|
|
283
|
-
provider: 'google-kms',
|
|
284
|
-
keyId: 'attacker-controlled-key',
|
|
285
|
-
algorithm: 'RSA-OAEP-2048-SHA256',
|
|
286
|
-
},
|
|
287
|
-
};
|
|
288
|
-
// Create CAR files for both
|
|
289
|
-
const { actualRootCID: originalCID } = await createTestCar(originalMetadata);
|
|
290
|
-
const { car: maliciousCar } = await createTestCar(maliciousMetadata);
|
|
291
|
-
// Mock fetch to return completely different metadata
|
|
292
|
-
const originalFetch = globalThis.fetch;
|
|
293
|
-
// @ts-ignore - Mock fetch for testing
|
|
294
|
-
globalThis.fetch = async (url) => ({
|
|
295
|
-
ok: true,
|
|
296
|
-
status: 200,
|
|
297
|
-
statusText: 'OK',
|
|
298
|
-
arrayBuffer: async () => maliciousCar.buffer, // Complete substitution attack!
|
|
299
|
-
});
|
|
300
|
-
try {
|
|
301
|
-
await getCarFileFromPublicGateway(gatewayURL, originalCID.toString());
|
|
302
|
-
assert.fail('Should detect complete metadata substitution via CID verification');
|
|
303
|
-
}
|
|
304
|
-
catch (error) {
|
|
305
|
-
assert(error instanceof Error, 'Should throw an Error');
|
|
306
|
-
assert(error.message.includes('CID verification failed'), `Should catch complete substitution via CID verification. Got: ${error.message}`);
|
|
307
|
-
}
|
|
308
|
-
finally {
|
|
309
|
-
globalThis.fetch = originalFetch;
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
});
|
|
313
|
-
});
|
|
314
|
-
//# sourceMappingURL=cid-verification.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"crypto-compatibility.spec.d.ts","sourceRoot":"","sources":["../../test/crypto-compatibility.spec.js"],"names":[],"mappings":""}
|
|
@@ -1,124 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"crypto-counter-security.spec.d.ts","sourceRoot":"","sources":["../../test/crypto-counter-security.spec.js"],"names":[],"mappings":""}
|