I've been working for some time on a pet project of mine: a note-taking application similar to Evernote with the difference that all content is client-side encrypted. I would like to get some feedback on some architectural descisions I made so far.
User Registration: Client
- User provides email and a password
- Client generates a random Content Encryption Key (CEK) using a cryptographic random number generator. This key is used to encrypt all user content including text content and media resources such as images, audio etc. If this key would not exist as intermediate layer, changing a user's password would involve re-encrypting all content.
- Derive a key for encrypting the CEK using:
- var input = padToMaxLength(email) + password;
- var salt = SHA512(input)
- var contentKeyEncryptionKey = PBKDF2(input, salt, 10000)
- Encrypt the CEK using contentKeyEncryptionKey derived in the previous step
- var encryptedContentKey = AES256(CEK, contentKeyEncryptionKey) (prefixed with HMAC-256 over encryptedContentKey)
- Derive a server authentication token with
- var input = contentKeyEncryptionKey;
- var salt = SHA512(padToMaxLength(email) + password)
- var authToken = PBKDF2(input, salt, 10000)
User Registration: Server
- Server receives request containing:
Generates a unique 256 Bit per-user salt using a cryptographic random number generator
Generates a server-side authentication token using:
- var serverSideAuthToken = PBKDF2(authToken, salt, 100000)
Stores Email, serverSideAuthToken and encryptedContentKey in Database
- Client computes server authentication token as described above and passes it along with the user's email to the server
- Server computes PBKDF2(authToken, saltFromDatabase, 100000). User is authenticated if Email and derived token matches.
Note: I realize that using email+password for salting is far from ideal.