I'm currently developing a product that requires user data to be encrypted on the client side. The product is a web app + a browser extension, so the focus is encrypting user data with Javascript. The concept is similar to what 1Password or Lastpass do, basically never storing user data in plaintext, and only the user has a key to decipher his/her own data.

I'm storing log entries for the user, though the concept can be expanded.

I've researched the subject quite a lot and found an article on medium with an approach that seems interesting: https://rhamedy.medium.com/encryption-decryption-of-data-based-on-users-password-using-pbkdf2-aes-algorithms-592f8c1bb79a

Based on this, I came up with a process I think works, but since I'm not a security/cryptography expert, this is definitely not something I want to take lightly.

Documentation source for the functions mentioned here: https://nodejs.org/api/crypto.html

Current Idea

1. The user kicks off the process by enabling this logging feature

At this point, on the server side, I generate a random 32 byte salt plus a 32 byte random IV using crypto.randomBytes(32), give them to the user and I store them in a document in a database so the user can retrieve them later.

2. Key generation

The user defines a master password (#1 concern here). With the master password, the salt, using 10000 iterations and HMAC SHA256, the user can generate a Key Encryption Key (KEK) with crypto.pbkdf2(). At the same time, the client also generates a 256 bit AES key using crypto.generateKey().

3. Key Encryption

The client encrypts the generated AES key with his KEK + the IV, and sends it to the server, where we add it to the user document (now containing a Salt, an IV and the newly added ciphered key). The client can do this with crypto.createCipheriv() using AES-GCM.

4. After all of that

Now for encrypting/decrypting data, the user would just need to grab his document containing the Salt, IV and key. With his Master Password + Salt, the user can recreate the KEK. With the KEK + IV, the user can now decipher the key. For actually encrypting and decrypting data now, the user can use this key + a randomly generated IV for each ciphertext, and I can store into the DB both of them.


  • Is this approach secure or am I missing something here?
  • Any library recommendations for server side and client side? WebCrypto looks good but the Node docs suggest it's still experimental (https://nodejs.org/api/documentation.html#documentation_stability_index)
  • What is the best practice for storing secrets in the user's device? Specifically, when the user decrypts the key, this needs to be stored somewhere. Is the browser's localStorage good enough? At this stage, this key is already in plain text, so I assume storing it, even if temporarily, could be a concern.

Thank you very much!

  • 1
    $\begingroup$ 32-byte IV? Who needs that? AES uses 128-bit and AES-GCM uses 92-bit IV ( suggested). You forgot that the AES-GCM is vulnerable if the file update has still hash the same key $\endgroup$
    – kelalaka
    Commented Feb 3, 2021 at 0:59
  • $\begingroup$ If you always generate a new key every time you encrypt you don't need to generate or save the IV, just use all zeroes. Also, you'd probably like a way to validate that user provided password is correct, generally this is done by putting some fixed text in the encrypted data and then when you decrypt you check for that data. If it's wrong the user specified the wrong password. $\endgroup$ Commented Feb 3, 2021 at 1:54
  • $\begingroup$ Thanks @kelalaka and @Swashbuckler! Very helpful suggestions/corrections. I may not have been entirely clear, the idea is to use 1 key only for encryption/decryption, but every IV is newly generated. I think this handles the vulnerability, but the HKDF approach you mentioned in the post is something to keep in mind. As for checking for wrong password, from what I've read in other posts, using a 0 block encrypted with the key is a good approach? Thank you! $\endgroup$ Commented Feb 3, 2021 at 13:09


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.