How to encrypt and decrypt messages using Virgil security? - encryption

I am trying to integrate Virgil security in my basic chat application but it won't as needed.
While chatting 2 users say A and B. After both users logged in and have some communication between them. Now both users reload/logged out or logged in then old messages was sent was not able to decrypt as new private keys will be generated.
I so not want to stored the public and private key of users in database or in local storage.

I'm Dim from Virgil. Let me help you with this use-case.
I prepared some additional information:
Steps #1 and #2 from Virgil Simple JS Demo (You’ve already seen it)
Step #1. User Registration Step.
users register their Public Keys
At this step:
You authenticate Alice
After you successfully authenticated Alice, you give her a JWT to grant her access to Virgil’s Cloud APIs
Alice generates her Private Key (2) -> stores her Private Key into Local Storage -> creates her Virgil Card
Alice Publishes (3) her Virgil Card on Virgil Cards Service (She uses her JWT to do it).
Then you authenticate Bob (User B): same story as Alice.
At the end, Alice and Bob have their own Private Keys and Virgil Cards (Public Key published into our cloud for users to find each other’s Public Keys).
Step #2. Simple Communication (chat).
Simple e2e encrypted communication
At this step:
Alice looks for & finds Bob’s Public Key at Virgil Cards Service (1)
Alice loads her Private Key from her Local Storage in a browser
Alice signs (with her Private Key) and encrypts (with Bob’s Public Key) a message and sends to Bob through your Application Server
Bob receives the encrypted and signed message
Bob goes to Cards Service to find Alice’s Public Key in order to verify her signature
Bob loads his Private Key from his Local Storage and decrypts the message
As a result, Bob verified Alice’s signature and decrypted the Message from Alice.
And now, comes the problem: Alice decided to use another Browser (or a Device B), but she can’t encrypt Bob’s previous messages on the new device.
Solution: you should transmit her Previous Private Key from Device A to Device B. To do this in the most secure way:
is to encrypt Private Key on a device A with some Additional Secret Key.
transmit encrypted Private Key to Device B.
use the same Additional Secret Key on Device B to decrypt the encrypted Private Key from Device A.
So, as you see you must use some special Additional Secret Key on both devices (some messengers do this with a QR code between the devices) to do it securely. To do it simpler though, we have a special tech for you to you generate the “Additional Secret Key” that will be the same on both devices. We call that Secret Key - a Brain Key. Brain Key - a strong cryptographic asymmetric key based on Alice’s PASSWORD.
You can find more information about this key here: https://developer.virgilsecurity.com/docs/java/use-cases/v1/brainkey
So now, let’s take a look at the next step.
Step #3. Store your Private Key in the Cloud
store somewhere encrypted Private Key
At this step:
Alice enters her Password
Alice generates a Brain Key from her password (1)
Alice encrypts her Private Key with the Brain Key (2)
Alice uploads her encrypted private key somewhere in Cloud (to your app’s backend/database)
As a result, Alice will have her encrypted Private Key in the cloud, that she used to chat with Bob.
Step #4. Alice now uses her new Browser (or new Device)
transmit a Private Key
At this step:
Alice enters her Password (the same that she used previously for Brain Key generation).
This generates her Brain Key (1)
Alice downloads her encrypted Private Key from the Cloud / from your backend/database (2)
Alice decrypts the encrypted Private Key with the Brain Key
As a result, Alice will get her Private Key on a Device B, that she used to chat with Bob on a Device A. This way, the chat conversation survived her device change.
Every time when Alice uses a new device, she can re-generate the same Brain Key and easily get her Private Key, the key that she uses on all her devices to chat with Bob.

David from Virgil here. Thanks for the question and sorry for making you puzzled.
I believe the issue is: while the sample app generated user private & public keys both for Alice and Bob, published the public keys to our Cards service, it hasn’t saved the private keys anywhere. So, the private keys remained in the browser's local storage and as such, didn’t sync to other browsers/devices where your users have logged in next. So in lack of their private keys, your users weren’t able to decrypt their messages... This is a limitation of the sample app and we should fix that ASAP (task created).
In order to get the app working, I.e. make your users be able to log in from another browser and decrypt messages, they need to retrieve their respected private keys from a central location. Now, it'd be too lame for me to recommend to just save the user private keys to your database (and enable others to access it + decrypt your users' messages), so we created a technology called BrainKey to encrypt the user private key with a key that we derive from the user’s password. Similar approach how Apple uses your TouchID-derived key to encrypt your keychain.
This is what you need to do to make it work:
During user signup (on the client device), generate a private key for your user and publish the public key to our Cards service.
Then use the user's password to generate a BrainKey (which technically is a private key).
Generate the public key from the BrainKey and encrypt the user's previously (Step 1) generated private key.
Now, save this encrypted private key to your database for the user.
Every single time your user logs in with her password, re-generate the same BrainKey from her password, download the encrypted private key from your DB, decrypt it with the BrainKey and now you can use this key to decrypt all chat messages that were sent to her.
Here’s a reference and sample code for our BrainKey tech: https://developer.virgilsecurity.com/docs/javascript/use-cases/v1/brainkey
Does it make sense? Would you like to see a sample code that does it? If you join our Slack, we can chat real-time, I’m #dave: https://join.slack.com/t/virgilsecurity/shared_invite/enQtMjg4MDE4ODM3ODA4LTc2OWQwOTQ3YjNhNTQ0ZjJiZDc2NjkzYjYxNTI0YzhmNTY2ZDliMGJjYWQ5YmZiOGU5ZWEzNmJiMWZhYWVmYTM
​David

Related

Flutter - Key storage for end-to-end encryption across multiple devices

I am writing a flutter application where the user can essentially create notes. I have written code to create a private key for the user, which is later used to encrypt their data. The encrypted data is stored in Firebase Firestore. When their data is retrieved it is decrypted using their key.
I've looked at using Flutter Secure Storage, which works great, except that the secret key is only available on that device. If the user logs in on a new device, they will not be able to decrypt their notes.
My question is:
How/where can I store the user's secret key for decrypting their data so that only they have access to it, regardless of what device they log in with? What is best practice around this kind of solution?
final AesGcm algorithm = AesGcm.with256bits();
final SecretKey secretKey = await algorithm.newSecretKey();
// TODO: Write secretKey to storage that only the user can access with their account.
As explained by David in the Virgil Security talk at GDG, this problem is overcome by utilizing the user's password to derive a key (which David refers to as a brain key) which is then used to encrypt the user's private key. So, when a user signs up, a private key is created for them, which is then encrypted using the brain key. This encrypted password key is stored in the cloud.
Thus, when the user logs in from a new device, they type in their password and get the brain key from the password which is then used to decrypt their private key and is consequently saved to the user's device. Now, you might be wondering what happens if the user changes their password. In order to change one's password, the old password is required which is then used to create a new brain key and store the private key in an encrypted format.

How to encrypt file storage in Firebase (client-side)?

I am building an Electron app and implementing Cloud Storage support. Users can upload files within my app to their account. Me, as an admin, I don't want to be able to read the files through the Firebase admin console. I also want to avoid a user password as people might forget it. Just logging into their account should be enough to access their files.
In my prototype I store user files in data/${user.uid}/. But now I am stuck and don't know which password I should use to encrypt the files.
There are a few questions around this topic which involve DigitalOcean which looks too overkill for what I am doing. Is there anything else I could use as a password that is part of the User object that is not exposed anywhere else?
I came across multiple options for Client Side Encryption in File Storage in Firebase. The encryption itself is simple enough to perform with existing libraries, using a symmetric key (a key that can both encrypt data, and decrypt the encrypted data). As the usual problem goes, we now need to find a secure place to store this all-powerful key.
Option 1 : Store Key on User Device
Pros : This stores the key on the user’s device, so the key is never in the application servers.
Cons : The key, and therefore the data, is not accessible from other devices. It’s not a bad solution depending on the use case and situation.
Option 2 : Google Key Management Service for Encryption
Pros : Encrypting the key with another data key stored in Google Key Management Service. The user’s key encrypts the data, and then the key is encrypted by a KMS key and stored in the database. As Andy rightly points out in his blog, that the KMS key belongs to a different Google account to the Firebase database, so no one user has permission to both read the data and decrypt it. A hacker would need to compromise both accounts to access the unencrypted data.
Cons : User has to manage two accounts.
Option 3 : Stash the Key in User’s Google Account
Pros : When the user logs in, we get the OAuth credentials to request the user’s personal encryption key, or create one if we can’t find one, from the user’s Google account. This way, the key is always fully in the user’s possession, but they never have to deal with it directly. Google Drive provides an API for creating a special application data folder (user consent is required during OAuth). The contents of this folder are not visible to the user, and is only accessible via your application’s credentials.
Cons : User has to be cautious not accidentally deleting their own encryption key.
Option 4 : Asymmetric Key Pair
Pros : User first gets the public keys of the recipients. He then generates a symmetric key for himself with which he encodes the file. He then creates a copy of this symmetric key for each recipient and encrypts it with the respective public keys. Finally, he transfers the encrypted copies of the symmetric key together with the encrypted file to the server and stores them there.If another user wants to download the file, he gets it in the encrypted form together with the copy of the symmetric key that is encrypted for him. He can decrypt the latter using his private key and now has the symmetric key with which he can decode the file.
Option 5 : Public and Private Key Encryption
Pros : Create private & public keys for your users when you sign them up. Encrypt data on User 1's device with User 2's public key. Store the encrypted data in your database. When User 2 reads up the encrypted data, his/her private key will be able to decrypt it.

Private key storage issue in OpenPGP encryption

I'm trying to implement OpenPGP end to end encryption on a simple messaging app which will be accessed on the web as well as mobile. I'm stuck at deciding where my private key should be generated and stored. The following two approaches are on the table(i don't want to go with approach 2 but that looks like the only feasible option) :-
Key storage approach 1:-
When the user logs into the Chat client the first time, the client (web/app) will generate a set of public and private keys.
The user's public key will be sent to the backend server. The user's private key will be encrypted and stored on the local storage of the client.
Problems with this approach:-
If the user has not logged into the client even once, how will the sender encrypt the message? (since the public key of the recipient is not available till now)
If the user logs into another client (another browser or different app), how will he get access to the private key? (since that is needed to decrypt the messages in the user's inbox)
Key storage approach 2:-
When the user is created on the backend server, create both public and private keys for the user using PGP on the backend itself. Public key can be stored in plain-text but the user's private key should be encrypted and stored. We can use symmetric encryption with a client-specific passphrase here.
When the user logs into the Chat client (web/app), the encrypted private key will be received from the backend upon login. In order to decrypt any message, then can decrypt their private key using their passphrase, then use the private key to decrypt the message received.
Problems with this approach:-
This is not truly end to end encryption, since who-ever knows the passphrase of the user, can have access to the private key.
Reg. approach 1: If the client hasn't had the opportunity to share their public key, then indeed, you cannot send encrypted messages. It's unclear how you would solve that problem. The private key you can encrypt with a passphrase and upload the encrypted version to your backend so that the client can then download it and have it available on other devices, as you write in your second approach. Or you can go a step further and generate the private from the passphrase.
Your approach 2 may actually not have much worse security properties, depending on how you look at it. You could argue that you have no infrastructure anyway to let clients make sure they have obtained the correct key material. So they cannot distinguish the actual public key of the user from a malicious one provided by the server.
How you design your system is up to you, of course. I suggest you start laying out your requirements. Your problems seems to become smaller if not-yet-known users do not need to receive encrypted messages.

How to encrypt user data in Firebase

I am using the email/password sign in method for Firebase. I would like to encrypt the data users save into the realtime database before sending it to the database. Firebase already handle the user password, but can I somehow use it to encrypt data which can't be decrypted by me only the client? It would be nice if I could achieve it with the client sdk.
So my flow would be something like this:
User sign in with it's credentials (which is handled by firebase itself)
User encrypt some data with some unique key, which can be generated only from the credentials or from some data available only for the user, but not me. (this key needs to be persistent between sessions, or after the user changed his password.)
Data is saved into the database (I cant read it since its encrypted with the user credentials)
User log in on a different device (the decryption key can be generated right away and data can be decrypted.)
You can easily do that the following way:
After user A logs in a random public private key pair is generated on his phone. eg.: use Ecc Curve25519
The private key from A is stored securely on his phone
The public key from A is stored in firebase and is accessible to anybody that chats with A.
If X sends a message to A he fetches the public key from A from firebase encrypts the message for A locally and stores the encrypted message on firebase in the inbox from A
A downloads the encrypted message from firebase and decrypts it with his private key stored on his phone
(vice versa for A to X)
If A want's to move to another phone or wants to use multiple phones you can do this that way:
Ask A to define a strong password to encrypt his locally stored private key. (or create a random passphrase and use QR codes for key exchange)
Encrypt the private key locally (eg.: use AES256) on his phone with the password from step 1 and upload it to firebase. (optional sign it with his private key)
Download the encrypted private key from the second device from A
Ask for the passphrase on the second device from A and store the private key securely (optional check the signature with the public key from A)
Delete the encrypted private key from firebase if no backup is wanted

Server-side public key encryption with browser-side decryption

My application requires certain fields to be encrypted when stored at rest, and allow the user to decrypt them locally in the browser by entering a passphrase. However, these encrypted fields are not entered into the database by the authenticated user himself, but submissions from other people who are supplying the user with private information.
If I stored the passphrase in the database to encrypt new submissions, then anyone who got access to the database would be able to easily decrypt the encrypted information. So, I believe I need to store a public key in the database, encrypt incoming information with the public key, and then serve that encrypted info to the user and let him decrypt it with his passphrase in his browser. (Thus, serverside, we can never decrypt anything.)
I've looked into libraries like ursa, but the problem is that the user would have to save his private key. (Correct?) Is there any way that I can do this with a relatively short passphrase instead of a long private key?
You can use something like JSBN to generate a public-private key pair on the client and use CryptoJS in password encryption mode to encrypt the private key with AES. The encrypted private key and the plaintext public key are then posted to the server and added into the database.
The next time the user gets information encrypted with his/her public key, you send the encrypted information and the encrypted private key to the client/browser. The user can enter the passphrase again to decrypt the private key and then in a second phase decrypt the payload.
Note that this is not absolutely secure because unencrypted traffic (e.g. no TLS) will enable an attacker to extend the html/js to include a data stealing script. Furthermore, the developers of your system can also add such a script whenever they want regardless of TLS or not.

Resources