Starting with Joplin v3.2, we introduced a series of new encryption methods. These methods are built on native libraries and are designed to enhance both performance and security. This document provides an overview and detailed specifications of these new encryption methods.
- **Master Password**: The user-provided password used to encrypt the Master Key.
- **Master Key**: A randomly generated 256-byte (2048-bit) key used to encrypt notes and resources. This is also called the `Data Key` in [this document](https://github.com/laurent22/joplin/blob/dev/readme/dev/spec/e2ee/workflow.md).
- **Master Key Encryption/Decryption**: In these parts, the Master Key is encrypted or decrypted using the Master Password.
- **Data Encryption/Decryption**: In these parts, notes and resources are encrypted or decrypted using the Master Key.
The Master Password is stored locally in the database and is never updated to the Sync Target. As a result, third-party Sync Targets cannot decrypt the Master Key, notes or resources.
- **KDF**: Key Derivation Function, used to derive cryptographically strong keys from passwords.
- **Encoder/Decoder**: The `encrypt()` and `decrypt()` methods take strings as input and produce strings as output. Since the cipher operates on binary data and generates binary outputs, encoders and decoders are used to convert between strings and binary data.
- **KDF (Key Derivation Function)**: The `encrypt()` and `decrypt()` methods are designed for password-based encryption, but passwords may not meet cryptographic key strength requirements. The KDF addresses this by taking a password and a salt as inputs and generating a secure cryptographic key for the actual encryption process.
- **Salt**: The salt is managed internally by the `encrypt()` and `decrypt()` methods (or more generally, within the `EncryptionService`), so callers don't need to handle it explicitly.
## 3. Specifications for the New Native Encryption Methods
The expanded flow is shown below. Some of the elements have been replaced with the specific implementation compared with the general implementation graphs. Additionally, extra elements have been introduced for the chosen cipher.
- For `KeyV1` and `FileV1`, the plaintext is initially binary but is encoded as strings to work with `encrypt()` and `decrypt()`, which operate only on strings. Before encryption, a proper decoder converts these strings back to their original binary form. This resolves the double Base64 encoding issue in the old encryption methods, resulting in smaller ciphertext.
- For `StringV1`, UTF-16 encoding ensures compatibility with all possible JavaScript characters.
The iteration count of `KeyV1` follows [OWASP recommendations](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2). For `StringV1` and `FileV1`, the randomly generated Master Key already provides sufficient cryptographic strength so the low iteration count is acceptable.</br>
However, applying the KDF to the Master Key is still necessary to resolve the short IV problem in the AES-GCM cipher. The details are provided in [Section 3.6](#36-extended-equivalent-nonce).</br>
The Initialization Vector (IV) length is set to 96 bits because extending it doesn't improve the cryptographic strength. Actually, the low-level implementation of AES-GCM always reduces longer IVs to 96 bits.
- The AES-GCM cipher has a maximum IV length of 96 bits (as discussed in [Section 3.5](#35-cipherdecipher-parameters)
), which is relatively short.
Although unlikely, a Joplin user could run into two pieces of ciphertext encrypted with the same IV even if the IV is randomly generated. This cause security vulnerabilities if the notes and resources is encrypted with the same Master Key directly. To resolve this, a 256-bit generated salt is used for each encryption to derive a new encryption key from the Master Key, so the key passed to the cipher changes every time. In theory, this approach provides a equivalent nonce with a length of (256+96) bits.