From 99d36f45c05142194b6698f71d94281c4cd68bfe Mon Sep 17 00:00:00 2001 From: Osmium Sorcerer Date: Wed, 23 Sep 2026 16:47:29 +0000 Subject: keyring: Improve key passphrase parameters Change Argon2id parameters from 1 gigabyte of memory and 3 iterations to 2 gigabytes and 2 iterations. Memory cost is the primary parameter that provides substantial hardening, and 2 GiB in particular is recommended by RFC 9106 (here with an additional iteration). This will impact performance of key derivation on devices with constrained RAM, but platform-backed keys should provide an alternative. Enforce minimum of 6 characters for the passphrase. This is still not enough considering an adversary can make unlimited offline guesses, even with significant slowdown. However, this is consistent with modern practices for authenticator policies (recommended, for example, by NIST SP 800-63B-4): enforce minimal length and nothing else. --- src/keyring.cpp | 6 +++--- src/widgets/key_generate_dialog.cpp | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/keyring.cpp b/src/keyring.cpp index 8f49dfa..4e8eeff 100644 --- a/src/keyring.cpp +++ b/src/keyring.cpp @@ -91,15 +91,15 @@ int generate_key(QStringView name, const QByteArray &password) } // Key wrapping. The crypto_pwhash derives the key from password with the - // specified parameters (3 iterations over 1 gigabyte of memory). This + // specified parameters (2 iterations over 2 gigabytes of memory). This // operation is slow and should ideally be handled asynchronously without // blocking the GUI thread. quint8 wrap_key[crypto_aead_chacha20poly1305_KEYBYTES]; sodium_mlock(wrap_key, sizeof(wrap_key)); quint8 salt[crypto_pwhash_SALTBYTES]; randombytes_buf(salt, sizeof(salt)); - quint32 pwhash_opslimit = crypto_pwhash_OPSLIMIT_MODERATE; - quint32 pwhash_memlimit = crypto_pwhash_MEMLIMIT_SENSITIVE; + quint32 pwhash_opslimit = 2u; + quint32 pwhash_memlimit = 2 * 1024 * 1024 * 1024u; quint8 pwhash_alg = crypto_pwhash_ALG_DEFAULT; if (crypto_pwhash(wrap_key, sizeof(wrap_key), password.constData(), password.size(), salt, pwhash_opslimit, pwhash_memlimit, pwhash_alg)) { diff --git a/src/widgets/key_generate_dialog.cpp b/src/widgets/key_generate_dialog.cpp index 1e9790e..72ba8a2 100644 --- a/src/widgets/key_generate_dialog.cpp +++ b/src/widgets/key_generate_dialog.cpp @@ -36,10 +36,12 @@ KeyGenerateDialog::KeyGenerateDialog(QWidget *parent) validate(); } -// Empty passwords should be also checked. void KeyGenerateDialog::validate() { - ui_key_gen_buttons->button(QDialogButtonBox::Ok)->setEnabled(!ui_key_password->text().isEmpty() && (ui_key_password->text() == ui_key_password_confirm->text())); + bool valid = !ui_key_password->text().isEmpty() && + (ui_key_password->text().length() >= 6) && + (ui_key_password->text() == ui_key_password_confirm->text()); + ui_key_gen_buttons->button(QDialogButtonBox::Ok)->setEnabled(valid); } QStringView KeyGenerateDialog::key_name() -- cgit