From 00ab29f5f080b491cf1a7b625b7a9c04454562ca Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Fri, 5 Dec 2025 10:34:41 +0200 Subject: [PATCH 1/9] data/settings: Remove nonexistent crypt_global_private_keys The setting is crypt_global_private_key. --- data/settings.js | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/data/settings.js b/data/settings.js index 16e247c07..6005c4715 100644 --- a/data/settings.js +++ b/data/settings.js @@ -2720,15 +2720,6 @@ the user's key pair. For EdDSA, you need to use X448 or X25519, case sensitive.` }, - crypt_global_private_keys: { - plugin: 'mail-crypt', - values: setting_types.NAMED_LIST_FILTER, - seealso: [ 'crypt_private_key_file', 'crypt_private_key_password' ], - text: ` -List of private keys to decrypt files. Add [[setting,crypt_private_key_file]] -and optionally [[setting,crypt_private_key_password]] inside each filter.` - }, - crypt_global_public_key_file: { plugin: 'mail-crypt', values: setting_types.FILE, @@ -2796,11 +2787,11 @@ regardless of this setting.` plugin: 'mail-crypt', values: setting_types.STRING, seealso: [ - 'crypt_global_private_keys', + 'crypt_global_private_key', 'crypt_user_key_encryption_key', ], text: ` -Name of the private key inside [[setting,crypt_global_private_keys]] or +Name of the private key inside [[setting,crypt_global_private_key]] or [[setting,crypt_user_key_encryption_key]].` }, @@ -2809,7 +2800,7 @@ Name of the private key inside [[setting,crypt_global_private_keys]] or values: setting_types.FILE, seealso: [ '[[link,mail_crypt_converting_ec_key_to_pkey]]', - 'crypt_global_private_keys', + 'crypt_global_private_key', 'crypt_user_key_encryption_key', ], text: ` @@ -2817,7 +2808,7 @@ Private key in [[link,mail_crypt_converting_ec_key_to_pkey]]. The PEM key may additionally be base64-encoded into a single line, which can make it easier to store into userdb extra fields. -Used inside [[setting,crypt_global_private_keys]] and +Used inside [[setting,crypt_global_private_key]] and [[setting,crypt_user_key_encryption_key]] lists.` }, From 3bbea29540d0346022c29db9be232fb56ac3cd07 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Fri, 5 Dec 2025 10:42:07 +0200 Subject: [PATCH 2/9] mail_crypt: Remove "Dynamic Settings" section It's a worse duplication of an existing "Base64-encoded Keys" section --- docs/core/plugins/mail_crypt.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/docs/core/plugins/mail_crypt.md b/docs/core/plugins/mail_crypt.md index e53a87df4..59eae5526 100644 --- a/docs/core/plugins/mail_crypt.md +++ b/docs/core/plugins/mail_crypt.md @@ -131,20 +131,6 @@ List of known algorithms that Dovecot supports as of writing. 3 Requires recent enough OpenSSL. -### Dynamic Settings - -Per-user settings may be returned by [[link,userdb_extra_fields]]. -To provide [[setting,crypt_global_private_key]] or -[[setting,crypt_global_public_key_file]] as a single line userdb attribute you -can base64 encode the original PEM key contents. For example: - -```sh -cat ecprivkey.pem | base64 -w0 -``` - -All configured keys must be in -[[link,mail_crypt_converting_ec_key_to_pkey,PEM]] format. - ## Modes Of Operation Mail crypt plugin can operate using **either** global keys or folder keys. From 2d18c4f9acfcd6521cf94f22362fc16975be9de9 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Fri, 5 Dec 2025 11:21:06 +0200 Subject: [PATCH 3/9] mail-crypt: Fix EC keys link This is a sub-sectin of global keys. The previously linked crypt_user_key_curve setting applies only for folder keys mode. --- docs/core/plugins/mail_crypt.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/core/plugins/mail_crypt.md b/docs/core/plugins/mail_crypt.md index 59eae5526..289453b8d 100644 --- a/docs/core/plugins/mail_crypt.md +++ b/docs/core/plugins/mail_crypt.md @@ -14,6 +14,9 @@ dovecotlinks: mail_crypt_global_keys: hash: global-keys text: "Mail Crypt Plugin: Global Keys" + mail_crypt_ec_key: + hash: elliptic-curve-ec-key + text: "Mail Crypt Plugin: Elliptic Curve (EC) Keys" mail_crypt_supported_sym_algorithms: hash: supported-symmetric-algorithms text: "Mail Crypt Plugin: Supported symmetric algorithms" @@ -304,7 +307,7 @@ Note that ED25519 keys are not suitable for X25519. #### RSA key ::: warning -Use of RSA keys is discouraged, please use [[setting,crypt_user_key_curve]] +Use of RSA keys is discouraged, please use [[link,mail_crypt_ec_key,EC keys]] instead. ::: From f6c11dd357f114c24e95dbc95d4a0aed5eac10a2 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Fri, 5 Dec 2025 11:23:03 +0200 Subject: [PATCH 4/9] mail-crypt: Reorder sections Settings should be near the end. --- docs/core/plugins/mail_crypt.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/core/plugins/mail_crypt.md b/docs/core/plugins/mail_crypt.md index 289453b8d..55d21fd47 100644 --- a/docs/core/plugins/mail_crypt.md +++ b/docs/core/plugins/mail_crypt.md @@ -89,10 +89,6 @@ encryption with care and backup encryption keys! This page assumes you are using configuring mail encryption from scratch with a recent version of Dovecot. -## Settings - - - ### Supported symmetric algorithms While mail crypt plugin does not support setting encryption algorithm, @@ -413,6 +409,11 @@ share the key to groups or someone with no public key. You can use [`decrypt.py`][https://github.com/dovecot/tools/blob/main/dcrypt-decrypt.py] to decrypt encrypted files. +## Settings + + + + ## `fs-crypt` `fs-crypt` is a [[link,fs,lib-fs wrapper]] that can encrypt and decrypt files. @@ -427,7 +428,7 @@ combined. Please make sure that compression is always applied before encryption. See [[plugin,fs-compress]] for an example and more details about compression. -## `fs-crypt` settings +### `fs-crypt` settings See [[link,mail_crypt_settings]] for generic mail-crypt settings. From fedb21a1106acd2517c5645f55286a94bf6e1a2e Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Fri, 5 Dec 2025 11:25:49 +0200 Subject: [PATCH 5/9] mail-crypt: Move global keys section before per-folder keys It's more commonly used. --- docs/core/plugins/mail_crypt.md | 144 ++++++++++++++++---------------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/docs/core/plugins/mail_crypt.md b/docs/core/plugins/mail_crypt.md index 55d21fd47..7fb9b8a91 100644 --- a/docs/core/plugins/mail_crypt.md +++ b/docs/core/plugins/mail_crypt.md @@ -135,78 +135,6 @@ List of known algorithms that Dovecot supports as of writing. Mail crypt plugin can operate using **either** global keys or folder keys. Using both is not supported. -### Folder Keys Mode - -In this mode, for the user a key pair is generated. Then for each folder a key -pair is generated. This folder is encrypted using the user's key pair. A user -can have more than one key pair but only one can be active. - -* [[setting,crypt_user_key_curve]] must be set. -* [[setting,mail_attribute]] must be set, as is is used to store the keys. - -#### Unencrypted User Keys - -In this version of the folder keys mode, each user's private key is stored -unencrypted on the server. - -Example config for folder keys with Maildir: - -```[dovecot.conf] -mail_plugins { - mail_crypt = yes -} -mail_attribute { - dict file { - path = %{home}/Maildir/dovecot-attributes - } -} - -crypt_user_key_curve = secp521r1 -``` - -#### Encrypted User Keys - -In this version of the folder keys mode, the users private key is stored -encrypted on the server. - -Example config for mandatory encrypted folder keys with Maildir: - -```[dovecot.conf] -mail_plugins { - mail_crypt = yes -} -mail_attribute { - dict file { - path = %{home}/Maildir/dovecot-attributes - } -} - -crypt_user_key_curve = secp521r1 -crypt_user_key_require_encrypted = yes -``` - -The password that is used to decrypt the users master/private key, must be -provided via password query: - -```[dovecot.conf] -passdb sql { - query = SELECT email as user, password, '%{password | sha256}' AS userdb_crypt_user_key_password \ - FROM virtual_users \ - WHERE email='%{user}' -} -``` - -#### Choosing Encryption Password - -DO NOT use passwords directly. It can contain `%` which is interpreted as -variable expansion and can cause errors. Also, it might be visible in -debug logging. Suggested approaches are base64 encoding, hex encoding -or hashing the password. With hashing, you get the extra benefit that -password won't be directly visible in logs. - -Another issue that you must consider when using user's password is that -when the password changes, **you must re-encrypt the user private key**. - ### Global keys In this mode, all keying material is taken from the settings: @@ -344,6 +272,78 @@ crypt_global_private_key main { } ``` +### Folder Keys Mode + +In this mode, for the user a key pair is generated. Then for each folder a key +pair is generated. This folder is encrypted using the user's key pair. A user +can have more than one key pair but only one can be active. + +* [[setting,crypt_user_key_curve]] must be set. +* [[setting,mail_attribute]] must be set, as is is used to store the keys. + +#### Unencrypted User Keys + +In this version of the folder keys mode, each user's private key is stored +unencrypted on the server. + +Example config for folder keys with Maildir: + +```[dovecot.conf] +mail_plugins { + mail_crypt = yes +} +mail_attribute { + dict file { + path = %{home}/Maildir/dovecot-attributes + } +} + +crypt_user_key_curve = secp521r1 +``` + +#### Encrypted User Keys + +In this version of the folder keys mode, the users private key is stored +encrypted on the server. + +Example config for mandatory encrypted folder keys with Maildir: + +```[dovecot.conf] +mail_plugins { + mail_crypt = yes +} +mail_attribute { + dict file { + path = %{home}/Maildir/dovecot-attributes + } +} + +crypt_user_key_curve = secp521r1 +crypt_user_key_require_encrypted = yes +``` + +The password that is used to decrypt the users master/private key, must be +provided via password query: + +```[dovecot.conf] +passdb sql { + query = SELECT email as user, password, '%{password | sha256}' AS userdb_crypt_user_key_password \ + FROM virtual_users \ + WHERE email='%{user}' +} +``` + +#### Choosing Encryption Password + +DO NOT use passwords directly. It can contain `%` which is interpreted as +variable expansion and can cause errors. Also, it might be visible in +debug logging. Suggested approaches are base64 encoding, hex encoding +or hashing the password. With hashing, you get the extra benefit that +password won't be directly visible in logs. + +Another issue that you must consider when using user's password is that +when the password changes, **you must re-encrypt the user private key**. + ## Base64-encoded Keys Mail-crypt plugin can read keys that are base64 encoded. This is intended From 6434d72126d0ddc57e78ad5f3507a5658c21c486 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Fri, 5 Dec 2025 11:41:48 +0200 Subject: [PATCH 6/9] mail-crypt: Rewrite "Choosing Encryption Password" After config-rewrite there are no more issues with % characters. --- docs/core/plugins/mail_crypt.md | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/core/plugins/mail_crypt.md b/docs/core/plugins/mail_crypt.md index 7fb9b8a91..95ae8f0a5 100644 --- a/docs/core/plugins/mail_crypt.md +++ b/docs/core/plugins/mail_crypt.md @@ -322,8 +322,21 @@ crypt_user_key_curve = secp521r1 crypt_user_key_require_encrypted = yes ``` -The password that is used to decrypt the users master/private key, must be -provided via password query: +The password that is used to decrypt the user's private key must be +provided via the [[setting,crypt_user_key_password]] setting. See below. + +#### Choosing Encryption Password + +It is recommended to use a hash of the user's plaintext login password as the +encryption key password instead of the plaintext password directly. This way +the plaintext password is less likely to become visible accidentally, such as +in debug logs. + +Another issue that you must consider when using the login password is that +when the password changes, **you must re-encrypt the user private key**. + +Example config where the user's login password is used as the encryption key +password: ```[dovecot.conf] passdb sql { @@ -333,17 +346,6 @@ passdb sql { } ``` -#### Choosing Encryption Password - -DO NOT use passwords directly. It can contain `%` which is interpreted as -variable expansion and can cause errors. Also, it might be visible in -debug logging. Suggested approaches are base64 encoding, hex encoding -or hashing the password. With hashing, you get the extra benefit that -password won't be directly visible in logs. - -Another issue that you must consider when using user's password is that -when the password changes, **you must re-encrypt the user private key**. - ## Base64-encoded Keys Mail-crypt plugin can read keys that are base64 encoded. This is intended From 0dfa1c33fa87a1f799a8dc3e96bb3a1b939d600e Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Fri, 5 Dec 2025 11:42:36 +0200 Subject: [PATCH 7/9] mail-crypt: Various cleanups --- data/settings.js | 18 ++++++++---------- docs/core/plugins/mail_crypt.md | 16 ++++++++++------ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/data/settings.js b/data/settings.js index 6005c4715..dc91e6c25 100644 --- a/data/settings.js +++ b/data/settings.js @@ -2698,7 +2698,14 @@ If enabled, you cannot share a key to groups or someone without a public key.` plugin: 'mail-crypt', values: setting_types.STRING, text: ` -Defines the elliptic curve to use for key generation. +Defines the elliptic curve to use for key generation. A key pair is generated +for the user, and a key pair is generated for each folder. The folder key is +encrypted using the user key. + +This must be set if you wish to use [[link,mail_crypt_folder_keys,folder keys]] +rather than [[link,mail_crypt_global_keys,global keys]]. With global keys +(either RSA or EC keys), all keying material is taken from the global key +settings and no key generation is performed. Any valid curve supported by the underlying cryptographic library is allowed. @@ -2708,15 +2715,6 @@ Example: crypt_user_key_curve = secp521r1 \`\`\` -This must be set if you wish to use folder keys rather than global keys. - -With global keys (either RSA or EC keys), all keying material is taken -from the setting and no key generation is performed. - -In folder-keys mode, a key pair is generated for the user, and a -folder-specific key pair is generated. The latter is encrypted by means of -the user's key pair. - For EdDSA, you need to use X448 or X25519, case sensitive.` }, diff --git a/docs/core/plugins/mail_crypt.md b/docs/core/plugins/mail_crypt.md index 95ae8f0a5..eb0ecf2df 100644 --- a/docs/core/plugins/mail_crypt.md +++ b/docs/core/plugins/mail_crypt.md @@ -14,6 +14,9 @@ dovecotlinks: mail_crypt_global_keys: hash: global-keys text: "Mail Crypt Plugin: Global Keys" + mail_crypt_folder_keys: + hash: folder-keys + text: "Mail Crypt Plugin: Folder Keys" mail_crypt_ec_key: hash: elliptic-curve-ec-key text: "Mail Crypt Plugin: Elliptic Curve (EC) Keys" @@ -272,14 +275,15 @@ crypt_global_private_key main { } ``` -### Folder Keys Mode +### Folder Keys -In this mode, for the user a key pair is generated. Then for each folder a key -pair is generated. This folder is encrypted using the user's key pair. A user -can have more than one key pair but only one can be active. +In this mode, a key pair is generated for the user. Then for each folder a key +pair is generated. This folder is encrypted using the user key. A user +can have more than one key pair for reading, but only one can be active for +writing. * [[setting,crypt_user_key_curve]] must be set. -* [[setting,mail_attribute]] must be set, as is is used to store the keys. +* [[setting,mail_attribute]] must be set, as it is used to store the keys. #### Unencrypted User Keys @@ -303,7 +307,7 @@ crypt_user_key_curve = secp521r1 #### Encrypted User Keys -In this version of the folder keys mode, the users private key is stored +In this version of the folder keys mode, the user's private key is stored encrypted on the server. Example config for mandatory encrypted folder keys with Maildir: From f855e7648db8ef755166e3e05adf80271b7bb316 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Fri, 5 Dec 2025 12:03:25 +0200 Subject: [PATCH 8/9] mail-crypt: Recommend using stronger hash for user key encryption password --- docs/core/plugins/mail_crypt.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/core/plugins/mail_crypt.md b/docs/core/plugins/mail_crypt.md index eb0ecf2df..b65a151f3 100644 --- a/docs/core/plugins/mail_crypt.md +++ b/docs/core/plugins/mail_crypt.md @@ -334,7 +334,8 @@ provided via the [[setting,crypt_user_key_password]] setting. See below. It is recommended to use a hash of the user's plaintext login password as the encryption key password instead of the plaintext password directly. This way the plaintext password is less likely to become visible accidentally, such as -in debug logs. +in debug logs. Also using a strong hash makes the key more resistant against +brute force attacks. Another issue that you must consider when using the login password is that when the password changes, **you must re-encrypt the user private key**. @@ -344,7 +345,7 @@ password: ```[dovecot.conf] passdb sql { - query = SELECT email as user, password, '%{password | sha256}' AS userdb_crypt_user_key_password \ + query = SELECT email as user, password, '%{password | hash("pbkdf2")}' AS userdb_crypt_user_key_password \ FROM virtual_users \ WHERE email='%{user}' } From ee0280e963107c92d58528dcfc5e0c456f9c38d8 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Fri, 5 Dec 2025 12:06:49 +0200 Subject: [PATCH 9/9] mail-crypt: Add a note about using sql/ldap as encryption key password source --- docs/core/plugins/mail_crypt.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/core/plugins/mail_crypt.md b/docs/core/plugins/mail_crypt.md index b65a151f3..1556f87c8 100644 --- a/docs/core/plugins/mail_crypt.md +++ b/docs/core/plugins/mail_crypt.md @@ -340,6 +340,9 @@ brute force attacks. Another issue that you must consider when using the login password is that when the password changes, **you must re-encrypt the user private key**. +Instead of using the login password, another possibility could be to store the +encryption password in a separate database (e.g. SQL or LDAP). + Example config where the user's login password is used as the encryption key password: