@storacha/encrypt-upload-client 0.0.39 → 1.0.0-0
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/core/client.d.ts +8 -12
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +12 -21
- package/dist/core/metadata/encrypted-metadata.d.ts +8 -0
- package/dist/core/metadata/encrypted-metadata.d.ts.map +1 -0
- package/dist/core/metadata/encrypted-metadata.js +69 -0
- package/dist/core/metadata/kms-metadata.d.ts +36 -0
- package/dist/core/metadata/kms-metadata.d.ts.map +1 -0
- package/dist/core/metadata/kms-metadata.js +156 -0
- package/dist/core/{encrypted-metadata.d.ts → metadata/lit-metadata.d.ts} +11 -11
- package/dist/core/metadata/lit-metadata.d.ts.map +1 -0
- package/dist/core/{encrypted-metadata.js → metadata/lit-metadata.js} +32 -42
- package/dist/crypto/adapters/kms-crypto-adapter.d.ts +148 -0
- package/dist/crypto/adapters/kms-crypto-adapter.d.ts.map +1 -0
- package/dist/crypto/adapters/kms-crypto-adapter.js +321 -0
- package/dist/crypto/adapters/lit-crypto-adapter.d.ts +96 -0
- package/dist/crypto/adapters/lit-crypto-adapter.d.ts.map +1 -0
- package/dist/crypto/adapters/lit-crypto-adapter.js +210 -0
- package/dist/crypto/factories.browser.d.ts +20 -0
- package/dist/crypto/factories.browser.d.ts.map +1 -0
- package/dist/crypto/factories.browser.js +28 -0
- package/dist/crypto/factories.node.d.ts +26 -0
- package/dist/crypto/factories.node.d.ts.map +1 -0
- package/dist/crypto/factories.node.js +38 -0
- package/dist/crypto/index.d.ts +5 -0
- package/dist/crypto/index.d.ts.map +1 -0
- package/dist/crypto/index.js +7 -0
- package/dist/crypto/symmetric/generic-aes-ctr-streaming-crypto.d.ts +76 -0
- package/dist/crypto/symmetric/generic-aes-ctr-streaming-crypto.d.ts.map +1 -0
- package/dist/crypto/symmetric/generic-aes-ctr-streaming-crypto.js +177 -0
- package/dist/crypto/symmetric/node-aes-cbc-crypto.d.ts +43 -0
- package/dist/crypto/symmetric/node-aes-cbc-crypto.d.ts.map +1 -0
- package/dist/crypto/symmetric/node-aes-cbc-crypto.js +110 -0
- package/dist/handlers/decrypt-handler.d.ts +9 -4
- package/dist/handlers/decrypt-handler.d.ts.map +1 -1
- package/dist/handlers/decrypt-handler.js +62 -93
- package/dist/handlers/encrypt-handler.d.ts +1 -1
- package/dist/handlers/encrypt-handler.d.ts.map +1 -1
- package/dist/handlers/encrypt-handler.js +31 -41
- package/dist/protocols/lit.d.ts +1 -3
- package/dist/protocols/lit.d.ts.map +1 -1
- package/dist/types.d.ts +135 -20
- package/dist/types.d.ts.map +1 -1
- package/package.json +27 -18
- package/dist/core/encrypted-metadata.d.ts.map +0 -1
- package/dist/crypto-adapters/browser-crypto-adapter.d.ts +0 -42
- package/dist/crypto-adapters/browser-crypto-adapter.d.ts.map +0 -1
- package/dist/crypto-adapters/browser-crypto-adapter.js +0 -109
- package/dist/crypto-adapters/node-crypto-adapter.d.ts +0 -17
- package/dist/crypto-adapters/node-crypto-adapter.d.ts.map +0 -1
- package/dist/crypto-adapters/node-crypto-adapter.js +0 -66
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { randomBytes, createCipheriv, createDecipheriv } from 'crypto';
|
|
2
|
+
import * as Type from '../../types.js';
|
|
3
|
+
const ENCRYPTION_ALGORITHM = 'aes-256-cbc';
|
|
4
|
+
const KEY_LENGTH = 256; // bits
|
|
5
|
+
const IV_LENGTH = 16; // bytes (128 bits, used as initialization vector)
|
|
6
|
+
/**
|
|
7
|
+
* NodeAesCbcCrypto implements AES-CBC symmetric encryption for Node.js environments.
|
|
8
|
+
* It uses AES-CBC mode for encryption via the Node.js crypto module.
|
|
9
|
+
* If you already encrypted a file with this class, you still need to use this class to decrypt it.
|
|
10
|
+
*
|
|
11
|
+
* @deprecated Use GenericAesCtrStreamingCrypto instead for new uploads
|
|
12
|
+
* @class
|
|
13
|
+
* @implements {Type.SymmetricCrypto}
|
|
14
|
+
*/
|
|
15
|
+
export class NodeAesCbcCrypto {
|
|
16
|
+
/** @param {Type.BlobLike} data */
|
|
17
|
+
async encryptStream(data) {
|
|
18
|
+
const symmetricKey = randomBytes(KEY_LENGTH / 8); // KEY_LENGTH bits for AES
|
|
19
|
+
const initializationVector = randomBytes(IV_LENGTH); // IV_LENGTH bytes for AES
|
|
20
|
+
const cipher = createCipheriv(ENCRYPTION_ALGORITHM, symmetricKey, initializationVector);
|
|
21
|
+
const encryptStream = new TransformStream({
|
|
22
|
+
transform: async (chunk, controller) => {
|
|
23
|
+
const encryptedChunk = cipher.update(chunk);
|
|
24
|
+
if (encryptedChunk.length) {
|
|
25
|
+
controller.enqueue(encryptedChunk);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
flush: (controller) => {
|
|
29
|
+
const final = cipher.final();
|
|
30
|
+
if (final.length) {
|
|
31
|
+
controller.enqueue(final);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
return Promise.resolve({
|
|
36
|
+
key: symmetricKey,
|
|
37
|
+
iv: initializationVector,
|
|
38
|
+
encryptedStream: data.stream().pipeThrough(encryptStream),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* @param {ReadableStream} encryptedData
|
|
43
|
+
* @param {Uint8Array} key
|
|
44
|
+
* @param {Uint8Array} iv
|
|
45
|
+
*/
|
|
46
|
+
async decryptStream(encryptedData, key, iv) {
|
|
47
|
+
const decipher = createDecipheriv(ENCRYPTION_ALGORITHM, key, iv);
|
|
48
|
+
const decryptor = new TransformStream({
|
|
49
|
+
async transform(chunk, controller) {
|
|
50
|
+
try {
|
|
51
|
+
const decryptedChunk = decipher.update(chunk);
|
|
52
|
+
if (decryptedChunk.length > 0) {
|
|
53
|
+
controller.enqueue(decryptedChunk);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
controller.error(err);
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
flush(controller) {
|
|
61
|
+
try {
|
|
62
|
+
const finalChunk = decipher.final();
|
|
63
|
+
if (finalChunk.length > 0) {
|
|
64
|
+
controller.enqueue(finalChunk);
|
|
65
|
+
}
|
|
66
|
+
controller.terminate();
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
controller.error(err);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
return Promise.resolve(encryptedData.pipeThrough(decryptor));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Combine key and IV into a single array for AES-CBC
|
|
77
|
+
*
|
|
78
|
+
* @param {Uint8Array} key - The AES key (KEY_LENGTH/8 bytes)
|
|
79
|
+
* @param {Uint8Array} iv - The AES-CBC IV (IV_LENGTH bytes)
|
|
80
|
+
* @returns {Uint8Array} Combined key and IV (KEY_LENGTH/8 + IV_LENGTH bytes)
|
|
81
|
+
*/
|
|
82
|
+
combineKeyAndIV(key, iv) {
|
|
83
|
+
const keyBytes = KEY_LENGTH / 8;
|
|
84
|
+
if (key.length !== keyBytes) {
|
|
85
|
+
throw new Error(`AES-${KEY_LENGTH} key must be ${keyBytes} bytes, got ${key.length}`);
|
|
86
|
+
}
|
|
87
|
+
if (iv.length !== IV_LENGTH) {
|
|
88
|
+
throw new Error(`AES-CBC IV must be ${IV_LENGTH} bytes, got ${iv.length}`);
|
|
89
|
+
}
|
|
90
|
+
return new Uint8Array([...key, ...iv]);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Split combined key and IV for AES-CBC
|
|
94
|
+
*
|
|
95
|
+
* @param {Uint8Array} combined - Combined key and IV (KEY_LENGTH/8 + IV_LENGTH bytes)
|
|
96
|
+
* @returns {{ key: Uint8Array, iv: Uint8Array }} Separated key and IV
|
|
97
|
+
*/
|
|
98
|
+
splitKeyAndIV(combined) {
|
|
99
|
+
const keyBytes = KEY_LENGTH / 8;
|
|
100
|
+
const expectedLength = keyBytes + IV_LENGTH;
|
|
101
|
+
if (combined.length !== expectedLength) {
|
|
102
|
+
throw new Error(`AES-${KEY_LENGTH}-CBC combined key+IV must be ${expectedLength} bytes, got ${combined.length}`);
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
key: combined.subarray(0, keyBytes),
|
|
106
|
+
iv: combined.subarray(keyBytes, keyBytes + IV_LENGTH),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=node-aes-cbc-crypto.js.map
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Decrypt file content using the decrypted symmetric key and IV.
|
|
3
|
+
*
|
|
2
4
|
* @param {Type.CryptoAdapter} cryptoAdapter - The crypto adapter responsible for performing
|
|
3
5
|
* encryption and decryption operations.
|
|
4
|
-
* @param {
|
|
5
|
-
* @param {Uint8Array}
|
|
6
|
+
* @param {Uint8Array} key - The symmetric key
|
|
7
|
+
* @param {Uint8Array} iv - The initialization vector
|
|
8
|
+
* @param {Uint8Array} content - The encrypted file content
|
|
9
|
+
* @returns {Promise<ReadableStream>} The decrypted file stream
|
|
6
10
|
*/
|
|
7
|
-
export function decryptFileWithKey(cryptoAdapter: Type.CryptoAdapter,
|
|
8
|
-
export function retrieveAndDecrypt(storachaClient: import("@storacha/client").Client,
|
|
11
|
+
export function decryptFileWithKey(cryptoAdapter: Type.CryptoAdapter, key: Uint8Array, iv: Uint8Array, content: Uint8Array): Promise<ReadableStream>;
|
|
12
|
+
export function retrieveAndDecrypt(storachaClient: import("@storacha/client").Client, cryptoAdapter: Type.CryptoAdapter, gatewayURL: URL, cid: Type.AnyLink, delegationCAR: Uint8Array, decryptionOptions: Type.DecryptionOptions): Promise<ReadableStream>;
|
|
13
|
+
export function getCarFileFromPublicGateway(gatewayURL: URL, cid: string): Promise<Uint8Array>;
|
|
9
14
|
import * as Type from '../types.js';
|
|
10
15
|
//# sourceMappingURL=decrypt-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decrypt-handler.d.ts","sourceRoot":"","sources":["../../src/handlers/decrypt-handler.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"decrypt-handler.d.ts","sourceRoot":"","sources":["../../src/handlers/decrypt-handler.js"],"names":[],"mappings":"AA4DA;;;;;;;;;GASG;AACH,kDAPW,IAAI,CAAC,aAAa,OAElB,UAAU,MACV,UAAU,WACV,UAAU,GACR,OAAO,CAAC,cAAc,CAAC,CAanC;AA9DM,mDATI,OAAO,kBAAkB,EAAE,MAAM,iBACjC,IAAI,CAAC,aAAa,cAElB,GAAG,OACH,IAAI,CAAC,OAAO,iBACZ,UAAU,qBACV,IAAI,CAAC,iBAAiB,GACpB,OAAO,CAAC,cAAc,CAAC,CAyCnC;AAoCM,wDAJI,GAAG,OACH,MAAM,GACJ,OAAO,CAAC,UAAU,CAAC,CA+B/B;sBAtHqB,aAAa"}
|
|
@@ -1,144 +1,113 @@
|
|
|
1
1
|
import { CID } from 'multiformats';
|
|
2
|
-
import { CarIndexer } from '@ipld/car';
|
|
2
|
+
import { CarIndexer, CarReader } from '@ipld/car';
|
|
3
3
|
import { exporter } from 'ipfs-unixfs-exporter';
|
|
4
4
|
import { MemoryBlockstore } from 'blockstore-core';
|
|
5
|
-
import { base64 } from 'multiformats/bases/base64';
|
|
6
|
-
import * as Lit from '../protocols/lit.js';
|
|
7
5
|
import * as Type from '../types.js';
|
|
8
|
-
import * as EncryptedMetadata from '../core/encrypted-metadata.js';
|
|
9
|
-
import { createDecryptWrappedInvocation } from '../utils.js';
|
|
10
6
|
/**
|
|
11
|
-
* Retrieve and decrypt a file from the IPFS gateway.
|
|
7
|
+
* Retrieve and decrypt a file from the IPFS gateway using any supported encryption strategy.
|
|
12
8
|
*
|
|
13
9
|
* @param {import('@storacha/client').Client} storachaClient - The Storacha client
|
|
14
|
-
* @param {import('@lit-protocol/lit-node-client').LitNodeClient} litClient - The Lit client
|
|
15
10
|
* @param {Type.CryptoAdapter} cryptoAdapter - The crypto adapter responsible for performing
|
|
16
11
|
* encryption and decryption operations.
|
|
17
12
|
* @param {URL} gatewayURL - The IPFS gateway URL
|
|
18
|
-
* @param {Type.LitWalletSigner | Type.LitPkpSigner} signer - The wallet or PKP key signer to decrypt the file
|
|
19
13
|
* @param {Type.AnyLink} cid - The link to the file to retrieve
|
|
20
|
-
* @param {Uint8Array} delegationCAR - The delegation that gives permission to decrypt
|
|
14
|
+
* @param {Uint8Array} delegationCAR - The delegation that gives permission to decrypt (required for both strategies)
|
|
15
|
+
* @param {Type.DecryptionOptions} decryptionOptions - User-provided decryption options
|
|
16
|
+
* @returns {Promise<ReadableStream>} The decrypted file stream
|
|
21
17
|
*/
|
|
22
|
-
export const retrieveAndDecrypt = async (storachaClient,
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
/** @type import('@lit-protocol/types').AccessControlConditions */ (
|
|
35
|
-
/** @type {unknown} */ (accessControlConditions));
|
|
36
|
-
const expiration = new Date(Date.now() + 1000 * 60 * 5).toISOString(); // 5 min
|
|
37
|
-
// TODO: store the session signature (https://developer.litprotocol.com/intro/first-request/generating-session-sigs#nodejs)
|
|
38
|
-
let sessionSigs;
|
|
39
|
-
if ('wallet' in signer) {
|
|
40
|
-
sessionSigs = await Lit.getSessionSigs(litClient, {
|
|
41
|
-
wallet: signer.wallet,
|
|
42
|
-
dataToEncryptHash: plaintextKeyHash,
|
|
43
|
-
expiration,
|
|
44
|
-
accessControlConditions: acc,
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
sessionSigs = await Lit.getPkpSessionSigs(litClient, {
|
|
49
|
-
pkpPublicKey: signer.pkpPublicKey,
|
|
50
|
-
authMethod: signer.authMethod,
|
|
51
|
-
dataToEncryptHash: plaintextKeyHash,
|
|
52
|
-
expiration,
|
|
53
|
-
accessControlConditions: acc,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
const wrappedInvocationJSON = await createDecryptWrappedInvocation({
|
|
18
|
+
export const retrieveAndDecrypt = async (storachaClient, cryptoAdapter, gatewayURL, cid, delegationCAR, decryptionOptions) => {
|
|
19
|
+
// Step 1: Get the encrypted metadata from the public gateway
|
|
20
|
+
const encryptedMetadataCar = await getCarFileFromPublicGateway(gatewayURL, cid.toString());
|
|
21
|
+
// Step 2: Extract encrypted metadata from the CAR file
|
|
22
|
+
const metadata = cryptoAdapter.extractEncryptedMetadata(encryptedMetadataCar);
|
|
23
|
+
// Step 3: Get the encrypted data from the CAR file
|
|
24
|
+
const encryptedData = await getEncryptedDataFromCar(encryptedMetadataCar, metadata.encryptedDataCID);
|
|
25
|
+
// Step 4: Decrypt the encrypted symmetric key
|
|
26
|
+
const encryptedSymmetricKey = cryptoAdapter.getEncryptedKey(metadata);
|
|
27
|
+
const { key, iv } = await cryptoAdapter.decryptSymmetricKey(encryptedSymmetricKey, {
|
|
28
|
+
decryptionOptions,
|
|
29
|
+
metadata,
|
|
57
30
|
delegationCAR,
|
|
58
|
-
spaceDID,
|
|
59
31
|
resourceCID: cid,
|
|
60
32
|
issuer: storachaClient.agent.issuer,
|
|
61
33
|
audience: storachaClient.defaultProvider(),
|
|
62
|
-
expiration: new Date(Date.now() + 1000 * 60 * 10).getTime(), // 10 min
|
|
63
34
|
});
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
spaceDID,
|
|
67
|
-
identityBoundCiphertext,
|
|
68
|
-
plaintextKeyHash,
|
|
69
|
-
accessControlConditions,
|
|
70
|
-
wrappedInvocationJSON,
|
|
71
|
-
});
|
|
72
|
-
return decryptFileWithKey(cryptoAdapter, decryptKey, encryptedData);
|
|
35
|
+
// Step 5: Decrypt the encrypted file content using the decrypted symmetric key and IV
|
|
36
|
+
return decryptFileWithKey(cryptoAdapter, key, iv, encryptedData);
|
|
73
37
|
};
|
|
74
38
|
/**
|
|
39
|
+
* Decrypt file content using the decrypted symmetric key and IV.
|
|
40
|
+
*
|
|
75
41
|
* @param {Type.CryptoAdapter} cryptoAdapter - The crypto adapter responsible for performing
|
|
76
42
|
* encryption and decryption operations.
|
|
77
|
-
* @param {
|
|
78
|
-
* @param {Uint8Array}
|
|
43
|
+
* @param {Uint8Array} key - The symmetric key
|
|
44
|
+
* @param {Uint8Array} iv - The initialization vector
|
|
45
|
+
* @param {Uint8Array} content - The encrypted file content
|
|
46
|
+
* @returns {Promise<ReadableStream>} The decrypted file stream
|
|
79
47
|
*/
|
|
80
|
-
export function decryptFileWithKey(cryptoAdapter,
|
|
81
|
-
// Split the decrypted data back into key and initializationVector
|
|
82
|
-
const decryptedKeyData = base64.decode(combinedKey);
|
|
83
|
-
const symmetricKey = decryptedKeyData.subarray(0, 32);
|
|
84
|
-
const initializationVector = decryptedKeyData.subarray(32);
|
|
85
|
-
// Create a ReadableStream from the Uint8Array
|
|
48
|
+
export function decryptFileWithKey(cryptoAdapter, key, iv, content) {
|
|
86
49
|
const contentStream = new ReadableStream({
|
|
87
50
|
start(controller) {
|
|
88
51
|
controller.enqueue(content);
|
|
89
52
|
controller.close();
|
|
90
53
|
},
|
|
91
54
|
});
|
|
92
|
-
const decryptedStream = cryptoAdapter.decryptStream(contentStream,
|
|
55
|
+
const decryptedStream = cryptoAdapter.decryptStream(contentStream, key, iv);
|
|
93
56
|
return decryptedStream;
|
|
94
57
|
}
|
|
95
58
|
/**
|
|
59
|
+
* Fetch a CAR file from the public IPFS gateway with root CID verification.
|
|
96
60
|
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
61
|
+
* SECURITY: This function provides metadata integrity protection (P0.2).
|
|
62
|
+
* Verifies the returned CAR matches the requested CID to prevent metadata tampering.
|
|
63
|
+
* Content integrity (P2.2) is handled by existing IPFS tools in getEncryptedDataFromCar.
|
|
64
|
+
*
|
|
65
|
+
* @param {URL} gatewayURL - The IPFS gateway URL
|
|
66
|
+
* @param {string} cid - The CID to fetch
|
|
67
|
+
* @returns {Promise<Uint8Array>} The verified CAR file bytes
|
|
99
68
|
*/
|
|
100
|
-
const
|
|
69
|
+
export const getCarFileFromPublicGateway = async (gatewayURL, cid) => {
|
|
101
70
|
const url = new URL(`/ipfs/${cid}?format=car`, gatewayURL);
|
|
102
71
|
const response = await fetch(url);
|
|
103
72
|
if (!response.ok) {
|
|
104
73
|
throw new Error(`Failed to fetch: ${response.status} ${response.statusText}`);
|
|
105
74
|
}
|
|
106
75
|
const car = new Uint8Array(await response.arrayBuffer());
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
throw encryptedContentResult.error;
|
|
76
|
+
// SECURITY: Verify the CAR's root CID matches what we requested
|
|
77
|
+
const reader = await CarReader.fromBytes(car);
|
|
78
|
+
const roots = await reader.getRoots();
|
|
79
|
+
const expectedCID = CID.parse(cid);
|
|
80
|
+
if (roots.length !== 1) {
|
|
81
|
+
throw new Error(`CAR file must have exactly one root CID, found ${roots.length}`);
|
|
82
|
+
}
|
|
83
|
+
if (!roots[0].equals(expectedCID)) {
|
|
84
|
+
throw new Error(`CID verification failed: expected ${expectedCID} but CAR contains ${roots[0]}`);
|
|
117
85
|
}
|
|
118
|
-
|
|
119
|
-
return encryptedContent;
|
|
86
|
+
return car;
|
|
120
87
|
};
|
|
121
88
|
/**
|
|
89
|
+
* Extract encrypted data from a CAR file.
|
|
122
90
|
*
|
|
123
|
-
* @param {Uint8Array} car
|
|
124
|
-
* @param {string} encryptedDataCID
|
|
91
|
+
* @param {Uint8Array} car - The CAR file bytes
|
|
92
|
+
* @param {string} encryptedDataCID - The CID of the encrypted data
|
|
93
|
+
* @returns {Promise<Uint8Array>} The encrypted data bytes
|
|
125
94
|
*/
|
|
126
95
|
const getEncryptedDataFromCar = async (car, encryptedDataCID) => {
|
|
127
|
-
//
|
|
96
|
+
// Step 1: Index the CAR file for efficient block lookup
|
|
128
97
|
const iterable = await CarIndexer.fromBytes(car);
|
|
129
|
-
const
|
|
98
|
+
const blockIndex = new Map();
|
|
130
99
|
for await (const { cid, blockLength, blockOffset } of iterable) {
|
|
131
|
-
|
|
132
|
-
await blockstore.put(cid, blockBytes);
|
|
100
|
+
blockIndex.set(cid.toString(), { blockOffset, blockLength });
|
|
133
101
|
}
|
|
134
|
-
//
|
|
102
|
+
// Step 2: Use the index to extract the encrypted data block bytes as needed
|
|
103
|
+
const { blockOffset, blockLength } = blockIndex.get(encryptedDataCID);
|
|
104
|
+
const blockBytes = car.subarray(blockOffset, blockOffset + blockLength);
|
|
105
|
+
// Step 3: Put the block in a blockstore for exporter compatibility
|
|
106
|
+
const blockstore = new MemoryBlockstore();
|
|
107
|
+
await blockstore.put(CID.parse(encryptedDataCID), blockBytes);
|
|
108
|
+
// Step 4: Get the encrypted data from the CAR file
|
|
135
109
|
const encryptedDataEntry = await exporter(CID.parse(encryptedDataCID), blockstore);
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
for await (const chunk of encryptedDataEntry.content()) {
|
|
139
|
-
encryptedDataBytes.set(chunk, offset);
|
|
140
|
-
offset += chunk.length;
|
|
141
|
-
}
|
|
142
|
-
return encryptedDataBytes;
|
|
110
|
+
// Step 5: Return the async iterable (stream of chunks)
|
|
111
|
+
return encryptedDataEntry.content(); // async iterable of Uint8Array
|
|
143
112
|
};
|
|
144
113
|
//# sourceMappingURL=decrypt-handler.js.map
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export function encryptAndUpload(storachaClient: import("@storacha/client").Client,
|
|
1
|
+
export function encryptAndUpload(storachaClient: import("@storacha/client").Client, cryptoAdapter: Type.CryptoAdapter, file: Type.BlobLike, encryptionConfig: Type.EncryptionConfig, uploadOptions?: Type.UploadOptions): Promise<Type.AnyLink>;
|
|
2
2
|
import * as Type from '../types.js';
|
|
3
3
|
//# sourceMappingURL=encrypt-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encrypt-handler.d.ts","sourceRoot":"","sources":["../../src/handlers/encrypt-handler.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"encrypt-handler.d.ts","sourceRoot":"","sources":["../../src/handlers/encrypt-handler.js"],"names":[],"mappings":"AAgBO,iDARI,OAAO,kBAAkB,EAAE,MAAM,iBACjC,IAAI,CAAC,aAAa,QAElB,IAAI,CAAC,QAAQ,oBACb,IAAI,CAAC,gBAAgB,kBACrB,IAAI,CAAC,aAAa,GAChB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CA6BjC;sBAxCqB,aAAa"}
|
|
@@ -1,42 +1,39 @@
|
|
|
1
1
|
import { CARWriterStream } from 'carstream';
|
|
2
|
-
import { base64 } from 'multiformats/bases/base64';
|
|
3
2
|
import { createFileEncoderStream } from '@storacha/upload-client/unixfs';
|
|
4
3
|
import * as Type from '../types.js';
|
|
5
|
-
import * as Lit from '../protocols/lit.js';
|
|
6
|
-
import * as EncryptedMetadata from '../core/encrypted-metadata.js';
|
|
7
4
|
/**
|
|
8
5
|
* Encrypt and upload a file to the Storacha network
|
|
9
6
|
*
|
|
10
7
|
* @param {import('@storacha/client').Client} storachaClient - The Storacha client
|
|
11
|
-
* @param {import('@lit-protocol/lit-node-client').LitNodeClient} litClient - The Lit client
|
|
12
8
|
* @param {Type.CryptoAdapter} cryptoAdapter - The crypto adapter responsible for performing
|
|
13
9
|
* encryption and decryption operations.
|
|
14
10
|
* @param {Type.BlobLike} file - The file to upload
|
|
11
|
+
* @param {Type.EncryptionConfig} encryptionConfig - User-provided encryption configuration
|
|
12
|
+
* @param {Type.UploadOptions} [uploadOptions] - User-provided upload options
|
|
15
13
|
* @returns {Promise<Type.AnyLink>} - The link to the uploaded file
|
|
16
14
|
*/
|
|
17
|
-
export const encryptAndUpload = async (storachaClient,
|
|
18
|
-
|
|
19
|
-
if (!spaceDID)
|
|
15
|
+
export const encryptAndUpload = async (storachaClient, cryptoAdapter, file, encryptionConfig, uploadOptions = {}) => {
|
|
16
|
+
// Step 1: Validate required configuration
|
|
17
|
+
if (!encryptionConfig.spaceDID)
|
|
20
18
|
throw new Error('No space selected!');
|
|
21
|
-
|
|
22
|
-
const encryptedPayload = await encryptFile(
|
|
23
|
-
|
|
19
|
+
// Step 2: Encrypt the file using the crypto adapter
|
|
20
|
+
const encryptedPayload = await encryptFile(cryptoAdapter, file, encryptionConfig);
|
|
21
|
+
// Step 3: Build and upload the encrypted metadata to the Storacha network
|
|
22
|
+
const rootCid = await buildAndUploadEncryptedMetadata(storachaClient, encryptedPayload, cryptoAdapter, uploadOptions);
|
|
23
|
+
// Step 4: Return the root CID of the encrypted metadata
|
|
24
24
|
return rootCid;
|
|
25
25
|
};
|
|
26
26
|
/**
|
|
27
27
|
* Upload encrypted metadata to the Storacha network
|
|
28
28
|
*
|
|
29
29
|
* @param {import('@storacha/client').Client} storachaClient - The Storacha client
|
|
30
|
-
* @param {Type.
|
|
31
|
-
* @param {
|
|
32
|
-
* @param {
|
|
33
|
-
* @param {boolean} [options.publishToFilecoin] - Whether to publish the data to Filecoin
|
|
30
|
+
* @param {Type.EncryptionPayload} encryptedPayload - The encrypted payload
|
|
31
|
+
* @param {Type.CryptoAdapter} cryptoAdapter - The crypto adapter for formatting metadata
|
|
32
|
+
* @param {Type.UploadOptions} [uploadOptions] - The upload options
|
|
34
33
|
* @returns {Promise<Type.AnyLink>} - The link to the uploaded metadata
|
|
35
34
|
*/
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
}) => {
|
|
39
|
-
const { identityBoundCiphertext, plaintextKeyHash, encryptedBlobLike } = encryptedPayload;
|
|
35
|
+
const buildAndUploadEncryptedMetadata = async (storachaClient, encryptedPayload, cryptoAdapter, uploadOptions) => {
|
|
36
|
+
const { encryptedKey, metadata, encryptedBlobLike } = encryptedPayload;
|
|
40
37
|
return storachaClient.uploadCAR({
|
|
41
38
|
stream() {
|
|
42
39
|
/** @type {any} */
|
|
@@ -50,45 +47,38 @@ const uploadEncryptedMetadata = async (storachaClient, encryptedPayload, accessC
|
|
|
50
47
|
async flush(controller) {
|
|
51
48
|
if (!root)
|
|
52
49
|
throw new Error('missing root block');
|
|
53
|
-
|
|
54
|
-
const uploadData = {
|
|
55
|
-
encryptedDataCID: root.cid.toString(),
|
|
56
|
-
identityBoundCiphertext,
|
|
57
|
-
plaintextKeyHash,
|
|
58
|
-
accessControlConditions:
|
|
59
|
-
/** @type {[Record<string, any>]} */ (
|
|
60
|
-
/** @type {unknown} */ (accessControlConditions)),
|
|
61
|
-
};
|
|
62
|
-
const encryptedMetadata = EncryptedMetadata.create(uploadData);
|
|
63
|
-
const { cid, bytes } = await encryptedMetadata.archiveBlock();
|
|
50
|
+
const { cid, bytes } = await cryptoAdapter.encodeMetadata(root.cid.toString(), encryptedKey, metadata);
|
|
64
51
|
controller.enqueue({ cid, bytes });
|
|
65
52
|
},
|
|
66
53
|
}))
|
|
67
54
|
.pipeThrough(new CARWriterStream());
|
|
68
55
|
},
|
|
69
56
|
}, {
|
|
70
|
-
|
|
71
|
-
|
|
57
|
+
...uploadOptions,
|
|
58
|
+
// the encrypted data won't be published to Filecoin, so we need to set pieceHasher to undefined
|
|
59
|
+
pieceHasher: undefined,
|
|
72
60
|
});
|
|
73
61
|
};
|
|
74
62
|
/**
|
|
75
|
-
* Encrypt a file
|
|
63
|
+
* Encrypt a file using the crypto adapter and return the encrypted payload.
|
|
64
|
+
* The encrypted payload contains the encrypted file, the encrypted symmetric key, and the metadata.
|
|
76
65
|
*
|
|
77
|
-
* @param {import('@lit-protocol/lit-node-client').LitNodeClient} litClient - The Lit client
|
|
78
66
|
* @param {Type.CryptoAdapter} cryptoAdapter - The crypto adapter responsible for performing
|
|
79
67
|
* encryption and decryption operations.
|
|
80
68
|
* @param {Type.BlobLike} file - The file to encrypt
|
|
81
|
-
* @param {
|
|
82
|
-
* @returns {Promise<Type.
|
|
69
|
+
* @param {Type.EncryptionConfig} encryptionConfig - The encryption configuration
|
|
70
|
+
* @returns {Promise<Type.EncryptionPayload>} - The encrypted file
|
|
83
71
|
*/
|
|
84
|
-
const encryptFile = async (
|
|
72
|
+
const encryptFile = async (cryptoAdapter, file, encryptionConfig) => {
|
|
73
|
+
// Step 1: Encrypt the file using the crypto adapter
|
|
85
74
|
const { key, iv, encryptedStream } = await cryptoAdapter.encryptStream(file);
|
|
86
|
-
//
|
|
87
|
-
const
|
|
88
|
-
|
|
75
|
+
// Step 2: Use crypto adapter to encrypt the symmetric key
|
|
76
|
+
const keyResult = await cryptoAdapter.encryptSymmetricKey(key, iv, encryptionConfig);
|
|
77
|
+
// Step 3: Return the encrypted payload
|
|
89
78
|
return {
|
|
90
|
-
|
|
91
|
-
|
|
79
|
+
strategy: keyResult.strategy,
|
|
80
|
+
encryptedKey: keyResult.encryptedKey,
|
|
81
|
+
metadata: keyResult.metadata,
|
|
92
82
|
encryptedBlobLike: { stream: () => encryptedStream },
|
|
93
83
|
};
|
|
94
84
|
};
|
package/dist/protocols/lit.d.ts
CHANGED
|
@@ -18,9 +18,7 @@ export function getSessionSigs(litClient: LitNodeClient, { wallet, accessControl
|
|
|
18
18
|
*/
|
|
19
19
|
export function getPkpSessionSigs(litClient: LitNodeClient, { pkpPublicKey, authMethod, accessControlConditions, dataToEncryptHash, expiration, capabilityAuthSigs, }: Type.PkpSessionSignatureOptions): Promise<import("@lit-protocol/types").SessionSigsMap>;
|
|
20
20
|
export { encryptString } from "@lit-protocol/encryption";
|
|
21
|
-
export function getAccessControlConditions(spaceDID:
|
|
22
|
-
protocol: "did:";
|
|
23
|
-
}>, any>): import("@lit-protocol/types").AccessControlConditions;
|
|
21
|
+
export function getAccessControlConditions(spaceDID: Type.SpaceDID): import("@lit-protocol/types").AccessControlConditions;
|
|
24
22
|
export function executeUcanValidationAction(litClient: LitNodeClient, options: Type.ExecuteUcanValidationOptions): Promise<any>;
|
|
25
23
|
import { LitNodeClient } from '@lit-protocol/lit-node-client';
|
|
26
24
|
import * as Type from '../types.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lit.d.ts","sourceRoot":"","sources":["../../src/protocols/lit.js"],"names":[],"mappings":"AAsCA;;GAEG;AACH,uDAQC;AAED;;;;GAIG;AACH,0CAJW,aAAa,2FACb,IAAI,CAAC,uBAAuB,GAC1B,OAAO,CAAC,OAAO,qBAAqB,EAAE,cAAc,CAAC,CAsDjE;AAED;;;;;;;GAOG;AACH,6CAJW,aAAa,6GACb,IAAI,CAAC,0BAA0B,GAC7B,OAAO,CAAC,OAAO,qBAAqB,EAAE,cAAc,CAAC,CAqCjE;;AAnIM
|
|
1
|
+
{"version":3,"file":"lit.d.ts","sourceRoot":"","sources":["../../src/protocols/lit.js"],"names":[],"mappings":"AAsCA;;GAEG;AACH,uDAQC;AAED;;;;GAIG;AACH,0CAJW,aAAa,2FACb,IAAI,CAAC,uBAAuB,GAC1B,OAAO,CAAC,OAAO,qBAAqB,EAAE,cAAc,CAAC,CAsDjE;AAED;;;;;;;GAOG;AACH,6CAJW,aAAa,6GACb,IAAI,CAAC,0BAA0B,GAC7B,OAAO,CAAC,OAAO,qBAAqB,EAAE,cAAc,CAAC,CAqCjE;;AAnIM,qDAHI,IAAI,CAAC,QAAQ,GACX,OAAO,qBAAqB,EAAE,uBAAuB,CAgBjE;AA6HM,uDAJI,aAAa,WACb,IAAI,CAAC,4BAA4B,gBA4B3C;8BA1L6B,+BAA+B;sBAUvC,aAAa"}
|