@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
package/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<h1 align="center">đ + đ<br/>Encrypt Upload Client</h1>
|
|
2
|
+
<p align="center">Use Lit Protocol and Storacha Network to enable private decentralized hot storage.</a></p>
|
|
3
|
+
|
|
4
|
+
## About
|
|
5
|
+
|
|
6
|
+
This library leverages @storacha/cli and @lit-protocol/lit-node-client to provide a simple interface for encrypting files with Lit Protocol and uploading them to the Storacha Network. It also enables anyone with a valid space/content/decrypt UCAN delegation to decrypt the file. With Lit Protocol, encryption keys are managed in a decentralized way, so you donât have to handle them yourself.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
You can add the `@storacha/encrypt-upload-client` package to your JavaScript or TypeScript project with `npm`:
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm @storacha/encrypt-upload-client
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
To use this library, you'll need to install `@storacha/cli` and `@lit-protocol/lit-node-client`, as they are required for initializationâthough the Lit client is optional. You must also provide a crypto adapter that implements the `CryptoAdapter` interface. A ready-to-use Node.js crypto adapter is already available.
|
|
19
|
+
|
|
20
|
+
#### CryptoAdapter Interface
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
interface CryptoAdapter {
|
|
24
|
+
encryptStream(data: BlobLike): EncryptOutput
|
|
25
|
+
decryptStream(encryptedData: ReadableStream, key: Uint8Array, iv: Uint8Array): ReadableStream
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface EncryptOutput {
|
|
29
|
+
key: Uint8Array,
|
|
30
|
+
iv: Uint8Array,
|
|
31
|
+
encryptedStream: ReadableStream
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
#### Example Usage
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
const encryptedClient = await EncryptClient.create({
|
|
39
|
+
storachaClient,
|
|
40
|
+
cryptoAdapter: new NodeCryptoAdapter(),
|
|
41
|
+
})
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Encryption
|
|
45
|
+
|
|
46
|
+
The encryption process automatically generates a custom Access Control Condition (ACC) based on the current space setup in your Storacha client. It then creates a symmetric key to encrypt the file and uses Lit Protocol to encrypt that key, so you donât have to manage it yourself. Once encrypted, both the file and the generated encrypted metadata are uploaded to Storacha.
|
|
47
|
+
|
|
48
|
+
#### Example Usage
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
const fileContent = await fs.promises.readFile('./README.md')
|
|
52
|
+
const blob = new Blob([fileContent])
|
|
53
|
+
const cid = await encryptedClient.uploadEncryptedFile(blob)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
You can find a full example in `examples/encrypt-test.js`.
|
|
57
|
+
|
|
58
|
+
### Decryption
|
|
59
|
+
|
|
60
|
+
To decrypt a file, you'll need the CID returned from `uploadEncryptedFile`, a UCAN delegation CAR with the `space/content/decrypt` capability (proving that the file owner has granted you decryption access), and an Ethereum wallet with available Capacity Credits on the Lit Network to cover the decryption cost.
|
|
61
|
+
|
|
62
|
+
For details on minting Capacity Credits, check out the [official documentation](https://developer.litprotocol.com/concepts/capacity-credits-concept).
|
|
63
|
+
|
|
64
|
+
#### Example Usage
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
const decryptedContent = await encryptedClient.retrieveAndDecryptFile(
|
|
68
|
+
wallet,
|
|
69
|
+
cid,
|
|
70
|
+
delegationCarBuffer
|
|
71
|
+
)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
You can find a full example in `examples/decrypt-test.js`.
|
|
75
|
+
|
|
76
|
+
## Contributing
|
|
77
|
+
|
|
78
|
+
Feel free to join in. All welcome. Please [open an issue](https://github.com/storacha/upload-service/issues)!
|
|
79
|
+
|
|
80
|
+
## License
|
|
81
|
+
|
|
82
|
+
Dual-licensed under [MIT + Apache 2.0](https://github.com/storacha/upload-service/blob/main/license.md)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/config/constants.js"],"names":[],"mappings":"AAAA,sCACE,gDAAgD,CAAA;AAClD,8BAAsD"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export default env;
|
|
2
|
+
declare const env: Schema.InferStruct<{
|
|
3
|
+
WALLET_PK: Schema.StringSchema<string, unknown>;
|
|
4
|
+
LIT_NETWORK: Schema.DefaultSchema<"custom" | "datil" | "datil-dev" | "datil-test", unknown>;
|
|
5
|
+
LIT_DEBUG: Schema.DefaultSchema<boolean, unknown>;
|
|
6
|
+
}>;
|
|
7
|
+
import { Schema } from '@ucanto/core';
|
|
8
|
+
//# sourceMappingURL=env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/config/env.js"],"names":[],"mappings":";AAuBA;;;;GAAsC;uBAtBf,cAAc"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
import { Schema } from '@ucanto/core';
|
|
3
|
+
import { LIT_NETWORK } from '@lit-protocol/constants';
|
|
4
|
+
dotenv.config();
|
|
5
|
+
const envSchema = Schema.struct({
|
|
6
|
+
WALLET_PK: Schema.text(),
|
|
7
|
+
LIT_NETWORK: Schema.enum([
|
|
8
|
+
LIT_NETWORK.Custom,
|
|
9
|
+
LIT_NETWORK.Datil,
|
|
10
|
+
LIT_NETWORK.DatilDev,
|
|
11
|
+
LIT_NETWORK.DatilTest,
|
|
12
|
+
]).default(LIT_NETWORK.DatilTest),
|
|
13
|
+
LIT_DEBUG: Schema.boolean().default(false),
|
|
14
|
+
});
|
|
15
|
+
const processEnv = {
|
|
16
|
+
LIT_DEBUG: process.env.LIT_DEBUG,
|
|
17
|
+
LIT_NETWORK: process.env.LIT_NETWORK,
|
|
18
|
+
WALLET_PK: process.env.WALLET_PK,
|
|
19
|
+
};
|
|
20
|
+
const env = envSchema.from(processEnv);
|
|
21
|
+
export default env;
|
|
22
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const accessServiceURL: URL;
|
|
2
|
+
export const accessServicePrincipal: client.UCAN.PrincipalView<"did:web:web3.storage">;
|
|
3
|
+
export const accessServiceConnection: client.ConnectionView<any>;
|
|
4
|
+
export const uploadServiceURL: URL;
|
|
5
|
+
export const uploadServicePrincipal: client.UCAN.PrincipalView<"did:web:web3.storage">;
|
|
6
|
+
export const uploadServiceConnection: client.ConnectionView<any>;
|
|
7
|
+
export const filecoinServiceURL: URL;
|
|
8
|
+
export const filecoinServicePrincipal: client.UCAN.PrincipalView<"did:web:web3.storage">;
|
|
9
|
+
export const filecoinServiceConnection: client.ConnectionView<any>;
|
|
10
|
+
/** @type {import('@storacha/client/types').ServiceConf} */
|
|
11
|
+
export const serviceConf: import("@storacha/client/types").ServiceConf;
|
|
12
|
+
export const receiptsEndpoint: URL;
|
|
13
|
+
import * as client from '@ucanto/client';
|
|
14
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/config/service.js"],"names":[],"mappings":"AAOA,mCAA2D;AAC3D,uFAAqE;AAErE,iEAIE;AAEF,mCAA2D;AAC3D,uFAAqE;AAErE,iEAIE;AAEF,qCAA6D;AAC7D,yFAAuE;AAEvE,mEAIE;AAEF,2DAA2D;AAC3D,0BADW,OAAO,wBAAwB,EAAE,WAAW,CAKtD;AAED,mCAAyE;wBAzCjD,gBAAgB"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as client from '@ucanto/client';
|
|
2
|
+
import { CAR, HTTP } from '@ucanto/transport';
|
|
3
|
+
import * as DID from '@ipld/dag-ucan/did';
|
|
4
|
+
const storachaServiceURL = 'https://up.web3.storage';
|
|
5
|
+
const storachaPrincipalDID = 'did:web:web3.storage';
|
|
6
|
+
export const accessServiceURL = new URL(storachaServiceURL);
|
|
7
|
+
export const accessServicePrincipal = DID.parse(storachaPrincipalDID);
|
|
8
|
+
export const accessServiceConnection = client.connect({
|
|
9
|
+
id: accessServicePrincipal,
|
|
10
|
+
codec: CAR.outbound,
|
|
11
|
+
channel: HTTP.open({ url: accessServiceURL, method: 'POST' }),
|
|
12
|
+
});
|
|
13
|
+
export const uploadServiceURL = new URL(storachaServiceURL);
|
|
14
|
+
export const uploadServicePrincipal = DID.parse(storachaPrincipalDID);
|
|
15
|
+
export const uploadServiceConnection = client.connect({
|
|
16
|
+
id: uploadServicePrincipal,
|
|
17
|
+
codec: CAR.outbound,
|
|
18
|
+
channel: HTTP.open({ url: accessServiceURL, method: 'POST' }),
|
|
19
|
+
});
|
|
20
|
+
export const filecoinServiceURL = new URL(storachaServiceURL);
|
|
21
|
+
export const filecoinServicePrincipal = DID.parse(storachaPrincipalDID);
|
|
22
|
+
export const filecoinServiceConnection = client.connect({
|
|
23
|
+
id: filecoinServicePrincipal,
|
|
24
|
+
codec: CAR.outbound,
|
|
25
|
+
channel: HTTP.open({ url: accessServiceURL, method: 'POST' }),
|
|
26
|
+
});
|
|
27
|
+
/** @type {import('@storacha/client/types').ServiceConf} */
|
|
28
|
+
export const serviceConf = {
|
|
29
|
+
access: accessServiceConnection,
|
|
30
|
+
upload: uploadServiceConnection,
|
|
31
|
+
filecoin: filecoinServiceConnection,
|
|
32
|
+
};
|
|
33
|
+
export const receiptsEndpoint = new URL(`${storachaServiceURL}/receipt/`);
|
|
34
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** @implements {Type.EncryptedClient} */
|
|
2
|
+
export class EncryptedClient implements Type.EncryptedClient {
|
|
3
|
+
/**
|
|
4
|
+
* @param {import('@storacha/client').Client} storachaClient
|
|
5
|
+
* @param {Type.CryptoAdapter} cryptoAdapter
|
|
6
|
+
* @param {import('@lit-protocol/lit-node-client').LitNodeClient} litClient
|
|
7
|
+
* @param {URL} gatewayURL
|
|
8
|
+
*/
|
|
9
|
+
constructor(storachaClient: import("@storacha/client").Client, cryptoAdapter: Type.CryptoAdapter, litClient: import("@lit-protocol/lit-node-client").LitNodeClient, gatewayURL: URL);
|
|
10
|
+
/**
|
|
11
|
+
* @type {Type.CryptoAdapter}
|
|
12
|
+
* @protected
|
|
13
|
+
*/
|
|
14
|
+
protected _cryptoAdapter: Type.CryptoAdapter;
|
|
15
|
+
/**
|
|
16
|
+
* @type {import('@storacha/client').Client}
|
|
17
|
+
* @protected
|
|
18
|
+
*/
|
|
19
|
+
protected _storachaClient: import("@storacha/client").Client;
|
|
20
|
+
/**
|
|
21
|
+
* @type {import('@lit-protocol/lit-node-client').LitNodeClient}
|
|
22
|
+
* @protected
|
|
23
|
+
*/
|
|
24
|
+
protected _litClient: import("@lit-protocol/lit-node-client").LitNodeClient;
|
|
25
|
+
/**
|
|
26
|
+
* @type {URL}
|
|
27
|
+
* @protected
|
|
28
|
+
*/
|
|
29
|
+
protected _gatewayURL: URL;
|
|
30
|
+
/**
|
|
31
|
+
* Upload an encrypted file to the Storacha network
|
|
32
|
+
*
|
|
33
|
+
* @param {Type.BlobLike} file - The file to upload
|
|
34
|
+
* @returns {Promise<Type.AnyLink>} - The link to the uploaded file
|
|
35
|
+
*/
|
|
36
|
+
uploadEncryptedFile(file: Type.BlobLike): Promise<Type.AnyLink>;
|
|
37
|
+
/**
|
|
38
|
+
* Retrieve and decrypt a file from the Storacha network
|
|
39
|
+
*
|
|
40
|
+
* @param {Wallet} wallet - The wallet to use to decrypt the file
|
|
41
|
+
* @param {Type.AnyLink} cid - The link to the file to retrieve
|
|
42
|
+
* @param {Uint8Array} delegationCAR - The delegation that gives permission to decrypt the file
|
|
43
|
+
* @returns {Promise<ReadableStream>} - The decrypted file
|
|
44
|
+
*/
|
|
45
|
+
retrieveAndDecryptFile(wallet: Wallet, cid: Type.AnyLink, delegationCAR: Uint8Array): Promise<ReadableStream>;
|
|
46
|
+
}
|
|
47
|
+
export function create(options: Type.EncryptedClientOptions): Promise<EncryptedClient>;
|
|
48
|
+
import * as Type from '../types.js';
|
|
49
|
+
import { Wallet } from 'ethers';
|
|
50
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.js"],"names":[],"mappings":"AAQA,yCAAyC;AACzC,wCADiB,IAAI,CAAC,eAAe;IA0BnC;;;;;OAKG;IACH,4BALW,OAAO,kBAAkB,EAAE,MAAM,iBACjC,IAAI,CAAC,aAAa,aAClB,OAAO,+BAA+B,EAAE,aAAa,cACrD,GAAG,EAOb;IAnCD;;;OAGG;IACH,0BAHU,IAAI,CAAC,aAAa,CAGd;IAEd;;;OAGG;IACH,2BAHU,OAAO,kBAAkB,EAAE,MAAM,CAG5B;IAEf;;;OAGG;IACH,sBAHU,OAAO,+BAA+B,EAAE,aAAa,CAGrD;IAEV;;;OAGG;IACH,uBAHU,GAAG,CAGF;IAeX;;;;;OAKG;IACH,0BAHW,IAAI,CAAC,QAAQ,GACX,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CASjC;IAED;;;;;;;OAOG;IACH,+BALW,MAAM,OACN,IAAI,CAAC,OAAO,iBACZ,UAAU,GACR,OAAO,CAAC,cAAc,CAAC,CAYnC;CACF;AAWM,gCAFI,IAAI,CAAC,sBAAsB,4BAcrC;sBAtGqB,aAAa;uBAFZ,QAAQ"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Wallet } from 'ethers';
|
|
2
|
+
import * as Type from '../types.js';
|
|
3
|
+
import { getLitClient } from '../protocols/lit.js';
|
|
4
|
+
import { GATEWAY_URL } from '../config/constants.js';
|
|
5
|
+
import { encryptAndUpload } from '../handlers/encrypt-handler.js';
|
|
6
|
+
import { retrieveAndDecrypt } from '../handlers/decrypt-handler.js';
|
|
7
|
+
/** @implements {Type.EncryptedClient} */
|
|
8
|
+
export class EncryptedClient {
|
|
9
|
+
/**
|
|
10
|
+
* @type {Type.CryptoAdapter}
|
|
11
|
+
* @protected
|
|
12
|
+
*/
|
|
13
|
+
_cryptoAdapter;
|
|
14
|
+
/**
|
|
15
|
+
* @type {import('@storacha/client').Client}
|
|
16
|
+
* @protected
|
|
17
|
+
*/
|
|
18
|
+
_storachaClient;
|
|
19
|
+
/**
|
|
20
|
+
* @type {import('@lit-protocol/lit-node-client').LitNodeClient}
|
|
21
|
+
* @protected
|
|
22
|
+
*/
|
|
23
|
+
_litClient;
|
|
24
|
+
/**
|
|
25
|
+
* @type {URL}
|
|
26
|
+
* @protected
|
|
27
|
+
*/
|
|
28
|
+
_gatewayURL;
|
|
29
|
+
/**
|
|
30
|
+
* @param {import('@storacha/client').Client} storachaClient
|
|
31
|
+
* @param {Type.CryptoAdapter} cryptoAdapter
|
|
32
|
+
* @param {import('@lit-protocol/lit-node-client').LitNodeClient} litClient
|
|
33
|
+
* @param {URL} gatewayURL
|
|
34
|
+
*/
|
|
35
|
+
constructor(storachaClient, cryptoAdapter, litClient, gatewayURL) {
|
|
36
|
+
this._storachaClient = storachaClient;
|
|
37
|
+
this._cryptoAdapter = cryptoAdapter;
|
|
38
|
+
this._litClient = litClient;
|
|
39
|
+
this._gatewayURL = gatewayURL;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Upload an encrypted file to the Storacha network
|
|
43
|
+
*
|
|
44
|
+
* @param {Type.BlobLike} file - The file to upload
|
|
45
|
+
* @returns {Promise<Type.AnyLink>} - The link to the uploaded file
|
|
46
|
+
*/
|
|
47
|
+
async uploadEncryptedFile(file) {
|
|
48
|
+
return encryptAndUpload(this._storachaClient, this._litClient, this._cryptoAdapter, file);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Retrieve and decrypt a file from the Storacha network
|
|
52
|
+
*
|
|
53
|
+
* @param {Wallet} wallet - The wallet to use to decrypt the file
|
|
54
|
+
* @param {Type.AnyLink} cid - The link to the file to retrieve
|
|
55
|
+
* @param {Uint8Array} delegationCAR - The delegation that gives permission to decrypt the file
|
|
56
|
+
* @returns {Promise<ReadableStream>} - The decrypted file
|
|
57
|
+
*/
|
|
58
|
+
async retrieveAndDecryptFile(wallet, cid, delegationCAR) {
|
|
59
|
+
return retrieveAndDecrypt(this._storachaClient, this._litClient, this._cryptoAdapter, this._gatewayURL, wallet, cid, delegationCAR);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Creates a new EncryptClient.
|
|
64
|
+
*
|
|
65
|
+
* If no Lit Client is provided, one will be created based on the LIT_NETWORK environment variable, defaulting to "DatilTest" if not set.
|
|
66
|
+
*
|
|
67
|
+
* If no Gateway URL is provided, the default value of 'https://w3s.link' will be used.
|
|
68
|
+
*
|
|
69
|
+
* @param {Type.EncryptedClientOptions} options
|
|
70
|
+
*/
|
|
71
|
+
export const create = async (options) => {
|
|
72
|
+
const litClient = options.litClient ?? (await getLitClient());
|
|
73
|
+
const cryptoAdapter = options.cryptoAdapter;
|
|
74
|
+
const gatewayURL = options.gatewayURL ?? GATEWAY_URL;
|
|
75
|
+
const storachaClient = options.storachaClient;
|
|
76
|
+
return new EncryptedClient(storachaClient, cryptoAdapter, litClient, gatewayURL);
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const version: "encrypted-metadata@0.1";
|
|
2
|
+
export const EncryptedMetadataSchema: Schema.VariantSchema<{
|
|
3
|
+
"encrypted-metadata@0.1": Schema.StructSchema<{
|
|
4
|
+
encryptedDataCID: Schema.Schema<Link.Link<unknown, number, number, 0 | 1>, any>;
|
|
5
|
+
identityBoundCiphertext: Schema.Schema<Uint8Array<ArrayBufferLike>, unknown>;
|
|
6
|
+
plaintextKeyHash: Schema.Schema<Uint8Array<ArrayBufferLike>, unknown>;
|
|
7
|
+
accessControlConditions: Schema.Schema<Schema.Dictionary<string, unknown>[], unknown>;
|
|
8
|
+
}, unknown>;
|
|
9
|
+
}, unknown>;
|
|
10
|
+
export const EncryptedMetadataInputSchema: Schema.StructSchema<{
|
|
11
|
+
encryptedDataCID: Schema.StringSchema<string, unknown>;
|
|
12
|
+
identityBoundCiphertext: Schema.StringSchema<string, unknown>;
|
|
13
|
+
plaintextKeyHash: Schema.StringSchema<string, unknown>;
|
|
14
|
+
accessControlConditions: Schema.Schema<Schema.Dictionary<string, unknown>[], unknown>;
|
|
15
|
+
}, unknown>;
|
|
16
|
+
export function create(encryptedMetadataInput: Types.EncryptedMetadata | Types.EncryptedMetadataInput): Types.EncryptedMetadataView;
|
|
17
|
+
export function toJSON(encryptedMetadata: Types.EncryptedMetadataView): Types.EncryptedMetadataInput;
|
|
18
|
+
export function parse(encryptedMetadataInput: Types.EncryptedMetadataInput): Types.EncryptedMetadata;
|
|
19
|
+
export function archiveBlock(encryptedMetadataInput: Types.EncryptedMetadata): Promise<import("@ucanto/interface").Block>;
|
|
20
|
+
export function archive(encryptedMetadataInput: Types.EncryptedMetadata): Promise<Types.Result<Uint8Array>>;
|
|
21
|
+
export function extract(archive: Uint8Array): Types.Result<Types.EncryptedMetadataView, Types.UnknownFormat>;
|
|
22
|
+
export function view({ root }: {
|
|
23
|
+
root: Types.IPLDBlock;
|
|
24
|
+
}): Types.Result<Types.EncryptedMetadataView, Types.UnknownFormat>;
|
|
25
|
+
import * as Link from 'multiformats/link';
|
|
26
|
+
import { Schema } from '@ucanto/core';
|
|
27
|
+
import * as Types from '../types.js';
|
|
28
|
+
//# sourceMappingURL=encrypted-metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encrypted-metadata.d.ts","sourceRoot":"","sources":["../../src/core/encrypted-metadata.js"],"names":[],"mappings":"AAUA,sBAAuB,wBAAwB,CAAA;AAE/C;;;;;;;YAUE;AAEF;;;;;YAQE;AA8EK,+CAHI,KAAK,CAAC,iBAAiB,GAAC,KAAK,CAAC,sBAAsB,GAClD,KAAK,CAAC,qBAAqB,CAGO;AAMxC,0CAHI,KAAK,CAAC,qBAAqB,GACzB,KAAK,CAAC,sBAAsB,CASvC;AAMK,8CAHI,KAAK,CAAC,sBAAsB,GAC1B,KAAK,CAAC,iBAAiB,CASlC;AAMK,qDAHI,KAAK,CAAC,iBAAiB,GACrB,OAAO,CAAC,OAAO,mBAAmB,EAAE,KAAK,CAAC,CAOtD;AAMM,gDAHI,KAAK,CAAC,iBAAiB,GACrB,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAK7C;AAMM,iCAHI,UAAU,GACR,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,CAAC,aAAa,CAAC,CAiB1E;AAOM,+BAHJ;IAAgC,IAAI,EAA5B,KAAK,CAAC,SAAS;CACvB,GAAU,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,CAAC,aAAa,CAAC,CAe1E;sBApMqB,mBAAmB;uBAEF,cAAc;uBAE9B,aAAa"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { CID } from 'multiformats';
|
|
2
|
+
import * as dagCBOR from '@ipld/dag-cbor';
|
|
3
|
+
import * as Link from 'multiformats/link';
|
|
4
|
+
import { sha256 } from 'multiformats/hashes/sha2';
|
|
5
|
+
import { CAR, ok, error, Schema } from '@ucanto/core';
|
|
6
|
+
import * as Types from '../types.js';
|
|
7
|
+
import { UnknownFormat } from './errors.js';
|
|
8
|
+
import { stringToBytes, bytesToString } from '../utils.js';
|
|
9
|
+
export const version = 'encrypted-metadata@0.1';
|
|
10
|
+
export const EncryptedMetadataSchema = Schema.variant({
|
|
11
|
+
[version]: Schema.struct({
|
|
12
|
+
encryptedDataCID: Schema.link(),
|
|
13
|
+
identityBoundCiphertext: Schema.bytes(),
|
|
14
|
+
plaintextKeyHash: Schema.bytes(),
|
|
15
|
+
accessControlConditions: Schema.dictionary({
|
|
16
|
+
key: Schema.text(),
|
|
17
|
+
value: Schema.unknown(),
|
|
18
|
+
}).array(),
|
|
19
|
+
}),
|
|
20
|
+
});
|
|
21
|
+
export const EncryptedMetadataInputSchema = Schema.struct({
|
|
22
|
+
encryptedDataCID: Schema.string(),
|
|
23
|
+
identityBoundCiphertext: Schema.string(),
|
|
24
|
+
plaintextKeyHash: Schema.string(),
|
|
25
|
+
accessControlConditions: Schema.dictionary({
|
|
26
|
+
key: Schema.text(),
|
|
27
|
+
value: Schema.unknown(),
|
|
28
|
+
}).array(),
|
|
29
|
+
});
|
|
30
|
+
/** @implements {Types.EncryptedMetadataView} */
|
|
31
|
+
class EncryptedMetadata {
|
|
32
|
+
#encryptedDataCID;
|
|
33
|
+
#identityBoundCiphertext;
|
|
34
|
+
#plaintextKeyHash;
|
|
35
|
+
#accessControlConditions;
|
|
36
|
+
/** @param {Types.EncryptedMetadata|Types.EncryptedMetadataInput} encryptedMetadataInput */
|
|
37
|
+
constructor(encryptedMetadataInput) {
|
|
38
|
+
/** @type {Types.EncryptedMetadata} */
|
|
39
|
+
let encryptedMetadata;
|
|
40
|
+
if (EncryptedMetadataInputSchema.is(encryptedMetadataInput)) {
|
|
41
|
+
encryptedMetadata = parse(
|
|
42
|
+
/** @type {Types.EncryptedMetadataInput} */ (encryptedMetadataInput));
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
encryptedMetadata = /** @type {Types.EncryptedMetadata} */ (encryptedMetadataInput);
|
|
46
|
+
}
|
|
47
|
+
this.#encryptedDataCID = encryptedMetadata.encryptedDataCID;
|
|
48
|
+
this.#identityBoundCiphertext = encryptedMetadata.identityBoundCiphertext;
|
|
49
|
+
this.#plaintextKeyHash = encryptedMetadata.plaintextKeyHash;
|
|
50
|
+
this.#accessControlConditions =
|
|
51
|
+
encryptedMetadataInput.accessControlConditions;
|
|
52
|
+
}
|
|
53
|
+
get encryptedDataCID() {
|
|
54
|
+
return this.#encryptedDataCID;
|
|
55
|
+
}
|
|
56
|
+
get identityBoundCiphertext() {
|
|
57
|
+
return this.#identityBoundCiphertext;
|
|
58
|
+
}
|
|
59
|
+
get plaintextKeyHash() {
|
|
60
|
+
return this.#plaintextKeyHash;
|
|
61
|
+
}
|
|
62
|
+
get accessControlConditions() {
|
|
63
|
+
return this.#accessControlConditions;
|
|
64
|
+
}
|
|
65
|
+
archive() {
|
|
66
|
+
/** @type {Types.EncryptedMetadata} */
|
|
67
|
+
const input = {
|
|
68
|
+
encryptedDataCID: this.encryptedDataCID,
|
|
69
|
+
identityBoundCiphertext: this.identityBoundCiphertext,
|
|
70
|
+
plaintextKeyHash: this.plaintextKeyHash,
|
|
71
|
+
accessControlConditions: this.accessControlConditions,
|
|
72
|
+
};
|
|
73
|
+
return archive(input);
|
|
74
|
+
}
|
|
75
|
+
archiveBlock() {
|
|
76
|
+
/** @type {Types.EncryptedMetadata} */
|
|
77
|
+
const input = {
|
|
78
|
+
encryptedDataCID: this.encryptedDataCID,
|
|
79
|
+
identityBoundCiphertext: this.identityBoundCiphertext,
|
|
80
|
+
plaintextKeyHash: this.plaintextKeyHash,
|
|
81
|
+
accessControlConditions: this.accessControlConditions,
|
|
82
|
+
};
|
|
83
|
+
return archiveBlock(input);
|
|
84
|
+
}
|
|
85
|
+
/** @returns {Types.EncryptedMetadataInput} */
|
|
86
|
+
toJSON() {
|
|
87
|
+
return toJSON(this);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* @param {Types.EncryptedMetadata|Types.EncryptedMetadataInput} encryptedMetadataInput
|
|
92
|
+
* @returns {Types.EncryptedMetadataView}
|
|
93
|
+
*/
|
|
94
|
+
export const create = (encryptedMetadataInput) => new EncryptedMetadata(encryptedMetadataInput);
|
|
95
|
+
/**
|
|
96
|
+
* @param {Types.EncryptedMetadataView} encryptedMetadata
|
|
97
|
+
* @returns {Types.EncryptedMetadataInput}
|
|
98
|
+
*/
|
|
99
|
+
export const toJSON = (encryptedMetadata) => ({
|
|
100
|
+
encryptedDataCID: encryptedMetadata.encryptedDataCID.toString(),
|
|
101
|
+
identityBoundCiphertext: bytesToString(encryptedMetadata.identityBoundCiphertext),
|
|
102
|
+
plaintextKeyHash: bytesToString(encryptedMetadata.plaintextKeyHash),
|
|
103
|
+
accessControlConditions: encryptedMetadata.accessControlConditions,
|
|
104
|
+
});
|
|
105
|
+
/**
|
|
106
|
+
* @param {Types.EncryptedMetadataInput} encryptedMetadataInput
|
|
107
|
+
* @returns {Types.EncryptedMetadata}
|
|
108
|
+
*/
|
|
109
|
+
export const parse = (encryptedMetadataInput) => ({
|
|
110
|
+
encryptedDataCID: CID.parse(encryptedMetadataInput.encryptedDataCID),
|
|
111
|
+
identityBoundCiphertext: stringToBytes(encryptedMetadataInput.identityBoundCiphertext),
|
|
112
|
+
plaintextKeyHash: stringToBytes(encryptedMetadataInput.plaintextKeyHash),
|
|
113
|
+
accessControlConditions: encryptedMetadataInput.accessControlConditions,
|
|
114
|
+
});
|
|
115
|
+
/**
|
|
116
|
+
* @param {Types.EncryptedMetadata} encryptedMetadataInput
|
|
117
|
+
* @returns {Promise<import('@ucanto/interface').Block>}
|
|
118
|
+
*/
|
|
119
|
+
export const archiveBlock = async (encryptedMetadataInput) => {
|
|
120
|
+
const bytes = dagCBOR.encode({ [version]: encryptedMetadataInput });
|
|
121
|
+
const digest = await sha256.digest(bytes);
|
|
122
|
+
const cid = Link.create(dagCBOR.code, digest);
|
|
123
|
+
return { cid, bytes };
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* @param {Types.EncryptedMetadata} encryptedMetadataInput
|
|
127
|
+
* @returns {Promise<Types.Result<Uint8Array>>}
|
|
128
|
+
*/
|
|
129
|
+
export const archive = async (encryptedMetadataInput) => {
|
|
130
|
+
const block = await archiveBlock(encryptedMetadataInput);
|
|
131
|
+
return ok(CAR.encode({ roots: [block] }));
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* @param {Uint8Array} archive
|
|
135
|
+
* @returns {Types.Result<Types.EncryptedMetadataView, Types.UnknownFormat>}
|
|
136
|
+
*/
|
|
137
|
+
export const extract = (archive) => {
|
|
138
|
+
const { roots } = CAR.decode(archive);
|
|
139
|
+
if (!roots.length) {
|
|
140
|
+
return error(new UnknownFormat('missing root block'));
|
|
141
|
+
}
|
|
142
|
+
const { code } = roots[0].cid;
|
|
143
|
+
if (code !== dagCBOR.code) {
|
|
144
|
+
return error(new UnknownFormat(`unexpected root CID codec: 0x${code.toString(16)}`));
|
|
145
|
+
}
|
|
146
|
+
return view({ root: roots[0] });
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* @param {object} source
|
|
150
|
+
* @param {Types.IPLDBlock} source.root
|
|
151
|
+
* @returns {Types.Result<Types.EncryptedMetadataView, Types.UnknownFormat>}
|
|
152
|
+
*/
|
|
153
|
+
export const view = ({ root }) => {
|
|
154
|
+
const value = dagCBOR.decode(root.bytes);
|
|
155
|
+
const [version, encryptedMetadataData] = EncryptedMetadataSchema.match(value);
|
|
156
|
+
switch (version) {
|
|
157
|
+
case version: {
|
|
158
|
+
const encryptedMetadata = create(
|
|
159
|
+
/** @type {Types.EncryptedMetadata}*/ (encryptedMetadataData));
|
|
160
|
+
return ok(encryptedMetadata);
|
|
161
|
+
}
|
|
162
|
+
default:
|
|
163
|
+
return error(new UnknownFormat(`unknown index version: ${version}`));
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
//# sourceMappingURL=encrypted-metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.js"],"names":[],"mappings":"AAEA;IAGE,+BAA+B;IAC/B,qBADY,MAAM,EAKjB;IAFC,sBAAkD;;CAOrD;wBAfuB,cAAc"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Failure } from '@ucanto/core';
|
|
2
|
+
export class UnknownFormat extends Failure {
|
|
3
|
+
#reason;
|
|
4
|
+
/** @param {string} [reason] */
|
|
5
|
+
constructor(reason) {
|
|
6
|
+
super();
|
|
7
|
+
this.name = /** @type {const} */ ('UnknownFormat');
|
|
8
|
+
this.#reason = reason;
|
|
9
|
+
}
|
|
10
|
+
describe() {
|
|
11
|
+
return this.#reason ?? 'unknown format';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** @implements {Type.CryptoAdapter} */
|
|
2
|
+
export class NodeCryptoAdapter implements Type.CryptoAdapter {
|
|
3
|
+
/** @param {Type.BlobLike} data */
|
|
4
|
+
encryptStream(data: Type.BlobLike): {
|
|
5
|
+
key: Buffer<ArrayBufferLike>;
|
|
6
|
+
iv: Buffer<ArrayBufferLike>;
|
|
7
|
+
encryptedStream: ReadableStream<any>;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* @param {ReadableStream} encryptedData
|
|
11
|
+
* @param {Uint8Array} key
|
|
12
|
+
* @param {Uint8Array} iv
|
|
13
|
+
*/
|
|
14
|
+
decryptStream(encryptedData: ReadableStream, key: Uint8Array, iv: Uint8Array): ReadableStream<any>;
|
|
15
|
+
}
|
|
16
|
+
import * as Type from '../types.js';
|
|
17
|
+
//# sourceMappingURL=node-crypto-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-crypto-adapter.d.ts","sourceRoot":"","sources":["../../src/crypto-adapters/node-crypto-adapter.js"],"names":[],"mappings":"AAMA,uCAAuC;AACvC,0CADiB,IAAI,CAAC,aAAa;IAEjC,mCAAmC;IACnC,oBADY,IAAI,CAAC,QAAQ;;;;MA+BxB;IAED;;;;OAIG;IACH,6BAJW,cAAc,OACd,UAAU,MACV,UAAU,uBA8BpB;CACF;sBAzEqB,aAAa"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { randomBytes, createCipheriv, createDecipheriv } from 'crypto';
|
|
2
|
+
import * as Type from '../types.js';
|
|
3
|
+
const ENCRYPTION_ALGORITHM = 'aes-256-cbc';
|
|
4
|
+
/** @implements {Type.CryptoAdapter} */
|
|
5
|
+
export class NodeCryptoAdapter {
|
|
6
|
+
/** @param {Type.BlobLike} data */
|
|
7
|
+
encryptStream(data) {
|
|
8
|
+
const symmetricKey = randomBytes(32); // 256 bits for AES-256
|
|
9
|
+
const initializationVector = randomBytes(16); // 16 bytes for AES
|
|
10
|
+
const cipher = createCipheriv(ENCRYPTION_ALGORITHM, symmetricKey, initializationVector);
|
|
11
|
+
const encryptStream = new TransformStream({
|
|
12
|
+
transform: async (chunk, controller) => {
|
|
13
|
+
const encryptedChunk = cipher.update(chunk);
|
|
14
|
+
if (encryptedChunk.length) {
|
|
15
|
+
controller.enqueue(encryptedChunk);
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
flush: (controller) => {
|
|
19
|
+
const final = cipher.final();
|
|
20
|
+
if (final.length) {
|
|
21
|
+
controller.enqueue(final);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
key: symmetricKey,
|
|
27
|
+
iv: initializationVector,
|
|
28
|
+
encryptedStream: data.stream().pipeThrough(encryptStream),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* @param {ReadableStream} encryptedData
|
|
33
|
+
* @param {Uint8Array} key
|
|
34
|
+
* @param {Uint8Array} iv
|
|
35
|
+
*/
|
|
36
|
+
decryptStream(encryptedData, key, iv) {
|
|
37
|
+
const decipher = createDecipheriv(ENCRYPTION_ALGORITHM, key, iv);
|
|
38
|
+
const decryptor = new TransformStream({
|
|
39
|
+
async transform(chunk, controller) {
|
|
40
|
+
try {
|
|
41
|
+
const decryptedChunk = decipher.update(chunk);
|
|
42
|
+
if (decryptedChunk.length > 0) {
|
|
43
|
+
controller.enqueue(decryptedChunk);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
controller.error(err);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
flush(controller) {
|
|
51
|
+
try {
|
|
52
|
+
const finalChunk = decipher.final();
|
|
53
|
+
if (finalChunk.length > 0) {
|
|
54
|
+
controller.enqueue(finalChunk);
|
|
55
|
+
}
|
|
56
|
+
controller.terminate();
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
controller.error(err);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
return encryptedData.pipeThrough(decryptor);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=node-crypto-adapter.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {Type.CryptoAdapter} cryptoAdapter - The crypto adapter responsible for performing
|
|
3
|
+
* encryption and decryption operations.
|
|
4
|
+
* @param {string} combinedKey
|
|
5
|
+
* @param {Uint8Array} content
|
|
6
|
+
*/
|
|
7
|
+
export function decryptFileWithKey(cryptoAdapter: Type.CryptoAdapter, combinedKey: string, content: Uint8Array): ReadableStream<any>;
|
|
8
|
+
export function retrieveAndDecrypt(storachaClient: import("@storacha/client").Client, litClient: import("@lit-protocol/lit-node-client").LitNodeClient, cryptoAdapter: Type.CryptoAdapter, gatewayURL: URL, wallet: Wallet, cid: Type.AnyLink, delegationCAR: Uint8Array): Promise<ReadableStream<any>>;
|
|
9
|
+
import * as Type from '../types.js';
|
|
10
|
+
import { Wallet } from 'ethers';
|
|
11
|
+
//# sourceMappingURL=decrypt-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decrypt-handler.d.ts","sourceRoot":"","sources":["../../src/handlers/decrypt-handler.js"],"names":[],"mappings":"AAsFA;;;;;GAKG;AACH,kDALW,IAAI,CAAC,aAAa,eAElB,MAAM,WACN,UAAU,uBAuBpB;AA1FM,mDATI,OAAO,kBAAkB,EAAE,MAAM,aACjC,OAAO,+BAA+B,EAAE,aAAa,iBACrD,IAAI,CAAC,aAAa,cAElB,GAAG,UACH,MAAM,OACN,IAAI,CAAC,OAAO,iBACZ,UAAU,gCA+DpB;sBA5EqB,aAAa;uBARZ,QAAQ"}
|