architecture/README.md
Your data is end-to-end encrypted with Ente.
Meaning, they are encrypted with your keys before they leave your device.
Our source code has been audited to
verify that these keys are available only to you.
Meaning, only you can access your data.
What follows is an explanation of how we do what we do.
When you sign up for Ente, your client generates a masterKey for you. This
never leaves your device unencrypted.
Once you choose a password, a keyEncryptionKey is derived from it. This never
leaves your device.
During registration, your masterKey is encrypted with your keyEncryptionKey,
and the resultant encryptedMasterKey is then sent to our servers for storage.
When you sign in on a secondary device, after you successfully verify your
email, our servers give you back your encryptedMasterKey that was sent to us
by your primary device.
You are then prompted to enter your password. Once entered, your
keyEncryptionKey is derived, and the client decrypts your encryptedMasterKey
with this, to yield your original masterKey.
If the decryption fails, the client will know that the derived
keyEncryptionKey was wrong, indicating an incorrect password, and this
information will be surfaced to you.
keyEncryptionKey.keyEncryptionKey, only you have access to
your masterKey.Keep reading to learn about how this
masterKeyis used to encrypt your data.
Each of your items in Ente belong to what we call a collection. A collection
can be either a folder (like "Camera" or "Screenshots") or an album (like
"Awkward Reunion"). In case of Auth, we create a default root collection.
Each collection has a collectionKey. These never leave your device
unencrypted.
Every piece of your data has a fileKey. These never leave your device
unencrypted.
fileKeys.fileKey is encrypted with the collectionKey of the collection
(folder/album) the file belongs to. In case such a collection does not
exist, one is created with a randomly generated collectionKey. All
collection metadata (like name, folder-path, etc) are encrypted with this
collectionKey.collectionKey is then encrypted with your masterKey.collectionKey with your masterKey.fileKey with their respective collectionKeys.fileKeys.masterKey.masterKey, only you can decrypt the
collectionKeys.collectionKeys, only you can decrypt the
fileKeys.fileKeys, only you can decrypt the files
and their associated metadata.When you sign up for Ente, your app generates a publicKey for you. This is
public, and is stored at our servers in plain text.
Verification ID is a human readable representation of a publicKey, that is
accessible within the clients for verifying the identity of a receiver.
Along with the publicKey, your app also generates a corresponding privateKey
for you. This never leaves your device unencrypted.
The privateKey is encrypted with your masterKey that only you have access
to. This encryptedPrivateKey is stored at our servers
Sharing is similar to the previous section, except that the collectionKey of a
collection is shared with a receiver after encrypting it with the receiver's
publicKey. To elaborate,
fileKeys.fileKeys were also encrypted with the collectionKey of the
collection (folder/album) that is now being shared.collectionKey is now encrypted with the publicKey of the receiver.collectionKey with their privateKey.fileKey with their respective
collectionKeys.fileKeys.masterKey, only they can decrypt
their encryptedPrivateKey to access their privateKey.privateKey, only they can
decrypt the collectionKey that was sent to them.collectionKey, only they can
decrypt the fileKeys of files belonging to that album/folder.fileKeys of files belonging to
that album/folder, only they can decrypt the files and associated metadata.A sender can view the Verification ID of the receiver within the app's sharing screen, and compare this with the Verification ID displayed on the receiver's device. The two identifiers matching across devices verifies the security of end-to-end encryption between the two parties.
When you sign up for Ente, your app generates a recoveryKey for you. This
never leaves your device unencrypted.
Your recoveryKey and masterKey are encrypted with each other and stored on
the server.
This encrypted recoveryKey is downloaded when you sign in on a new device.
This is decrypted with your masterKey and surfaced to you whenever you request
for it.
Post email verification, if you're unable to unlock your account because you
have forgotten your password, the client will prompt you to enter your
recoveryKey.
The client then pulls the masterKey that was earlier encrypted and pushed to
the server (as discussed in Key Encryption, and decrypts it
with the entered recoveryKey. If the decryption succeeds, the client will know
that you have entered the correct recoveryKey.
Now that you have your masterKey, the client will prompt you to set a new
password, using which it will derive a new keyEncryptionKey. This is then used
to encrypt your masterKey and this new encryptedMasterKey is uploaded to our
servers, similar to what was earlier discussed in Key
Encryption.
masterKey, only you can access your
recoveryKey.recoveryKey, only you can reset your
password.When you attempt to verify ownership of an email address, our server generates a
oneTimeToken, that if presented confirms your access to the said email
address. This token is valid for a short time and can only be used once.
When you successfully authenticate yourself against our server by proving
ownership of your email (and in future any other configured vectors), the server
generates an authToken, that can from there on be used to authenticate against
our private APIs.
A generated authToken is returned to your client after being encrypted with
your publicKey. This encryptedAuthToken can only be decrypted with your
privateKey.
oneTimeToken is sent.authToken is
generated and an encryptedAuthToken is returned to you, along with your
other encrypted keys.masterKey is
derived (as discussed here).masterKey, the rest of your keys, including your privateKey is
decrypted (as discussed here).privateKey, the client will then decrypt the encryptedAuthToken
that was earlier encrypted by our server with your publicKey.authToken can then from there on be used to authenticate all
API calls against our servers.Only by verifying access to your email and knowing your password can you obtain
an authToken that can be used to authenticate yourself against our servers.
We rely on the high level APIs exposed by this wonderful library called libsodium.
crypto_secretbox_keygen
is used to generate all random keys within the application. Your masterKey,
recoveryKey, collectionKey, fileKey are all 256-bit keys generated using
this API.
crypto_box_keypair
is used to generate your publicKey and privateKey pairs.
crypto_pwhash
is used to derive your keyEncryptionKey from your password.
crypto_pwhash_OPSLIMIT_SENSITIVE and crypto_pwhash_MEMLIMIT_SENSITIVE are
used as the limits for computation and memory respectively. If the operation
fails due to insufficient memory, the former is doubled and the latter is halved
progressively, until a key can be derived. If during this process the memory
limit is reduced to a value less than crypto_pwhash_MEMLIMIT_MIN, the client
will not let you register from that device.
Internally, this uses Argon2 v1.3, which is regarded as one of the best hashing algorithms currently available.
crypto_secretbox_easy
is used to encrypt your masterKey, recoveryKey, privateKey,
collectionKeys and fileKeys. Internally, this uses
XSalsa20
stream cipher with Poly1305
MAC for
authentication.
crypto_secretstream_*
APIs are used to encrypt your file data in chunks. Internally, this uses
XChaCha20
stream cipher with Poly1305
MAC for
authentication.
crypto_box_seal
is used in sharing to encrypt a collectionKey with the receiver's publicKey.
It is also used to encrypt an authToken that is issued to a user, with their
publicKey.
Internally, this uses X25519 for key exchange, XSalsa20 stream cipher for encryption and Poly1305 MAC for authentication.
randombytes_buf is
used to generate a new salt/nonce every time data needs to be hashed/encrypted.
Verification ID is generated by converting the sha256 value of a publicKey
to it's corresponding BIP39 mnemonic
phrase.
Thank you for reading this far! For implementation details, we request you to checkout our code.
If you'd like to help us improve this document, kindly email [email protected].
We have a separate document that outlines how we replicate your data across 3 different cloud providers to ensure reliability.