Encryption Protocol Specification v003
#
Version 0.0.3It is important that there exist a separation of concerns between the server and the client. That is, the client should not trust the server, and vice versa.
Encryption keys are generated by stretching the user's input password using a key derivation function.
The resulting key is split in three โ the first third is sent to the server as the user's password, the second third is saved locally as the user's master encryption key, and the last third is used as an authentication key. In this setup, the server is never able to compute the encryption key or the user's original password given just a fraction of the resulting key.
Note: client-server connections must be made securely through SSL/TLS.
#
Elaboration on User model encryption related fieldsname | details |
---|---|
pw_cost | The number of iterations to be used by the KDF. The minimum for version 003 is 100,000. However note that non-native clients (web clients not using WebCrypto) will not be able to handle any more than 3,000 iterations. |
pw_nonce | A nonce for password derivation. This value is initially created by the client during registration. |
#
Key Generation#
Client InstructionsGiven a user inputted password uip
, the client's job is to generate a password pw
to send to the server, a master key mk
that the user stores locally to encrypt/decrypt data, and an auth key ak
for authenticating encrypted data.
#
Login StepsClient makes GET request with user's email to
auth/params
to retrieve password nonce, cost, and version.Client verifies cost >= minimum cost (100,000 for 003.)
Client computes
pw
,mk
, andak
using PBKDF2 with SHA512 as the hashing function and output length of 768 bits:Client sends
pw
to the server as the user's "regular" password and storesmk
andak
locally. (mk
andak
are never sent to the server).
#
Registration StepsClient chooses default for
pw_cost
(minimum 100,000).Client generates
pw_nonce
:Client computes
pw
,mk
, andak
using step (3) from Login Steps.Client registers with
email
,pw
,pw_cost
,pw_nonce
, andversion
.
#
Item EncryptionIn general, when encrypting a string, one should use an IV so that two subsequent encryptions of the same content yield different results, and one should authenticate the data as to ascertain its authenticity and lack of tampering.
In Standard Notes, two strings are encrypted for every item:
- The item's
content
. - The item's
enc_item_key
.
#
Client-sideAn item is encrypted using a random key generated for each item.
#
Encryption:Note that when encrypting/decrypting data, keys should be converted to the proper format your platform function supports. It's best to convert keys to binary data before running through any encryption/hashing algorithm.
For every item:
- Generate a random 512 bit key
item_key
(in hex format). - Split
item_key
in half; set item encryption keyitem_ek = first_half
and item authentication keyitem_ak = second_half
. - Encrypt
content
usingitem_ek
anditem_ak
following the instructions "Encrypting a string using the 003 scheme" below and send to server ascontent
. - Encrypt
item_key
using the globalmk
and globalak
following the instructions "Encrypting a string using the 003 scheme" below and send to server asenc_item_key
.
#
Decryption:Check the first 3 characters of the content
string. This will be the encryption version.
If it is equal to "001", which is a legacy scheme, decrypt according to the 001 instructions found here.
If it is equal to "002" or "003", decrypt as follows:
- Decrypt
enc_item_key
using the globalmk
and globalak
according to the "Decrypting a string using the 003 scheme" instructions below to getitem_key
. - Split
item_key
in half; set encryption keyitem_ek = first_half
and authentication keyitem_ak = second_half
. - Decrypt
content
usingitem_ek
anditem_ak
according to the "Decrypting a string using the 003 scheme" instructions below.
- Decrypt
#
Encrypting a string using the 003 scheme:Given a string_to_encrypt
, an encryption_key
, and an auth_key
:
- Generate a random 128 bit string called IV.
- Encrypt
string_to_encrypt
usingAES-CBC-256:Base64
,encryption_key
, andIV
:
- Generate
string_to_auth
by combining the encryption version (003), the item's UUID, the IV, and the ciphertext using the colon ":" character:
- Compute
auth_hash = HMAC-SHA256:Hex(string_to_auth, auth_key)
. - Generate the final result by combining the five components into a
:
separated string:
#
Decrypting a string using the 003 scheme:Given a string_to_decrypt
, an encryption_key
, and an auth_key
:
- Split the string into its constituent parts:
components = string_to_decrypt.split(":")
. - Assign local variables:
- Ensure that
uuid == item.uuid
. If not, abort decryption. - Generate
string_to_auth = [version, uuid, IV, ciphertext].join(":")
. - Compute
local_auth_hash = HMAC-SHA256(string_to_auth, auth_key)
. Comparelocal_auth_hash
toauth_hash
. If they are not the same, skip decrypting this item, as this indicates that the string has been tampered with. - Decrypt
ciphertext
to get final result:result = AES-Decrypt(ciphertext, encryption_key, IV)
.
#
Server-sideFor every received item:
- (Optional but recommended) Encrypt
content
using server known key and store. Decrypt before sending back to client.
#
Next StepsJoin the Slack group to discuss implementation details and ask any questions you may have.
You can also email help@standardnotes.org.
Follow @standardnotes on Twitter for updates and announcements.