mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-07-24 23:35:37 -04:00
Refactor Database and Database widgets (#2491)
The Database, DatabaseWidget, and DatabaseTabWidget classes share many responsibilities in inconsistent ways resulting in impenetrable and unmaintainable code and a diverse set of bugs and architecture restrictions. This patch reworks the architecture, responsibilities of, and dependencies between these classes. The core changes are: * Move loading and saving logic from widgets into the Database class * Get rid of the DatabaseManagerStruct and move all the information contained in it into the Database * Let database objects keep track of modifications and dirty/clean state instead of handing this to external widgets * Move GUI interactions for loading and saving from the DatabaseTabWidget into the DatabaseWidget (resolves #2494 as a side-effect) * Heavily clean up DatabaseTabWidget and degrade it to a slightly glorified QTabWidget * Use QSharedPointers for all Database objects * Remove the modifiedImmediate signal and replace it with a markAsModified() method * Implement proper tabName() method instead of reading back titles from GUI widgets (resolves #1389 and its duplicates #2146 #855) * Fix unwanted AES-KDF downgrade if database uses Argon2 and has CustomData * Improve code This patch is also the first major step towards solving issues #476 and #2322.
This commit is contained in:
parent
917c4cc18b
commit
d612cad09a
115 changed files with 2116 additions and 2165 deletions
|
@ -28,85 +28,85 @@
|
|||
#include "streams/QtIOCompressor"
|
||||
#include "streams/SymmetricCipherStream.h"
|
||||
|
||||
Database* Kdbx4Reader::readDatabaseImpl(QIODevice* device,
|
||||
const QByteArray& headerData,
|
||||
QSharedPointer<const CompositeKey> key,
|
||||
bool keepDatabase)
|
||||
bool Kdbx4Reader::readDatabaseImpl(QIODevice* device,
|
||||
const QByteArray& headerData,
|
||||
QSharedPointer<const CompositeKey> key,
|
||||
Database* db)
|
||||
{
|
||||
Q_ASSERT(m_kdbxVersion == KeePass2::FILE_VERSION_4);
|
||||
|
||||
m_binaryPoolInverse.clear();
|
||||
|
||||
if (hasError()) {
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if all required headers were present
|
||||
if (m_masterSeed.isEmpty() || m_encryptionIV.isEmpty() || m_db->cipher().isNull()) {
|
||||
if (m_masterSeed.isEmpty() || m_encryptionIV.isEmpty() || db->cipher().isNull()) {
|
||||
raiseError(tr("missing database headers"));
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_db->setKey(key, false, false)) {
|
||||
if (!db->setKey(key, false, false)) {
|
||||
raiseError(tr("Unable to calculate master key"));
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
CryptoHash hash(CryptoHash::Sha256);
|
||||
hash.addData(m_masterSeed);
|
||||
hash.addData(m_db->transformedMasterKey());
|
||||
hash.addData(db->transformedMasterKey());
|
||||
QByteArray finalKey = hash.result();
|
||||
|
||||
QByteArray headerSha256 = device->read(32);
|
||||
QByteArray headerHmac = device->read(32);
|
||||
if (headerSha256.size() != 32 || headerHmac.size() != 32) {
|
||||
raiseError(tr("Invalid header checksum size"));
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
if (headerSha256 != CryptoHash::hash(headerData, CryptoHash::Sha256)) {
|
||||
raiseError(tr("Header SHA256 mismatch"));
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray hmacKey = KeePass2::hmacKey(m_masterSeed, m_db->transformedMasterKey());
|
||||
QByteArray hmacKey = KeePass2::hmacKey(m_masterSeed, db->transformedMasterKey());
|
||||
if (headerHmac
|
||||
!= CryptoHash::hmac(headerData, HmacBlockStream::getHmacKey(UINT64_MAX, hmacKey), CryptoHash::Sha256)) {
|
||||
raiseError(tr("Wrong key or database file is corrupt. (HMAC mismatch)"));
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
HmacBlockStream hmacStream(device, hmacKey);
|
||||
if (!hmacStream.open(QIODevice::ReadOnly)) {
|
||||
raiseError(hmacStream.errorString());
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
SymmetricCipher::Algorithm cipher = SymmetricCipher::cipherToAlgorithm(m_db->cipher());
|
||||
SymmetricCipher::Algorithm cipher = SymmetricCipher::cipherToAlgorithm(db->cipher());
|
||||
if (cipher == SymmetricCipher::InvalidAlgorithm) {
|
||||
raiseError(tr("Unknown cipher"));
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
SymmetricCipherStream cipherStream(
|
||||
&hmacStream, cipher, SymmetricCipher::algorithmMode(cipher), SymmetricCipher::Decrypt);
|
||||
if (!cipherStream.init(finalKey, m_encryptionIV)) {
|
||||
raiseError(cipherStream.errorString());
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
if (!cipherStream.open(QIODevice::ReadOnly)) {
|
||||
raiseError(cipherStream.errorString());
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
QIODevice* xmlDevice = nullptr;
|
||||
QScopedPointer<QtIOCompressor> ioCompressor;
|
||||
|
||||
if (m_db->compressionAlgo() == Database::CompressionNone) {
|
||||
if (db->compressionAlgorithm() == Database::CompressionNone) {
|
||||
xmlDevice = &cipherStream;
|
||||
} else {
|
||||
ioCompressor.reset(new QtIOCompressor(&cipherStream));
|
||||
ioCompressor->setStreamFormat(QtIOCompressor::GzipFormat);
|
||||
if (!ioCompressor->open(QIODevice::ReadOnly)) {
|
||||
raiseError(ioCompressor->errorString());
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
xmlDevice = ioCompressor.data();
|
||||
}
|
||||
|
@ -115,32 +115,29 @@ Database* Kdbx4Reader::readDatabaseImpl(QIODevice* device,
|
|||
}
|
||||
|
||||
if (hasError()) {
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
KeePass2RandomStream randomStream(m_irsAlgo);
|
||||
if (!randomStream.init(m_protectedStreamKey)) {
|
||||
raiseError(randomStream.errorString());
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_ASSERT(xmlDevice);
|
||||
|
||||
KdbxXmlReader xmlReader(KeePass2::FILE_VERSION_4, binaryPool());
|
||||
xmlReader.readDatabase(xmlDevice, m_db.data(), &randomStream);
|
||||
xmlReader.readDatabase(xmlDevice, db, &randomStream);
|
||||
|
||||
if (xmlReader.hasError()) {
|
||||
raiseError(xmlReader.errorString());
|
||||
if (keepDatabase) {
|
||||
return m_db.take();
|
||||
}
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_db.take();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Kdbx4Reader::readHeaderField(StoreDataStream& device)
|
||||
bool Kdbx4Reader::readHeaderField(StoreDataStream& device, Database* db)
|
||||
{
|
||||
QByteArray fieldIDArray = device.read(1);
|
||||
if (fieldIDArray.size() != 1) {
|
||||
|
@ -197,7 +194,7 @@ bool Kdbx4Reader::readHeaderField(StoreDataStream& device)
|
|||
raiseError(tr("Unsupported key derivation function (KDF) or invalid parameters"));
|
||||
return false;
|
||||
}
|
||||
m_db->setKdf(kdf);
|
||||
db->setKdf(kdf);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -206,7 +203,7 @@ bool Kdbx4Reader::readHeaderField(StoreDataStream& device)
|
|||
variantBuffer.setBuffer(&fieldData);
|
||||
variantBuffer.open(QBuffer::ReadOnly);
|
||||
QVariantMap data = readVariantMap(&variantBuffer);
|
||||
m_db->setPublicCustomData(data);
|
||||
db->setPublicCustomData(data);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue