|
| 1 | +<!-- omit in toc --> |
| 2 | +# Encrypting a File |
| 3 | + |
| 4 | +After connecting to the Lit Network and authenticating a session, you can encrypt a file using the Lit SDK's [encrypt](https://v6-api-doc-lit-js-sdk.vercel.app/classes/lit_node_client_src.LitNodeClientNodeJs.html#encrypt) method. This method takes in the file you want to encrypt, as well as an array of [Access Control Conditions (ACCs)](https://developer.litprotocol.com/docs/advanced-topics/access-control-conditions) that specify who is authorized to decrypt the data. |
| 5 | + |
| 6 | +Encryption happens client side, as the instance of `LitNodeClient` you created has retrieved the public BLS key for the Lit Network you are connected to. |
| 7 | + |
| 8 | +When you want to decrypt the file, you will submit the _encryption metadata_ (ciphertext, dataToEncryptHash, and access control conditions) and your Session Signatures to the Lit Network. Each Lit node will verify that you have the necessary permissions to decrypt the data (by parsing the access control conditions the data was encrypted with) and then generate their decryption shares if authorized. Once all the decryption shares are generated, they are returned back to the client where the Lit SDK will combine them and return the decrypted file. |
| 9 | + |
| 10 | +- [Prerequisites](#prerequisites) |
| 11 | +- [Running the Code Example](#running-the-code-example) |
| 12 | + - [Requirements](#requirements) |
| 13 | + - [Steps](#steps) |
| 14 | + - [Expected Output](#expected-output) |
| 15 | +- [Understanding the Code](#understanding-the-code) |
| 16 | + - [Reading the Unencrypted File](#reading-the-unencrypted-file) |
| 17 | + - [Creating an Ethers signer](#creating-an-ethers-signer) |
| 18 | + - [Defining the Access Control Conditions](#defining-the-access-control-conditions) |
| 19 | + - [Encrypting the Data](#encrypting-the-data) |
| 20 | + - [Requesting Session Signatures](#requesting-session-signatures) |
| 21 | + - [Decrypting the Data](#decrypting-the-data) |
| 22 | +- [Next Steps](#next-steps) |
| 23 | + |
| 24 | +## Prerequisites |
| 25 | + |
| 26 | +- Understanding of Lit core terminology and concepts covered [here](../README.md#core-terminology) |
| 27 | +- Understanding of Lit encryption terminology and concepts covered [here](../README.md#relevant-terminology) |
| 28 | +- Understanding of the [Connecting to the Lit Network](../connecting-to-lit/README.md) guide |
| 29 | +- Understanding of the [Authenticating a Session](../../_getting-started/authenticating-a-session/README.md) guide |
| 30 | + |
| 31 | +## Running the Code Example |
| 32 | + |
| 33 | +### Requirements |
| 34 | + |
| 35 | +- [Node.js](https://nodejs.org/en) |
| 36 | +- [Yarn](https://yarnpkg.com/getting-started) |
| 37 | +- `@lit-protocol/constants` |
| 38 | +- `@lit-protocol/lit-node-client` |
| 39 | +- `@lit-protocol/auth-helpers` |
| 40 | +- `@lit-protocol/types` |
| 41 | + |
| 42 | +### Steps |
| 43 | + |
| 44 | +1. `yarn` to install the dependencies |
| 45 | +2. `yarn test` to run the code example |
| 46 | + |
| 47 | +### Expected Output |
| 48 | + |
| 49 | +After running the code example, you should see output in your terminal: |
| 50 | + |
| 51 | +1. An indication that a connection to the Lit Network was successfully established |
| 52 | +2. The file was successfully encrypted |
| 53 | + - The `dataToEncryptHash` is logged to the terminal for demonstration purposes (`ciphertext` was omitted for brevity): |
| 54 | + |
| 55 | +```bash |
| 56 | +ℹ️ dataToEncryptHash: 397acd6bbbf08a2f55da61e8ab552f787f3ad1cc8ccb82dac9fcb6ace26f7148 |
| 57 | +``` |
| 58 | + |
| 59 | +3. Session Signatures were successfully generated for the requested session |
| 60 | +4. After the JavaScript test passes, you should see the path for the decrypted file logged to the terminal: |
| 61 | + - NOTE: The decrypted file is actually deleted after the test is complete, for cleanup and sanity purposes. You can disable this behavior by commenting out the `afterEach` function in the [./test/index.spec.ts](./test/index.spec.ts) file. |
| 62 | + - The test compares the decrypted file to the original file to ensure they are the same before deleting the decrypted file. |
| 63 | + |
| 64 | +```bash |
| 65 | +ℹ️ Decrypted content saved to: /Users/user/developer-guides-code/hacker-guides/encryption/encrypt-file/src/loremIpsum-decrypted.txt |
| 66 | +``` |
| 67 | + |
| 68 | +## Understanding the Code |
| 69 | + |
| 70 | +The following code from [./src/index.ts](./src/index.ts) does the following: |
| 71 | + |
| 72 | +### Reading the Unencrypted File |
| 73 | + |
| 74 | +First, we read the unencrypted file and store its contents in the `fileContent` variable: |
| 75 | + |
| 76 | +```typescript |
| 77 | +const filePath = join(process.cwd(), "src", "loremIpsum.txt"); |
| 78 | +const fileContent = await fs.readFile(filePath, "utf8"); |
| 79 | +``` |
| 80 | + |
| 81 | +When encrypting files with the Lit SDK, it's important to note that the entire file must be loaded into memory as a `Uint8Array` before encryption can begin. |
| 82 | + |
| 83 | +The Lit SDK has a maximum payload size limit of approximately `1.5MB` for encryption operations. Since the SDK currently doesn't support file chunking, attempting to encrypt files larger than this will result in out of memory errors. |
| 84 | + |
| 85 | +For larger files, consider using envelope encryption: |
| 86 | + |
| 87 | +1. Encrypt the large file using a symmetric key (e.g. with AES encryption) |
| 88 | +2. Use the Lit SDK to encrypt just the symmetric key itself |
| 89 | + |
| 90 | +### Creating an Ethers signer |
| 91 | + |
| 92 | +As covered in the [Authenticating a Session](../../_getting-started/authenticating-a-session/README.md#creating-an-ethers-signer) guide, the `ethersSigner` is used to generate an Authentication Signature which is a [ERC-5573](https://eips.ethereum.org/EIPS/eip-5573) message that specifies what Lit Resources and corresponding abilities the Session will be authorized to use. |
| 93 | + |
| 94 | +### Defining the Access Control Conditions |
| 95 | + |
| 96 | +The following are the Access Control Conditions that we are using as part of the encryption process. They specify that the data can only be decrypted by the Ethereum address that corresponds to the `ethersSigner` we created earlier: |
| 97 | + |
| 98 | +```typescript |
| 99 | +const accessControlConditions: AccessControlConditions = [ |
| 100 | + { |
| 101 | + contractAddress: "", |
| 102 | + standardContractType: "", |
| 103 | + chain: "ethereum", |
| 104 | + method: "", |
| 105 | + parameters: [":userAddress"], |
| 106 | + returnValueTest: { |
| 107 | + comparator: "=", |
| 108 | + value: await ethersSigner.getAddress(), |
| 109 | + }, |
| 110 | + }, |
| 111 | +]; |
| 112 | +``` |
| 113 | + |
| 114 | +These conditions are what you'll want to customize based on your project's use case. You can learn more about the different types of conditions available [here](https://developer.litprotocol.com/category/advanced-topics). |
| 115 | + |
| 116 | +### Encrypting the Data |
| 117 | + |
| 118 | +The following code is what actually encrypts the data using the Lit SDK and the Lit network's public BLS key: |
| 119 | + |
| 120 | +```typescript |
| 121 | +const { ciphertext, dataToEncryptHash } = await litNodeClient.encrypt({ |
| 122 | + dataToEncrypt: new TextEncoder().encode(fileContent), |
| 123 | + accessControlConditions, |
| 124 | +}); |
| 125 | + |
| 126 | +console.log(`ℹ️ dataToEncryptHash: ${dataToEncryptHash}`); |
| 127 | +``` |
| 128 | + |
| 129 | +`dataToEncrypt` takes a `Uint8Array` as an argument, so we need to convert the file's contents to a `Uint8Array` using `new TextEncoder().encode`. |
| 130 | + |
| 131 | +The output of the `encrypt` method is the `ciphertext` and `dataToEncryptHash`. Coupled with the `accessControlConditions`, this forms the _encryption metadata_ that we'll need to submit to the Lit Network when we want to decrypt the data later. |
| 132 | + |
| 133 | +### Requesting Session Signatures |
| 134 | + |
| 135 | +Our request for session signatures is the same as covered in the [Authenticating a Session](../../_getting-started/authenticating-a-session/README.md#requesting-session-signatures) guide. |
| 136 | + |
| 137 | +However, one thing to note is that the signer of the ERC-5573 message is identity the Lit nodes will use when performing the authorization check for our decryption request. In other words, the Ethereum address derived from the Authentication Signature is what will be checked against the `accessControlConditions` we defined earlier. This derived address **must** match what we specified as `returnValueTest.value` (in the case of this example, that's `await ethersSigner.getAddress()`). |
| 138 | + |
| 139 | +### Decrypting the Data |
| 140 | + |
| 141 | +Lastly, we call the `decrypt` method to decrypt the data. This method takes in the encryption metadata and your Session Signatures as arguments. Similar to when requesting Session Signatures, the `chain` argument signals the signature schema and message format that the Lit nodes will use to authenticate the decryption request (and should almost always be `ethereum`). |
| 142 | + |
| 143 | +The decryption response is returned which is an object with the `Uint8Array` property: `decryptedData`. We can convert this back to a string by calling `new TextDecoder().decode` to get the decrypted data as a string. |
| 144 | + |
| 145 | +```typescript |
| 146 | +const decryptionResponse = await litNodeClient.decrypt({ |
| 147 | + chain: "ethereum", |
| 148 | + sessionSigs: sessionSignatures, |
| 149 | + ciphertext, |
| 150 | + dataToEncryptHash, |
| 151 | + accessControlConditions, |
| 152 | +}); |
| 153 | + |
| 154 | +const decryptedString = new TextDecoder().decode( |
| 155 | + decryptionResponse.decryptedData |
| 156 | +); |
| 157 | +``` |
| 158 | + |
| 159 | +Finally we write the decrypted data to a file: |
| 160 | + |
| 161 | +```typescript |
| 162 | +const outputPath = join(process.cwd(), "src", "loremIpsum-decrypted.txt"); |
| 163 | +await fs.writeFile(outputPath, decryptedString, "utf8"); |
| 164 | + |
| 165 | +console.log(`ℹ️ Decrypted content saved to: ${outputPath}`); |
| 166 | +``` |
| 167 | + |
| 168 | +## Next Steps |
0 commit comments