mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Add support for various algorithms for kdbx4
* Add SHA512 support to CryptoHash * Add ChaCha20 support * Add HMAC support * Add new HmacBlockStream, used in KDBX 4 * Add support for ChaCha20 protected stream
This commit is contained in:
parent
4532108678
commit
6a0d05e1ef
@ -262,7 +262,7 @@ set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_NONE QT_NO_DEBUG)
|
|||||||
|
|
||||||
find_package(LibGPGError REQUIRED)
|
find_package(LibGPGError REQUIRED)
|
||||||
|
|
||||||
find_package(Gcrypt 1.6.0 REQUIRED)
|
find_package(Gcrypt 1.7.0 REQUIRED)
|
||||||
|
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
|
|
||||||
|
@ -148,6 +148,7 @@ set(keepassx_SOURCES
|
|||||||
keys/PasswordKey.cpp
|
keys/PasswordKey.cpp
|
||||||
keys/YkChallengeResponseKey.cpp
|
keys/YkChallengeResponseKey.cpp
|
||||||
streams/HashedBlockStream.cpp
|
streams/HashedBlockStream.cpp
|
||||||
|
streams/HmacBlockStream.cpp
|
||||||
streams/LayeredStream.cpp
|
streams/LayeredStream.cpp
|
||||||
streams/qtiocompressor.cpp
|
streams/qtiocompressor.cpp
|
||||||
streams/StoreDataStream.cpp
|
streams/StoreDataStream.cpp
|
||||||
|
@ -95,18 +95,28 @@ bool Crypto::checkAlgorithms()
|
|||||||
qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr));
|
qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (gcry_cipher_algo_info(GCRY_CIPHER_CHACHA20, GCRYCTL_TEST_ALGO, nullptr, nullptr) != 0) {
|
||||||
|
m_errorStr = "GCRY_CIPHER_CHACHA20 not found.";
|
||||||
|
qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (gcry_md_test_algo(GCRY_MD_SHA256) != 0) {
|
if (gcry_md_test_algo(GCRY_MD_SHA256) != 0) {
|
||||||
m_errorStr = "GCRY_MD_SHA256 not found.";
|
m_errorStr = "GCRY_MD_SHA256 not found.";
|
||||||
qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr));
|
qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (gcry_md_test_algo(GCRY_MD_SHA512) != 0) {
|
||||||
|
m_errorStr = "GCRY_MD_SHA512 not found.";
|
||||||
|
qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Crypto::selfTest()
|
bool Crypto::selfTest()
|
||||||
{
|
{
|
||||||
return testSha256() && testAes256Cbc() && testAes256Ecb() && testTwofish() && testSalsa20();
|
return testSha256() && testSha512() && testAes256Cbc() && testAes256Ecb() && testTwofish() && testSalsa20() && testChaCha20();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Crypto::raiseError(const QString& str)
|
void Crypto::raiseError(const QString& str)
|
||||||
@ -128,6 +138,19 @@ bool Crypto::testSha256()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Crypto::testSha512()
|
||||||
|
{
|
||||||
|
QByteArray sha512Test = CryptoHash::hash("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
|
||||||
|
CryptoHash::Sha512);
|
||||||
|
|
||||||
|
if (sha512Test != QByteArray::fromHex("204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445")) {
|
||||||
|
raiseError("SHA-512 mismatch.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Crypto::testAes256Cbc()
|
bool Crypto::testAes256Cbc()
|
||||||
{
|
{
|
||||||
QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
|
QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
|
||||||
@ -285,3 +308,30 @@ bool Crypto::testSalsa20()
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Crypto::testChaCha20() {
|
||||||
|
QByteArray chacha20Key = QByteArray::fromHex("0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
QByteArray chacha20iv = QByteArray::fromHex("0000000000000000");
|
||||||
|
QByteArray chacha20Plain = QByteArray::fromHex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
QByteArray chacha20Cipher = QByteArray::fromHex("76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586");
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
SymmetricCipher chacha20Stream(SymmetricCipher::ChaCha20, SymmetricCipher::Stream,
|
||||||
|
SymmetricCipher::Encrypt);
|
||||||
|
if (!chacha20Stream.init(chacha20Key, chacha20iv)) {
|
||||||
|
raiseError(chacha20Stream.errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray chacha20Processed = chacha20Stream.process(chacha20Plain, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
raiseError(chacha20Stream.errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (chacha20Processed != chacha20Cipher) {
|
||||||
|
raiseError("ChaCha20 stream cipher mismatch.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
@ -35,10 +35,12 @@ private:
|
|||||||
static bool selfTest();
|
static bool selfTest();
|
||||||
static void raiseError(const QString& str);
|
static void raiseError(const QString& str);
|
||||||
static bool testSha256();
|
static bool testSha256();
|
||||||
|
static bool testSha512();
|
||||||
static bool testAes256Cbc();
|
static bool testAes256Cbc();
|
||||||
static bool testAes256Ecb();
|
static bool testAes256Ecb();
|
||||||
static bool testTwofish();
|
static bool testTwofish();
|
||||||
static bool testSalsa20();
|
static bool testSalsa20();
|
||||||
|
static bool testChaCha20();
|
||||||
|
|
||||||
static bool m_initalized;
|
static bool m_initalized;
|
||||||
static QString m_errorStr;
|
static QString m_errorStr;
|
||||||
|
@ -29,27 +29,42 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
CryptoHash::CryptoHash(CryptoHash::Algorithm algo)
|
CryptoHash::CryptoHash(CryptoHash::Algorithm algo)
|
||||||
|
: CryptoHash::CryptoHash(algo, false) {}
|
||||||
|
|
||||||
|
CryptoHash::CryptoHash(CryptoHash::Algorithm algo, bool hmac)
|
||||||
: d_ptr(new CryptoHashPrivate())
|
: d_ptr(new CryptoHashPrivate())
|
||||||
{
|
{
|
||||||
Q_D(CryptoHash);
|
Q_D(CryptoHash);
|
||||||
|
|
||||||
Q_ASSERT(Crypto::initalized());
|
Q_ASSERT(Crypto::initalized());
|
||||||
|
|
||||||
int algoGcrypt;
|
int algoGcrypt = -1;
|
||||||
|
unsigned int flagsGcrypt = 0;
|
||||||
|
|
||||||
switch (algo) {
|
switch (algo) {
|
||||||
case CryptoHash::Sha256:
|
case CryptoHash::Sha256:
|
||||||
algoGcrypt = GCRY_MD_SHA256;
|
algoGcrypt = GCRY_MD_SHA256;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CryptoHash::Sha512:
|
||||||
|
algoGcrypt = GCRY_MD_SHA512;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
gcry_error_t error = gcry_md_open(&d->ctx, algoGcrypt, 0);
|
if (hmac) {
|
||||||
|
flagsGcrypt |= GCRY_MD_FLAG_HMAC;
|
||||||
|
}
|
||||||
|
|
||||||
|
gcry_error_t error = gcry_md_open(&d->ctx, algoGcrypt, flagsGcrypt);
|
||||||
|
if (error) {
|
||||||
|
qWarning("Gcrypt error (ctor): %s", gcry_strerror(error));
|
||||||
|
qWarning("Gcrypt error (ctor): %s", gcry_strsource(error));
|
||||||
|
}
|
||||||
Q_ASSERT(error == 0); // TODO: error handling
|
Q_ASSERT(error == 0); // TODO: error handling
|
||||||
Q_UNUSED(error);
|
|
||||||
|
|
||||||
d->hashLen = gcry_md_get_algo_dlen(algoGcrypt);
|
d->hashLen = gcry_md_get_algo_dlen(algoGcrypt);
|
||||||
}
|
}
|
||||||
@ -74,6 +89,18 @@ void CryptoHash::addData(const QByteArray& data)
|
|||||||
gcry_md_write(d->ctx, data.constData(), data.size());
|
gcry_md_write(d->ctx, data.constData(), data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CryptoHash::setKey(const QByteArray& data)
|
||||||
|
{
|
||||||
|
Q_D(CryptoHash);
|
||||||
|
|
||||||
|
gcry_error_t error = gcry_md_setkey(d->ctx, data.constData(), data.size());
|
||||||
|
if (error) {
|
||||||
|
qWarning("Gcrypt error (setKey): %s", gcry_strerror(error));
|
||||||
|
qWarning("Gcrypt error (setKey): %s", gcry_strsource(error));
|
||||||
|
}
|
||||||
|
Q_ASSERT(error == 0);
|
||||||
|
}
|
||||||
|
|
||||||
void CryptoHash::reset()
|
void CryptoHash::reset()
|
||||||
{
|
{
|
||||||
Q_D(CryptoHash);
|
Q_D(CryptoHash);
|
||||||
@ -96,3 +123,12 @@ QByteArray CryptoHash::hash(const QByteArray& data, CryptoHash::Algorithm algo)
|
|||||||
cryptoHash.addData(data);
|
cryptoHash.addData(data);
|
||||||
return cryptoHash.result();
|
return cryptoHash.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray CryptoHash::hmac(const QByteArray& data, const QByteArray& key, CryptoHash::Algorithm algo)
|
||||||
|
{
|
||||||
|
// replace with gcry_md_hash_buffer()?
|
||||||
|
CryptoHash cryptoHash(algo, true);
|
||||||
|
cryptoHash.setKey(key);
|
||||||
|
cryptoHash.addData(data);
|
||||||
|
return cryptoHash.result();
|
||||||
|
}
|
||||||
|
@ -27,16 +27,20 @@ class CryptoHash
|
|||||||
public:
|
public:
|
||||||
enum Algorithm
|
enum Algorithm
|
||||||
{
|
{
|
||||||
Sha256
|
Sha256,
|
||||||
|
Sha512
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit CryptoHash(CryptoHash::Algorithm algo);
|
explicit CryptoHash(CryptoHash::Algorithm algo);
|
||||||
|
explicit CryptoHash(CryptoHash::Algorithm algo, bool hmac);
|
||||||
~CryptoHash();
|
~CryptoHash();
|
||||||
void addData(const QByteArray& data);
|
void addData(const QByteArray& data);
|
||||||
void reset();
|
void reset();
|
||||||
QByteArray result() const;
|
QByteArray result() const;
|
||||||
|
void setKey(const QByteArray& data);
|
||||||
|
|
||||||
static QByteArray hash(const QByteArray& data, CryptoHash::Algorithm algo);
|
static QByteArray hash(const QByteArray& data, CryptoHash::Algorithm algo);
|
||||||
|
static QByteArray hmac(const QByteArray& data, const QByteArray& key, Algorithm algo);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CryptoHashPrivate* const d_ptr;
|
CryptoHashPrivate* const d_ptr;
|
||||||
|
@ -24,6 +24,7 @@ SymmetricCipher::SymmetricCipher(SymmetricCipher::Algorithm algo, SymmetricCiphe
|
|||||||
SymmetricCipher::Direction direction)
|
SymmetricCipher::Direction direction)
|
||||||
: m_backend(createBackend(algo, mode, direction))
|
: m_backend(createBackend(algo, mode, direction))
|
||||||
, m_initialized(false)
|
, m_initialized(false)
|
||||||
|
, m_algo(algo)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,6 +62,7 @@ SymmetricCipherBackend* SymmetricCipher::createBackend(SymmetricCipher::Algorith
|
|||||||
case SymmetricCipher::Aes256:
|
case SymmetricCipher::Aes256:
|
||||||
case SymmetricCipher::Twofish:
|
case SymmetricCipher::Twofish:
|
||||||
case SymmetricCipher::Salsa20:
|
case SymmetricCipher::Salsa20:
|
||||||
|
case SymmetricCipher::ChaCha20:
|
||||||
return new SymmetricCipherGcrypt(algo, mode, direction);
|
return new SymmetricCipherGcrypt(algo, mode, direction);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -93,10 +95,14 @@ SymmetricCipher::Algorithm SymmetricCipher::cipherToAlgorithm(Uuid cipher)
|
|||||||
{
|
{
|
||||||
if (cipher == KeePass2::CIPHER_AES) {
|
if (cipher == KeePass2::CIPHER_AES) {
|
||||||
return SymmetricCipher::Aes256;
|
return SymmetricCipher::Aes256;
|
||||||
}
|
} else if (cipher == KeePass2::CIPHER_CHACHA20) {
|
||||||
else {
|
return SymmetricCipher::ChaCha20;
|
||||||
|
} else if (cipher == KeePass2::CIPHER_TWOFISH) {
|
||||||
return SymmetricCipher::Twofish;
|
return SymmetricCipher::Twofish;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qWarning("SymmetricCipher::cipherToAlgorithm: invalid Uuid %s", cipher.toByteArray().toHex().data());
|
||||||
|
return InvalidAlgorithm;
|
||||||
}
|
}
|
||||||
|
|
||||||
Uuid SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm algo)
|
Uuid SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm algo)
|
||||||
@ -104,7 +110,42 @@ Uuid SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm algo)
|
|||||||
switch (algo) {
|
switch (algo) {
|
||||||
case SymmetricCipher::Aes256:
|
case SymmetricCipher::Aes256:
|
||||||
return KeePass2::CIPHER_AES;
|
return KeePass2::CIPHER_AES;
|
||||||
default:
|
case SymmetricCipher::ChaCha20:
|
||||||
|
return KeePass2::CIPHER_CHACHA20;
|
||||||
|
case SymmetricCipher::Twofish:
|
||||||
return KeePass2::CIPHER_TWOFISH;
|
return KeePass2::CIPHER_TWOFISH;
|
||||||
|
default:
|
||||||
|
qWarning("SymmetricCipher::algorithmToCipher: invalid algorithm %d", algo);
|
||||||
|
return Uuid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SymmetricCipher::algorithmIvSize(SymmetricCipher::Algorithm algo) {
|
||||||
|
switch (algo) {
|
||||||
|
case SymmetricCipher::ChaCha20:
|
||||||
|
return 12;
|
||||||
|
case SymmetricCipher::Aes256:
|
||||||
|
case SymmetricCipher::Twofish:
|
||||||
|
return 16;
|
||||||
|
default:
|
||||||
|
qWarning("SymmetricCipher::algorithmIvSize: invalid algorithm %d", algo);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SymmetricCipher::Mode SymmetricCipher::algorithmMode(SymmetricCipher::Algorithm algo) {
|
||||||
|
switch (algo) {
|
||||||
|
case SymmetricCipher::ChaCha20:
|
||||||
|
return SymmetricCipher::Stream;
|
||||||
|
case SymmetricCipher::Aes256:
|
||||||
|
case SymmetricCipher::Twofish:
|
||||||
|
return SymmetricCipher::Cbc;
|
||||||
|
default:
|
||||||
|
qWarning("SymmetricCipher::algorithmMode: invalid algorithm %d", algo);
|
||||||
|
return SymmetricCipher::InvalidMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SymmetricCipher::Algorithm SymmetricCipher::algorithm() const {
|
||||||
|
return m_algo;
|
||||||
|
}
|
||||||
|
@ -33,7 +33,9 @@ public:
|
|||||||
{
|
{
|
||||||
Aes256,
|
Aes256,
|
||||||
Twofish,
|
Twofish,
|
||||||
Salsa20
|
Salsa20,
|
||||||
|
ChaCha20,
|
||||||
|
InvalidAlgorithm = -1
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Mode
|
enum Mode
|
||||||
@ -41,7 +43,8 @@ public:
|
|||||||
Cbc,
|
Cbc,
|
||||||
Ctr,
|
Ctr,
|
||||||
Ecb,
|
Ecb,
|
||||||
Stream
|
Stream,
|
||||||
|
InvalidMode = -1
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Direction
|
enum Direction
|
||||||
@ -74,9 +77,12 @@ public:
|
|||||||
int keySize() const;
|
int keySize() const;
|
||||||
int blockSize() const;
|
int blockSize() const;
|
||||||
QString errorString() const;
|
QString errorString() const;
|
||||||
|
Algorithm algorithm() const;
|
||||||
|
|
||||||
static SymmetricCipher::Algorithm cipherToAlgorithm(Uuid cipher);
|
static Algorithm cipherToAlgorithm(Uuid cipher);
|
||||||
static Uuid algorithmToCipher(SymmetricCipher::Algorithm algo);
|
static Uuid algorithmToCipher(Algorithm algo);
|
||||||
|
static int algorithmIvSize(Algorithm algo);
|
||||||
|
static Mode algorithmMode(Algorithm algo);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static SymmetricCipherBackend* createBackend(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
static SymmetricCipherBackend* createBackend(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
||||||
@ -84,6 +90,7 @@ private:
|
|||||||
|
|
||||||
const QScopedPointer<SymmetricCipherBackend> m_backend;
|
const QScopedPointer<SymmetricCipherBackend> m_backend;
|
||||||
bool m_initialized;
|
bool m_initialized;
|
||||||
|
Algorithm m_algo;
|
||||||
|
|
||||||
Q_DISABLE_COPY(SymmetricCipher)
|
Q_DISABLE_COPY(SymmetricCipher)
|
||||||
};
|
};
|
||||||
|
@ -46,6 +46,9 @@ int SymmetricCipherGcrypt::gcryptAlgo(SymmetricCipher::Algorithm algo)
|
|||||||
case SymmetricCipher::Salsa20:
|
case SymmetricCipher::Salsa20:
|
||||||
return GCRY_CIPHER_SALSA20;
|
return GCRY_CIPHER_SALSA20;
|
||||||
|
|
||||||
|
case SymmetricCipher::ChaCha20:
|
||||||
|
return GCRY_CIPHER_CHACHA20;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
const Uuid KeePass2::CIPHER_AES = Uuid(QByteArray::fromHex("31c1f2e6bf714350be5805216afc5aff"));
|
const Uuid KeePass2::CIPHER_AES = Uuid(QByteArray::fromHex("31c1f2e6bf714350be5805216afc5aff"));
|
||||||
const Uuid KeePass2::CIPHER_TWOFISH = Uuid(QByteArray::fromHex("ad68f29f576f4bb9a36ad47af965346c"));
|
const Uuid KeePass2::CIPHER_TWOFISH = Uuid(QByteArray::fromHex("ad68f29f576f4bb9a36ad47af965346c"));
|
||||||
|
const Uuid KeePass2::CIPHER_CHACHA20 = Uuid(QByteArray::fromHex("D6038A2B8B6F4CB5A524339A31DBB59A"));
|
||||||
|
|
||||||
const Uuid KeePass2::KDF_AES = Uuid(QByteArray::fromHex("C9D9F39A628A4460BF740D08C18A4FEA"));
|
const Uuid KeePass2::KDF_AES = Uuid(QByteArray::fromHex("C9D9F39A628A4460BF740D08C18A4FEA"));
|
||||||
|
|
||||||
@ -30,6 +31,7 @@ const QByteArray KeePass2::INNER_STREAM_SALSA20_IV("\xE8\x30\x09\x4B\x97\x20\x5D
|
|||||||
const QList<KeePass2::UuidNamePair> KeePass2::CIPHERS {
|
const QList<KeePass2::UuidNamePair> KeePass2::CIPHERS {
|
||||||
KeePass2::UuidNamePair(KeePass2::CIPHER_AES, "AES: 256-bit"),
|
KeePass2::UuidNamePair(KeePass2::CIPHER_AES, "AES: 256-bit"),
|
||||||
KeePass2::UuidNamePair(KeePass2::CIPHER_TWOFISH, "Twofish: 256-bit"),
|
KeePass2::UuidNamePair(KeePass2::CIPHER_TWOFISH, "Twofish: 256-bit"),
|
||||||
|
KeePass2::UuidNamePair(KeePass2::CIPHER_CHACHA20, "ChaCha20: 256-bit")
|
||||||
};
|
};
|
||||||
const QList<KeePass2::UuidNamePair> KeePass2::KDFS {
|
const QList<KeePass2::UuidNamePair> KeePass2::KDFS {
|
||||||
KeePass2::UuidNamePair(KeePass2::KDF_AES, "AES-KDF"),
|
KeePass2::UuidNamePair(KeePass2::KDF_AES, "AES-KDF"),
|
||||||
@ -53,6 +55,20 @@ Uuid KeePass2::kdfToUuid(const Kdf& kdf)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeePass2::ProtectedStreamAlgo KeePass2::idToProtectedStreamAlgo(quint32 id)
|
||||||
|
{
|
||||||
|
switch (id) {
|
||||||
|
case static_cast<quint32>(KeePass2::ArcFourVariant):
|
||||||
|
return KeePass2::ArcFourVariant;
|
||||||
|
case static_cast<quint32>(KeePass2::Salsa20):
|
||||||
|
return KeePass2::Salsa20;
|
||||||
|
case static_cast<quint32>(KeePass2::ChaCha20):
|
||||||
|
return KeePass2::ChaCha20;
|
||||||
|
default:
|
||||||
|
return KeePass2::InvalidProtectedStreamAlgo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
KeePass2::UuidNamePair::UuidNamePair(const Uuid& uuid, const QString& name)
|
KeePass2::UuidNamePair::UuidNamePair(const Uuid& uuid, const QString& name)
|
||||||
: m_uuid(uuid)
|
: m_uuid(uuid)
|
||||||
, m_name(name)
|
, m_name(name)
|
||||||
|
@ -37,6 +37,7 @@ namespace KeePass2
|
|||||||
|
|
||||||
extern const Uuid CIPHER_AES;
|
extern const Uuid CIPHER_AES;
|
||||||
extern const Uuid CIPHER_TWOFISH;
|
extern const Uuid CIPHER_TWOFISH;
|
||||||
|
extern const Uuid CIPHER_CHACHA20;
|
||||||
|
|
||||||
extern const Uuid KDF_AES;
|
extern const Uuid KDF_AES;
|
||||||
|
|
||||||
@ -75,11 +76,14 @@ namespace KeePass2
|
|||||||
enum ProtectedStreamAlgo
|
enum ProtectedStreamAlgo
|
||||||
{
|
{
|
||||||
ArcFourVariant = 1,
|
ArcFourVariant = 1,
|
||||||
Salsa20 = 2
|
Salsa20 = 2,
|
||||||
|
ChaCha20 = 3,
|
||||||
|
InvalidProtectedStreamAlgo = -1
|
||||||
};
|
};
|
||||||
|
|
||||||
Kdf* uuidToKdf(const Uuid& uuid);
|
Kdf* uuidToKdf(const Uuid& uuid);
|
||||||
Uuid kdfToUuid(const Kdf& kdf);
|
Uuid kdfToUuid(const Kdf& kdf);
|
||||||
|
ProtectedStreamAlgo idToProtectedStreamAlgo(quint32 id);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // KEEPASSX_KEEPASS2_H
|
#endif // KEEPASSX_KEEPASS2_H
|
||||||
|
@ -20,16 +20,27 @@
|
|||||||
#include "crypto/CryptoHash.h"
|
#include "crypto/CryptoHash.h"
|
||||||
#include "format/KeePass2.h"
|
#include "format/KeePass2.h"
|
||||||
|
|
||||||
KeePass2RandomStream::KeePass2RandomStream()
|
KeePass2RandomStream::KeePass2RandomStream(KeePass2::ProtectedStreamAlgo algo)
|
||||||
: m_cipher(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt)
|
: m_cipher(mapAlgo(algo), SymmetricCipher::Stream, SymmetricCipher::Encrypt)
|
||||||
, m_offset(0)
|
, m_offset(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KeePass2RandomStream::init(const QByteArray& key)
|
bool KeePass2RandomStream::init(const QByteArray& key)
|
||||||
{
|
{
|
||||||
return m_cipher.init(CryptoHash::hash(key, CryptoHash::Sha256),
|
switch (m_cipher.algorithm()) {
|
||||||
KeePass2::INNER_STREAM_SALSA20_IV);
|
case SymmetricCipher::Salsa20:
|
||||||
|
return m_cipher.init(CryptoHash::hash(key, CryptoHash::Sha256),
|
||||||
|
KeePass2::INNER_STREAM_SALSA20_IV);
|
||||||
|
case SymmetricCipher::ChaCha20: {
|
||||||
|
QByteArray keyIv = CryptoHash::hash(key, CryptoHash::Sha512);
|
||||||
|
return m_cipher.init(keyIv.left(32), keyIv.mid(32, 12));
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
qWarning("Invalid stream algorithm (%d)", m_cipher.algorithm());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray KeePass2RandomStream::randomBytes(int size, bool* ok)
|
QByteArray KeePass2RandomStream::randomBytes(int size, bool* ok)
|
||||||
@ -109,3 +120,14 @@ bool KeePass2RandomStream::loadBlock()
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SymmetricCipher::Algorithm KeePass2RandomStream::mapAlgo(KeePass2::ProtectedStreamAlgo algo) {
|
||||||
|
switch (algo) {
|
||||||
|
case KeePass2::ChaCha20:
|
||||||
|
return SymmetricCipher::ChaCha20;
|
||||||
|
case KeePass2::Salsa20:
|
||||||
|
return SymmetricCipher::Salsa20;
|
||||||
|
default:
|
||||||
|
return SymmetricCipher::InvalidAlgorithm;
|
||||||
|
}
|
||||||
|
}
|
@ -21,11 +21,13 @@
|
|||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
|
||||||
#include "crypto/SymmetricCipher.h"
|
#include "crypto/SymmetricCipher.h"
|
||||||
|
#include "KeePass2.h"
|
||||||
|
|
||||||
class KeePass2RandomStream
|
class KeePass2RandomStream
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KeePass2RandomStream();
|
KeePass2RandomStream(KeePass2::ProtectedStreamAlgo algo);
|
||||||
|
|
||||||
bool init(const QByteArray& key);
|
bool init(const QByteArray& key);
|
||||||
QByteArray randomBytes(int size, bool* ok);
|
QByteArray randomBytes(int size, bool* ok);
|
||||||
QByteArray process(const QByteArray& data, bool* ok);
|
QByteArray process(const QByteArray& data, bool* ok);
|
||||||
@ -38,6 +40,8 @@ private:
|
|||||||
SymmetricCipher m_cipher;
|
SymmetricCipher m_cipher;
|
||||||
QByteArray m_buffer;
|
QByteArray m_buffer;
|
||||||
int m_offset;
|
int m_offset;
|
||||||
|
|
||||||
|
static SymmetricCipher::Algorithm mapAlgo(KeePass2::ProtectedStreamAlgo algo);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_KEEPASS2RANDOMSTREAM_H
|
#endif // KEEPASSX_KEEPASS2RANDOMSTREAM_H
|
||||||
|
@ -41,6 +41,7 @@ KeePass2Reader::KeePass2Reader()
|
|||||||
, m_headerEnd(false)
|
, m_headerEnd(false)
|
||||||
, m_saveXml(false)
|
, m_saveXml(false)
|
||||||
, m_db(nullptr)
|
, m_db(nullptr)
|
||||||
|
, m_irsAlgo(KeePass2::InvalidProtectedStreamAlgo)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +165,7 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
|
|||||||
xmlDevice = ioCompressor.data();
|
xmlDevice = ioCompressor.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
KeePass2RandomStream randomStream;
|
KeePass2RandomStream randomStream(m_irsAlgo);
|
||||||
if (!randomStream.init(m_protectedStreamKey)) {
|
if (!randomStream.init(m_protectedStreamKey)) {
|
||||||
raiseError(randomStream.errorString());
|
raiseError(randomStream.errorString());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -447,9 +448,14 @@ void KeePass2Reader::setInnerRandomStreamID(const QByteArray& data)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
quint32 id = Endian::bytesToUInt32(data, KeePass2::BYTEORDER);
|
quint32 id = Endian::bytesToUInt32(data, KeePass2::BYTEORDER);
|
||||||
|
m_irsAlgo = KeePass2::idToProtectedStreamAlgo(id);
|
||||||
if (id != KeePass2::Salsa20) {
|
if (m_irsAlgo == KeePass2::ArcFourVariant || m_irsAlgo == KeePass2::InvalidProtectedStreamAlgo) {
|
||||||
raiseError("Unsupported random stream algorithm");
|
raiseError("Unsupported random stream algorithm");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeePass2::ProtectedStreamAlgo KeePass2Reader::protectedStreamAlgo() const
|
||||||
|
{
|
||||||
|
return m_irsAlgo;
|
||||||
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
|
||||||
#include "keys/CompositeKey.h"
|
#include "keys/CompositeKey.h"
|
||||||
|
#include "format/KeePass2.h"
|
||||||
|
|
||||||
class Database;
|
class Database;
|
||||||
class QIODevice;
|
class QIODevice;
|
||||||
@ -38,6 +39,7 @@ public:
|
|||||||
void setSaveXml(bool save);
|
void setSaveXml(bool save);
|
||||||
QByteArray xmlData();
|
QByteArray xmlData();
|
||||||
QByteArray streamKey();
|
QByteArray streamKey();
|
||||||
|
KeePass2::ProtectedStreamAlgo protectedStreamAlgo() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void raiseError(const QString& errorMessage);
|
void raiseError(const QString& errorMessage);
|
||||||
@ -67,6 +69,7 @@ private:
|
|||||||
QByteArray m_encryptionIV;
|
QByteArray m_encryptionIV;
|
||||||
QByteArray m_streamStartBytes;
|
QByteArray m_streamStartBytes;
|
||||||
QByteArray m_protectedStreamKey;
|
QByteArray m_protectedStreamKey;
|
||||||
|
KeePass2::ProtectedStreamAlgo m_irsAlgo;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_KEEPASS2READER_H
|
#endif // KEEPASSX_KEEPASS2READER_H
|
||||||
|
@ -71,7 +71,7 @@ KeePass2Repair::RepairOutcome KeePass2Repair::repairDatabase(QIODevice* device,
|
|||||||
return qMakePair(RepairFailed, nullptr);
|
return qMakePair(RepairFailed, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
KeePass2RandomStream randomStream;
|
KeePass2RandomStream randomStream(reader.protectedStreamAlgo());
|
||||||
randomStream.init(reader.streamKey());
|
randomStream.init(reader.streamKey());
|
||||||
KeePass2XmlReader xmlReader;
|
KeePass2XmlReader xmlReader;
|
||||||
QBuffer buffer(&xmlData);
|
QBuffer buffer(&xmlData);
|
||||||
|
@ -131,7 +131,7 @@ void KeePass2Writer::writeDatabase(QIODevice* device, Database* db)
|
|||||||
m_device = ioCompressor.data();
|
m_device = ioCompressor.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
KeePass2RandomStream randomStream;
|
KeePass2RandomStream randomStream(KeePass2::Salsa20);
|
||||||
if (!randomStream.init(protectedStreamKey)) {
|
if (!randomStream.init(protectedStreamKey)) {
|
||||||
raiseError(randomStream.errorString());
|
raiseError(randomStream.errorString());
|
||||||
return;
|
return;
|
||||||
|
267
src/streams/HmacBlockStream.cpp
Normal file
267
src/streams/HmacBlockStream.cpp
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 KeePassXC Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "HmacBlockStream.h"
|
||||||
|
|
||||||
|
#include "core/Endian.h"
|
||||||
|
#include "crypto/CryptoHash.h"
|
||||||
|
|
||||||
|
const QSysInfo::Endian HmacBlockStream::ByteOrder = QSysInfo::LittleEndian;
|
||||||
|
|
||||||
|
HmacBlockStream::HmacBlockStream(QIODevice* baseDevice, QByteArray key)
|
||||||
|
: LayeredStream(baseDevice)
|
||||||
|
, m_blockSize(1024*1024)
|
||||||
|
, m_key(key)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
HmacBlockStream::HmacBlockStream(QIODevice* baseDevice, QByteArray key, qint32 blockSize)
|
||||||
|
: LayeredStream(baseDevice)
|
||||||
|
, m_blockSize(blockSize)
|
||||||
|
, m_key(key)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
HmacBlockStream::~HmacBlockStream()
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HmacBlockStream::init()
|
||||||
|
{
|
||||||
|
m_buffer.clear();
|
||||||
|
m_bufferPos = 0;
|
||||||
|
m_blockIndex = 0;
|
||||||
|
m_eof = false;
|
||||||
|
m_error = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HmacBlockStream::reset()
|
||||||
|
{
|
||||||
|
// Write final block(s) only if device is writable and we haven't
|
||||||
|
// already written a final block.
|
||||||
|
if (isWritable() && (!m_buffer.isEmpty() || m_blockIndex != 0)) {
|
||||||
|
if (!m_buffer.isEmpty()) {
|
||||||
|
if (!writeHashedBlock()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write empty final block
|
||||||
|
if (!writeHashedBlock()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HmacBlockStream::close()
|
||||||
|
{
|
||||||
|
// Write final block(s) only if device is writable and we haven't
|
||||||
|
// already written a final block.
|
||||||
|
if (isWritable() && (!m_buffer.isEmpty() || m_blockIndex != 0)) {
|
||||||
|
if (!m_buffer.isEmpty()) {
|
||||||
|
writeHashedBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
// write empty final block
|
||||||
|
writeHashedBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
LayeredStream::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 HmacBlockStream::readData(char* data, qint64 maxSize)
|
||||||
|
{
|
||||||
|
if (m_error) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else if (m_eof) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 bytesRemaining = maxSize;
|
||||||
|
qint64 offset = 0;
|
||||||
|
|
||||||
|
while (bytesRemaining > 0) {
|
||||||
|
if (m_bufferPos == m_buffer.size()) {
|
||||||
|
if (!readHashedBlock()) {
|
||||||
|
if (m_error) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return maxSize - bytesRemaining;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int bytesToCopy = qMin(bytesRemaining, static_cast<qint64>(m_buffer.size() - m_bufferPos));
|
||||||
|
|
||||||
|
memcpy(data + offset, m_buffer.constData() + m_bufferPos, bytesToCopy);
|
||||||
|
|
||||||
|
offset += bytesToCopy;
|
||||||
|
m_bufferPos += bytesToCopy;
|
||||||
|
bytesRemaining -= bytesToCopy;
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HmacBlockStream::readHashedBlock()
|
||||||
|
{
|
||||||
|
if (m_eof) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QByteArray hmac = m_baseDevice->read(32);
|
||||||
|
if (hmac.size() != 32) {
|
||||||
|
m_error = true;
|
||||||
|
setErrorString("Invalid HMAC size.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray blockSizeBytes = m_baseDevice->read(4);
|
||||||
|
if (blockSizeBytes.size() != 4) {
|
||||||
|
m_error = true;
|
||||||
|
setErrorString("Invalid block size size.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
qint32 blockSize = Endian::bytesToInt32(blockSizeBytes, ByteOrder);
|
||||||
|
if (blockSize < 0) {
|
||||||
|
m_error = true;
|
||||||
|
setErrorString("Invalid block size.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_buffer = m_baseDevice->read(blockSize);
|
||||||
|
if (m_buffer.size() != blockSize) {
|
||||||
|
m_error = true;
|
||||||
|
setErrorString("Block too short.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoHash hasher(CryptoHash::Sha256, true);
|
||||||
|
hasher.setKey(getCurrentHmacKey());
|
||||||
|
hasher.addData(Endian::uint64ToBytes(m_blockIndex, ByteOrder));
|
||||||
|
hasher.addData(blockSizeBytes);
|
||||||
|
hasher.addData(m_buffer);
|
||||||
|
|
||||||
|
if (hmac != hasher.result()) {
|
||||||
|
m_error = true;
|
||||||
|
setErrorString("Mismatch between hash and data.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_bufferPos = 0;
|
||||||
|
m_blockIndex++;
|
||||||
|
|
||||||
|
if (blockSize == 0) {
|
||||||
|
m_eof = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 HmacBlockStream::writeData(const char* data, qint64 maxSize)
|
||||||
|
{
|
||||||
|
Q_ASSERT(maxSize >= 0);
|
||||||
|
|
||||||
|
if (m_error) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 bytesRemaining = maxSize;
|
||||||
|
qint64 offset = 0;
|
||||||
|
|
||||||
|
while (bytesRemaining > 0) {
|
||||||
|
int bytesToCopy = qMin(bytesRemaining, static_cast<qint64>(m_blockSize - m_buffer.size()));
|
||||||
|
|
||||||
|
m_buffer.append(data + offset, bytesToCopy);
|
||||||
|
|
||||||
|
offset += bytesToCopy;
|
||||||
|
bytesRemaining -= bytesToCopy;
|
||||||
|
|
||||||
|
if (m_buffer.size() == m_blockSize) {
|
||||||
|
if (!writeHashedBlock()) {
|
||||||
|
if (m_error) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return maxSize - bytesRemaining;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HmacBlockStream::writeHashedBlock()
|
||||||
|
{
|
||||||
|
CryptoHash hasher(CryptoHash::Sha256, true);
|
||||||
|
hasher.setKey(getCurrentHmacKey());
|
||||||
|
hasher.addData(Endian::uint64ToBytes(m_blockIndex, ByteOrder));
|
||||||
|
hasher.addData(Endian::int32ToBytes(m_buffer.size(), ByteOrder));
|
||||||
|
hasher.addData(m_buffer);
|
||||||
|
QByteArray hash = hasher.result();
|
||||||
|
|
||||||
|
if (m_baseDevice->write(hash) != hash.size()) {
|
||||||
|
m_error = true;
|
||||||
|
setErrorString(m_baseDevice->errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Endian::writeInt32(m_buffer.size(), m_baseDevice, ByteOrder)) {
|
||||||
|
m_error = true;
|
||||||
|
setErrorString(m_baseDevice->errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_buffer.isEmpty()) {
|
||||||
|
if (m_baseDevice->write(m_buffer) != m_buffer.size()) {
|
||||||
|
m_error = true;
|
||||||
|
setErrorString(m_baseDevice->errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_buffer.clear();
|
||||||
|
}
|
||||||
|
m_blockIndex++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray HmacBlockStream::getCurrentHmacKey() const {
|
||||||
|
return getHmacKey(m_blockIndex, m_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray HmacBlockStream::getHmacKey(quint64 blockIndex, QByteArray key) {
|
||||||
|
Q_ASSERT(key.size() == 64);
|
||||||
|
QByteArray indexBytes = Endian::uint64ToBytes(blockIndex, ByteOrder);
|
||||||
|
CryptoHash hasher(CryptoHash::Sha512);
|
||||||
|
hasher.addData(indexBytes);
|
||||||
|
hasher.addData(key);
|
||||||
|
return hasher.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HmacBlockStream::atEnd() const {
|
||||||
|
return m_eof;
|
||||||
|
}
|
61
src/streams/HmacBlockStream.h
Normal file
61
src/streams/HmacBlockStream.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 KeePassXC Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KEEPASSX_HMACBLOCKSTREAM_H
|
||||||
|
#define KEEPASSX_HMACBLOCKSTREAM_H
|
||||||
|
|
||||||
|
#include <QSysInfo>
|
||||||
|
|
||||||
|
#include "streams/LayeredStream.h"
|
||||||
|
|
||||||
|
class HmacBlockStream : public LayeredStream
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit HmacBlockStream(QIODevice* baseDevice, QByteArray key);
|
||||||
|
HmacBlockStream(QIODevice* baseDevice, QByteArray key, qint32 blockSize);
|
||||||
|
~HmacBlockStream();
|
||||||
|
|
||||||
|
bool reset() override;
|
||||||
|
void close() override;
|
||||||
|
|
||||||
|
static QByteArray getHmacKey(quint64 blockIndex, QByteArray key);
|
||||||
|
|
||||||
|
bool atEnd() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
qint64 readData(char* data, qint64 maxSize) override;
|
||||||
|
qint64 writeData(const char* data, qint64 maxSize) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
|
bool readHashedBlock();
|
||||||
|
bool writeHashedBlock();
|
||||||
|
QByteArray getCurrentHmacKey() const;
|
||||||
|
|
||||||
|
static const QSysInfo::Endian ByteOrder;
|
||||||
|
qint32 m_blockSize;
|
||||||
|
QByteArray m_buffer;
|
||||||
|
QByteArray m_key;
|
||||||
|
int m_bufferPos;
|
||||||
|
quint64 m_blockIndex;
|
||||||
|
bool m_eof;
|
||||||
|
bool m_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEEPASSX_HMACBLOCKSTREAM_H
|
@ -44,4 +44,17 @@ void TestCryptoHash::test()
|
|||||||
cryptoHash3.addData(QString("ssX").toLatin1());
|
cryptoHash3.addData(QString("ssX").toLatin1());
|
||||||
QCOMPARE(cryptoHash3.result(),
|
QCOMPARE(cryptoHash3.result(),
|
||||||
QByteArray::fromHex("0b56e5f65263e747af4a833bd7dd7ad26a64d7a4de7c68e52364893dca0766b4"));
|
QByteArray::fromHex("0b56e5f65263e747af4a833bd7dd7ad26a64d7a4de7c68e52364893dca0766b4"));
|
||||||
|
|
||||||
|
CryptoHash cryptoHash2(CryptoHash::Sha512);
|
||||||
|
QCOMPARE(cryptoHash2.result(),
|
||||||
|
QByteArray::fromHex("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"));
|
||||||
|
|
||||||
|
QByteArray result3 = CryptoHash::hash(source2, CryptoHash::Sha512);
|
||||||
|
QCOMPARE(result3, QByteArray::fromHex("0d41b612584ed39ff72944c29494573e40f4bb95283455fae2e0be1e3565aa9f48057d59e6ffd777970e282871c25a549a2763e5b724794f312c97021c42f91d"));
|
||||||
|
|
||||||
|
CryptoHash cryptoHash4(CryptoHash::Sha512);
|
||||||
|
cryptoHash4.addData(QString("KeePa").toLatin1());
|
||||||
|
cryptoHash4.addData(QString("ssX").toLatin1());
|
||||||
|
QCOMPARE(cryptoHash4.result(),
|
||||||
|
QByteArray::fromHex("0d41b612584ed39ff72944c29494573e40f4bb95283455fae2e0be1e3565aa9f48057d59e6ffd777970e282871c25a549a2763e5b724794f312c97021c42f91d"));
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ void TestKeePass2RandomStream::test()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
KeePass2RandomStream randomStream;
|
KeePass2RandomStream randomStream(KeePass2::Salsa20);
|
||||||
bool ok;
|
bool ok;
|
||||||
QVERIFY(randomStream.init(key));
|
QVERIFY(randomStream.init(key));
|
||||||
QByteArray randomStreamData;
|
QByteArray randomStreamData;
|
||||||
|
@ -342,6 +342,56 @@ void TestSymmetricCipher::testSalsa20()
|
|||||||
QCOMPARE(cipherTextB.mid(448, 64), expectedCipherText4);
|
QCOMPARE(cipherTextB.mid(448, 64), expectedCipherText4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestSymmetricCipher::testChaCha20()
|
||||||
|
{
|
||||||
|
// https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
{
|
||||||
|
QByteArray key = QByteArray::fromHex("0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
QByteArray iv = QByteArray::fromHex("0000000000000000");
|
||||||
|
SymmetricCipher cipher(SymmetricCipher::ChaCha20, SymmetricCipher::Stream, SymmetricCipher::Encrypt);
|
||||||
|
QVERIFY(cipher.init(key, iv));
|
||||||
|
QCOMPARE(cipher.process(QByteArray(64, 0), &ok),
|
||||||
|
QByteArray::fromHex(
|
||||||
|
"76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"));
|
||||||
|
QVERIFY(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
QByteArray key = QByteArray::fromHex("0000000000000000000000000000000000000000000000000000000000000001");
|
||||||
|
QByteArray iv = QByteArray::fromHex("0000000000000000");
|
||||||
|
SymmetricCipher cipher(SymmetricCipher::ChaCha20, SymmetricCipher::Stream, SymmetricCipher::Encrypt);
|
||||||
|
QVERIFY(cipher.init(key, iv));
|
||||||
|
QCOMPARE(cipher.process(QByteArray(64, 0), &ok),
|
||||||
|
QByteArray::fromHex(
|
||||||
|
"4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d792b1c43fea817e9ad275ae546963"));
|
||||||
|
QVERIFY(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
QByteArray key = QByteArray::fromHex("0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
QByteArray iv = QByteArray::fromHex("0000000000000001");
|
||||||
|
SymmetricCipher cipher(SymmetricCipher::ChaCha20, SymmetricCipher::Stream, SymmetricCipher::Encrypt);
|
||||||
|
QVERIFY(cipher.init(key, iv));
|
||||||
|
QCOMPARE(cipher.process(QByteArray(60, 0), &ok),
|
||||||
|
QByteArray::fromHex(
|
||||||
|
"de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a050278a7084527214f73efc7fa5b5277062eb7a0433e445f41e3"));
|
||||||
|
QVERIFY(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
QByteArray key = QByteArray::fromHex("0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
QByteArray iv = QByteArray::fromHex("0100000000000000");
|
||||||
|
SymmetricCipher cipher(SymmetricCipher::ChaCha20, SymmetricCipher::Stream, SymmetricCipher::Encrypt);
|
||||||
|
QVERIFY(cipher.init(key, iv));
|
||||||
|
QCOMPARE(cipher.process(QByteArray(64, 0), &ok),
|
||||||
|
QByteArray::fromHex(
|
||||||
|
"ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc497a0b466e7d6bbdb0041b2f586b"));
|
||||||
|
QVERIFY(ok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TestSymmetricCipher::testPadding()
|
void TestSymmetricCipher::testPadding()
|
||||||
{
|
{
|
||||||
QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
|
QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
|
||||||
|
@ -34,6 +34,7 @@ private slots:
|
|||||||
void testTwofish256CbcEncryption();
|
void testTwofish256CbcEncryption();
|
||||||
void testTwofish256CbcDecryption();
|
void testTwofish256CbcDecryption();
|
||||||
void testSalsa20();
|
void testSalsa20();
|
||||||
|
void testChaCha20();
|
||||||
void testPadding();
|
void testPadding();
|
||||||
void testStreamReset();
|
void testStreamReset();
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user