I'm designing a small personal journal web application for my wife. I'm a crypto newb, but I want her to feel secure knowing only she will have access to her journal entries. I am thinking of the following scheme to achieve this using the Web Crypto API:

  1. User registers. A masterKey is generated and wrapped (using AES-KW) using a PBKDF2 derived key (masterKeyWrapKey) from the user's password and a random salt, masterKeyWrapSalt. We now have wrappedMasterKey. wrappedMasterKey along with masterKeyWrapSalt is sent the server for storage. When changing passwords, we simply perform the previous steps with the new password, re-using masterKey instead of generating a new one.
  2. User logs in. Their password is hashed (yet to determine which hash to use) on the client side and sent to the server, so that the server doesn't get to use it to derive the masterKeyWrapKey. The hash used here must be different than the one used to generate masterKeyWrapKey, otherwise we'll just be sending the key to unwrap wrappedMasterKey to the server on each log in.
  3. Upon successful logging in, the server sends the user's wrappedMasterKey and masterKeyWrapSalt. We use PBKDF2 against password and masterKeyWrapSalt to derive unwrapKey and use that to unwrap wrappedMasterKey, getting back our masterKey.
  4. The user can now encrypt and decrypt their journal entries using their masterKey. I am planning on using AES-256-GCM with a 96 bit random IV produced from Crypto.getRandomValues(). Given the amount of posts there's likely to be, I don't think I need to worry about an IV collision.

It sounds pretty safe to me I'd love to hear what people in the know think. I've read that one should never implement their own encryption ANYTHING, but the temptation is strong.. especially on such a small project.

Edit to add information:

PBKDF2 was chosen as it is the only password based key derivation function available in the Web Crypto API - I want to avoid dependence on any external crypto libraries.

  • $\begingroup$ In step 2, I recognize that sending the user's hashed password to the server is effectively treating the hash AS the password.. The hash would be re-hashed on the server before being stored. i.e. the server treats the incoming password no different than if it was sent plain-text by the client. $\endgroup$
    – Wolt
    Jul 28, 2020 at 20:03
  • $\begingroup$ Welcome to Cryptography. You can edit your question. Instead of PBKDF2, prefer Argon2id. Do you guarantee that users have password with good entropy? Why the server keep the users' keys? Just send the salt to user to derive their keys on the client? $\endgroup$
    – kelalaka
    Jul 28, 2020 at 21:32
  • $\begingroup$ @kelalaka thanks! I added some info to the question. The masterKey should not change and so it needs to be stored (securely) somewhere. Wrapping it with the key derived from the user's password derived key on the client side and storing the wrapped version on the server seemed sensible. If I were to forgo having a masterKey and only use the key derived from the user's password, then yes, I would only need to keep the salt on the server. I could include a minimum password strength requirement using one of various libraries. $\endgroup$
    – Wolt
    Jul 29, 2020 at 6:36


Your Answer

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

Browse other questions tagged or ask your own question.