LibreChat
LibreChat copied to clipboard
WIP šfeat: Implement End-to-End Encryption `E2EE` Across Messaging
Summary
This PR introduces a comprehensive end-to-end encryption (E2EE) feature that secures both message content and user credentials using modern cryptographic standards. The changes span across backend models, API endpoints, controllers, and client-side components to ensure that sensitive data is encrypted during transit and at rest.
Closes: #5712 Closes: #5975 reference: #5856
Key highlights include:
-
Message Encryption:
- AES-GCM Encryption: All outgoing messages are now encrypted using AES-GCM. When a user with an active encryption configuration sends or receives a message, the plaintext is converted to ciphertext using a randomly generated 256-bit AES key and a 12-byte initialization vector (IV).
- RSA Key Wrapping: The AES key is encrypted with the recipientās RSA public key (provided in PEM format) using RSA-OAEP with SHA-256. This ensures that only the intended recipient, who possesses the corresponding private key, can decrypt the AES key and then the message content.
- Data Model Updates: The
Messagemodel and its schema have been updated to include additional fields such asiv,authTag, andencryptedKeyfor storing encryption metadata. These changes propagate through all related methods (e.g., saving, updating, and retrieving messages) to handle encryption parameters properly.
-
User Encryption Settings:
- Encryption Setup UI: A new UI component,
EncryptionPassphrase, has been added to the settings tab. Users can set or change their encryption passphrase. The passphrase is used to derive a symmetric AES-GCM key (via PBKDF2) to encrypt the userās RSA private key. - Key Pair Generation: Upon activation, a new RSA-OAEP key pair is generated. The public key is stored in the userās profile, while the private key is encrypted using the derived key, with both encryption salt and IV stored as part of the userās settings.
- Disabling Encryption: Users also have the option to disable encryption. In this case, all encryption-related fields are set to
null, ensuring that no cryptographic operations occur when handling messages or other sensitive data.
- Encryption Setup UI: A new UI component,
-
Controller Enhancements:
- AskController Enhancements: The response generation logic in
AskControllernow includes an encryption branch. If the user has a valid encryption public key, the plaintext response is encrypted before being sent to the client. This ensures that even automated or AI-generated messages are secured. - UserController Updates: Additional endpoints have been added for updating user encryption settings, enabling clients to change their encryption keys or disable encryption entirely.
- AskController Enhancements: The response generation logic in
-
Client-Side Decryption:
- MessageContent Updates: On the client side, message components (e.g.,
MessageContent.tsx) have been updated to include decryption logic. If a message is received with encryption metadata (i.e.,iv,authTag, andencryptedKey), the client uses the stored RSA private key (after decryption with the userās passphrase) to decrypt the AES key, which in turn decrypts the message. - Error Handling: Enhanced error logging and error UI components are in place to handle decryption failures gracefully, providing users with clear notifications if any issues occur during the decryption process.
- MessageContent Updates: On the client side, message components (e.g.,
-
API & Data Provider Adjustments:
- New Endpoints: An API endpoint (
/api/user/encryption) has been added to handle updates to user encryption settings. - Mutation Hooks: New hooks and mutations (e.g.,
useSetUserEncryptionMutation) have been implemented to seamlessly update user encryption keys from the client. - Schema Updates: Data schemas have been revised to ensure that encryption fields are treated as nullable strings, which is vital for proper type handling and backward compatibility.
- New Endpoints: An API endpoint (
Change Type
- [x] New feature (non-breaking change which adds functionality)
- [x] This change requires a documentation update
Testing
To test the E2EE functionality, please follow these steps:
Test Configuration:
- Frontend Testing:
- Encryption Activation:
- Navigate to the chat settings and activate encryption by entering a valid passphrase. Verify that a new RSA key pair is generated and that the public key is displayed (truncated for brevity) in the UI.
- Message Encryption/Decryption:
- Send a message from an account with encryption enabled. On the receiving side, check that the message content is decrypted correctly.
- Temporarily disable encryption and verify that messages are transmitted and rendered in plaintext.
- Error Scenarios:
- Test with an incorrect passphrase to simulate decryption failure. Ensure that a clear error message is displayed and logged.
- UI Regression: Validate that non-encryption related functionalities (e.g., file attachments, UI layout) continue to operate as expected.
- Encryption Activation:
Checklist
- [x] My code adheres to this project's style guidelines
- [ ] I have performed a self-review of my own code
- [x] I have commented in any complex areas of my code (especially in the encryption and decryption logic)
- [x] I have made pertinent documentation changes (see inline comments and updated README/technical docs)
- [x] My changes do not introduce new warnings
- [x] I have written tests demonstrating that my changes are effective or that my feature works
- [ ] Local unit tests pass with my changes
- [x] Any changes dependent on mine have been merged and published in downstream modules.
- [ ] A pull request for updating the documentation has been submitted.
@danny-avila need some help here š
to decrypt the refresh callback? where can I find this because it's a little maze with the chat's and messages and conversations
https://github.com/user-attachments/assets/616be36c-7cca-4b97-8c09-36b25073f720
the hook useGetMessagesByConvoId is what makes the api/messages request, that would be the easiest place to start. it should be done within the hook.
() => dataService.getMessagesByConvoId(id),
what this function returns is what ends up getting cached in the query cache, so it would be best to do it here, i think.
to use atom store, you would need to move useGetMessagesByConvoId from packages/data-provider to client/src/data-provider/Messages/queries.ts (doesn't exist yet)
https://tanstack.com/query/v4/docs/framework/react/reference/useQuery
Reference ā¹ļø ^
Hi @danny-avila and @rubentalstra! Firstly I wanted to say this project is awesome and really the best open source LLM chat around! I'm taking it to the company I work on and the feature addressed here was a main concern of mine. I got very happy to see it was already under development, but I noticed that this PR is not mentioned on the project roadmap. Is it planned to be integrated in next releases?
+1
Hi @danny-avila and @rubentalstra! Firstly I wanted to say this project is awesome and really the best open source LLM chat around! I'm taking it to the company I work on and the feature addressed here was a main concern of mine. I got very happy to see it was already under development, but I noticed that this PR is not mentioned on the project roadmap. Is it planned to be integrated in next releases?
@rubentalstra, is the work still ongoing?
@rubentalstra, is the work still ongoing?
No not this PR. Sorry about that.
@rubentalstra, is the work still ongoing?
No not this PR. Sorry about that.
@rubentalstra Any other PR or plans aiming for E2E encryption of user chats?
@rubentalstra this is exactly what I'm looking for! Did you discontinue work for lack of time, or was it because there were deeper issues that made it seem infeasible? I'm wondering if I can finish this up and get it to a stage where it's ready for review. Thank you for getting this ball rolling.
@rubentalstra this is exactly what I'm looking for! Did you discontinue work for lack of time, or was it because there were deeper issues that made it seem infeasible? I'm wondering if I can finish this up and get it to a stage where it's ready for review. Thank you for getting this ball rolling.
@borenstein the main issue from my side was a lack of time. I'm sorry about that.
so I assume bedrock PRs will also be closed? huh?
@rubentalstra Nothing to apologize for! I'm thinking of picking it up. Do you recall where you left things?
@rubentalstra Nothing to apologize for! I'm thinking of picking it up. Do you recall where you left things?
@borenstein I think starting from scratch is the best thing to do. Because one of the roadblocks was also the problem that the whole frontend mechanism would be rewritten during my development of this feature.
The struggle I encountered:
- Streaming data from api to server to save it was encrypted but then the frontend did a full refresh of the response and it lost its encryption.
I hoop this clears some stuff up?
Indeed it does, and thank you for your quick responses! @danny-avila is the front-end stable enough now for a new try at E2EE?
Iām also interested in this feature. In an ideal scenario, users would feel more comfortable using the app if none of the admins could read their messages. @borenstein, Iād appreciate it if you could add me to any PRs youāre working on or find a way to keep us updated on the progress.