mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-01-27 23:07:11 -05:00
Catch and handle all errors from libgcrypt.
This commit is contained in:
parent
a7f4e2d0cd
commit
a762cef0a9
@ -188,32 +188,51 @@ void Database::setCompressionAlgo(Database::CompressionAlgorithm algo)
|
|||||||
m_data.compressionAlgo = algo;
|
m_data.compressionAlgo = algo;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::setTransformRounds(quint64 rounds)
|
bool Database::setTransformRounds(quint64 rounds)
|
||||||
{
|
{
|
||||||
if (m_data.transformRounds != rounds) {
|
if (m_data.transformRounds != rounds) {
|
||||||
|
quint64 oldRounds = m_data.transformRounds;
|
||||||
|
|
||||||
m_data.transformRounds = rounds;
|
m_data.transformRounds = rounds;
|
||||||
|
|
||||||
if (m_data.hasKey) {
|
if (m_data.hasKey) {
|
||||||
setKey(m_data.key);
|
if (!setKey(m_data.key)) {
|
||||||
|
m_data.transformRounds = oldRounds;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::setKey(const CompositeKey& key, const QByteArray& transformSeed, bool updateChangedTime)
|
bool Database::setKey(const CompositeKey& key, const QByteArray& transformSeed,
|
||||||
|
bool updateChangedTime)
|
||||||
{
|
{
|
||||||
|
bool ok;
|
||||||
|
QString errorString;
|
||||||
|
|
||||||
|
QByteArray transformedMasterKey =
|
||||||
|
key.transform(transformSeed, transformRounds(), &ok, &errorString);
|
||||||
|
if (!ok) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
m_data.key = key;
|
m_data.key = key;
|
||||||
m_data.transformSeed = transformSeed;
|
m_data.transformSeed = transformSeed;
|
||||||
m_data.transformedMasterKey = key.transform(transformSeed, transformRounds());
|
m_data.transformedMasterKey = transformedMasterKey;
|
||||||
m_data.hasKey = true;
|
m_data.hasKey = true;
|
||||||
if (updateChangedTime) {
|
if (updateChangedTime) {
|
||||||
m_metadata->setMasterKeyChanged(Tools::currentDateTimeUtc());
|
m_metadata->setMasterKeyChanged(Tools::currentDateTimeUtc());
|
||||||
}
|
}
|
||||||
Q_EMIT modifiedImmediate();
|
Q_EMIT modifiedImmediate();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::setKey(const CompositeKey& key)
|
bool Database::setKey(const CompositeKey& key)
|
||||||
{
|
{
|
||||||
setKey(key, randomGen()->randomArray(32));
|
return setKey(key, randomGen()->randomArray(32));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Database::hasKey() const
|
bool Database::hasKey() const
|
||||||
|
@ -90,13 +90,14 @@ public:
|
|||||||
|
|
||||||
void setCipher(const Uuid& cipher);
|
void setCipher(const Uuid& cipher);
|
||||||
void setCompressionAlgo(Database::CompressionAlgorithm algo);
|
void setCompressionAlgo(Database::CompressionAlgorithm algo);
|
||||||
void setTransformRounds(quint64 rounds);
|
bool setTransformRounds(quint64 rounds);
|
||||||
void setKey(const CompositeKey& key, const QByteArray& transformSeed, bool updateChangedTime = true);
|
bool setKey(const CompositeKey& key, const QByteArray& transformSeed,
|
||||||
|
bool updateChangedTime = true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the database key and generates a random transform seed.
|
* Sets the database key and generates a random transform seed.
|
||||||
*/
|
*/
|
||||||
void setKey(const CompositeKey& key);
|
bool setKey(const CompositeKey& key);
|
||||||
bool hasKey() const;
|
bool hasKey() const;
|
||||||
bool verifyKey(const CompositeKey& key) const;
|
bool verifyKey(const CompositeKey& key) const;
|
||||||
void recycleEntry(Entry* entry);
|
void recycleEntry(Entry* entry);
|
||||||
|
@ -142,66 +142,138 @@ bool Crypto::checkAlgorithms()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Crypto::selfTest()
|
bool Crypto::selfTest()
|
||||||
|
{
|
||||||
|
return testSha256() && testAes256() && testTwofish() && testSalsa20();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Crypto::raiseError(const QString& str)
|
||||||
|
{
|
||||||
|
m_errorStr = str;
|
||||||
|
qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Crypto::testSha256()
|
||||||
{
|
{
|
||||||
QByteArray sha256Test = CryptoHash::hash("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
|
QByteArray sha256Test = CryptoHash::hash("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
|
||||||
CryptoHash::Sha256);
|
CryptoHash::Sha256);
|
||||||
|
|
||||||
if (sha256Test != QByteArray::fromHex("248D6A61D20638B8E5C026930C3E6039A33CE45964FF2167F6ECEDD419DB06C1")) {
|
if (sha256Test != QByteArray::fromHex("248D6A61D20638B8E5C026930C3E6039A33CE45964FF2167F6ECEDD419DB06C1")) {
|
||||||
m_errorStr = "SHA-256 mismatch.";
|
raiseError("SHA-256 mismatch.");
|
||||||
qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Crypto::testAes256()
|
||||||
|
{
|
||||||
QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
|
QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
|
||||||
QByteArray iv = QByteArray::fromHex("000102030405060708090a0b0c0d0e0f");
|
QByteArray iv = QByteArray::fromHex("000102030405060708090a0b0c0d0e0f");
|
||||||
QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a");
|
QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a");
|
||||||
plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51"));
|
plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51"));
|
||||||
QByteArray cipherText = QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd6");
|
QByteArray cipherText = QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd6");
|
||||||
cipherText.append(QByteArray::fromHex("9cfc4e967edb808d679f777bc6702c7d"));
|
cipherText.append(QByteArray::fromHex("9cfc4e967edb808d679f777bc6702c7d"));
|
||||||
|
bool ok;
|
||||||
|
|
||||||
SymmetricCipher aes256Encrypt(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt, key, iv);
|
SymmetricCipher aes256Encrypt(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt);
|
||||||
if (aes256Encrypt.process(plainText) != cipherText) {
|
if (!aes256Encrypt.init(key, iv)) {
|
||||||
m_errorStr = "AES-256 encryption mismatch.";
|
raiseError(aes256Encrypt.errorString());
|
||||||
qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr));
|
return false;
|
||||||
|
}
|
||||||
|
QByteArray encryptedText = aes256Encrypt.process(plainText, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
raiseError(aes256Encrypt.errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (encryptedText != cipherText) {
|
||||||
|
raiseError("AES-256 encryption mismatch.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SymmetricCipher aes256Descrypt(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt, key, iv);
|
SymmetricCipher aes256Descrypt(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt);
|
||||||
if (aes256Descrypt.process(cipherText) != plainText) {
|
if (!aes256Descrypt.init(key, iv)) {
|
||||||
m_errorStr = "AES-256 decryption mismatch.";
|
raiseError(aes256Descrypt.errorString());
|
||||||
qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
QByteArray decryptedText = aes256Descrypt.process(cipherText, &ok);
|
||||||
// Twofish
|
if (!ok) {
|
||||||
cipherText = QByteArray::fromHex("e0227c3cc80f3cb1b2ed847cc6f57d3c");
|
raiseError(aes256Descrypt.errorString());
|
||||||
cipherText.append(QByteArray::fromHex("657b1e7960b30fb7c8d62e72ae37c3a0"));
|
|
||||||
|
|
||||||
SymmetricCipher twofishEncrypt(SymmetricCipher::Twofish, SymmetricCipher::Cbc, SymmetricCipher::Encrypt, key, iv);
|
|
||||||
if (twofishEncrypt.process(plainText) != cipherText) {
|
|
||||||
m_errorStr = "Twofish encryption mismatch.";
|
|
||||||
qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (decryptedText != plainText) {
|
||||||
SymmetricCipher twofishDecrypt(SymmetricCipher::Twofish, SymmetricCipher::Cbc, SymmetricCipher::Decrypt, key, iv);
|
raiseError("AES-256 decryption mismatch.");
|
||||||
if (twofishDecrypt.process(cipherText) != plainText) {
|
return false;
|
||||||
m_errorStr = "Twofish decryption mismatch.";
|
}
|
||||||
qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr));
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray salsa20Key = QByteArray::fromHex("F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112");
|
bool Crypto::testTwofish()
|
||||||
QByteArray salsa20iv = QByteArray::fromHex("0000000000000000");
|
{
|
||||||
QByteArray salsa20Plain = QByteArray::fromHex("00000000000000000000000000000000");
|
QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
|
||||||
QByteArray salsa20Cipher = QByteArray::fromHex("B4C0AFA503BE7FC29A62058166D56F8F");
|
QByteArray iv = QByteArray::fromHex("000102030405060708090a0b0c0d0e0f");
|
||||||
|
QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a");
|
||||||
SymmetricCipher salsa20Stream(SymmetricCipher::Salsa20, SymmetricCipher::Stream,
|
plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51"));
|
||||||
SymmetricCipher::Encrypt, salsa20Key, salsa20iv);
|
QByteArray cipherText = QByteArray::fromHex("e0227c3cc80f3cb1b2ed847cc6f57d3c");
|
||||||
|
cipherText.append(QByteArray::fromHex("657b1e7960b30fb7c8d62e72ae37c3a0"));
|
||||||
if (salsa20Stream.process(salsa20Plain) != salsa20Cipher) {
|
bool ok;
|
||||||
m_errorStr = "Salsa20 stream cipher mismatch.";
|
|
||||||
qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr));
|
SymmetricCipher twofishEncrypt(SymmetricCipher::Twofish, SymmetricCipher::Cbc, SymmetricCipher::Encrypt);
|
||||||
|
if (!twofishEncrypt.init(key, iv)) {
|
||||||
|
raiseError(twofishEncrypt.errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QByteArray encryptedText = twofishEncrypt.process(plainText, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
raiseError(twofishEncrypt.errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (encryptedText != cipherText) {
|
||||||
|
raiseError("Twofish encryption mismatch.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SymmetricCipher twofishDecrypt(SymmetricCipher::Twofish, SymmetricCipher::Cbc, SymmetricCipher::Decrypt);
|
||||||
|
if (!twofishDecrypt.init(key, iv)) {
|
||||||
|
raiseError(twofishEncrypt.errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QByteArray decryptedText = twofishDecrypt.process(cipherText, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
raiseError(twofishDecrypt.errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (decryptedText != plainText) {
|
||||||
|
raiseError("Twofish encryption mismatch.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Crypto::testSalsa20()
|
||||||
|
{
|
||||||
|
QByteArray salsa20Key = QByteArray::fromHex("F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112");
|
||||||
|
QByteArray salsa20iv = QByteArray::fromHex("0000000000000000");
|
||||||
|
QByteArray salsa20Plain = QByteArray::fromHex("00000000000000000000000000000000");
|
||||||
|
QByteArray salsa20Cipher = QByteArray::fromHex("B4C0AFA503BE7FC29A62058166D56F8F");
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
SymmetricCipher salsa20Stream(SymmetricCipher::Salsa20, SymmetricCipher::Stream,
|
||||||
|
SymmetricCipher::Encrypt);
|
||||||
|
if (!salsa20Stream.init(salsa20Key, salsa20iv)) {
|
||||||
|
raiseError(salsa20Stream.errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray salsaProcessed = salsa20Stream.process(salsa20Plain, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
raiseError(salsa20Stream.errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (salsaProcessed != salsa20Cipher) {
|
||||||
|
raiseError("Salsa20 stream cipher mismatch.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,11 @@ private:
|
|||||||
Crypto();
|
Crypto();
|
||||||
static bool checkAlgorithms();
|
static bool checkAlgorithms();
|
||||||
static bool selfTest();
|
static bool selfTest();
|
||||||
|
static void raiseError(const QString& str);
|
||||||
|
static bool testSha256();
|
||||||
|
static bool testAes256();
|
||||||
|
static bool testTwofish();
|
||||||
|
static bool testSalsa20();
|
||||||
|
|
||||||
static bool m_initalized;
|
static bool m_initalized;
|
||||||
static QString m_errorStr;
|
static QString m_errorStr;
|
||||||
|
@ -22,17 +22,39 @@
|
|||||||
#include "crypto/SymmetricCipherSalsa20.h"
|
#include "crypto/SymmetricCipherSalsa20.h"
|
||||||
|
|
||||||
SymmetricCipher::SymmetricCipher(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
SymmetricCipher::SymmetricCipher(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
||||||
SymmetricCipher::Direction direction, const QByteArray& key, const QByteArray& iv)
|
SymmetricCipher::Direction direction)
|
||||||
: m_backend(createBackend(algo, mode, direction))
|
: m_backend(createBackend(algo, mode, direction))
|
||||||
|
, m_initialized(false)
|
||||||
{
|
{
|
||||||
m_backend->setKey(key);
|
|
||||||
m_backend->setIv(iv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SymmetricCipher::~SymmetricCipher()
|
SymmetricCipher::~SymmetricCipher()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SymmetricCipher::init(const QByteArray& key, const QByteArray& iv)
|
||||||
|
{
|
||||||
|
if (!m_backend->init()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_backend->setKey(key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_backend->setIv(iv)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_initialized = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SymmetricCipher::isInitalized() const
|
||||||
|
{
|
||||||
|
return m_initialized;
|
||||||
|
}
|
||||||
|
|
||||||
SymmetricCipherBackend* SymmetricCipher::createBackend(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
SymmetricCipherBackend* SymmetricCipher::createBackend(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
||||||
SymmetricCipher::Direction direction)
|
SymmetricCipher::Direction direction)
|
||||||
{
|
{
|
||||||
@ -55,12 +77,17 @@ SymmetricCipherBackend* SymmetricCipher::createBackend(SymmetricCipher::Algorith
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymmetricCipher::reset()
|
bool SymmetricCipher::reset()
|
||||||
{
|
{
|
||||||
m_backend->reset();
|
return m_backend->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
int SymmetricCipher::blockSize() const
|
int SymmetricCipher::blockSize() const
|
||||||
{
|
{
|
||||||
return m_backend->blockSize();
|
return m_backend->blockSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString SymmetricCipher::errorString() const
|
||||||
|
{
|
||||||
|
return m_backend->errorString();
|
||||||
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QScopedPointer>
|
#include <QScopedPointer>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
#include "core/Global.h"
|
#include "core/Global.h"
|
||||||
#include "crypto/SymmetricCipherBackend.h"
|
#include "crypto/SymmetricCipherBackend.h"
|
||||||
@ -48,30 +49,35 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
SymmetricCipher(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
SymmetricCipher(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
||||||
SymmetricCipher::Direction direction, const QByteArray& key, const QByteArray& iv);
|
SymmetricCipher::Direction direction);
|
||||||
~SymmetricCipher();
|
~SymmetricCipher();
|
||||||
|
|
||||||
inline QByteArray process(const QByteArray& data) {
|
bool init(const QByteArray& key, const QByteArray& iv);
|
||||||
return m_backend->process(data);
|
bool isInitalized() const;
|
||||||
|
|
||||||
|
inline QByteArray process(const QByteArray& data, bool* ok) {
|
||||||
|
return m_backend->process(data, ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void processInPlace(QByteArray& data) {
|
inline bool processInPlace(QByteArray& data) {
|
||||||
m_backend->processInPlace(data);
|
return m_backend->processInPlace(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void processInPlace(QByteArray& data, quint64 rounds) {
|
inline bool processInPlace(QByteArray& data, quint64 rounds) {
|
||||||
Q_ASSERT(rounds > 0);
|
Q_ASSERT(rounds > 0);
|
||||||
m_backend->processInPlace(data, rounds);
|
return m_backend->processInPlace(data, rounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset();
|
bool reset();
|
||||||
int blockSize() const;
|
int blockSize() const;
|
||||||
|
QString errorString() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static SymmetricCipherBackend* createBackend(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
static SymmetricCipherBackend* createBackend(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
||||||
SymmetricCipher::Direction direction);
|
SymmetricCipher::Direction direction);
|
||||||
|
|
||||||
const QScopedPointer<SymmetricCipherBackend> m_backend;
|
const QScopedPointer<SymmetricCipherBackend> m_backend;
|
||||||
|
bool m_initialized;
|
||||||
|
|
||||||
Q_DISABLE_COPY(SymmetricCipher)
|
Q_DISABLE_COPY(SymmetricCipher)
|
||||||
};
|
};
|
||||||
|
@ -24,15 +24,18 @@ class SymmetricCipherBackend
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~SymmetricCipherBackend() {}
|
virtual ~SymmetricCipherBackend() {}
|
||||||
virtual void setKey(const QByteArray& key) = 0;
|
virtual bool init() = 0;
|
||||||
virtual void setIv(const QByteArray& iv) = 0;
|
virtual bool setKey(const QByteArray& key) = 0;
|
||||||
|
virtual bool setIv(const QByteArray& iv) = 0;
|
||||||
|
|
||||||
virtual QByteArray process(const QByteArray& data) = 0;
|
virtual QByteArray process(const QByteArray& data, bool* ok) = 0;
|
||||||
virtual void processInPlace(QByteArray& data) = 0;
|
virtual bool processInPlace(QByteArray& data) = 0;
|
||||||
virtual void processInPlace(QByteArray& data, quint64 rounds) = 0;
|
virtual bool processInPlace(QByteArray& data, quint64 rounds) = 0;
|
||||||
|
|
||||||
virtual void reset() = 0;
|
virtual bool reset() = 0;
|
||||||
virtual int blockSize() const = 0;
|
virtual int blockSize() const = 0;
|
||||||
|
|
||||||
|
virtual QString errorString() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_SYMMETRICCIPHERBACKEND_H
|
#endif // KEEPASSX_SYMMETRICCIPHERBACKEND_H
|
||||||
|
@ -22,22 +22,12 @@
|
|||||||
|
|
||||||
SymmetricCipherGcrypt::SymmetricCipherGcrypt(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
SymmetricCipherGcrypt::SymmetricCipherGcrypt(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
||||||
SymmetricCipher::Direction direction)
|
SymmetricCipher::Direction direction)
|
||||||
: m_algo(gcryptAlgo(algo))
|
: m_ctx(Q_NULLPTR)
|
||||||
|
, m_algo(gcryptAlgo(algo))
|
||||||
, m_mode(gcryptMode(mode))
|
, m_mode(gcryptMode(mode))
|
||||||
, m_direction(direction)
|
, m_direction(direction)
|
||||||
, m_blockSize(-1)
|
, m_blockSize(-1)
|
||||||
{
|
{
|
||||||
Q_ASSERT(Crypto::initalized());
|
|
||||||
|
|
||||||
gcry_error_t error;
|
|
||||||
|
|
||||||
error = gcry_cipher_open(&m_ctx, m_algo, m_mode, 0);
|
|
||||||
Q_ASSERT(error == 0); // TODO: real error checking
|
|
||||||
|
|
||||||
size_t blockSizeT;
|
|
||||||
error = gcry_cipher_algo_info(m_algo, GCRYCTL_GET_BLKLEN, Q_NULLPTR, &blockSizeT);
|
|
||||||
Q_ASSERT(error == 0);
|
|
||||||
m_blockSize = blockSizeT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SymmetricCipherGcrypt::~SymmetricCipherGcrypt()
|
SymmetricCipherGcrypt::~SymmetricCipherGcrypt()
|
||||||
@ -83,21 +73,65 @@ int SymmetricCipherGcrypt::gcryptMode(SymmetricCipher::Mode mode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymmetricCipherGcrypt::setKey(const QByteArray& key)
|
void SymmetricCipherGcrypt::setErrorString(gcry_error_t err)
|
||||||
|
{
|
||||||
|
const char* gcryptError = gcry_strerror(err);
|
||||||
|
const char* gcryptErrorSource = gcry_strsource(err);
|
||||||
|
|
||||||
|
m_errorString = QString("%1/%2").arg(QString::fromLocal8Bit(gcryptErrorSource),
|
||||||
|
QString::fromLocal8Bit(gcryptError));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SymmetricCipherGcrypt::init()
|
||||||
|
{
|
||||||
|
Q_ASSERT(Crypto::initalized());
|
||||||
|
|
||||||
|
gcry_error_t error;
|
||||||
|
|
||||||
|
error = gcry_cipher_open(&m_ctx, m_algo, m_mode, 0);
|
||||||
|
if (error != 0) {
|
||||||
|
setErrorString(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t blockSizeT;
|
||||||
|
error = gcry_cipher_algo_info(m_algo, GCRYCTL_GET_BLKLEN, Q_NULLPTR, &blockSizeT);
|
||||||
|
if (error != 0) {
|
||||||
|
setErrorString(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_blockSize = blockSizeT;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SymmetricCipherGcrypt::setKey(const QByteArray& key)
|
||||||
{
|
{
|
||||||
m_key = key;
|
m_key = key;
|
||||||
gcry_error_t error = gcry_cipher_setkey(m_ctx, m_key.constData(), m_key.size());
|
gcry_error_t error = gcry_cipher_setkey(m_ctx, m_key.constData(), m_key.size());
|
||||||
Q_ASSERT(error == 0);
|
|
||||||
|
if (error != 0) {
|
||||||
|
setErrorString(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymmetricCipherGcrypt::setIv(const QByteArray& iv)
|
bool SymmetricCipherGcrypt::setIv(const QByteArray& iv)
|
||||||
{
|
{
|
||||||
m_iv = iv;
|
m_iv = iv;
|
||||||
gcry_error_t error = gcry_cipher_setiv(m_ctx, m_iv.constData(), m_iv.size());
|
gcry_error_t error = gcry_cipher_setiv(m_ctx, m_iv.constData(), m_iv.size());
|
||||||
Q_ASSERT(error == 0);
|
|
||||||
|
if (error != 0) {
|
||||||
|
setErrorString(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray SymmetricCipherGcrypt::process(const QByteArray& data)
|
QByteArray SymmetricCipherGcrypt::process(const QByteArray& data, bool* ok)
|
||||||
{
|
{
|
||||||
// TODO: check block size
|
// TODO: check block size
|
||||||
|
|
||||||
@ -113,12 +147,16 @@ QByteArray SymmetricCipherGcrypt::process(const QByteArray& data)
|
|||||||
error = gcry_cipher_encrypt(m_ctx, result.data(), data.size(), data.constData(), data.size());
|
error = gcry_cipher_encrypt(m_ctx, result.data(), data.size(), data.constData(), data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_ASSERT(error == 0);
|
if (error != 0) {
|
||||||
|
setErrorString(error);
|
||||||
|
*ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ok = true;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymmetricCipherGcrypt::processInPlace(QByteArray& data)
|
bool SymmetricCipherGcrypt::processInPlace(QByteArray& data)
|
||||||
{
|
{
|
||||||
// TODO: check block size
|
// TODO: check block size
|
||||||
|
|
||||||
@ -131,10 +169,15 @@ void SymmetricCipherGcrypt::processInPlace(QByteArray& data)
|
|||||||
error = gcry_cipher_encrypt(m_ctx, data.data(), data.size(), Q_NULLPTR, 0);
|
error = gcry_cipher_encrypt(m_ctx, data.data(), data.size(), Q_NULLPTR, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_ASSERT(error == 0);
|
if (error != 0) {
|
||||||
|
setErrorString(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymmetricCipherGcrypt::processInPlace(QByteArray& data, quint64 rounds)
|
bool SymmetricCipherGcrypt::processInPlace(QByteArray& data, quint64 rounds)
|
||||||
{
|
{
|
||||||
// TODO: check block size
|
// TODO: check block size
|
||||||
|
|
||||||
@ -146,28 +189,52 @@ void SymmetricCipherGcrypt::processInPlace(QByteArray& data, quint64 rounds)
|
|||||||
if (m_direction == SymmetricCipher::Decrypt) {
|
if (m_direction == SymmetricCipher::Decrypt) {
|
||||||
for (quint64 i = 0; i != rounds; ++i) {
|
for (quint64 i = 0; i != rounds; ++i) {
|
||||||
error = gcry_cipher_decrypt(m_ctx, rawData, size, Q_NULLPTR, 0);
|
error = gcry_cipher_decrypt(m_ctx, rawData, size, Q_NULLPTR, 0);
|
||||||
Q_ASSERT(error == 0);
|
|
||||||
|
if (error != 0) {
|
||||||
|
setErrorString(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (quint64 i = 0; i != rounds; ++i) {
|
for (quint64 i = 0; i != rounds; ++i) {
|
||||||
error = gcry_cipher_encrypt(m_ctx, rawData, size, Q_NULLPTR, 0);
|
error = gcry_cipher_encrypt(m_ctx, rawData, size, Q_NULLPTR, 0);
|
||||||
Q_ASSERT(error == 0);
|
|
||||||
|
if (error != 0) {
|
||||||
|
setErrorString(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymmetricCipherGcrypt::reset()
|
bool SymmetricCipherGcrypt::reset()
|
||||||
{
|
{
|
||||||
gcry_error_t error;
|
gcry_error_t error;
|
||||||
|
|
||||||
error = gcry_cipher_reset(m_ctx);
|
error = gcry_cipher_reset(m_ctx);
|
||||||
Q_ASSERT(error == 0);
|
if (error != 0) {
|
||||||
|
setErrorString(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
error = gcry_cipher_setiv(m_ctx, m_iv.constData(), m_iv.size());
|
error = gcry_cipher_setiv(m_ctx, m_iv.constData(), m_iv.size());
|
||||||
Q_ASSERT(error == 0);
|
if (error != 0) {
|
||||||
|
setErrorString(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SymmetricCipherGcrypt::blockSize() const
|
int SymmetricCipherGcrypt::blockSize() const
|
||||||
{
|
{
|
||||||
return m_blockSize;
|
return m_blockSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString SymmetricCipherGcrypt::errorString() const
|
||||||
|
{
|
||||||
|
return m_errorString;
|
||||||
|
}
|
||||||
|
@ -29,19 +29,24 @@ public:
|
|||||||
SymmetricCipherGcrypt(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
SymmetricCipherGcrypt(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
||||||
SymmetricCipher::Direction direction);
|
SymmetricCipher::Direction direction);
|
||||||
~SymmetricCipherGcrypt();
|
~SymmetricCipherGcrypt();
|
||||||
void setKey(const QByteArray& key);
|
|
||||||
void setIv(const QByteArray& iv);
|
|
||||||
|
|
||||||
QByteArray process(const QByteArray& data);
|
bool init();
|
||||||
void processInPlace(QByteArray& data);
|
bool setKey(const QByteArray& key);
|
||||||
void processInPlace(QByteArray& data, quint64 rounds);
|
bool setIv(const QByteArray& iv);
|
||||||
|
|
||||||
void reset();
|
QByteArray process(const QByteArray& data, bool* ok);
|
||||||
|
bool processInPlace(QByteArray& data);
|
||||||
|
bool processInPlace(QByteArray& data, quint64 rounds);
|
||||||
|
|
||||||
|
bool reset();
|
||||||
int blockSize() const;
|
int blockSize() const;
|
||||||
|
|
||||||
|
QString errorString() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int gcryptAlgo(SymmetricCipher::Algorithm algo);
|
static int gcryptAlgo(SymmetricCipher::Algorithm algo);
|
||||||
static int gcryptMode(SymmetricCipher::Mode mode);
|
static int gcryptMode(SymmetricCipher::Mode mode);
|
||||||
|
void setErrorString(gcry_error_t err);
|
||||||
|
|
||||||
gcry_cipher_hd_t m_ctx;
|
gcry_cipher_hd_t m_ctx;
|
||||||
const int m_algo;
|
const int m_algo;
|
||||||
@ -50,6 +55,7 @@ private:
|
|||||||
QByteArray m_key;
|
QByteArray m_key;
|
||||||
QByteArray m_iv;
|
QByteArray m_iv;
|
||||||
int m_blockSize;
|
int m_blockSize;
|
||||||
|
QString m_errorString;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_SYMMETRICCIPHERGCRYPT_H
|
#endif // KEEPASSX_SYMMETRICCIPHERGCRYPT_H
|
||||||
|
@ -28,17 +28,18 @@ public:
|
|||||||
SymmetricCipherSalsa20(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
SymmetricCipherSalsa20(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
||||||
SymmetricCipher::Direction direction);
|
SymmetricCipher::Direction direction);
|
||||||
~SymmetricCipherSalsa20();
|
~SymmetricCipherSalsa20();
|
||||||
|
bool init();
|
||||||
void setAlgorithm(SymmetricCipher::Algorithm algo);
|
void setAlgorithm(SymmetricCipher::Algorithm algo);
|
||||||
void setMode(SymmetricCipher::Mode mode);
|
void setMode(SymmetricCipher::Mode mode);
|
||||||
void setDirection(SymmetricCipher::Direction direction);
|
void setDirection(SymmetricCipher::Direction direction);
|
||||||
void setKey(const QByteArray& key);
|
bool setKey(const QByteArray& key);
|
||||||
void setIv(const QByteArray& iv);
|
bool setIv(const QByteArray& iv);
|
||||||
|
|
||||||
QByteArray process(const QByteArray& data);
|
QByteArray process(const QByteArray& data, bool* ok);
|
||||||
void processInPlace(QByteArray& data);
|
bool processInPlace(QByteArray& data);
|
||||||
void processInPlace(QByteArray& data, quint64 rounds);
|
bool processInPlace(QByteArray& data, quint64 rounds);
|
||||||
|
|
||||||
void reset();
|
bool reset();
|
||||||
int blockSize() const;
|
int blockSize() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -154,14 +154,16 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
|
|||||||
raiseError("Invalid number of transform rounds");
|
raiseError("Invalid number of transform rounds");
|
||||||
return Q_NULLPTR;
|
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();
|
qint64 contentPos = m_device->pos();
|
||||||
|
|
||||||
QScopedPointer<SymmetricCipherStream> cipherStream(testKeys(password, keyfileData, contentPos));
|
QScopedPointer<SymmetricCipherStream> cipherStream(testKeys(password, keyfileData, contentPos));
|
||||||
|
|
||||||
if (!cipherStream) {
|
if (!cipherStream) {
|
||||||
raiseError("Unable to create cipher stream");
|
|
||||||
return Q_NULLPTR;
|
return Q_NULLPTR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,7 +236,10 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
|
|||||||
key.addKey(newFileKey);
|
key.addKey(newFileKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
db->setKey(key);
|
if (!db->setKey(key)) {
|
||||||
|
raiseError(tr("Unable to calculate master key"));
|
||||||
|
return Q_NULLPTR;
|
||||||
|
}
|
||||||
|
|
||||||
return db.take();
|
return db.take();
|
||||||
}
|
}
|
||||||
@ -326,16 +331,26 @@ SymmetricCipherStream* KeePass1Reader::testKeys(const QString& password, const Q
|
|||||||
}
|
}
|
||||||
|
|
||||||
QByteArray finalKey = key(passwordData, keyfileData);
|
QByteArray finalKey = key(passwordData, keyfileData);
|
||||||
|
if (finalKey.isEmpty()) {
|
||||||
|
return Q_NULLPTR;
|
||||||
|
}
|
||||||
if (m_encryptionFlags & KeePass1::Rijndael) {
|
if (m_encryptionFlags & KeePass1::Rijndael) {
|
||||||
cipherStream.reset(new SymmetricCipherStream(m_device, SymmetricCipher::Aes256,
|
cipherStream.reset(new SymmetricCipherStream(m_device, SymmetricCipher::Aes256,
|
||||||
SymmetricCipher::Cbc, SymmetricCipher::Decrypt, finalKey, m_encryptionIV));
|
SymmetricCipher::Cbc, SymmetricCipher::Decrypt));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cipherStream.reset(new SymmetricCipherStream(m_device, SymmetricCipher::Twofish,
|
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());
|
bool success = verifyKey(cipherStream.data());
|
||||||
|
|
||||||
@ -372,9 +387,18 @@ QByteArray KeePass1Reader::key(const QByteArray& password, const QByteArray& key
|
|||||||
key.setPassword(password);
|
key.setPassword(password);
|
||||||
key.setKeyfileData(keyfileData);
|
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);
|
CryptoHash hash(CryptoHash::Sha256);
|
||||||
hash.addData(m_masterSeed);
|
hash.addData(m_masterSeed);
|
||||||
hash.addData(key.transform(m_transformSeed, m_transformRounds));
|
hash.addData(transformedKey);
|
||||||
return hash.result();
|
return hash.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,14 +20,19 @@
|
|||||||
#include "crypto/CryptoHash.h"
|
#include "crypto/CryptoHash.h"
|
||||||
#include "format/KeePass2.h"
|
#include "format/KeePass2.h"
|
||||||
|
|
||||||
KeePass2RandomStream::KeePass2RandomStream(const QByteArray& key)
|
KeePass2RandomStream::KeePass2RandomStream()
|
||||||
: m_cipher(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt,
|
: m_cipher(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt)
|
||||||
CryptoHash::hash(key, CryptoHash::Sha256), KeePass2::INNER_STREAM_SALSA20_IV)
|
|
||||||
, m_offset(0)
|
, 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;
|
QByteArray result;
|
||||||
|
|
||||||
@ -35,7 +40,10 @@ QByteArray KeePass2RandomStream::randomBytes(int size)
|
|||||||
|
|
||||||
while (bytesRemaining > 0) {
|
while (bytesRemaining > 0) {
|
||||||
if (m_buffer.size() == m_offset) {
|
if (m_buffer.size() == m_offset) {
|
||||||
loadBlock();
|
if (!loadBlock()) {
|
||||||
|
*ok = false;
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int bytesToCopy = qMin(bytesRemaining, m_buffer.size() - m_offset);
|
int bytesToCopy = qMin(bytesRemaining, m_buffer.size() - m_offset);
|
||||||
@ -44,12 +52,20 @@ QByteArray KeePass2RandomStream::randomBytes(int size)
|
|||||||
bytesRemaining -= bytesToCopy;
|
bytesRemaining -= bytesToCopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*ok = true;
|
||||||
return result;
|
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;
|
QByteArray result;
|
||||||
result.resize(data.size());
|
result.resize(data.size());
|
||||||
|
|
||||||
@ -57,23 +73,39 @@ QByteArray KeePass2RandomStream::process(const QByteArray& data)
|
|||||||
result[i] = data[i] ^ randomData[i];
|
result[i] = data[i] ^ randomData[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*ok = true;
|
||||||
return result;
|
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++) {
|
for (int i = 0; i < data.size(); i++) {
|
||||||
data[i] = data[i] ^ randomData[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());
|
Q_ASSERT(m_offset == m_buffer.size());
|
||||||
|
|
||||||
m_buffer.fill('\0', m_cipher.blockSize());
|
m_buffer.fill('\0', m_cipher.blockSize());
|
||||||
m_cipher.processInPlace(m_buffer);
|
if (!m_cipher.processInPlace(m_buffer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
m_offset = 0;
|
m_offset = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -25,13 +25,15 @@
|
|||||||
class KeePass2RandomStream
|
class KeePass2RandomStream
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit KeePass2RandomStream(const QByteArray& key);
|
KeePass2RandomStream();
|
||||||
QByteArray randomBytes(int size);
|
bool init(const QByteArray& key);
|
||||||
QByteArray process(const QByteArray& data);
|
QByteArray randomBytes(int size, bool* ok);
|
||||||
void processInPlace(QByteArray& data);
|
QByteArray process(const QByteArray& data, bool* ok);
|
||||||
|
bool processInPlace(QByteArray& data);
|
||||||
|
QString errorString() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadBlock();
|
bool loadBlock();
|
||||||
|
|
||||||
SymmetricCipher m_cipher;
|
SymmetricCipher m_cipher;
|
||||||
QByteArray m_buffer;
|
QByteArray m_buffer;
|
||||||
|
@ -96,16 +96,26 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
|
|||||||
return Q_NULLPTR;
|
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);
|
CryptoHash hash(CryptoHash::Sha256);
|
||||||
hash.addData(m_masterSeed);
|
hash.addData(m_masterSeed);
|
||||||
hash.addData(m_db->transformedMasterKey());
|
hash.addData(m_db->transformedMasterKey());
|
||||||
QByteArray finalKey = hash.result();
|
QByteArray finalKey = hash.result();
|
||||||
|
|
||||||
SymmetricCipherStream cipherStream(m_device, SymmetricCipher::Aes256, SymmetricCipher::Cbc,
|
SymmetricCipherStream cipherStream(m_device, SymmetricCipher::Aes256,
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray realStart = cipherStream.read(32);
|
QByteArray realStart = cipherStream.read(32);
|
||||||
|
|
||||||
@ -115,7 +125,10 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
|
|||||||
}
|
}
|
||||||
|
|
||||||
HashedBlockStream hashedStream(&cipherStream);
|
HashedBlockStream hashedStream(&cipherStream);
|
||||||
hashedStream.open(QIODevice::ReadOnly);
|
if (!hashedStream.open(QIODevice::ReadOnly)) {
|
||||||
|
raiseError(hashedStream.errorString());
|
||||||
|
return Q_NULLPTR;
|
||||||
|
}
|
||||||
|
|
||||||
QIODevice* xmlDevice;
|
QIODevice* xmlDevice;
|
||||||
QScopedPointer<QtIOCompressor> ioCompressor;
|
QScopedPointer<QtIOCompressor> ioCompressor;
|
||||||
@ -126,11 +139,18 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
|
|||||||
else {
|
else {
|
||||||
ioCompressor.reset(new QtIOCompressor(&hashedStream));
|
ioCompressor.reset(new QtIOCompressor(&hashedStream));
|
||||||
ioCompressor->setStreamFormat(QtIOCompressor::GzipFormat);
|
ioCompressor->setStreamFormat(QtIOCompressor::GzipFormat);
|
||||||
ioCompressor->open(QIODevice::ReadOnly);
|
if (!ioCompressor->open(QIODevice::ReadOnly)) {
|
||||||
|
raiseError(ioCompressor->errorString());
|
||||||
|
return Q_NULLPTR;
|
||||||
|
}
|
||||||
xmlDevice = ioCompressor.data();
|
xmlDevice = ioCompressor.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
KeePass2RandomStream randomStream(m_protectedStreamKey);
|
KeePass2RandomStream randomStream;
|
||||||
|
if (!randomStream.init(m_protectedStreamKey)) {
|
||||||
|
raiseError(randomStream.errorString());
|
||||||
|
return Q_NULLPTR;
|
||||||
|
}
|
||||||
|
|
||||||
QScopedPointer<QBuffer> buffer;
|
QScopedPointer<QBuffer> buffer;
|
||||||
|
|
||||||
@ -340,7 +360,9 @@ void KeePass2Reader::setTansformRounds(const QByteArray& data)
|
|||||||
raiseError("Invalid transform rounds size");
|
raiseError("Invalid transform rounds size");
|
||||||
}
|
}
|
||||||
else {
|
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()));
|
CHECK_RETURN(writeData(header.data()));
|
||||||
|
|
||||||
SymmetricCipherStream cipherStream(device, SymmetricCipher::Aes256, SymmetricCipher::Cbc,
|
SymmetricCipherStream cipherStream(device, SymmetricCipher::Aes256, SymmetricCipher::Cbc,
|
||||||
SymmetricCipher::Encrypt, finalKey, encryptionIV);
|
SymmetricCipher::Encrypt);
|
||||||
cipherStream.open(QIODevice::WriteOnly);
|
cipherStream.init(finalKey, encryptionIV);
|
||||||
|
if (!cipherStream.open(QIODevice::WriteOnly)) {
|
||||||
|
raiseError(cipherStream.errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_device = &cipherStream;
|
m_device = &cipherStream;
|
||||||
CHECK_RETURN(writeData(startBytes));
|
CHECK_RETURN(writeData(startBytes));
|
||||||
|
|
||||||
HashedBlockStream hashedStream(&cipherStream);
|
HashedBlockStream hashedStream(&cipherStream);
|
||||||
hashedStream.open(QIODevice::WriteOnly);
|
if (!hashedStream.open(QIODevice::WriteOnly)) {
|
||||||
|
raiseError(hashedStream.errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QScopedPointer<QtIOCompressor> ioCompressor;
|
QScopedPointer<QtIOCompressor> ioCompressor;
|
||||||
|
|
||||||
@ -104,14 +111,25 @@ void KeePass2Writer::writeDatabase(QIODevice* device, Database* db)
|
|||||||
else {
|
else {
|
||||||
ioCompressor.reset(new QtIOCompressor(&hashedStream));
|
ioCompressor.reset(new QtIOCompressor(&hashedStream));
|
||||||
ioCompressor->setStreamFormat(QtIOCompressor::GzipFormat);
|
ioCompressor->setStreamFormat(QtIOCompressor::GzipFormat);
|
||||||
ioCompressor->open(QIODevice::WriteOnly);
|
if (!ioCompressor->open(QIODevice::WriteOnly)) {
|
||||||
|
raiseError(ioCompressor->errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_device = ioCompressor.data();
|
m_device = ioCompressor.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
KeePass2RandomStream randomStream(protectedStreamKey);
|
KeePass2RandomStream randomStream;
|
||||||
|
if (!randomStream.init(protectedStreamKey)) {
|
||||||
|
raiseError(randomStream.errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
KeePass2XmlWriter xmlWriter;
|
KeePass2XmlWriter xmlWriter;
|
||||||
xmlWriter.writeDatabase(m_device, db, &randomStream, headerHash);
|
xmlWriter.writeDatabase(m_device, db, &randomStream, headerHash);
|
||||||
|
|
||||||
|
if (xmlWriter.hasError()) {
|
||||||
|
raiseError(xmlWriter.errorString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KeePass2Writer::writeData(const QByteArray& data)
|
bool KeePass2Writer::writeData(const QByteArray& data)
|
||||||
|
@ -809,7 +809,16 @@ void KeePass2XmlReader::parseEntryString(Entry* entry)
|
|||||||
|
|
||||||
if (isProtected && !value.isEmpty()) {
|
if (isProtected && !value.isEmpty()) {
|
||||||
if (m_randomStream) {
|
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 {
|
else {
|
||||||
raiseError("Unable to decrypt entry string");
|
raiseError("Unable to decrypt entry string");
|
||||||
|
@ -28,6 +28,7 @@ KeePass2XmlWriter::KeePass2XmlWriter()
|
|||||||
: m_db(Q_NULLPTR)
|
: m_db(Q_NULLPTR)
|
||||||
, m_meta(Q_NULLPTR)
|
, m_meta(Q_NULLPTR)
|
||||||
, m_randomStream(Q_NULLPTR)
|
, m_randomStream(Q_NULLPTR)
|
||||||
|
, m_error(false)
|
||||||
{
|
{
|
||||||
m_xml.setAutoFormatting(true);
|
m_xml.setAutoFormatting(true);
|
||||||
m_xml.setAutoFormattingIndent(-1); // 1 tab
|
m_xml.setAutoFormattingIndent(-1); // 1 tab
|
||||||
@ -65,6 +66,16 @@ void KeePass2XmlWriter::writeDatabase(const QString& filename, Database* db)
|
|||||||
writeDatabase(&file, db);
|
writeDatabase(&file, db);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool KeePass2XmlWriter::hasError()
|
||||||
|
{
|
||||||
|
return m_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString KeePass2XmlWriter::errorString()
|
||||||
|
{
|
||||||
|
return m_errorStr;
|
||||||
|
}
|
||||||
|
|
||||||
void KeePass2XmlWriter::generateIdMap()
|
void KeePass2XmlWriter::generateIdMap()
|
||||||
{
|
{
|
||||||
QList<Entry*> allEntries = m_db->rootGroup()->entriesRecursive(true);
|
QList<Entry*> allEntries = m_db->rootGroup()->entriesRecursive(true);
|
||||||
@ -340,7 +351,11 @@ void KeePass2XmlWriter::writeEntry(const Entry* entry)
|
|||||||
if (protect) {
|
if (protect) {
|
||||||
if (m_randomStream) {
|
if (m_randomStream) {
|
||||||
m_xml.writeAttribute("Protected", "True");
|
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());
|
value = QString::fromLatin1(rawData.toBase64());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -527,3 +542,9 @@ QString KeePass2XmlWriter::colorPartToString(int value)
|
|||||||
|
|
||||||
return str;
|
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,
|
void writeDatabase(QIODevice* device, Database* db, KeePass2RandomStream* randomStream = Q_NULLPTR,
|
||||||
const QByteArray& headerHash = QByteArray());
|
const QByteArray& headerHash = QByteArray());
|
||||||
void writeDatabase(const QString& filename, Database* db);
|
void writeDatabase(const QString& filename, Database* db);
|
||||||
bool error();
|
bool hasError();
|
||||||
QString errorString();
|
QString errorString();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -74,12 +74,16 @@ private:
|
|||||||
void writeTriState(const QString& qualifiedName, Group::TriState triState);
|
void writeTriState(const QString& qualifiedName, Group::TriState triState);
|
||||||
QString colorPartToString(int value);
|
QString colorPartToString(int value);
|
||||||
|
|
||||||
|
void raiseError(const QString& errorMessage);
|
||||||
|
|
||||||
QXmlStreamWriter m_xml;
|
QXmlStreamWriter m_xml;
|
||||||
Database* m_db;
|
Database* m_db;
|
||||||
Metadata* m_meta;
|
Metadata* m_meta;
|
||||||
KeePass2RandomStream* m_randomStream;
|
KeePass2RandomStream* m_randomStream;
|
||||||
QByteArray m_headerHash;
|
QByteArray m_headerHash;
|
||||||
QHash<QByteArray, int> m_idMap;
|
QHash<QByteArray, int> m_idMap;
|
||||||
|
bool m_error;
|
||||||
|
QString m_errorStr;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_KEEPASS2XMLWRITER_H
|
#endif // KEEPASSX_KEEPASS2XMLWRITER_H
|
||||||
|
@ -599,8 +599,13 @@ void DatabaseWidget::updateMasterKey(bool accepted)
|
|||||||
{
|
{
|
||||||
if (accepted) {
|
if (accepted) {
|
||||||
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
||||||
m_db->setKey(m_changeMasterKeyWidget->newMasterKey());
|
bool result = m_db->setKey(m_changeMasterKeyWidget->newMasterKey());
|
||||||
QApplication::restoreOverrideCursor();
|
QApplication::restoreOverrideCursor();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
MessageBox::critical(this, tr("Error"), tr("Unable to calculate master key"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (!m_db->hasKey()) {
|
else if (!m_db->hasKey()) {
|
||||||
Q_EMIT closeRequest();
|
Q_EMIT closeRequest();
|
||||||
|
@ -81,34 +81,62 @@ QByteArray CompositeKey::rawKey() const
|
|||||||
return cryptoHash.result();
|
return cryptoHash.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray CompositeKey::transform(const QByteArray& seed, quint64 rounds) const
|
QByteArray CompositeKey::transform(const QByteArray& seed, quint64 rounds,
|
||||||
|
bool* ok, QString* errorString) const
|
||||||
{
|
{
|
||||||
Q_ASSERT(seed.size() == 32);
|
Q_ASSERT(seed.size() == 32);
|
||||||
Q_ASSERT(rounds > 0);
|
Q_ASSERT(rounds > 0);
|
||||||
|
|
||||||
|
bool okLeft;
|
||||||
|
QString errorStringLeft;
|
||||||
|
bool okRight;
|
||||||
|
QString errorStringRight;
|
||||||
|
|
||||||
QByteArray key = rawKey();
|
QByteArray key = rawKey();
|
||||||
|
|
||||||
QFuture<QByteArray> future = QtConcurrent::run(transformKeyRaw, key.left(16), seed, rounds);
|
QFuture<QByteArray> future = QtConcurrent::run(transformKeyRaw, key.left(16), seed, rounds, &okLeft, &errorStringLeft);
|
||||||
QByteArray result2 = transformKeyRaw(key.right(16), seed, rounds);
|
QByteArray result2 = transformKeyRaw(key.right(16), seed, rounds, &okRight, &errorStringRight);
|
||||||
|
|
||||||
QByteArray transformed;
|
QByteArray transformed;
|
||||||
transformed.append(future.result());
|
transformed.append(future.result());
|
||||||
transformed.append(result2);
|
transformed.append(result2);
|
||||||
|
|
||||||
|
*ok = (okLeft && okRight);
|
||||||
|
|
||||||
|
if (!okLeft) {
|
||||||
|
*errorString = errorStringLeft;
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!okRight) {
|
||||||
|
*errorString = errorStringRight;
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
return CryptoHash::hash(transformed, CryptoHash::Sha256);
|
return CryptoHash::hash(transformed, CryptoHash::Sha256);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray CompositeKey::transformKeyRaw(const QByteArray& key, const QByteArray& seed,
|
QByteArray CompositeKey::transformKeyRaw(const QByteArray& key, const QByteArray& seed,
|
||||||
quint64 rounds)
|
quint64 rounds, bool* ok, QString* errorString)
|
||||||
{
|
{
|
||||||
QByteArray iv(16, 0);
|
QByteArray iv(16, 0);
|
||||||
SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Ecb,
|
SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Ecb,
|
||||||
SymmetricCipher::Encrypt, seed, iv);
|
SymmetricCipher::Encrypt);
|
||||||
|
if (!cipher.init(seed, iv)) {
|
||||||
|
*ok = false;
|
||||||
|
*errorString = cipher.errorString();
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray result = key;
|
QByteArray result = key;
|
||||||
|
|
||||||
cipher.processInPlace(result, rounds);
|
if (!cipher.processInPlace(result, rounds)) {
|
||||||
|
*ok = false;
|
||||||
|
*errorString = cipher.errorString();
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
*ok = true;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +179,8 @@ void TransformKeyBenchmarkThread::run()
|
|||||||
QByteArray iv(16, 0);
|
QByteArray iv(16, 0);
|
||||||
|
|
||||||
SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Ecb,
|
SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Ecb,
|
||||||
SymmetricCipher::Encrypt, seed, iv);
|
SymmetricCipher::Encrypt);
|
||||||
|
cipher.init(seed, iv);
|
||||||
|
|
||||||
QTime t;
|
QTime t;
|
||||||
t.start();
|
t.start();
|
||||||
|
@ -34,14 +34,15 @@ public:
|
|||||||
CompositeKey& operator=(const CompositeKey& key);
|
CompositeKey& operator=(const CompositeKey& key);
|
||||||
|
|
||||||
QByteArray rawKey() const;
|
QByteArray rawKey() const;
|
||||||
QByteArray transform(const QByteArray& seed, quint64 rounds) const;
|
QByteArray transform(const QByteArray& seed, quint64 rounds,
|
||||||
|
bool* ok, QString* errorString) const;
|
||||||
void addKey(const Key& key);
|
void addKey(const Key& key);
|
||||||
|
|
||||||
static int transformKeyBenchmark(int msec);
|
static int transformKeyBenchmark(int msec);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static QByteArray transformKeyRaw(const QByteArray& key, const QByteArray& seed,
|
static QByteArray transformKeyRaw(const QByteArray& key, const QByteArray& seed,
|
||||||
quint64 rounds);
|
quint64 rounds, bool* ok, QString* errorString);
|
||||||
|
|
||||||
QList<Key*> m_keys;
|
QList<Key*> m_keys;
|
||||||
};
|
};
|
||||||
|
@ -34,11 +34,6 @@ bool LayeredStream::isSequential() const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString LayeredStream::errorString() const
|
|
||||||
{
|
|
||||||
return m_baseDevice->errorString();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LayeredStream::open(QIODevice::OpenMode mode)
|
bool LayeredStream::open(QIODevice::OpenMode mode)
|
||||||
{
|
{
|
||||||
if (isOpen()) {
|
if (isOpen()) {
|
||||||
|
@ -31,7 +31,6 @@ public:
|
|||||||
virtual ~LayeredStream();
|
virtual ~LayeredStream();
|
||||||
|
|
||||||
bool isSequential() const Q_DECL_OVERRIDE;
|
bool isSequential() const Q_DECL_OVERRIDE;
|
||||||
virtual QString errorString() const;
|
|
||||||
bool open(QIODevice::OpenMode mode) Q_DECL_OVERRIDE;
|
bool open(QIODevice::OpenMode mode) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -41,6 +41,10 @@ QByteArray StoreDataStream::storedData() const
|
|||||||
qint64 StoreDataStream::readData(char* data, qint64 maxSize)
|
qint64 StoreDataStream::readData(char* data, qint64 maxSize)
|
||||||
{
|
{
|
||||||
qint64 bytesRead = LayeredStream::readData(data, maxSize);
|
qint64 bytesRead = LayeredStream::readData(data, maxSize);
|
||||||
|
if (bytesRead == -1) {
|
||||||
|
setErrorString(m_baseDevice->errorString());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
m_storedData.append(data, bytesRead);
|
m_storedData.append(data, bytesRead);
|
||||||
|
|
||||||
|
@ -18,10 +18,9 @@
|
|||||||
#include "SymmetricCipherStream.h"
|
#include "SymmetricCipherStream.h"
|
||||||
|
|
||||||
SymmetricCipherStream::SymmetricCipherStream(QIODevice* baseDevice, SymmetricCipher::Algorithm algo,
|
SymmetricCipherStream::SymmetricCipherStream(QIODevice* baseDevice, SymmetricCipher::Algorithm algo,
|
||||||
SymmetricCipher::Mode mode, SymmetricCipher::Direction direction,
|
SymmetricCipher::Mode mode, SymmetricCipher::Direction direction)
|
||||||
const QByteArray& key, const QByteArray& iv)
|
|
||||||
: LayeredStream(baseDevice)
|
: LayeredStream(baseDevice)
|
||||||
, m_cipher(new SymmetricCipher(algo, mode, direction, key, iv))
|
, m_cipher(new SymmetricCipher(algo, mode, direction))
|
||||||
, m_bufferPos(0)
|
, m_bufferPos(0)
|
||||||
, m_bufferFilling(false)
|
, m_bufferFilling(false)
|
||||||
, m_error(false)
|
, m_error(false)
|
||||||
@ -33,6 +32,25 @@ SymmetricCipherStream::~SymmetricCipherStream()
|
|||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SymmetricCipherStream::init(const QByteArray& key, const QByteArray& iv)
|
||||||
|
{
|
||||||
|
m_isInitalized = m_cipher->init(key, iv);
|
||||||
|
if (!m_isInitalized) {
|
||||||
|
setErrorString(m_cipher->errorString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_isInitalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SymmetricCipherStream::open(QIODevice::OpenMode mode)
|
||||||
|
{
|
||||||
|
if (!m_isInitalized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LayeredStream::open(mode);
|
||||||
|
}
|
||||||
|
|
||||||
bool SymmetricCipherStream::reset()
|
bool SymmetricCipherStream::reset()
|
||||||
{
|
{
|
||||||
if (isWritable()) {
|
if (isWritable()) {
|
||||||
@ -108,7 +126,11 @@ bool SymmetricCipherStream::readBlock()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_cipher->processInPlace(m_buffer);
|
if (!m_cipher->processInPlace(m_buffer)) {
|
||||||
|
m_error = true;
|
||||||
|
setErrorString(m_cipher->errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
m_bufferPos = 0;
|
m_bufferPos = 0;
|
||||||
m_bufferFilling = false;
|
m_bufferFilling = false;
|
||||||
|
|
||||||
@ -187,7 +209,11 @@ bool SymmetricCipherStream::writeBlock(bool lastBlock)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_cipher->processInPlace(m_buffer);
|
if (!m_cipher->processInPlace(m_buffer)) {
|
||||||
|
m_error = true;
|
||||||
|
setErrorString(m_cipher->errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_baseDevice->write(m_buffer) != m_buffer.size()) {
|
if (m_baseDevice->write(m_buffer) != m_buffer.size()) {
|
||||||
m_error = true;
|
m_error = true;
|
||||||
|
@ -29,11 +29,13 @@ class SymmetricCipherStream : public LayeredStream
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SymmetricCipherStream(QIODevice* baseDevice, SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
SymmetricCipherStream(QIODevice* baseDevice, SymmetricCipher::Algorithm algo,
|
||||||
SymmetricCipher::Direction direction, const QByteArray& key, const QByteArray& iv);
|
SymmetricCipher::Mode mode, SymmetricCipher::Direction direction);
|
||||||
~SymmetricCipherStream();
|
~SymmetricCipherStream();
|
||||||
bool reset();
|
bool init(const QByteArray& key, const QByteArray& iv);
|
||||||
void close();
|
bool open(QIODevice::OpenMode mode) Q_DECL_OVERRIDE;
|
||||||
|
bool reset() Q_DECL_OVERRIDE;
|
||||||
|
void close() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
qint64 readData(char* data, qint64 maxSize) Q_DECL_OVERRIDE;
|
qint64 readData(char* data, qint64 maxSize) Q_DECL_OVERRIDE;
|
||||||
@ -48,6 +50,7 @@ private:
|
|||||||
int m_bufferPos;
|
int m_bufferPos;
|
||||||
bool m_bufferFilling;
|
bool m_bufferFilling;
|
||||||
bool m_error;
|
bool m_error;
|
||||||
|
bool m_isInitalized;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_SYMMETRICCIPHERSTREAM_H
|
#endif // KEEPASSX_SYMMETRICCIPHERSTREAM_H
|
||||||
|
@ -39,8 +39,8 @@ void TestKeePass2RandomStream::test()
|
|||||||
const int Size = 128;
|
const int Size = 128;
|
||||||
|
|
||||||
|
|
||||||
SymmetricCipher cipher(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt,
|
SymmetricCipher cipher(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt);
|
||||||
CryptoHash::hash(key, CryptoHash::Sha256), KeePass2::INNER_STREAM_SALSA20_IV);
|
QVERIFY(cipher.init(CryptoHash::hash(key, CryptoHash::Sha256), KeePass2::INNER_STREAM_SALSA20_IV));
|
||||||
|
|
||||||
const QByteArray data(QByteArray::fromHex("601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5"
|
const QByteArray data(QByteArray::fromHex("601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5"
|
||||||
"2b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6"
|
"2b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6"
|
||||||
@ -59,20 +59,27 @@ void TestKeePass2RandomStream::test()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
KeePass2RandomStream randomStream(key);
|
KeePass2RandomStream randomStream;
|
||||||
|
bool ok;
|
||||||
|
QVERIFY(randomStream.init(key));
|
||||||
QByteArray randomStreamData;
|
QByteArray randomStreamData;
|
||||||
randomStreamData.append(randomStream.process(data.mid(0, 7)));
|
randomStreamData.append(randomStream.process(data.mid(0, 7), &ok));
|
||||||
randomStreamData.append(randomStream.process(data.mid(7, 1)));
|
QVERIFY(ok);
|
||||||
|
randomStreamData.append(randomStream.process(data.mid(7, 1), &ok));
|
||||||
|
QVERIFY(ok);
|
||||||
QByteArray tmpData = data.mid(8, 12);
|
QByteArray tmpData = data.mid(8, 12);
|
||||||
randomStream.processInPlace(tmpData);
|
randomStream.processInPlace(tmpData);
|
||||||
randomStreamData.append(tmpData);
|
randomStreamData.append(tmpData);
|
||||||
randomStreamData.append(randomStream.process(data.mid(20, 44)));
|
randomStreamData.append(randomStream.process(data.mid(20, 44), &ok));
|
||||||
randomStreamData.append(randomStream.process(data.mid(64, 64)));
|
QVERIFY(ok);
|
||||||
|
randomStreamData.append(randomStream.process(data.mid(64, 64), &ok));
|
||||||
|
QVERIFY(ok);
|
||||||
|
|
||||||
|
|
||||||
SymmetricCipher cipherEncrypt(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt,
|
SymmetricCipher cipherEncrypt(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt);
|
||||||
CryptoHash::hash(key, CryptoHash::Sha256), KeePass2::INNER_STREAM_SALSA20_IV);
|
QVERIFY(cipherEncrypt.init(CryptoHash::hash(key, CryptoHash::Sha256), KeePass2::INNER_STREAM_SALSA20_IV));
|
||||||
QByteArray cipherDataEncrypt = cipherEncrypt.process(data);
|
QByteArray cipherDataEncrypt = cipherEncrypt.process(data, &ok);
|
||||||
|
QVERIFY(ok);
|
||||||
|
|
||||||
|
|
||||||
QCOMPARE(randomStreamData.size(), Size);
|
QCOMPARE(randomStreamData.size(), Size);
|
||||||
|
@ -43,6 +43,8 @@ void TestKeys::testComposite()
|
|||||||
CompositeKey* compositeKey1 = new CompositeKey();
|
CompositeKey* compositeKey1 = new CompositeKey();
|
||||||
PasswordKey* passwordKey1 = new PasswordKey();
|
PasswordKey* passwordKey1 = new PasswordKey();
|
||||||
PasswordKey* passwordKey2 = new PasswordKey("test");
|
PasswordKey* passwordKey2 = new PasswordKey("test");
|
||||||
|
bool ok;
|
||||||
|
QString errorString;
|
||||||
|
|
||||||
// make sure that addKey() creates a copy of the keys
|
// make sure that addKey() creates a copy of the keys
|
||||||
compositeKey1->addKey(*passwordKey1);
|
compositeKey1->addKey(*passwordKey1);
|
||||||
@ -50,13 +52,15 @@ void TestKeys::testComposite()
|
|||||||
delete passwordKey1;
|
delete passwordKey1;
|
||||||
delete passwordKey2;
|
delete passwordKey2;
|
||||||
|
|
||||||
QByteArray transformed = compositeKey1->transform(QByteArray(32, '\0'), 1);
|
QByteArray transformed = compositeKey1->transform(QByteArray(32, '\0'), 1, &ok, &errorString);
|
||||||
|
QVERIFY(ok);
|
||||||
QCOMPARE(transformed.size(), 32);
|
QCOMPARE(transformed.size(), 32);
|
||||||
|
|
||||||
// make sure the subkeys are copied
|
// make sure the subkeys are copied
|
||||||
CompositeKey* compositeKey2 = compositeKey1->clone();
|
CompositeKey* compositeKey2 = compositeKey1->clone();
|
||||||
delete compositeKey1;
|
delete compositeKey1;
|
||||||
QCOMPARE(compositeKey2->transform(QByteArray(32, '\0'), 1), transformed);
|
QCOMPARE(compositeKey2->transform(QByteArray(32, '\0'), 1, &ok, &errorString), transformed);
|
||||||
|
QVERIFY(ok);
|
||||||
delete compositeKey2;
|
delete compositeKey2;
|
||||||
|
|
||||||
CompositeKey* compositeKey3 = new CompositeKey();
|
CompositeKey* compositeKey3 = new CompositeKey();
|
||||||
@ -130,7 +134,7 @@ void TestKeys::testCreateFileKey()
|
|||||||
compositeKey.addKey(fileKey);
|
compositeKey.addKey(fileKey);
|
||||||
|
|
||||||
Database* dbOrg = new Database();
|
Database* dbOrg = new Database();
|
||||||
dbOrg->setKey(compositeKey);
|
QVERIFY(dbOrg->setKey(compositeKey));
|
||||||
dbOrg->metadata()->setName(dbName);
|
dbOrg->metadata()->setName(dbName);
|
||||||
|
|
||||||
QBuffer dbBuffer;
|
QBuffer dbBuffer;
|
||||||
@ -182,7 +186,10 @@ void TestKeys::benchmarkTransformKey()
|
|||||||
|
|
||||||
QByteArray seed(32, '\x4B');
|
QByteArray seed(32, '\x4B');
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
QString errorString;
|
||||||
|
|
||||||
QBENCHMARK {
|
QBENCHMARK {
|
||||||
compositeKey.transform(seed, 1e6);
|
compositeKey.transform(seed, 1e6, &ok, &errorString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,17 +42,20 @@ void TestSymmetricCipher::testAes256CbcEncryption()
|
|||||||
plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51"));
|
plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51"));
|
||||||
QByteArray cipherText = QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd6");
|
QByteArray cipherText = QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd6");
|
||||||
cipherText.append(QByteArray::fromHex("9cfc4e967edb808d679f777bc6702c7d"));
|
cipherText.append(QByteArray::fromHex("9cfc4e967edb808d679f777bc6702c7d"));
|
||||||
|
bool ok;
|
||||||
|
|
||||||
SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt,
|
SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt);
|
||||||
key, iv);
|
QVERIFY(cipher.init(key, iv));
|
||||||
QCOMPARE(cipher.blockSize(), 16);
|
QCOMPARE(cipher.blockSize(), 16);
|
||||||
|
|
||||||
QCOMPARE(cipher.process(plainText),
|
QCOMPARE(cipher.process(plainText, &ok),
|
||||||
cipherText);
|
cipherText);
|
||||||
|
QVERIFY(ok);
|
||||||
|
|
||||||
QBuffer buffer;
|
QBuffer buffer;
|
||||||
SymmetricCipherStream stream(&buffer, SymmetricCipher::Aes256, SymmetricCipher::Cbc,
|
SymmetricCipherStream stream(&buffer, SymmetricCipher::Aes256, SymmetricCipher::Cbc,
|
||||||
SymmetricCipher::Encrypt, key, iv);
|
SymmetricCipher::Encrypt);
|
||||||
|
QVERIFY(stream.init(key, iv));
|
||||||
buffer.open(QIODevice::WriteOnly);
|
buffer.open(QIODevice::WriteOnly);
|
||||||
stream.open(QIODevice::WriteOnly);
|
stream.open(QIODevice::WriteOnly);
|
||||||
QVERIFY(stream.reset());
|
QVERIFY(stream.reset());
|
||||||
@ -86,18 +89,22 @@ void TestSymmetricCipher::testAes256CbcDecryption()
|
|||||||
cipherText.append(QByteArray::fromHex("9cfc4e967edb808d679f777bc6702c7d"));
|
cipherText.append(QByteArray::fromHex("9cfc4e967edb808d679f777bc6702c7d"));
|
||||||
QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a");
|
QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a");
|
||||||
plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51"));
|
plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51"));
|
||||||
|
bool ok;
|
||||||
|
|
||||||
SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt, key, iv);
|
SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt);
|
||||||
|
QVERIFY(cipher.init(key, iv));
|
||||||
QCOMPARE(cipher.blockSize(), 16);
|
QCOMPARE(cipher.blockSize(), 16);
|
||||||
|
|
||||||
QCOMPARE(cipher.process(cipherText),
|
QCOMPARE(cipher.process(cipherText, &ok),
|
||||||
plainText);
|
plainText);
|
||||||
|
QVERIFY(ok);
|
||||||
|
|
||||||
// padded with 16 0x16 bytes
|
// padded with 16 0x16 bytes
|
||||||
QByteArray cipherTextPadded = cipherText + QByteArray::fromHex("3a3aa5e0213db1a9901f9036cf5102d2");
|
QByteArray cipherTextPadded = cipherText + QByteArray::fromHex("3a3aa5e0213db1a9901f9036cf5102d2");
|
||||||
QBuffer buffer(&cipherTextPadded);
|
QBuffer buffer(&cipherTextPadded);
|
||||||
SymmetricCipherStream stream(&buffer, SymmetricCipher::Aes256, SymmetricCipher::Cbc,
|
SymmetricCipherStream stream(&buffer, SymmetricCipher::Aes256, SymmetricCipher::Cbc,
|
||||||
SymmetricCipher::Decrypt, key, iv);
|
SymmetricCipher::Decrypt);
|
||||||
|
QVERIFY(stream.init(key, iv));
|
||||||
buffer.open(QIODevice::ReadOnly);
|
buffer.open(QIODevice::ReadOnly);
|
||||||
stream.open(QIODevice::ReadOnly);
|
stream.open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
@ -123,16 +130,20 @@ void TestSymmetricCipher::testSalsa20()
|
|||||||
|
|
||||||
QByteArray key = QByteArray::fromHex("F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112");
|
QByteArray key = QByteArray::fromHex("F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112");
|
||||||
QByteArray iv = QByteArray::fromHex("0000000000000000");
|
QByteArray iv = QByteArray::fromHex("0000000000000000");
|
||||||
|
bool ok;
|
||||||
|
|
||||||
SymmetricCipher cipher(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt, key, iv);
|
SymmetricCipher cipher(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt);
|
||||||
|
QVERIFY(cipher.init(key, iv));
|
||||||
|
|
||||||
QByteArray cipherTextA;
|
QByteArray cipherTextA;
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
cipherTextA.append(cipher.process(QByteArray(64, '\0')));
|
cipherTextA.append(cipher.process(QByteArray(64, '\0'), &ok));
|
||||||
|
QVERIFY(ok);
|
||||||
}
|
}
|
||||||
cipher.reset();
|
cipher.reset();
|
||||||
|
|
||||||
QByteArray cipherTextB = cipher.process(QByteArray(512, '\0'));
|
QByteArray cipherTextB = cipher.process(QByteArray(512, '\0'), &ok);
|
||||||
|
QVERIFY(ok);
|
||||||
cipher.reset();
|
cipher.reset();
|
||||||
|
|
||||||
QByteArray expectedCipherText1;
|
QByteArray expectedCipherText1;
|
||||||
@ -180,7 +191,8 @@ void TestSymmetricCipher::testPadding()
|
|||||||
buffer.open(QIODevice::ReadWrite);
|
buffer.open(QIODevice::ReadWrite);
|
||||||
|
|
||||||
SymmetricCipherStream streamEnc(&buffer, SymmetricCipher::Aes256, SymmetricCipher::Cbc,
|
SymmetricCipherStream streamEnc(&buffer, SymmetricCipher::Aes256, SymmetricCipher::Cbc,
|
||||||
SymmetricCipher::Encrypt, key, iv);
|
SymmetricCipher::Encrypt);
|
||||||
|
QVERIFY(streamEnc.init(key, iv));
|
||||||
streamEnc.open(QIODevice::WriteOnly);
|
streamEnc.open(QIODevice::WriteOnly);
|
||||||
streamEnc.write(plainText);
|
streamEnc.write(plainText);
|
||||||
streamEnc.close();
|
streamEnc.close();
|
||||||
@ -189,7 +201,8 @@ void TestSymmetricCipher::testPadding()
|
|||||||
QCOMPARE(buffer.buffer().size(), 16);
|
QCOMPARE(buffer.buffer().size(), 16);
|
||||||
|
|
||||||
SymmetricCipherStream streamDec(&buffer, SymmetricCipher::Aes256, SymmetricCipher::Cbc,
|
SymmetricCipherStream streamDec(&buffer, SymmetricCipher::Aes256, SymmetricCipher::Cbc,
|
||||||
SymmetricCipher::Decrypt, key, iv);
|
SymmetricCipher::Decrypt);
|
||||||
|
QVERIFY(streamDec.init(key, iv));
|
||||||
streamDec.open(QIODevice::ReadOnly);
|
streamDec.open(QIODevice::ReadOnly);
|
||||||
QByteArray decrypted = streamDec.readAll();
|
QByteArray decrypted = streamDec.readAll();
|
||||||
QCOMPARE(decrypted, plainText);
|
QCOMPARE(decrypted, plainText);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user