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:
Janek Bevendorff 2018-01-10 21:24:53 +01:00 committed by Jonathan White
parent d327c16904
commit b04c8c2b6e
No known key found for this signature in database
GPG key ID: 440FC65F2E0C6E01
9 changed files with 59 additions and 26 deletions

View file

@ -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"

View file

@ -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) {

View file

@ -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;

View file

@ -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);