mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-08-21 04:28:26 -04:00
Catch and handle all errors from libgcrypt.
This commit is contained in:
parent
a7f4e2d0cd
commit
a762cef0a9
29 changed files with 622 additions and 194 deletions
|
@ -154,14 +154,16 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
|
|||
raiseError("Invalid number of transform rounds");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
m_db->setTransformRounds(m_transformRounds);
|
||||
if (!m_db->setTransformRounds(m_transformRounds)) {
|
||||
raiseError(tr("Unable to calculate master key"));
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
qint64 contentPos = m_device->pos();
|
||||
|
||||
QScopedPointer<SymmetricCipherStream> cipherStream(testKeys(password, keyfileData, contentPos));
|
||||
|
||||
if (!cipherStream) {
|
||||
raiseError("Unable to create cipher stream");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
|
@ -234,7 +236,10 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
|
|||
key.addKey(newFileKey);
|
||||
}
|
||||
|
||||
db->setKey(key);
|
||||
if (!db->setKey(key)) {
|
||||
raiseError(tr("Unable to calculate master key"));
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
return db.take();
|
||||
}
|
||||
|
@ -326,16 +331,26 @@ SymmetricCipherStream* KeePass1Reader::testKeys(const QString& password, const Q
|
|||
}
|
||||
|
||||
QByteArray finalKey = key(passwordData, keyfileData);
|
||||
if (finalKey.isEmpty()) {
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
if (m_encryptionFlags & KeePass1::Rijndael) {
|
||||
cipherStream.reset(new SymmetricCipherStream(m_device, SymmetricCipher::Aes256,
|
||||
SymmetricCipher::Cbc, SymmetricCipher::Decrypt, finalKey, m_encryptionIV));
|
||||
SymmetricCipher::Cbc, SymmetricCipher::Decrypt));
|
||||
}
|
||||
else {
|
||||
cipherStream.reset(new SymmetricCipherStream(m_device, SymmetricCipher::Twofish,
|
||||
SymmetricCipher::Cbc, SymmetricCipher::Decrypt, finalKey, m_encryptionIV));
|
||||
SymmetricCipher::Cbc, SymmetricCipher::Decrypt));
|
||||
}
|
||||
|
||||
cipherStream->open(QIODevice::ReadOnly);
|
||||
if (!cipherStream->init(finalKey, m_encryptionIV)) {
|
||||
raiseError(cipherStream->errorString());
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
if (!cipherStream->open(QIODevice::ReadOnly)) {
|
||||
raiseError(cipherStream->errorString());
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
bool success = verifyKey(cipherStream.data());
|
||||
|
||||
|
@ -372,9 +387,18 @@ QByteArray KeePass1Reader::key(const QByteArray& password, const QByteArray& key
|
|||
key.setPassword(password);
|
||||
key.setKeyfileData(keyfileData);
|
||||
|
||||
bool ok;
|
||||
QString errorString;
|
||||
QByteArray transformedKey = key.transform(m_transformSeed, m_transformRounds, &ok, &errorString);
|
||||
|
||||
if (!ok) {
|
||||
raiseError(errorString);
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
CryptoHash hash(CryptoHash::Sha256);
|
||||
hash.addData(m_masterSeed);
|
||||
hash.addData(key.transform(m_transformSeed, m_transformRounds));
|
||||
hash.addData(transformedKey);
|
||||
return hash.result();
|
||||
}
|
||||
|
||||
|
|
|
@ -20,14 +20,19 @@
|
|||
#include "crypto/CryptoHash.h"
|
||||
#include "format/KeePass2.h"
|
||||
|
||||
KeePass2RandomStream::KeePass2RandomStream(const QByteArray& key)
|
||||
: m_cipher(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt,
|
||||
CryptoHash::hash(key, CryptoHash::Sha256), KeePass2::INNER_STREAM_SALSA20_IV)
|
||||
KeePass2RandomStream::KeePass2RandomStream()
|
||||
: m_cipher(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt)
|
||||
, m_offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
QByteArray KeePass2RandomStream::randomBytes(int size)
|
||||
bool KeePass2RandomStream::init(const QByteArray& key)
|
||||
{
|
||||
return m_cipher.init(CryptoHash::hash(key, CryptoHash::Sha256),
|
||||
KeePass2::INNER_STREAM_SALSA20_IV);
|
||||
}
|
||||
|
||||
QByteArray KeePass2RandomStream::randomBytes(int size, bool* ok)
|
||||
{
|
||||
QByteArray result;
|
||||
|
||||
|
@ -35,7 +40,10 @@ QByteArray KeePass2RandomStream::randomBytes(int size)
|
|||
|
||||
while (bytesRemaining > 0) {
|
||||
if (m_buffer.size() == m_offset) {
|
||||
loadBlock();
|
||||
if (!loadBlock()) {
|
||||
*ok = false;
|
||||
return QByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
int bytesToCopy = qMin(bytesRemaining, m_buffer.size() - m_offset);
|
||||
|
@ -44,12 +52,20 @@ QByteArray KeePass2RandomStream::randomBytes(int size)
|
|||
bytesRemaining -= bytesToCopy;
|
||||
}
|
||||
|
||||
*ok = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
QByteArray KeePass2RandomStream::process(const QByteArray& data)
|
||||
QByteArray KeePass2RandomStream::process(const QByteArray& data, bool* ok)
|
||||
{
|
||||
QByteArray randomData = randomBytes(data.size());
|
||||
bool randomBytesOk;
|
||||
|
||||
QByteArray randomData = randomBytes(data.size(), &randomBytesOk);
|
||||
if (!randomBytesOk) {
|
||||
*ok = false;
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray result;
|
||||
result.resize(data.size());
|
||||
|
||||
|
@ -57,23 +73,39 @@ QByteArray KeePass2RandomStream::process(const QByteArray& data)
|
|||
result[i] = data[i] ^ randomData[i];
|
||||
}
|
||||
|
||||
*ok = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
void KeePass2RandomStream::processInPlace(QByteArray& data)
|
||||
bool KeePass2RandomStream::processInPlace(QByteArray& data)
|
||||
{
|
||||
QByteArray randomData = randomBytes(data.size());
|
||||
bool ok;
|
||||
QByteArray randomData = randomBytes(data.size(), &ok);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
data[i] = data[i] ^ randomData[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KeePass2RandomStream::loadBlock()
|
||||
QString KeePass2RandomStream::errorString() const
|
||||
{
|
||||
return m_cipher.errorString();
|
||||
}
|
||||
|
||||
bool KeePass2RandomStream::loadBlock()
|
||||
{
|
||||
Q_ASSERT(m_offset == m_buffer.size());
|
||||
|
||||
m_buffer.fill('\0', m_cipher.blockSize());
|
||||
m_cipher.processInPlace(m_buffer);
|
||||
if (!m_cipher.processInPlace(m_buffer)) {
|
||||
return false;
|
||||
}
|
||||
m_offset = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -25,13 +25,15 @@
|
|||
class KeePass2RandomStream
|
||||
{
|
||||
public:
|
||||
explicit KeePass2RandomStream(const QByteArray& key);
|
||||
QByteArray randomBytes(int size);
|
||||
QByteArray process(const QByteArray& data);
|
||||
void processInPlace(QByteArray& data);
|
||||
KeePass2RandomStream();
|
||||
bool init(const QByteArray& key);
|
||||
QByteArray randomBytes(int size, bool* ok);
|
||||
QByteArray process(const QByteArray& data, bool* ok);
|
||||
bool processInPlace(QByteArray& data);
|
||||
QString errorString() const;
|
||||
|
||||
private:
|
||||
void loadBlock();
|
||||
bool loadBlock();
|
||||
|
||||
SymmetricCipher m_cipher;
|
||||
QByteArray m_buffer;
|
||||
|
|
|
@ -96,16 +96,26 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
|
|||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
m_db->setKey(key, m_transformSeed, false);
|
||||
if (!m_db->setKey(key, m_transformSeed, false)) {
|
||||
raiseError(tr("Unable to calculate master key"));
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
CryptoHash hash(CryptoHash::Sha256);
|
||||
hash.addData(m_masterSeed);
|
||||
hash.addData(m_db->transformedMasterKey());
|
||||
QByteArray finalKey = hash.result();
|
||||
|
||||
SymmetricCipherStream cipherStream(m_device, SymmetricCipher::Aes256, SymmetricCipher::Cbc,
|
||||
SymmetricCipher::Decrypt, finalKey, m_encryptionIV);
|
||||
cipherStream.open(QIODevice::ReadOnly);
|
||||
SymmetricCipherStream cipherStream(m_device, SymmetricCipher::Aes256,
|
||||
SymmetricCipher::Cbc, SymmetricCipher::Decrypt);
|
||||
if (!cipherStream.init(finalKey, m_encryptionIV)) {
|
||||
raiseError(cipherStream.errorString());
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
if (!cipherStream.open(QIODevice::ReadOnly)) {
|
||||
raiseError(cipherStream.errorString());
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
QByteArray realStart = cipherStream.read(32);
|
||||
|
||||
|
@ -115,7 +125,10 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
|
|||
}
|
||||
|
||||
HashedBlockStream hashedStream(&cipherStream);
|
||||
hashedStream.open(QIODevice::ReadOnly);
|
||||
if (!hashedStream.open(QIODevice::ReadOnly)) {
|
||||
raiseError(hashedStream.errorString());
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
QIODevice* xmlDevice;
|
||||
QScopedPointer<QtIOCompressor> ioCompressor;
|
||||
|
@ -126,11 +139,18 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
|
|||
else {
|
||||
ioCompressor.reset(new QtIOCompressor(&hashedStream));
|
||||
ioCompressor->setStreamFormat(QtIOCompressor::GzipFormat);
|
||||
ioCompressor->open(QIODevice::ReadOnly);
|
||||
if (!ioCompressor->open(QIODevice::ReadOnly)) {
|
||||
raiseError(ioCompressor->errorString());
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
xmlDevice = ioCompressor.data();
|
||||
}
|
||||
|
||||
KeePass2RandomStream randomStream(m_protectedStreamKey);
|
||||
KeePass2RandomStream randomStream;
|
||||
if (!randomStream.init(m_protectedStreamKey)) {
|
||||
raiseError(randomStream.errorString());
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
QScopedPointer<QBuffer> buffer;
|
||||
|
||||
|
@ -340,7 +360,9 @@ void KeePass2Reader::setTansformRounds(const QByteArray& data)
|
|||
raiseError("Invalid transform rounds size");
|
||||
}
|
||||
else {
|
||||
m_db->setTransformRounds(Endian::bytesToUInt64(data, KeePass2::BYTEORDER));
|
||||
if (!m_db->setTransformRounds(Endian::bytesToUInt64(data, KeePass2::BYTEORDER))) {
|
||||
raiseError(tr("Unable to calculate master key"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -88,13 +88,20 @@ void KeePass2Writer::writeDatabase(QIODevice* device, Database* db)
|
|||
CHECK_RETURN(writeData(header.data()));
|
||||
|
||||
SymmetricCipherStream cipherStream(device, SymmetricCipher::Aes256, SymmetricCipher::Cbc,
|
||||
SymmetricCipher::Encrypt, finalKey, encryptionIV);
|
||||
cipherStream.open(QIODevice::WriteOnly);
|
||||
SymmetricCipher::Encrypt);
|
||||
cipherStream.init(finalKey, encryptionIV);
|
||||
if (!cipherStream.open(QIODevice::WriteOnly)) {
|
||||
raiseError(cipherStream.errorString());
|
||||
return;
|
||||
}
|
||||
m_device = &cipherStream;
|
||||
CHECK_RETURN(writeData(startBytes));
|
||||
|
||||
HashedBlockStream hashedStream(&cipherStream);
|
||||
hashedStream.open(QIODevice::WriteOnly);
|
||||
if (!hashedStream.open(QIODevice::WriteOnly)) {
|
||||
raiseError(hashedStream.errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
QScopedPointer<QtIOCompressor> ioCompressor;
|
||||
|
||||
|
@ -104,14 +111,25 @@ void KeePass2Writer::writeDatabase(QIODevice* device, Database* db)
|
|||
else {
|
||||
ioCompressor.reset(new QtIOCompressor(&hashedStream));
|
||||
ioCompressor->setStreamFormat(QtIOCompressor::GzipFormat);
|
||||
ioCompressor->open(QIODevice::WriteOnly);
|
||||
if (!ioCompressor->open(QIODevice::WriteOnly)) {
|
||||
raiseError(ioCompressor->errorString());
|
||||
return;
|
||||
}
|
||||
m_device = ioCompressor.data();
|
||||
}
|
||||
|
||||
KeePass2RandomStream randomStream(protectedStreamKey);
|
||||
KeePass2RandomStream randomStream;
|
||||
if (!randomStream.init(protectedStreamKey)) {
|
||||
raiseError(randomStream.errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
KeePass2XmlWriter xmlWriter;
|
||||
xmlWriter.writeDatabase(m_device, db, &randomStream, headerHash);
|
||||
|
||||
if (xmlWriter.hasError()) {
|
||||
raiseError(xmlWriter.errorString());
|
||||
}
|
||||
}
|
||||
|
||||
bool KeePass2Writer::writeData(const QByteArray& data)
|
||||
|
|
|
@ -809,7 +809,16 @@ void KeePass2XmlReader::parseEntryString(Entry* entry)
|
|||
|
||||
if (isProtected && !value.isEmpty()) {
|
||||
if (m_randomStream) {
|
||||
value = QString::fromUtf8(m_randomStream->process(QByteArray::fromBase64(value.toLatin1())));
|
||||
QByteArray ciphertext = QByteArray::fromBase64(value.toLatin1());
|
||||
bool ok;
|
||||
QByteArray plaintext = m_randomStream->process(ciphertext, &ok);
|
||||
if (!ok) {
|
||||
value.clear();
|
||||
raiseError(m_randomStream->errorString());
|
||||
}
|
||||
else {
|
||||
value = QString::fromUtf8(plaintext);
|
||||
}
|
||||
}
|
||||
else {
|
||||
raiseError("Unable to decrypt entry string");
|
||||
|
|
|
@ -28,6 +28,7 @@ KeePass2XmlWriter::KeePass2XmlWriter()
|
|||
: m_db(Q_NULLPTR)
|
||||
, m_meta(Q_NULLPTR)
|
||||
, m_randomStream(Q_NULLPTR)
|
||||
, m_error(false)
|
||||
{
|
||||
m_xml.setAutoFormatting(true);
|
||||
m_xml.setAutoFormattingIndent(-1); // 1 tab
|
||||
|
@ -65,6 +66,16 @@ void KeePass2XmlWriter::writeDatabase(const QString& filename, Database* db)
|
|||
writeDatabase(&file, db);
|
||||
}
|
||||
|
||||
bool KeePass2XmlWriter::hasError()
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
QString KeePass2XmlWriter::errorString()
|
||||
{
|
||||
return m_errorStr;
|
||||
}
|
||||
|
||||
void KeePass2XmlWriter::generateIdMap()
|
||||
{
|
||||
QList<Entry*> allEntries = m_db->rootGroup()->entriesRecursive(true);
|
||||
|
@ -340,7 +351,11 @@ void KeePass2XmlWriter::writeEntry(const Entry* entry)
|
|||
if (protect) {
|
||||
if (m_randomStream) {
|
||||
m_xml.writeAttribute("Protected", "True");
|
||||
QByteArray rawData = m_randomStream->process(entry->attributes()->value(key).toUtf8());
|
||||
bool ok;
|
||||
QByteArray rawData = m_randomStream->process(entry->attributes()->value(key).toUtf8(), &ok);
|
||||
if (!ok) {
|
||||
raiseError(m_randomStream->errorString());
|
||||
}
|
||||
value = QString::fromLatin1(rawData.toBase64());
|
||||
}
|
||||
else {
|
||||
|
@ -527,3 +542,9 @@ QString KeePass2XmlWriter::colorPartToString(int value)
|
|||
|
||||
return str;
|
||||
}
|
||||
|
||||
void KeePass2XmlWriter::raiseError(const QString& errorMessage)
|
||||
{
|
||||
m_error = true;
|
||||
m_errorStr = errorMessage;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ public:
|
|||
void writeDatabase(QIODevice* device, Database* db, KeePass2RandomStream* randomStream = Q_NULLPTR,
|
||||
const QByteArray& headerHash = QByteArray());
|
||||
void writeDatabase(const QString& filename, Database* db);
|
||||
bool error();
|
||||
bool hasError();
|
||||
QString errorString();
|
||||
|
||||
private:
|
||||
|
@ -74,12 +74,16 @@ private:
|
|||
void writeTriState(const QString& qualifiedName, Group::TriState triState);
|
||||
QString colorPartToString(int value);
|
||||
|
||||
void raiseError(const QString& errorMessage);
|
||||
|
||||
QXmlStreamWriter m_xml;
|
||||
Database* m_db;
|
||||
Metadata* m_meta;
|
||||
KeePass2RandomStream* m_randomStream;
|
||||
QByteArray m_headerHash;
|
||||
QHash<QByteArray, int> m_idMap;
|
||||
bool m_error;
|
||||
QString m_errorStr;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_KEEPASS2XMLWRITER_H
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue