alialbaali / Noto

Minimal Note-Taking App
https://play.google.com/store/apps/details?id=com.noto
Apache License 2.0
502 stars 26 forks source link

Client-Side Encryption #92

Open alialbaali opened 2 years ago

alialbaali commented 2 years ago

Noto will support cloud hosting as discussed here. It's currently in the work using Supabase for cloud storage, and Bouncy Castle for encryption.

After doing a lot of research and brainstorming, I found out that there are multiple ways it could be implemented. I'll probably go with the following approach:

Upon registration:

  1. User creates an account with email and password.
  2. The password is hashed locally, before transmitting to the server, by using Argon2id algorithm, with a random generated salt in the client.
  3. The password and the salt are sent to the server. Then, the password is rehashed on the server using bcrypt with a work factor of 10, and stored.
  4. Noto generates a password-derived key, called P, by using a KDF (Key derivation function) such as PBKDF2. Store the key locally in a safe, inaccessible location and never transmit it to the server in any circumstances.
  5. For a new folder, generate a symmetric key, called K. Encrypt that folder, its notes, and labels with K. Store the K locally in a safe, inaccessible location, and never transmit it to the server in plaintext.
  6. Encrypt K with P, which results in an encrypted key, called E.
  7. Transmit E to the server, along with the encrypted data.

The process, from 5 to 7, is repeated for each new folder with a different symmetric key K.

Upon login

  1. User enters their email and password.
  2. Get the salt from the server using the user email. Hash the password locally, and send it along with the email address to the server. If password hashes match, the user is logged in.
  3. Noto generates a password-derived key, called P, by using a KDF (Key derivation function) such as PBKDF2. Store the key locally in a safe, inaccessible location and never transmit it to the server in any circumstances.
  4. Get all the encrypted data from the server, along with a different K for each folder.
  5. Decrypt each symmetric key K using password-derived key P.
  6. Decrypt every folder using its own symmetric key K.

This approach is how it's planned to be. I'm still in the early process of the implementation, so things might change. I'll keep updating this issue as I work on the implementation.

Advantages of this approach:

  1. All the data are encrypted and no one would be able to view them except their owner.
  2. Ability to change user password without having to re-encrypt all the data, but only the symmetric keys for folders.
  3. By using a random different salt for each password, we're making sure that two passwords won't produce the same hash.
  4. Not sending the password to the server, meaning the server doesn’t know the user password at all, as the server could be logging or affected in some way.
  5. If an attacker managed to get access to the server database, they won't be able to decrypt the data, as they need the password-derived key P, which is never transmitted and only stored locally on the user device.
  6. If an attacker managed to get access to the server database, they won't be able to obtain the password quickly, as a different salt is being used for each new user. Meaning, it would prevent pre-computed password-hash pairs aka Rainbow Tables. They would need to create a new table for each password.
  7. If an attacker managed to get access to the communication between the server and the client during registration or login, they won't be able to obtain the password which is used to encrypt the data, as the hash is sent. This also helps to prevent the attacker from knowing the password pattern for that user, which has a high possibility of being used in other websites or apps.

Disadvantages of this approach:

  1. If the user forgets their password, they would lose all of their data, since the key that used to encrypt the data is based on the password. However, they might be able to get their account back without any data.
  2. The salt is available publicly for anyone that has the API key. The API key is never shared to anyone nor available in the version control (GitHub). But, it is possible to obtain it by decompiling the APK. So, I might need to find a better way to store it.

If you have any suggestions, please share them!

Resources: https://security.stackexchange.com/questions/88984/encrypting-with-passwords-encryption-of-key-vs-data https://security.stackexchange.com/questions/30193/encrypting-user-data-using-password-and-forgot-my-password https://stackoverflow.com/questions/7529582/how-to-store-private-encrypted-user-data-in-the-database-but-make-them-availabl https://stackoverflow.com/questions/18057390/how-can-i-encrypt-data-with-a-password-but-allow-said-password-to-be-reset https://stackoverflow.com/questions/12935409/safely-generated-encryption-key-from-users-password https://bitwarden.com/help/what-encryption-is-used/#aes-cbc https://security.stackexchange.com/questions/174647/end-to-end-encryption-with-multiple-users https://crypto.stackexchange.com/questions/22678/how-secure-is-it-to-use-password-as-aes-key https://stackoverflow.com/questions/45563332/aes-encryption-example-class-to-encrypt-data-using-users-password https://stackoverflow.com/questions/1949640/does-iv-work-like-salt https://cryptobook.nakov.com/ https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html

opk12 commented 1 year ago

Matrix (mentioned in #91) implements transparent E2EE in the client-side libraries. You get security updates for free and don't have to roll your own crypto (which general crypto wisdom considers a red flag).