@storacha/encrypt-upload-client 0.0.8-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/README.md +82 -0
- package/dist/config/constants.d.ts +3 -0
- package/dist/config/constants.d.ts.map +1 -0
- package/dist/config/constants.js +3 -0
- package/dist/config/env.d.ts +8 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +22 -0
- package/dist/config/service.d.ts +14 -0
- package/dist/config/service.d.ts.map +1 -0
- package/dist/config/service.js +34 -0
- package/dist/core/client.d.ts +50 -0
- package/dist/core/client.d.ts.map +1 -0
- package/dist/core/client.js +78 -0
- package/dist/core/encrypted-metadata.d.ts +28 -0
- package/dist/core/encrypted-metadata.d.ts.map +1 -0
- package/dist/core/encrypted-metadata.js +166 -0
- package/dist/core/errors.d.ts +8 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +14 -0
- package/dist/crypto-adapters/node-crypto-adapter.d.ts +17 -0
- package/dist/crypto-adapters/node-crypto-adapter.d.ts.map +1 -0
- package/dist/crypto-adapters/node-crypto-adapter.js +66 -0
- package/dist/handlers/decrypt-handler.d.ts +11 -0
- package/dist/handlers/decrypt-handler.d.ts.map +1 -0
- package/dist/handlers/decrypt-handler.js +127 -0
- package/dist/handlers/encrypt-handler.d.ts +3 -0
- package/dist/handlers/encrypt-handler.d.ts.map +1 -0
- package/dist/handlers/encrypt-handler.js +87 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/protocols/lit.d.ts +18 -0
- package/dist/protocols/lit.d.ts.map +1 -0
- package/dist/protocols/lit.js +106 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/utils.d.ts +15 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +50 -0
- package/package.json +127 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Wallet } from 'ethers';
|
|
2
|
+
import { CID } from 'multiformats';
|
|
3
|
+
import { CarIndexer } from '@ipld/car';
|
|
4
|
+
import { exporter } from 'ipfs-unixfs-exporter';
|
|
5
|
+
import { MemoryBlockstore } from 'blockstore-core';
|
|
6
|
+
import { base64 } from 'multiformats/bases/base64';
|
|
7
|
+
import * as Lit from '../protocols/lit.js';
|
|
8
|
+
import * as Type from '../types.js';
|
|
9
|
+
import * as EncryptedMetadata from '../core/encrypted-metadata.js';
|
|
10
|
+
import { createDecryptWrappedInvocation } from '../utils.js';
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @param {import('@storacha/client').Client} storachaClient - The Storacha client
|
|
14
|
+
* @param {import('@lit-protocol/lit-node-client').LitNodeClient} litClient - The Lit client
|
|
15
|
+
* @param {Type.CryptoAdapter} cryptoAdapter - The crypto adapter responsible for performing
|
|
16
|
+
* encryption and decryption operations.
|
|
17
|
+
* @param {URL} gatewayURL - The IPFS gateway URL
|
|
18
|
+
* @param {Wallet} wallet - The wallet to use to decrypt the file
|
|
19
|
+
* @param {Type.AnyLink} cid - The link to the file to retrieve
|
|
20
|
+
* @param {Uint8Array} delegationCAR - The delegation that gives permission to decrypt the file
|
|
21
|
+
*/
|
|
22
|
+
export const retrieveAndDecrypt = async (storachaClient, litClient, cryptoAdapter, gatewayURL, wallet, cid, delegationCAR) => {
|
|
23
|
+
const encryptedMetadataCar = await getCarFileFromGateway(gatewayURL, cid.toString());
|
|
24
|
+
const { encryptedDataCID, identityBoundCiphertext, plaintextKeyHash, accessControlConditions, } = extractEncryptedMetadata(encryptedMetadataCar);
|
|
25
|
+
const spaceDID = /** @type {`did:key:${string}`} */ (accessControlConditions[0].parameters[1]);
|
|
26
|
+
const encryptedData = await getEncryptedDataFromCar(encryptedMetadataCar, encryptedDataCID);
|
|
27
|
+
/**
|
|
28
|
+
* TODO: check if the wallet has capacity credits, if not get it
|
|
29
|
+
*/
|
|
30
|
+
// TODO: store the session signature (https://developer.litprotocol.com/intro/first-request/generating-session-sigs#nodejs)
|
|
31
|
+
const sessionSigs = await Lit.getSessionSigs(litClient, {
|
|
32
|
+
wallet,
|
|
33
|
+
dataToEncryptHash: plaintextKeyHash,
|
|
34
|
+
expiration: new Date(Date.now() + 1000 * 60 * 5).toISOString(), // 5 min
|
|
35
|
+
accessControlConditions:
|
|
36
|
+
/** @type import('@lit-protocol/types').AccessControlConditions */ (
|
|
37
|
+
/** @type {unknown} */ (accessControlConditions)),
|
|
38
|
+
});
|
|
39
|
+
const wrappedInvocationJSON = await createDecryptWrappedInvocation({
|
|
40
|
+
delegationCAR,
|
|
41
|
+
spaceDID,
|
|
42
|
+
resourceCID: cid,
|
|
43
|
+
issuer: storachaClient.agent.issuer,
|
|
44
|
+
audience: storachaClient.defaultProvider(),
|
|
45
|
+
expiration: new Date(Date.now() + 1000 * 60 * 10).getTime(), // 10 min
|
|
46
|
+
});
|
|
47
|
+
const decryptKey = await Lit.executeUcanValidatoinAction(litClient, {
|
|
48
|
+
sessionSigs,
|
|
49
|
+
spaceDID,
|
|
50
|
+
identityBoundCiphertext,
|
|
51
|
+
plaintextKeyHash,
|
|
52
|
+
accessControlConditions,
|
|
53
|
+
wrappedInvocationJSON,
|
|
54
|
+
});
|
|
55
|
+
return decryptFileWithKey(cryptoAdapter, decryptKey, encryptedData);
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* @param {Type.CryptoAdapter} cryptoAdapter - The crypto adapter responsible for performing
|
|
59
|
+
* encryption and decryption operations.
|
|
60
|
+
* @param {string} combinedKey
|
|
61
|
+
* @param {Uint8Array} content
|
|
62
|
+
*/
|
|
63
|
+
export function decryptFileWithKey(cryptoAdapter, combinedKey, content) {
|
|
64
|
+
// Split the decrypted data back into key and initializationVector
|
|
65
|
+
const decryptedKeyData = base64.decode(combinedKey);
|
|
66
|
+
const symmetricKey = decryptedKeyData.subarray(0, 32);
|
|
67
|
+
const initializationVector = decryptedKeyData.subarray(32);
|
|
68
|
+
// Create a ReadableStream from the Uint8Array
|
|
69
|
+
const contentStream = new ReadableStream({
|
|
70
|
+
start(controller) {
|
|
71
|
+
controller.enqueue(content);
|
|
72
|
+
controller.close();
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
const decryptedStream = cryptoAdapter.decryptStream(contentStream, symmetricKey, initializationVector);
|
|
76
|
+
return decryptedStream;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
*
|
|
80
|
+
* @param {URL} gatewayURL
|
|
81
|
+
* @param {string} cid
|
|
82
|
+
*/
|
|
83
|
+
const getCarFileFromGateway = async (gatewayURL, cid) => {
|
|
84
|
+
const url = new URL(`/ipfs/${cid}?format=car`, gatewayURL);
|
|
85
|
+
const response = await fetch(url);
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
throw new Error(`Failed to fetch: ${response.status} ${response.statusText}`);
|
|
88
|
+
}
|
|
89
|
+
const car = new Uint8Array(await response.arrayBuffer());
|
|
90
|
+
return car;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
*
|
|
94
|
+
* @param {Uint8Array} car
|
|
95
|
+
*/
|
|
96
|
+
const extractEncryptedMetadata = (car) => {
|
|
97
|
+
const encryptedContentResult = EncryptedMetadata.extract(car);
|
|
98
|
+
if (encryptedContentResult.error) {
|
|
99
|
+
throw encryptedContentResult.error;
|
|
100
|
+
}
|
|
101
|
+
let encryptedContent = encryptedContentResult.ok.toJSON();
|
|
102
|
+
return encryptedContent;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
*
|
|
106
|
+
* @param {Uint8Array} car
|
|
107
|
+
* @param {string} encryptedDataCID
|
|
108
|
+
*/
|
|
109
|
+
const getEncryptedDataFromCar = async (car, encryptedDataCID) => {
|
|
110
|
+
// NOTE: convert CAR to a block store
|
|
111
|
+
const iterable = await CarIndexer.fromBytes(car);
|
|
112
|
+
const blockstore = new MemoryBlockstore();
|
|
113
|
+
for await (const { cid, blockLength, blockOffset } of iterable) {
|
|
114
|
+
const blockBytes = car.slice(blockOffset, blockOffset + blockLength);
|
|
115
|
+
await blockstore.put(cid, blockBytes);
|
|
116
|
+
}
|
|
117
|
+
// NOTE: get the encrypted Data from the CAR file
|
|
118
|
+
const encryptedDataEntry = await exporter(CID.parse(encryptedDataCID), blockstore);
|
|
119
|
+
const encryptedDataBytes = new Uint8Array(Number(encryptedDataEntry.size));
|
|
120
|
+
let offset = 0;
|
|
121
|
+
for await (const chunk of encryptedDataEntry.content()) {
|
|
122
|
+
encryptedDataBytes.set(chunk, offset);
|
|
123
|
+
offset += chunk.length;
|
|
124
|
+
}
|
|
125
|
+
return encryptedDataBytes;
|
|
126
|
+
};
|
|
127
|
+
//# sourceMappingURL=decrypt-handler.js.map
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export function encryptAndUpload(storachaClient: import("@storacha/client").Client, litClient: import("@lit-protocol/lit-node-client").LitNodeClient, cryptoAdapter: Type.CryptoAdapter, file: Type.BlobLike): Promise<Type.AnyLink>;
|
|
2
|
+
import * as Type from '../types.js';
|
|
3
|
+
//# sourceMappingURL=encrypt-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encrypt-handler.d.ts","sourceRoot":"","sources":["../../src/handlers/encrypt-handler.js"],"names":[],"mappings":"AAkBO,iDAPI,OAAO,kBAAkB,EAAE,MAAM,aACjC,OAAO,+BAA+B,EAAE,aAAa,iBACrD,IAAI,CAAC,aAAa,QAElB,IAAI,CAAC,QAAQ,GACX,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CA4BjC;sBAxCqB,aAAa"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { CARWriterStream } from 'carstream';
|
|
2
|
+
import { base64 } from 'multiformats/bases/base64';
|
|
3
|
+
import { createFileEncoderStream } from '@storacha/upload-client/unixfs';
|
|
4
|
+
import * as Type from '../types.js';
|
|
5
|
+
import * as Lit from '../protocols/lit.js';
|
|
6
|
+
import * as EncryptedMetadata from '../core/encrypted-metadata.js';
|
|
7
|
+
/**
|
|
8
|
+
* Encrypt and upload a file to the Storacha network
|
|
9
|
+
*
|
|
10
|
+
* @param {import('@storacha/client').Client} storachaClient - The Storacha client
|
|
11
|
+
* @param {import('@lit-protocol/lit-node-client').LitNodeClient} litClient - The Lit client
|
|
12
|
+
* @param {Type.CryptoAdapter} cryptoAdapter - The crypto adapter responsible for performing
|
|
13
|
+
* encryption and decryption operations.
|
|
14
|
+
* @param {Type.BlobLike} file - The file to upload
|
|
15
|
+
* @returns {Promise<Type.AnyLink>} - The link to the uploaded file
|
|
16
|
+
*/
|
|
17
|
+
export const encryptAndUpload = async (storachaClient, litClient, cryptoAdapter, file) => {
|
|
18
|
+
const spaceDID = /** @type {Type.SpaceDID | undefined} */ (storachaClient.agent.currentSpace());
|
|
19
|
+
if (!spaceDID)
|
|
20
|
+
throw new Error('No space selected!');
|
|
21
|
+
const accessControlConditions = Lit.getAccessControlConditions(spaceDID);
|
|
22
|
+
const encryptedPayload = await encryptFile(litClient, cryptoAdapter, file, accessControlConditions);
|
|
23
|
+
const rootCid = await uploadEncryptedMetadata(storachaClient, encryptedPayload, accessControlConditions);
|
|
24
|
+
return rootCid;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Upload encrypted metadata to the Storacha network
|
|
28
|
+
*
|
|
29
|
+
* @param {import('@storacha/client').Client} storachaClient - The Storacha client
|
|
30
|
+
* @param {Type.EncryptedPayload} encryptedPayload - The encrypted payload
|
|
31
|
+
* @param {import('@lit-protocol/types').AccessControlConditions} accessControlConditions - The access control conditions
|
|
32
|
+
* @returns {Promise<Type.AnyLink>} - The link to the uploaded metadata
|
|
33
|
+
*/
|
|
34
|
+
const uploadEncryptedMetadata = async (storachaClient, encryptedPayload, accessControlConditions) => {
|
|
35
|
+
const { identityBoundCiphertext, plaintextKeyHash, encryptedBlobLike } = encryptedPayload;
|
|
36
|
+
return storachaClient.uploadCAR({
|
|
37
|
+
stream() {
|
|
38
|
+
/** @type {any} */
|
|
39
|
+
let root;
|
|
40
|
+
return createFileEncoderStream(encryptedBlobLike)
|
|
41
|
+
.pipeThrough(new TransformStream({
|
|
42
|
+
transform(block, controller) {
|
|
43
|
+
root = block;
|
|
44
|
+
controller.enqueue(block);
|
|
45
|
+
},
|
|
46
|
+
async flush(controller) {
|
|
47
|
+
if (!root)
|
|
48
|
+
throw new Error('missing root block');
|
|
49
|
+
/** @type {Type.EncryptedMetadataInput} */
|
|
50
|
+
const uploadData = {
|
|
51
|
+
encryptedDataCID: root.cid.toString(),
|
|
52
|
+
identityBoundCiphertext,
|
|
53
|
+
plaintextKeyHash,
|
|
54
|
+
accessControlConditions: /** @type {[Record<string, any>]} */ (
|
|
55
|
+
/** @type {unknown} */ (accessControlConditions)),
|
|
56
|
+
};
|
|
57
|
+
const encryptedMetadata = EncryptedMetadata.create(uploadData);
|
|
58
|
+
const { cid, bytes } = await encryptedMetadata.archiveBlock();
|
|
59
|
+
controller.enqueue({ cid, bytes });
|
|
60
|
+
},
|
|
61
|
+
}))
|
|
62
|
+
.pipeThrough(new CARWriterStream());
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Encrypt a file
|
|
68
|
+
*
|
|
69
|
+
* @param {import('@lit-protocol/lit-node-client').LitNodeClient} litClient - The Lit client
|
|
70
|
+
* @param {Type.CryptoAdapter} cryptoAdapter - The crypto adapter responsible for performing
|
|
71
|
+
* encryption and decryption operations.
|
|
72
|
+
* @param {Type.BlobLike} file - The file to encrypt
|
|
73
|
+
* @param {import('@lit-protocol/types').AccessControlConditions} accessControlConditions - The access control conditions
|
|
74
|
+
* @returns {Promise<Type.EncryptedPayload>} - The encrypted file
|
|
75
|
+
*/
|
|
76
|
+
const encryptFile = async (litClient, cryptoAdapter, file, accessControlConditions) => {
|
|
77
|
+
const { key, iv, encryptedStream } = cryptoAdapter.encryptStream(file);
|
|
78
|
+
// Combine key and initializationVector for Lit encryption
|
|
79
|
+
const dataToEncrypt = base64.encode(new Uint8Array([...key, ...iv]));
|
|
80
|
+
const { ciphertext, dataToEncryptHash } = await Lit.encryptString({ dataToEncrypt, accessControlConditions }, litClient);
|
|
81
|
+
return {
|
|
82
|
+
identityBoundCiphertext: ciphertext,
|
|
83
|
+
plaintextKeyHash: dataToEncryptHash,
|
|
84
|
+
encryptedBlobLike: { stream: () => encryptedStream },
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=encrypt-handler.js.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get a LitClient instance.
|
|
3
|
+
*/
|
|
4
|
+
export function getLitClient(): Promise<LitNodeClient>;
|
|
5
|
+
/**
|
|
6
|
+
* @param {LitNodeClient} litClient
|
|
7
|
+
* @param {Type.SessionSignatureOptions} param0
|
|
8
|
+
* @returns {Promise<import('@lit-protocol/types').SessionSigsMap>}
|
|
9
|
+
*/
|
|
10
|
+
export function getSessionSigs(litClient: LitNodeClient, { wallet, accessControlConditions, dataToEncryptHash, expiration, capabilityAuthSigs, }: Type.SessionSignatureOptions): Promise<import("@lit-protocol/types").SessionSigsMap>;
|
|
11
|
+
export { encryptString } from "@lit-protocol/encryption";
|
|
12
|
+
export function getAccessControlConditions(spaceDID: import("@ucanto/core/schema").Schema<`did:key:${string}` & `did:${string}` & import("multiformats").Phantom<{
|
|
13
|
+
protocol: "did:";
|
|
14
|
+
}>, any>): import("@lit-protocol/types").AccessControlConditions;
|
|
15
|
+
export function executeUcanValidatoinAction(litClient: LitNodeClient, options: Type.ExecuteUcanValidationOptions): Promise<any>;
|
|
16
|
+
import { LitNodeClient } from '@lit-protocol/lit-node-client';
|
|
17
|
+
import * as Type from '../types.js';
|
|
18
|
+
//# sourceMappingURL=lit.d.ts.map
|
|
@@ -0,0 +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;;AAtFM;;WAFM,OAAO,qBAAqB,EAAE,uBAAuB,CAgBjE;AAgFM,uDAJI,aAAa,WACb,IAAI,CAAC,4BAA4B,gBA4B3C;8BA7I6B,+BAA+B;sBAUvC,aAAa"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { LitNodeClient } from '@lit-protocol/lit-node-client';
|
|
2
|
+
import { LIT_ABILITY } from '@lit-protocol/constants';
|
|
3
|
+
import { generateAuthSig, LitActionResource, createSiweMessage, LitAccessControlConditionResource, } from '@lit-protocol/auth-helpers';
|
|
4
|
+
import env from '../config/env.js';
|
|
5
|
+
import * as Type from '../types.js';
|
|
6
|
+
import { STORACHA_LIT_ACTION_CID } from '../config/constants.js';
|
|
7
|
+
export { encryptString } from '@lit-protocol/encryption';
|
|
8
|
+
/**
|
|
9
|
+
* Create access control conditions required to use Lit Protocol.
|
|
10
|
+
* This ensures that the Storacha Lit Action is used to validate decryption permissions for the specified space DID.
|
|
11
|
+
*
|
|
12
|
+
* @param {Type.SpaceDID} spaceDID - The DID of the space
|
|
13
|
+
* @returns {import('@lit-protocol/types').AccessControlConditions} - The access control conditions
|
|
14
|
+
*/
|
|
15
|
+
export const getAccessControlConditions = (spaceDID) => {
|
|
16
|
+
return [
|
|
17
|
+
{
|
|
18
|
+
contractAddress: '',
|
|
19
|
+
standardContractType: '',
|
|
20
|
+
chain: 'ethereum',
|
|
21
|
+
method: '',
|
|
22
|
+
parameters: [':currentActionIpfsId', spaceDID],
|
|
23
|
+
returnValueTest: {
|
|
24
|
+
comparator: '=',
|
|
25
|
+
value: STORACHA_LIT_ACTION_CID,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Get a LitClient instance.
|
|
32
|
+
*/
|
|
33
|
+
export async function getLitClient() {
|
|
34
|
+
const litNodeClient = new LitNodeClient({
|
|
35
|
+
litNetwork: env.LIT_NETWORK,
|
|
36
|
+
debug: env.LIT_DEBUG,
|
|
37
|
+
});
|
|
38
|
+
await litNodeClient.connect();
|
|
39
|
+
return litNodeClient;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* @param {LitNodeClient} litClient
|
|
43
|
+
* @param {Type.SessionSignatureOptions} param0
|
|
44
|
+
* @returns {Promise<import('@lit-protocol/types').SessionSigsMap>}
|
|
45
|
+
*/
|
|
46
|
+
export async function getSessionSigs(litClient, { wallet, accessControlConditions, dataToEncryptHash, expiration, capabilityAuthSigs, }) {
|
|
47
|
+
const accsResourceString = await LitAccessControlConditionResource.generateResourceString(accessControlConditions, dataToEncryptHash);
|
|
48
|
+
const sessionSigs = await litClient.getSessionSigs({
|
|
49
|
+
chain: 'ethereum',
|
|
50
|
+
capabilityAuthSigs,
|
|
51
|
+
expiration,
|
|
52
|
+
resourceAbilityRequests: [
|
|
53
|
+
{
|
|
54
|
+
resource: new LitAccessControlConditionResource(accsResourceString),
|
|
55
|
+
ability: LIT_ABILITY.AccessControlConditionDecryption,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
resource: new LitActionResource('*'),
|
|
59
|
+
ability: LIT_ABILITY.LitActionExecution,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
authNeededCallback: async ({ uri, expiration, resourceAbilityRequests, }) => {
|
|
63
|
+
const toSign = await createSiweMessage({
|
|
64
|
+
uri,
|
|
65
|
+
expiration,
|
|
66
|
+
resources: resourceAbilityRequests,
|
|
67
|
+
walletAddress: wallet.address,
|
|
68
|
+
nonce: await litClient.getLatestBlockhash(),
|
|
69
|
+
litNodeClient: litClient,
|
|
70
|
+
});
|
|
71
|
+
return await generateAuthSig({
|
|
72
|
+
signer: wallet,
|
|
73
|
+
toSign,
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
return sessionSigs;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
*
|
|
81
|
+
* @param {LitNodeClient} litClient
|
|
82
|
+
* @param {Type.ExecuteUcanValidationOptions} options
|
|
83
|
+
* @returns
|
|
84
|
+
*/
|
|
85
|
+
export const executeUcanValidatoinAction = async (litClient, options) => {
|
|
86
|
+
const { sessionSigs, ...jsParams } = options;
|
|
87
|
+
const litActionResponse = await litClient.executeJs({
|
|
88
|
+
ipfsId: STORACHA_LIT_ACTION_CID,
|
|
89
|
+
sessionSigs,
|
|
90
|
+
jsParams,
|
|
91
|
+
});
|
|
92
|
+
if (!litActionResponse.response) {
|
|
93
|
+
throw new Error('Error getting lit action response.');
|
|
94
|
+
}
|
|
95
|
+
const parsedResponse = JSON.parse(
|
|
96
|
+
/** @type string*/ (litActionResponse.response));
|
|
97
|
+
const decryptedData = parsedResponse.decryptedString;
|
|
98
|
+
if (!decryptedData) {
|
|
99
|
+
let errorMsg;
|
|
100
|
+
if (parsedResponse.error)
|
|
101
|
+
errorMsg = parsedResponse.error;
|
|
102
|
+
throw new Error(`Decrypted data does not exist! Error message: ${errorMsg}`);
|
|
103
|
+
}
|
|
104
|
+
return decryptedData;
|
|
105
|
+
};
|
|
106
|
+
//# sourceMappingURL=lit.js.map
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Wallet } from 'ethers';
|
|
2
|
+
import { UnknownLink } from 'multiformats';
|
|
3
|
+
import { Client as StorachaClient } from '@storacha/client';
|
|
4
|
+
import { Result, Failure, Block } from '@ucanto/interface';
|
|
5
|
+
import { LitNodeClient } from '@lit-protocol/lit-node-client';
|
|
6
|
+
import { AccessControlConditions, AuthSig, SessionSigsMap } from '@lit-protocol/types';
|
|
7
|
+
import type { BlobLike, AnyLink, Signer, DID, SigAlg } from '@storacha/client/types';
|
|
8
|
+
export type { IPLDBlock } from '@ucanto/interface';
|
|
9
|
+
export type { SpaceDID } from '@storacha/capabilities/utils';
|
|
10
|
+
export type { UnknownFormat } from '@storacha/capabilities/types';
|
|
11
|
+
export type { Result, UnknownLink };
|
|
12
|
+
export type { BlobLike, AnyLink };
|
|
13
|
+
export interface EncryptedClient {
|
|
14
|
+
uploadEncryptedFile(file: BlobLike): Promise<AnyLink>;
|
|
15
|
+
retrieveAndDecryptFile(wallet: Wallet, cid: AnyLink, delegationCAR: Uint8Array): Promise<ReadableStream>;
|
|
16
|
+
}
|
|
17
|
+
export type EncryptedClientOptions = {
|
|
18
|
+
storachaClient: StorachaClient;
|
|
19
|
+
cryptoAdapter: CryptoAdapter;
|
|
20
|
+
litClient?: LitNodeClient;
|
|
21
|
+
gatewayURL?: URL;
|
|
22
|
+
};
|
|
23
|
+
export interface CryptoAdapter {
|
|
24
|
+
encryptStream(data: BlobLike): EncryptOutput;
|
|
25
|
+
decryptStream(encryptedData: ReadableStream, key: Uint8Array, iv: Uint8Array): ReadableStream;
|
|
26
|
+
}
|
|
27
|
+
export interface EncryptOutput {
|
|
28
|
+
key: Uint8Array;
|
|
29
|
+
iv: Uint8Array;
|
|
30
|
+
encryptedStream: ReadableStream;
|
|
31
|
+
}
|
|
32
|
+
export type EncryptedPayload = {
|
|
33
|
+
identityBoundCiphertext: string;
|
|
34
|
+
plaintextKeyHash: string;
|
|
35
|
+
encryptedBlobLike: BlobLike;
|
|
36
|
+
};
|
|
37
|
+
export type GenericAccessControlCondition = [Record<string, any>];
|
|
38
|
+
export interface EncryptedMetadataInput {
|
|
39
|
+
encryptedDataCID: string;
|
|
40
|
+
identityBoundCiphertext: string;
|
|
41
|
+
plaintextKeyHash: string;
|
|
42
|
+
accessControlConditions: GenericAccessControlCondition;
|
|
43
|
+
}
|
|
44
|
+
export interface EncryptedMetadata {
|
|
45
|
+
encryptedDataCID: UnknownLink;
|
|
46
|
+
identityBoundCiphertext: Uint8Array;
|
|
47
|
+
plaintextKeyHash: Uint8Array;
|
|
48
|
+
accessControlConditions: GenericAccessControlCondition;
|
|
49
|
+
}
|
|
50
|
+
export interface EncryptedMetadataView extends EncryptedMetadata {
|
|
51
|
+
/** Encode it to a CAR file. */
|
|
52
|
+
archive(): Promise<Result<Uint8Array>>;
|
|
53
|
+
archiveBlock(): Promise<Block>;
|
|
54
|
+
toJSON(): EncryptedMetadataInput;
|
|
55
|
+
}
|
|
56
|
+
export interface DecodeFailure extends Failure {
|
|
57
|
+
name: 'DecodeFailure';
|
|
58
|
+
}
|
|
59
|
+
export interface SessionSignatureOptions {
|
|
60
|
+
wallet: Wallet;
|
|
61
|
+
accessControlConditions: AccessControlConditions;
|
|
62
|
+
dataToEncryptHash: string;
|
|
63
|
+
expiration?: string;
|
|
64
|
+
capabilityAuthSigs?: AuthSig[];
|
|
65
|
+
}
|
|
66
|
+
export interface CreateDecryptWrappedInvocationOptions {
|
|
67
|
+
delegationCAR: Uint8Array;
|
|
68
|
+
issuer: Signer<DID, SigAlg>;
|
|
69
|
+
audience: `did:${string}:${string}`;
|
|
70
|
+
spaceDID: `did:key:${string}`;
|
|
71
|
+
resourceCID: AnyLink;
|
|
72
|
+
expiration: number;
|
|
73
|
+
}
|
|
74
|
+
export interface ExecuteUcanValidationOptions {
|
|
75
|
+
sessionSigs: SessionSigsMap;
|
|
76
|
+
spaceDID: `did:key:${string}`;
|
|
77
|
+
identityBoundCiphertext: string;
|
|
78
|
+
plaintextKeyHash: string;
|
|
79
|
+
accessControlConditions: AccessControlConditions;
|
|
80
|
+
wrappedInvocationJSON: string;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @param {string} str
|
|
4
|
+
* @returns {Uint8Array}
|
|
5
|
+
*/
|
|
6
|
+
export function stringToBytes(str: string): Uint8Array;
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @param {Uint8Array} bytes
|
|
10
|
+
* @returns {string}
|
|
11
|
+
*/
|
|
12
|
+
export function bytesToString(bytes: Uint8Array): string;
|
|
13
|
+
export function createDecryptWrappedInvocation({ delegationCAR, issuer, spaceDID, resourceCID, audience, expiration, }: Type.CreateDecryptWrappedInvocationOptions): Promise<import("@ucanto/server").ToString<Uint8Array<ArrayBufferLike>, string>>;
|
|
14
|
+
import * as Type from './types.js';
|
|
15
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.js"],"names":[],"mappings":"AA+CA;;;;GAIG;AACH,mCAHW,MAAM,GACJ,UAAU,CAItB;AAED;;;;GAIG;AACH,qCAHW,UAAU,GACR,MAAM,CAIlB;AApDM,wHAFI,IAAI,CAAC,qCAAqC,mFAoCpD;sBAxCqB,YAAY"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { DID } from '@ucanto/server';
|
|
2
|
+
import * as dagJSON from '@ipld/dag-json';
|
|
3
|
+
import { extract } from '@ucanto/core/delegation';
|
|
4
|
+
import * as Space from '@storacha/capabilities/space';
|
|
5
|
+
import * as Type from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @param {Type.CreateDecryptWrappedInvocationOptions} param0
|
|
9
|
+
*/
|
|
10
|
+
export const createDecryptWrappedInvocation = async ({ delegationCAR, issuer, spaceDID, resourceCID, audience, expiration, }) => {
|
|
11
|
+
const delegation = await extract(delegationCAR);
|
|
12
|
+
if (delegation.error) {
|
|
13
|
+
throw delegation.error;
|
|
14
|
+
}
|
|
15
|
+
const invocationOptions = {
|
|
16
|
+
issuer,
|
|
17
|
+
audience: DID.parse(audience),
|
|
18
|
+
with: spaceDID,
|
|
19
|
+
nb: {
|
|
20
|
+
resource: resourceCID,
|
|
21
|
+
},
|
|
22
|
+
expiration: expiration,
|
|
23
|
+
proofs: [delegation.ok],
|
|
24
|
+
};
|
|
25
|
+
const decryptWrappedInvocation = await Space.decrypt
|
|
26
|
+
.invoke(invocationOptions)
|
|
27
|
+
.delegate();
|
|
28
|
+
const carEncoded = await decryptWrappedInvocation.archive();
|
|
29
|
+
if (carEncoded.error) {
|
|
30
|
+
throw carEncoded.error;
|
|
31
|
+
}
|
|
32
|
+
return dagJSON.stringify(carEncoded.ok);
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
*
|
|
36
|
+
* @param {string} str
|
|
37
|
+
* @returns {Uint8Array}
|
|
38
|
+
*/
|
|
39
|
+
export function stringToBytes(str) {
|
|
40
|
+
return new TextEncoder().encode(str);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
*
|
|
44
|
+
* @param {Uint8Array} bytes
|
|
45
|
+
* @returns {string}
|
|
46
|
+
*/
|
|
47
|
+
export function bytesToString(bytes) {
|
|
48
|
+
return new TextDecoder().decode(bytes);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=utils.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@storacha/encrypt-upload-client",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.8-0",
|
|
5
|
+
"license": "Apache-2.0 OR MIT",
|
|
6
|
+
"description": "Client for upload and download encrypted files",
|
|
7
|
+
"author": "Storacha",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/storacha/upload-service.git",
|
|
14
|
+
"directory": "packages/encrypt-upload-client"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"storacha",
|
|
18
|
+
"lit protocol",
|
|
19
|
+
"web3",
|
|
20
|
+
"storage",
|
|
21
|
+
"upload",
|
|
22
|
+
"store",
|
|
23
|
+
"encrypt",
|
|
24
|
+
"decrypt",
|
|
25
|
+
"IPLD",
|
|
26
|
+
"UCAN",
|
|
27
|
+
"IPFS"
|
|
28
|
+
],
|
|
29
|
+
"main": "dist/index.js",
|
|
30
|
+
"types": "dist/index.d.ts",
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"!dist/**/*.js.map"
|
|
34
|
+
],
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"import": "./dist/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./crypto/adapters": {
|
|
41
|
+
"types": "./dist/crypto-adapters/node-crypto-adapter.d.ts",
|
|
42
|
+
"import": "./dist/crypto-adapters/node-crypto-adapter.js"
|
|
43
|
+
},
|
|
44
|
+
"./types": "./dist/types.js"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@ipld/car": "^5.4.0",
|
|
48
|
+
"@ipld/dag-cbor": "^9.0.6",
|
|
49
|
+
"@ipld/dag-json": "^10.2.3",
|
|
50
|
+
"@ipld/dag-ucan": "^3.4.5",
|
|
51
|
+
"@ipld/schema": "^6.0.6",
|
|
52
|
+
"@lit-protocol/auth-helpers": "^7.0.2",
|
|
53
|
+
"@lit-protocol/constants": "^7.0.2",
|
|
54
|
+
"@lit-protocol/contracts-sdk": "^7.0.2",
|
|
55
|
+
"@lit-protocol/encryption": "^7.0.2",
|
|
56
|
+
"@lit-protocol/lit-auth-client": "^7.0.3",
|
|
57
|
+
"@lit-protocol/lit-node-client": "^7.0.2",
|
|
58
|
+
"@ucanto/client": "^9.0.1",
|
|
59
|
+
"@ucanto/core": "^10.3.1",
|
|
60
|
+
"@ucanto/interface": "^10.2.0",
|
|
61
|
+
"@ucanto/principal": "^9.0.2",
|
|
62
|
+
"@ucanto/server": "^10.2.0",
|
|
63
|
+
"@ucanto/transport": "^9.1.1",
|
|
64
|
+
"@ucanto/validator": "^9.1.0",
|
|
65
|
+
"blockstore-core": "^3.0.0",
|
|
66
|
+
"carstream": "^2.1.0",
|
|
67
|
+
"dotenv": "^16.4.7",
|
|
68
|
+
"ethers": "5.7.1",
|
|
69
|
+
"ipfs-unixfs-exporter": "^10.0.0",
|
|
70
|
+
"multiformats": "^13.3.2",
|
|
71
|
+
"@storacha/capabilities": "^1.4.0",
|
|
72
|
+
"@storacha/upload-client": "^1.0.9",
|
|
73
|
+
"@storacha/client": "^1.2.4"
|
|
74
|
+
},
|
|
75
|
+
"devDependencies": {
|
|
76
|
+
"@lit-protocol/types": "^7.0.8",
|
|
77
|
+
"esbuild": "^0.25.1",
|
|
78
|
+
"typescript": "^5.7.3",
|
|
79
|
+
"@storacha/eslint-config": "^0.0.0"
|
|
80
|
+
},
|
|
81
|
+
"eslintConfig": {
|
|
82
|
+
"extends": [
|
|
83
|
+
"@storacha/eslint-config"
|
|
84
|
+
],
|
|
85
|
+
"parserOptions": {
|
|
86
|
+
"project": "./tsconfig.json"
|
|
87
|
+
},
|
|
88
|
+
"env": {
|
|
89
|
+
"es2022": true,
|
|
90
|
+
"mocha": true,
|
|
91
|
+
"browser": true,
|
|
92
|
+
"node": true
|
|
93
|
+
},
|
|
94
|
+
"ignorePatterns": [
|
|
95
|
+
"dist",
|
|
96
|
+
"docs",
|
|
97
|
+
"docs-generated",
|
|
98
|
+
"coverage",
|
|
99
|
+
"src/types.js",
|
|
100
|
+
"*.min.js"
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
"depcheck": {
|
|
104
|
+
"ignorePatterns": [
|
|
105
|
+
"dist"
|
|
106
|
+
],
|
|
107
|
+
"ignores": [
|
|
108
|
+
"@typescript-eslint/eslint-plugin",
|
|
109
|
+
"@typescript-eslint/parser",
|
|
110
|
+
"assert",
|
|
111
|
+
"c8"
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
"engines": {
|
|
115
|
+
"node": ">=18"
|
|
116
|
+
},
|
|
117
|
+
"engineStrict": true,
|
|
118
|
+
"scripts": {
|
|
119
|
+
"dev": "tsc --build --watch --preserveWatchOutput",
|
|
120
|
+
"clean": "rm -rf dist *.tsbuildinfo",
|
|
121
|
+
"lint": "tsc --build && eslint '**/*.{js,ts}' --ignore-pattern 'lit-actions/**' && prettier --check '**/*.{js,ts,yml,json}' --ignore-path ../../.gitignore",
|
|
122
|
+
"lint:fix": "tsc --build && eslint '**/*.{js,ts}' --fix && prettier --write '**/*.{js,ts,yml,json}' --ignore-path ../../.gitignore",
|
|
123
|
+
"build-actions": "node lit-actions/esbuild.js",
|
|
124
|
+
"attw": "attw --pack .",
|
|
125
|
+
"rc": "npm version prerelease --preid rc"
|
|
126
|
+
}
|
|
127
|
+
}
|