mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-12-04 15:39:34 +01:00
Address PR comments
This commit is contained in:
@@ -353,6 +353,9 @@ void ApplicationSettingsWidget::loadSettings()
|
||||
#ifdef Q_OS_LINUX
|
||||
// Remembering quick unlock is not supported on Linux
|
||||
m_secUi->quickUnlockRememberCheckBox->setVisible(false);
|
||||
#else
|
||||
// Only show this option if Touch ID or Windows Hello are available for use
|
||||
m_secUi->quickUnlockRememberCheckBox->setVisible(getQuickUnlock()->isNativeAvailable());
|
||||
#endif
|
||||
|
||||
for (const ExtraPage& page : asConst(m_extraPages)) {
|
||||
|
||||
@@ -174,11 +174,8 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="quickUnlockRememberCheckBox">
|
||||
<property name="toolTip">
|
||||
<string>Quick unlock can only be remembered when using Touch ID or Windows Hello</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remember quick unlock after database is closed (Touch ID / Windows Hello only)</string>
|
||||
<string>Remember quick unlock after database is closed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -484,7 +484,7 @@
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Quick Unlock</string>
|
||||
<string>Quick Unlock</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
@@ -609,7 +609,7 @@
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>8</height>
|
||||
<height>4</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
@@ -621,6 +621,12 @@
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="resetQuickUnlockButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reset</string>
|
||||
</property>
|
||||
@@ -636,7 +642,7 @@
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>6</width>
|
||||
<width>4</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
@@ -644,6 +650,12 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeQuickUnlockButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close Database</string>
|
||||
</property>
|
||||
@@ -742,7 +754,6 @@
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>quickUnlockButton</tabstop>
|
||||
<tabstop>editPassword</tabstop>
|
||||
<tabstop>keyFileLineEdit</tabstop>
|
||||
<tabstop>buttonBrowseFile</tabstop>
|
||||
@@ -750,7 +761,11 @@
|
||||
<tabstop>hardwareKeyCombo</tabstop>
|
||||
<tabstop>refreshHardwareKeys</tabstop>
|
||||
<tabstop>addKeyFileLinkLabel</tabstop>
|
||||
<tabstop>enableQuickUnlockCheckBox</tabstop>
|
||||
<tabstop>buttonBox</tabstop>
|
||||
<tabstop>quickUnlockButton</tabstop>
|
||||
<tabstop>resetQuickUnlockButton</tabstop>
|
||||
<tabstop>closeQuickUnlockButton</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2023 KeePassXC Team <team@keepassxc.org>
|
||||
/*
|
||||
* Copyright (C) 2025 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,13 +20,17 @@
|
||||
#include "crypto/CryptoHash.h"
|
||||
#include "crypto/Random.h"
|
||||
#include "crypto/SymmetricCipher.h"
|
||||
#include "crypto/kdf/Argon2Kdf.h"
|
||||
|
||||
#include <QInputDialog>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#define MIN_PIN_LENGTH 4
|
||||
#define MAX_PIN_LENGTH 8
|
||||
#define MAX_PIN_ATTEMPTS 3
|
||||
namespace
|
||||
{
|
||||
constexpr int MIN_PIN_LENGTH = 6;
|
||||
constexpr int MAX_PIN_LENGTH = 10;
|
||||
constexpr int MAX_PIN_ATTEMPTS = 3;
|
||||
} // namespace
|
||||
|
||||
bool PinUnlock::isAvailable() const
|
||||
{
|
||||
@@ -47,7 +51,7 @@ bool PinUnlock::setKey(const QUuid& dbUuid, const QByteArray& data)
|
||||
pin = QInputDialog::getText(
|
||||
nullptr,
|
||||
QObject::tr("Quick Unlock Pin Entry"),
|
||||
QObject::tr("Enter a %1 to %2 digit pin to use for quick unlock:").arg(MIN_PIN_LENGTH).arg(MAX_PIN_LENGTH),
|
||||
QObject::tr("Enter a %1–%2 digit pin to use for quick unlock:").arg(MIN_PIN_LENGTH).arg(MAX_PIN_LENGTH),
|
||||
QLineEdit::Password,
|
||||
{},
|
||||
&ok);
|
||||
@@ -63,15 +67,20 @@ bool PinUnlock::setKey(const QUuid& dbUuid, const QByteArray& data)
|
||||
}
|
||||
}
|
||||
|
||||
// Hash the pin and use it as the key for the encryption
|
||||
// Hash the pin then run it through Argon2 to derive the encryption key
|
||||
QByteArray key(32, '\0');
|
||||
Argon2Kdf kdf(Argon2Kdf::Type::Argon2id);
|
||||
CryptoHash hash(CryptoHash::Sha256);
|
||||
hash.addData(pin.toLatin1());
|
||||
auto key = hash.result();
|
||||
if (!kdf.transform(hash.result(), key)) {
|
||||
m_error = QObject::tr("Failed to derive key using Argon2");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate a random IV
|
||||
auto iv = Random::instance()->randomArray(SymmetricCipher::defaultIvSize(SymmetricCipher::Aes256_GCM));
|
||||
const auto iv = Random::instance()->randomArray(SymmetricCipher::defaultIvSize(SymmetricCipher::Aes256_GCM));
|
||||
|
||||
// Encrypt the data using AES-256-CBC
|
||||
// Encrypt the data using AES-256-GCM
|
||||
SymmetricCipher cipher;
|
||||
if (!cipher.init(SymmetricCipher::Aes256_GCM, SymmetricCipher::Encrypt, key, iv)) {
|
||||
m_error = QObject::tr("Failed to init KeePassXC crypto.");
|
||||
@@ -117,13 +126,18 @@ bool PinUnlock::getKey(const QUuid& dbUuid, QByteArray& data)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hash the pin and use it as the key for the encryption
|
||||
// Hash the pin then run it through Argon2 to derive the encryption key
|
||||
QByteArray key(32, '\0');
|
||||
Argon2Kdf kdf(Argon2Kdf::Type::Argon2id);
|
||||
CryptoHash hash(CryptoHash::Sha256);
|
||||
hash.addData(pin.toLatin1());
|
||||
auto key = hash.result();
|
||||
if (!kdf.transform(hash.result(), key)) {
|
||||
m_error = QObject::tr("Failed to derive key using Argon2");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the previously used challenge and encrypted data
|
||||
auto ivSize = SymmetricCipher::defaultIvSize(SymmetricCipher::Aes256_GCM);
|
||||
const auto ivSize = SymmetricCipher::defaultIvSize(SymmetricCipher::Aes256_GCM);
|
||||
const auto& keydata = pairData.second;
|
||||
auto challenge = keydata.left(ivSize);
|
||||
auto encrypted = keydata.mid(ivSize);
|
||||
@@ -145,7 +159,7 @@ bool PinUnlock::getKey(const QUuid& dbUuid, QByteArray& data)
|
||||
}
|
||||
|
||||
data.clear();
|
||||
m_error = QObject::tr("Maximum pin attempts have been reached.");
|
||||
m_error = QObject::tr("Too many pin attempts.");
|
||||
reset(dbUuid);
|
||||
return false;
|
||||
}
|
||||
@@ -155,11 +169,6 @@ bool PinUnlock::hasKey(const QUuid& dbUuid) const
|
||||
return m_encryptedKeys.contains(dbUuid);
|
||||
}
|
||||
|
||||
bool PinUnlock::canRemember() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void PinUnlock::reset(const QUuid& dbUuid)
|
||||
{
|
||||
m_encryptedKeys.remove(dbUuid);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2023 KeePassXC Team <team@keepassxc.org>
|
||||
/*
|
||||
* Copyright (C) 2025 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -34,8 +34,6 @@ public:
|
||||
bool getKey(const QUuid& dbUuid, QByteArray& key) override;
|
||||
bool hasKey(const QUuid& dbUuid) const override;
|
||||
|
||||
bool canRemember() const override;
|
||||
|
||||
void reset(const QUuid& dbUuid) override;
|
||||
void reset() override;
|
||||
|
||||
|
||||
@@ -237,11 +237,6 @@ bool Polkit::getKey(const QUuid& dbUuid, QByteArray& key)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Polkit::canRemember() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Polkit::hasKey(const QUuid& dbUuid) const
|
||||
{
|
||||
if (!m_encryptedMasterKeys.contains(dbUuid)) {
|
||||
|
||||
@@ -36,8 +36,6 @@ public:
|
||||
bool getKey(const QUuid& dbUuid, QByteArray& key) override;
|
||||
bool hasKey(const QUuid& dbUuid) const override;
|
||||
|
||||
bool canRemember() const override;
|
||||
|
||||
void reset(const QUuid& dbUuid) override;
|
||||
void reset() override;
|
||||
|
||||
|
||||
@@ -68,11 +68,3 @@ bool QuickUnlockManager::isNativeAvailable() const
|
||||
{
|
||||
return m_nativeInterface && m_nativeInterface->isAvailable();
|
||||
}
|
||||
|
||||
bool QuickUnlockManager::isRememberAvailable() const
|
||||
{
|
||||
if (isNativeAvailable()) {
|
||||
return m_nativeInterface->canRemember();
|
||||
}
|
||||
return m_fallbackInterface->canRemember();
|
||||
}
|
||||
|
||||
@@ -36,8 +36,6 @@ public:
|
||||
virtual bool getKey(const QUuid& dbUuid, QByteArray& key) = 0;
|
||||
virtual bool hasKey(const QUuid& dbUuid) const = 0;
|
||||
|
||||
virtual bool canRemember() const = 0;
|
||||
|
||||
virtual void reset(const QUuid& dbUuid) = 0;
|
||||
virtual void reset() = 0;
|
||||
};
|
||||
@@ -52,7 +50,6 @@ public:
|
||||
|
||||
QSharedPointer<QuickUnlockInterface> interface() const;
|
||||
bool isNativeAvailable() const;
|
||||
bool isRememberAvailable() const;
|
||||
|
||||
private:
|
||||
QSharedPointer<QuickUnlockInterface> m_nativeInterface;
|
||||
|
||||
@@ -31,8 +31,6 @@ public:
|
||||
bool getKey(const QUuid& dbUuid, QByteArray& passwordKey) override;
|
||||
bool hasKey(const QUuid& dbUuid) const override;
|
||||
|
||||
bool canRemember() const override;
|
||||
|
||||
void reset(const QUuid& dbUuid = "") override;
|
||||
void reset() override;
|
||||
|
||||
@@ -44,8 +42,6 @@ private:
|
||||
|
||||
static void deleteKeyEntry(const QString& accountName);
|
||||
static QString databaseKeyName(const QUuid& dbUuid);
|
||||
|
||||
QHash<QUuid, QByteArray> m_encryptedMasterKeys;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_TOUCHID_H
|
||||
|
||||
@@ -25,6 +25,8 @@ inline void debug(const char *message, ...)
|
||||
}
|
||||
#endif
|
||||
|
||||
static const auto s_touchIdKeyPrefix = QStringLiteral("KeepassXC_TouchID_Keys_");
|
||||
|
||||
inline std::string StatusToErrorMessage(OSStatus status)
|
||||
{
|
||||
CFStringRef text = SecCopyErrorMessageString(status, NULL);
|
||||
@@ -73,8 +75,7 @@ void TouchID::deleteKeyEntry(const QString& accountName)
|
||||
|
||||
QString TouchID::databaseKeyName(const QUuid& dbUuid)
|
||||
{
|
||||
static const QString keyPrefix = "KeepassXC_TouchID_Keys_";
|
||||
return keyPrefix + dbUuid.toString();
|
||||
return s_touchIdKeyPrefix + dbUuid.toString();
|
||||
}
|
||||
|
||||
QString TouchID::errorString() const
|
||||
@@ -85,8 +86,35 @@ QString TouchID::errorString() const
|
||||
|
||||
void TouchID::reset()
|
||||
{
|
||||
// TODO: Clear all credentials associated with KeePassXC
|
||||
m_encryptedMasterKeys.clear();
|
||||
// Query for all generic password items
|
||||
CFMutableDictionaryRef query = makeDictionary();
|
||||
CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword);
|
||||
CFDictionarySetValue(query, kSecReturnAttributes, kCFBooleanTrue);
|
||||
CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
|
||||
|
||||
CFTypeRef result = nullptr;
|
||||
OSStatus status = SecItemCopyMatching(query, &result);
|
||||
if (status != errSecSuccess || !result) {
|
||||
LogStatusError("TouchID::deleteAllKeyEntriesWithPrefix - Error querying keychain", status);
|
||||
CFRelease(query);
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray* items = (__bridge NSArray*)result;
|
||||
for (NSDictionary* item in items) {
|
||||
NSString* account = item[(id)kSecAttrAccount];
|
||||
if (account && [account hasPrefix:s_touchIdKeyPrefix.toNSString()]) {
|
||||
// Build a query to delete this item
|
||||
CFMutableDictionaryRef delQuery = makeDictionary();
|
||||
CFDictionarySetValue(delQuery, kSecClass, kSecClassGenericPassword);
|
||||
CFDictionarySetValue(delQuery, kSecAttrAccount, (__bridge CFStringRef)account);
|
||||
OSStatus delStatus = SecItemDelete(delQuery);
|
||||
LogStatusError("TouchID::deleteAllKeyEntriesWithPrefix - Error deleting item", delStatus);
|
||||
CFRelease(delQuery);
|
||||
}
|
||||
}
|
||||
CFRelease(result);
|
||||
CFRelease(query);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,9 +212,6 @@ bool TouchID::setKey(const QUuid& dbUuid, const QByteArray& key, const bool igno
|
||||
return false;
|
||||
}
|
||||
|
||||
// memorize which database the stored key is for
|
||||
// TODO: Do we need to store the db uuid's to do a full reset later?
|
||||
//m_encryptedMasterKeys.insert(dbUuid, encryptedMasterKey);
|
||||
debug("TouchID::setKey - Success!");
|
||||
return true;
|
||||
}
|
||||
@@ -363,11 +388,6 @@ bool TouchID::isAvailable() const
|
||||
return isWatchAvailable() || isTouchIdAvailable() || isPasswordFallbackPossible();
|
||||
}
|
||||
|
||||
bool TouchID::canRemember() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the inner state either for all or for the given database
|
||||
*/
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace
|
||||
{
|
||||
try {
|
||||
auto vault = PasswordVault();
|
||||
vault.Remove({s_winHelloKeyName, winrt::to_hstring(uuid.toString().toStdString()), L"blah"});
|
||||
vault.Remove({s_winHelloKeyName, winrt::to_hstring(uuid.toString().toStdString()), L"nodata"});
|
||||
} catch (winrt::hresult_error const& ex) {
|
||||
}
|
||||
}
|
||||
@@ -234,11 +234,6 @@ bool WindowsHello::hasKey(const QUuid& dbUuid) const
|
||||
return !loadCredential(dbUuid).isEmpty();
|
||||
}
|
||||
|
||||
bool WindowsHello::canRemember() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void WindowsHello::reset()
|
||||
{
|
||||
resetCredentials();
|
||||
|
||||
@@ -32,8 +32,6 @@ public:
|
||||
bool getKey(const QUuid& dbUuid, QByteArray& key) override;
|
||||
bool hasKey(const QUuid& dbUuid) const override;
|
||||
|
||||
bool canRemember() const override;
|
||||
|
||||
void reset(const QUuid& dbUuid) override;
|
||||
void reset() override;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user