mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Explicitly support AES-KDF in KDBX4 and don't convert KDBX4 files with AES-KDF back to KDBX3 when saving
This commit is contained in:
parent
d327c16904
commit
b04c8c2b6e
@ -44,7 +44,10 @@ Database::Database()
|
||||
{
|
||||
m_data.cipher = KeePass2::CIPHER_AES;
|
||||
m_data.compressionAlgo = CompressionGZip;
|
||||
m_data.kdf = QSharedPointer<AesKdf>::create();
|
||||
|
||||
// instantiate default AES-KDF with legacy KDBX3 flag set
|
||||
// KDBX4+ will re-initialize the KDF using parameters read from the KDBX file
|
||||
m_data.kdf = QSharedPointer<AesKdf>::create(true);
|
||||
m_data.kdf->randomizeSeed();
|
||||
m_data.hasKey = false;
|
||||
|
||||
|
@ -23,7 +23,15 @@
|
||||
#include "crypto/CryptoHash.h"
|
||||
|
||||
AesKdf::AesKdf()
|
||||
: Kdf::Kdf(KeePass2::KDF_AES)
|
||||
: Kdf::Kdf(KeePass2::KDF_AES_KDBX4)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param legacyKdbx3 initialize as legacy KDBX3 KDF
|
||||
*/
|
||||
AesKdf::AesKdf(bool legacyKdbx3)
|
||||
: Kdf::Kdf(legacyKdbx3 ? KeePass2::KDF_AES_KDBX3 : KeePass2::KDF_AES_KDBX4)
|
||||
{
|
||||
}
|
||||
|
||||
@ -36,17 +44,16 @@ bool AesKdf::processParameters(const QVariantMap &p)
|
||||
}
|
||||
|
||||
QByteArray seed = p.value(KeePass2::KDFPARAM_AES_SEED).toByteArray();
|
||||
if (!setSeed(seed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return setSeed(seed);
|
||||
}
|
||||
|
||||
QVariantMap AesKdf::writeParameters()
|
||||
{
|
||||
QVariantMap p;
|
||||
p.insert(KeePass2::KDFPARAM_UUID, KeePass2::KDF_AES.toByteArray());
|
||||
|
||||
// always write old KDBX3 AES-KDF UUID for compatibility with other applications
|
||||
p.insert(KeePass2::KDFPARAM_UUID, KeePass2::KDF_AES_KDBX3.toByteArray());
|
||||
|
||||
p.insert(KeePass2::KDFPARAM_AES_ROUNDS, rounds());
|
||||
p.insert(KeePass2::KDFPARAM_AES_SEED, seed());
|
||||
return p;
|
||||
|
@ -24,6 +24,7 @@ class AesKdf: public Kdf
|
||||
{
|
||||
public:
|
||||
AesKdf();
|
||||
explicit AesKdf(bool legacyKdbx3);
|
||||
|
||||
bool processParameters(const QVariantMap& p) override;
|
||||
QVariantMap writeParameters() override;
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "core/Group.h"
|
||||
#include "core/Endian.h"
|
||||
#include "crypto/CryptoHash.h"
|
||||
#include "crypto/kdf/AesKdf.h"
|
||||
#include "format/KeePass2RandomStream.h"
|
||||
#include "format/KdbxXmlReader.h"
|
||||
#include "streams/HmacBlockStream.h"
|
||||
|
@ -25,7 +25,8 @@ const Uuid KeePass2::CIPHER_AES = Uuid(QByteArray::fromHex("31c1f2e6bf714350be58
|
||||
const Uuid KeePass2::CIPHER_TWOFISH = Uuid(QByteArray::fromHex("ad68f29f576f4bb9a36ad47af965346c"));
|
||||
const Uuid KeePass2::CIPHER_CHACHA20 = Uuid(QByteArray::fromHex("D6038A2B8B6F4CB5A524339A31DBB59A"));
|
||||
|
||||
const Uuid KeePass2::KDF_AES = Uuid(QByteArray::fromHex("C9D9F39A628A4460BF740D08C18A4FEA"));
|
||||
const Uuid KeePass2::KDF_AES_KDBX3 = Uuid(QByteArray::fromHex("C9D9F39A628A4460BF740D08C18A4FEA"));
|
||||
const Uuid KeePass2::KDF_AES_KDBX4 = Uuid(QByteArray::fromHex("7C02BB8279A74AC0927D114A00648238"));
|
||||
const Uuid KeePass2::KDF_ARGON2 = Uuid(QByteArray::fromHex("EF636DDF8C29444B91F7A9A403E30A0C"));
|
||||
|
||||
const QByteArray KeePass2::INNER_STREAM_SALSA20_IV("\xE8\x30\x09\x4B\x97\x20\x5D\x2A");
|
||||
@ -43,14 +44,16 @@ const QString KeePass2::KDFPARAM_ARGON2_VERSION("V");
|
||||
const QString KeePass2::KDFPARAM_ARGON2_SECRET("K");
|
||||
const QString KeePass2::KDFPARAM_ARGON2_ASSOCDATA("A");
|
||||
|
||||
const QList<QPair<Uuid, QString>> KeePass2::CIPHERS {
|
||||
qMakePair(KeePass2::CIPHER_AES, QObject::tr("AES: 256-bit")),
|
||||
qMakePair(KeePass2::CIPHER_TWOFISH, QObject::tr("Twofish: 256-bit")),
|
||||
qMakePair(KeePass2::CIPHER_CHACHA20, QObject::tr("ChaCha20: 256-bit"))
|
||||
const QList<QPair<Uuid, QString>> KeePass2::CIPHERS{
|
||||
qMakePair(KeePass2::CIPHER_AES, QObject::tr("AES: 256-bit")),
|
||||
qMakePair(KeePass2::CIPHER_TWOFISH, QObject::tr("Twofish: 256-bit")),
|
||||
qMakePair(KeePass2::CIPHER_CHACHA20, QObject::tr("ChaCha20: 256-bit"))
|
||||
};
|
||||
const QList<QPair<Uuid, QString>> KeePass2::KDFS {
|
||||
qMakePair(KeePass2::KDF_AES, QObject::tr("AES-KDF")),
|
||||
qMakePair(KeePass2::KDF_ARGON2, QObject::tr("Argon2")),
|
||||
|
||||
const QList<QPair<Uuid, QString>> KeePass2::KDFS{
|
||||
qMakePair(KeePass2::KDF_ARGON2, QObject::tr("Argon2 (recommended)")),
|
||||
qMakePair(KeePass2::KDF_AES_KDBX4, QObject::tr("AES-KDF (KDBX 4)")),
|
||||
qMakePair(KeePass2::KDF_AES_KDBX3, QObject::tr("AES-KDF (KDBX 3.1)"))
|
||||
};
|
||||
|
||||
QByteArray KeePass2::hmacKey(QByteArray masterSeed, QByteArray transformedMasterKey) {
|
||||
@ -61,6 +64,12 @@ QByteArray KeePass2::hmacKey(QByteArray masterSeed, QByteArray transformedMaster
|
||||
return hmacKeyHash.result();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create KDF object from KDBX4+ KDF parameters.
|
||||
*
|
||||
* @param p variant map containing parameters
|
||||
* @return initialized KDF
|
||||
*/
|
||||
QSharedPointer<Kdf> KeePass2::kdfFromParameters(const QVariantMap &p)
|
||||
{
|
||||
QByteArray uuidBytes = p.value(KDFPARAM_UUID).toByteArray();
|
||||
@ -68,7 +77,12 @@ QSharedPointer<Kdf> KeePass2::kdfFromParameters(const QVariantMap &p)
|
||||
return {};
|
||||
}
|
||||
|
||||
QSharedPointer<Kdf> kdf(uuidToKdf(Uuid(uuidBytes)));
|
||||
Uuid kdfUuid(uuidBytes);
|
||||
if (kdfUuid == KDF_AES_KDBX3) {
|
||||
// upgrade to non-legacy AES-KDF, since KDBX3 doesn't have any KDF parameters
|
||||
kdfUuid = KDF_AES_KDBX4;
|
||||
}
|
||||
QSharedPointer<Kdf> kdf = uuidToKdf(kdfUuid);
|
||||
if (kdf.isNull()) {
|
||||
return {};
|
||||
}
|
||||
@ -87,7 +101,10 @@ QVariantMap KeePass2::kdfToParameters(QSharedPointer<Kdf> kdf)
|
||||
|
||||
QSharedPointer<Kdf> KeePass2::uuidToKdf(const Uuid& uuid)
|
||||
{
|
||||
if (uuid == KDF_AES) {
|
||||
if (uuid == KDF_AES_KDBX3) {
|
||||
return QSharedPointer<AesKdf>::create(true);
|
||||
}
|
||||
if (uuid == KDF_AES_KDBX4) {
|
||||
return QSharedPointer<AesKdf>::create();
|
||||
}
|
||||
if (uuid == KDF_ARGON2) {
|
||||
|
@ -46,7 +46,8 @@ namespace KeePass2
|
||||
extern const Uuid CIPHER_TWOFISH;
|
||||
extern const Uuid CIPHER_CHACHA20;
|
||||
|
||||
extern const Uuid KDF_AES;
|
||||
extern const Uuid KDF_AES_KDBX3;
|
||||
extern const Uuid KDF_AES_KDBX4;
|
||||
extern const Uuid KDF_ARGON2;
|
||||
|
||||
extern const QByteArray INNER_STREAM_SALSA20_IV;
|
||||
|
@ -18,8 +18,9 @@
|
||||
#include <QIODevice>
|
||||
#include <QFile>
|
||||
|
||||
#include "format/KeePass2Writer.h"
|
||||
#include "core/Database.h"
|
||||
#include "crypto/kdf/AesKdf.h"
|
||||
#include "format/KeePass2Writer.h"
|
||||
#include "format/Kdbx3Writer.h"
|
||||
#include "format/Kdbx4Writer.h"
|
||||
|
||||
@ -52,12 +53,12 @@ bool KeePass2Writer::writeDatabase(QIODevice* device, Database* db) {
|
||||
m_errorStr.clear();
|
||||
|
||||
// determine KDBX3 vs KDBX4
|
||||
if (db->kdf()->uuid() != KeePass2::KDF_AES || db->publicCustomData().size() > 0) {
|
||||
m_version = KeePass2::FILE_VERSION_4;
|
||||
m_writer.reset(new Kdbx4Writer());
|
||||
} else {
|
||||
if (db->kdf()->uuid() == KeePass2::KDF_AES_KDBX3 && db->publicCustomData().isEmpty()) {
|
||||
m_version = KeePass2::FILE_VERSION_3;
|
||||
m_writer.reset(new Kdbx3Writer());
|
||||
} else {
|
||||
m_version = KeePass2::FILE_VERSION_4;
|
||||
m_writer.reset(new Kdbx4Writer());
|
||||
}
|
||||
|
||||
return m_writer->writeDatabase(device, db);
|
||||
|
@ -151,7 +151,8 @@ void DatabaseSettingsWidget::save()
|
||||
if (warning.clickedButton() != ok) {
|
||||
return;
|
||||
}
|
||||
} else if (kdf->uuid() == KeePass2::KDF_AES && m_uiEncryption->transformRoundsSpinBox->value() < 100000) {
|
||||
} else if ((kdf->uuid() == KeePass2::KDF_AES_KDBX3 || kdf->uuid() == KeePass2::KDF_AES_KDBX4)
|
||||
&& m_uiEncryption->transformRoundsSpinBox->value() < 100000) {
|
||||
QMessageBox warning;
|
||||
warning.setIcon(QMessageBox::Warning);
|
||||
warning.setWindowTitle(tr("Number of rounds too low"));
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <format/KeePass2.h>
|
||||
|
||||
#include "core/Global.h"
|
||||
#include "crypto/kdf/AesKdf.h"
|
||||
#include "crypto/CryptoHash.h"
|
||||
|
||||
CompositeKey::CompositeKey()
|
||||
@ -130,7 +131,7 @@ QByteArray CompositeKey::rawKey(const QByteArray* transformSeed) const
|
||||
*/
|
||||
bool CompositeKey::transform(const Kdf& kdf, QByteArray& result) const
|
||||
{
|
||||
if (kdf.uuid() == KeePass2::KDF_AES) {
|
||||
if (kdf.uuid() == KeePass2::KDF_AES_KDBX3) {
|
||||
// legacy KDBX3 AES-KDF, challenge response is added later to the hash
|
||||
return kdf.transform(rawKey(), result);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user