0

Let's say I want to make a messaging(just an example, take it as any data) web(react + node) application(more like an email rather than chat) with end-to-end encryption. So at some point, I will want to send an encrypted(with public key) message and the recipient will need to decrypt it(with private key). Now, say I want to make an encryption process happen on the client(isn't always should be on the client?), so:

  1. user_1 want to send a message to user_2
  2. user_1 requests public key of user_2
  3. user_1 encrypt his message with public key from step 2 and sends it
  4. user_2 receive the message and decrypt it with his private key

Now, all sounds great, but...:

  1. For good UX, a user should know/care only about his own password and all the encryption should magically happen(right?), so generating and storing private key should be done behind the scenes. But how exactly? Storing private key encrypted with user's password, and on the Auth process, send it to the client and then decrypt the private key and store locally for the session. Is that really supposed to be like that?
  2. Where to store the private key then exactly? Storing in local host isn't really nice due to XSS, storing inside a cookie(secured, samesite etc) also isn't nice due to same XSS and CSRF. Where then?
  3. Also, it isn't really clear how to secure the connection between the client and the server. Is using SSL(https) really enough? What else can be done?

2 Answers 2

2

There are many ways that private key can be stored by the client in a web-based end-to-end encryption application.

One way is not to store the key it at all, but instead, derive the key from a password. The user enters a password, then the private key is derived from the password using a key derivation function, such as PBKDF2.

Another way is to use the Web Crypto API to store the private key in a CryptoKey object, then store the CryptoKey object in the web browser's indexDB storage. See https://crypto.stackexchange.com/questions/35530/where-and-how-to-store-private-keys-in-web-applications-for-private-messaging-wi/52488#52488 for more info on this method.

Another way of storing the private key is the way that ProtonMail does it. The private key is encrypted using another key derived from a password, then the encrypted private key is stored on their server. See https://protonmail.com/support/knowledge-base/how-is-the-private-key-stored/ for more info.

Yet another way of storing a private key is the way that EncryptedSend does it (full disclosure, I am the developer of EncryptedSend). The private key is encrypted using another key derived from a password, then this encrypted blob is written to a file that the user stores on his/her system. To begin a session, the user simply provides this file and the password, then the private key is used both to decrypt messages and authenticate the user with the server.

5
  • But... not storing the private key will end up in asking the user for a password each time? How is that an option? Same goes for the Web Crypto API(I guess), as it isn't accessible via JS. As regards of how ProtonMail does it, they store the private key on the server secured(I know how to do that), but how the heck the decryption happens? On the server? As regards of EncryptedSend(nice app buddy!), how do you deal with XSS?
    – sembaas
    Commented Apr 3, 2021 at 17:15
  • For all of your questions pertaining to decrypting the private key using a password: decryption happens on the client-side, in-browser, using javascript with the Web Crypto API. See developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/decrypt. As for how to protect against XSS - use a strict CSP, escape all external content, write all external content to innerText (not innerHTML), etc.
    – mti2935
    Commented Apr 3, 2021 at 18:27
  • So the only option is to use Web Crypto API(instead of others mentioned by you), just to be clear? As for the CSP, it doesn't seem to work well with modern development frameworks(react etc), any alternatives perhaps?
    – sembaas
    Commented Apr 3, 2021 at 18:58
  • Also, can you please tell why you decided to use a file instead of Web Crypto API?
    – sembaas
    Commented Apr 3, 2021 at 19:09
  • The Web Crypto API is just an API for doing client-side in-browser cryptography with javascript. It can be used to implement any of the methods that I described in my answer. I don't use react, but, I'm not sure why you wouldn't be able to use a strict CSP with react - it works fine with jquery.
    – mti2935
    Commented Apr 3, 2021 at 21:22
0

Generally, these days we want something that is forward secure that is, past messages will be secure even if long term private keys are compromised. That cannot be provided by encrypting the message, or even just the key with the public key. In actual communication processes we use forward secure mechanisms like Diffie-Hellman with ephemeral key pairs. Signal for instance uses Extended triple Diffie-Hellman (X3DH) over curve25519 which uses a combination of long term and short term and one-time-use Diffie-Hellman key pairs for session establishment and uses double ratchet algorithm which uses DH every time message sender changes. TLS 1.3 has dropped support for all non-forward safe key exchange, it uses long term private keys in order to sign and authenticate the key exchange so that they can be sure that the one in the other side is indeed who it is supposed to be and the key exchange hasn't been tampered with a man-in-the-middle.

So basically in nutshell, end-to-end encrypted systems need something which use the long term private/public keys for authentication but use ephemeral key pairs for actual session establishment and data encryption.

1 & 2. You can store your private key after encrypting with a key derived from your password using suitable KDF's like scrypt or argon2. Try to not ever share private key, even in encrypted form because passwords can be more easily guessed and attackked. But for your application you can also use Password-Authenticated-Key-Exchange, which you can look for in the internet. An improved version of dragonfly key (without its original clumsiness) might do. About the security of dragonfly, you can ask at crypto.stackexchange.com, about that, I am not sure how secure dragonfly beyond its clumsy specification in RFC, you might get better answer there but only if it is available. Don't try to implement it yourself without in depth understanding of side channel attack prevention, it will almost certainly be insecure from side channel attacks, even if dragonfly is proven secure mathematically and correctly implemented.

  1. TLS is best we have, so your communication will be as secure as it gets with https alone, you don't need anything else
1
  • TLS can not be used in browser by code beyond 1 use case - communication between browser and server. But when purpose is to make server just "low-trusted messages transport', ensuring messages content not exposed even if server compormised, this is beyond TLS use case. Also TLS can not be used for asynchronous communications (Alice sends message to Bob while he is offline). It can be used, and designed for specific use cases - client-to-server and server-to-server "synchronous" communication. Essentially this is why plenty of people have to "reinvent asynchronous TLS in code" over and over.
    – Kote Isaev
    Commented Aug 27, 2023 at 13:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .