@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.
Files changed (41) hide show
  1. package/README.md +82 -0
  2. package/dist/config/constants.d.ts +3 -0
  3. package/dist/config/constants.d.ts.map +1 -0
  4. package/dist/config/constants.js +3 -0
  5. package/dist/config/env.d.ts +8 -0
  6. package/dist/config/env.d.ts.map +1 -0
  7. package/dist/config/env.js +22 -0
  8. package/dist/config/service.d.ts +14 -0
  9. package/dist/config/service.d.ts.map +1 -0
  10. package/dist/config/service.js +34 -0
  11. package/dist/core/client.d.ts +50 -0
  12. package/dist/core/client.d.ts.map +1 -0
  13. package/dist/core/client.js +78 -0
  14. package/dist/core/encrypted-metadata.d.ts +28 -0
  15. package/dist/core/encrypted-metadata.d.ts.map +1 -0
  16. package/dist/core/encrypted-metadata.js +166 -0
  17. package/dist/core/errors.d.ts +8 -0
  18. package/dist/core/errors.d.ts.map +1 -0
  19. package/dist/core/errors.js +14 -0
  20. package/dist/crypto-adapters/node-crypto-adapter.d.ts +17 -0
  21. package/dist/crypto-adapters/node-crypto-adapter.d.ts.map +1 -0
  22. package/dist/crypto-adapters/node-crypto-adapter.js +66 -0
  23. package/dist/handlers/decrypt-handler.d.ts +11 -0
  24. package/dist/handlers/decrypt-handler.d.ts.map +1 -0
  25. package/dist/handlers/decrypt-handler.js +127 -0
  26. package/dist/handlers/encrypt-handler.d.ts +3 -0
  27. package/dist/handlers/encrypt-handler.d.ts.map +1 -0
  28. package/dist/handlers/encrypt-handler.js +87 -0
  29. package/dist/index.d.ts +4 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +4 -0
  32. package/dist/protocols/lit.d.ts +18 -0
  33. package/dist/protocols/lit.d.ts.map +1 -0
  34. package/dist/protocols/lit.js +106 -0
  35. package/dist/types.d.ts +82 -0
  36. package/dist/types.d.ts.map +1 -0
  37. package/dist/types.js +2 -0
  38. package/dist/utils.d.ts +15 -0
  39. package/dist/utils.d.ts.map +1 -0
  40. package/dist/utils.js +50 -0
  41. 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,3 @@
1
+ export const STORACHA_LIT_ACTION_CID: "QmPS28E5jcwn3GDs5heNkE2w84nKdYYkaimJHCisHiBc7C";
2
+ export const GATEWAY_URL: URL;
3
+ //# sourceMappingURL=constants.d.ts.map
@@ -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,3 @@
1
+ export const STORACHA_LIT_ACTION_CID = 'QmPS28E5jcwn3GDs5heNkE2w84nKdYYkaimJHCisHiBc7C';
2
+ export const GATEWAY_URL = new URL('https://w3s.link');
3
+ //# sourceMappingURL=constants.js.map
@@ -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,8 @@
1
+ export class UnknownFormat extends Failure {
2
+ /** @param {string} [reason] */
3
+ constructor(reason?: string);
4
+ name: "UnknownFormat";
5
+ #private;
6
+ }
7
+ import { Failure } from '@ucanto/core';
8
+ //# sourceMappingURL=errors.d.ts.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"}