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:
@@ -95,18 +95,28 @@ bool Crypto::checkAlgorithms()
|
||||
qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr));
|
||||
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) {
|
||||
m_errorStr = "GCRY_MD_SHA256 not found.";
|
||||
qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr));
|
||||
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;
|
||||
}
|
||||
|
||||
bool Crypto::selfTest()
|
||||
{
|
||||
return testSha256() && testAes256Cbc() && testAes256Ecb() && testTwofish() && testSalsa20();
|
||||
return testSha256() && testSha512() && testAes256Cbc() && testAes256Ecb() && testTwofish() && testSalsa20() && testChaCha20();
|
||||
}
|
||||
|
||||
void Crypto::raiseError(const QString& str)
|
||||
@@ -128,6 +138,19 @@ bool Crypto::testSha256()
|
||||
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()
|
||||
{
|
||||
QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
|
||||
@@ -285,3 +308,30 @@ bool Crypto::testSalsa20()
|
||||
|
||||
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 void raiseError(const QString& str);
|
||||
static bool testSha256();
|
||||
static bool testSha512();
|
||||
static bool testAes256Cbc();
|
||||
static bool testAes256Ecb();
|
||||
static bool testTwofish();
|
||||
static bool testSalsa20();
|
||||
static bool testChaCha20();
|
||||
|
||||
static bool m_initalized;
|
||||
static QString m_errorStr;
|
||||
|
||||
@@ -29,27 +29,42 @@ public:
|
||||
};
|
||||
|
||||
CryptoHash::CryptoHash(CryptoHash::Algorithm algo)
|
||||
: CryptoHash::CryptoHash(algo, false) {}
|
||||
|
||||
CryptoHash::CryptoHash(CryptoHash::Algorithm algo, bool hmac)
|
||||
: d_ptr(new CryptoHashPrivate())
|
||||
{
|
||||
Q_D(CryptoHash);
|
||||
|
||||
Q_ASSERT(Crypto::initalized());
|
||||
|
||||
int algoGcrypt;
|
||||
int algoGcrypt = -1;
|
||||
unsigned int flagsGcrypt = 0;
|
||||
|
||||
switch (algo) {
|
||||
case CryptoHash::Sha256:
|
||||
algoGcrypt = GCRY_MD_SHA256;
|
||||
break;
|
||||
|
||||
case CryptoHash::Sha512:
|
||||
algoGcrypt = GCRY_MD_SHA512;
|
||||
break;
|
||||
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
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_UNUSED(error);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
Q_D(CryptoHash);
|
||||
@@ -96,3 +123,12 @@ QByteArray CryptoHash::hash(const QByteArray& data, CryptoHash::Algorithm algo)
|
||||
cryptoHash.addData(data);
|
||||
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:
|
||||
enum Algorithm
|
||||
{
|
||||
Sha256
|
||||
Sha256,
|
||||
Sha512
|
||||
};
|
||||
|
||||
explicit CryptoHash(CryptoHash::Algorithm algo);
|
||||
explicit CryptoHash(CryptoHash::Algorithm algo, bool hmac);
|
||||
~CryptoHash();
|
||||
void addData(const QByteArray& data);
|
||||
void reset();
|
||||
QByteArray result() const;
|
||||
void setKey(const QByteArray& data);
|
||||
|
||||
static QByteArray hash(const QByteArray& data, CryptoHash::Algorithm algo);
|
||||
static QByteArray hmac(const QByteArray& data, const QByteArray& key, Algorithm algo);
|
||||
|
||||
private:
|
||||
CryptoHashPrivate* const d_ptr;
|
||||
|
||||
@@ -24,6 +24,7 @@ SymmetricCipher::SymmetricCipher(SymmetricCipher::Algorithm algo, SymmetricCiphe
|
||||
SymmetricCipher::Direction direction)
|
||||
: m_backend(createBackend(algo, mode, direction))
|
||||
, m_initialized(false)
|
||||
, m_algo(algo)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -61,6 +62,7 @@ SymmetricCipherBackend* SymmetricCipher::createBackend(SymmetricCipher::Algorith
|
||||
case SymmetricCipher::Aes256:
|
||||
case SymmetricCipher::Twofish:
|
||||
case SymmetricCipher::Salsa20:
|
||||
case SymmetricCipher::ChaCha20:
|
||||
return new SymmetricCipherGcrypt(algo, mode, direction);
|
||||
|
||||
default:
|
||||
@@ -93,10 +95,14 @@ SymmetricCipher::Algorithm SymmetricCipher::cipherToAlgorithm(Uuid cipher)
|
||||
{
|
||||
if (cipher == KeePass2::CIPHER_AES) {
|
||||
return SymmetricCipher::Aes256;
|
||||
}
|
||||
else {
|
||||
} else if (cipher == KeePass2::CIPHER_CHACHA20) {
|
||||
return SymmetricCipher::ChaCha20;
|
||||
} else if (cipher == KeePass2::CIPHER_TWOFISH) {
|
||||
return SymmetricCipher::Twofish;
|
||||
}
|
||||
|
||||
qWarning("SymmetricCipher::cipherToAlgorithm: invalid Uuid %s", cipher.toByteArray().toHex().data());
|
||||
return InvalidAlgorithm;
|
||||
}
|
||||
|
||||
Uuid SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm algo)
|
||||
@@ -104,7 +110,42 @@ Uuid SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm algo)
|
||||
switch (algo) {
|
||||
case SymmetricCipher::Aes256:
|
||||
return KeePass2::CIPHER_AES;
|
||||
default:
|
||||
case SymmetricCipher::ChaCha20:
|
||||
return KeePass2::CIPHER_CHACHA20;
|
||||
case SymmetricCipher::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,
|
||||
Twofish,
|
||||
Salsa20
|
||||
Salsa20,
|
||||
ChaCha20,
|
||||
InvalidAlgorithm = -1
|
||||
};
|
||||
|
||||
enum Mode
|
||||
@@ -41,7 +43,8 @@ public:
|
||||
Cbc,
|
||||
Ctr,
|
||||
Ecb,
|
||||
Stream
|
||||
Stream,
|
||||
InvalidMode = -1
|
||||
};
|
||||
|
||||
enum Direction
|
||||
@@ -74,9 +77,12 @@ public:
|
||||
int keySize() const;
|
||||
int blockSize() const;
|
||||
QString errorString() const;
|
||||
Algorithm algorithm() const;
|
||||
|
||||
static SymmetricCipher::Algorithm cipherToAlgorithm(Uuid cipher);
|
||||
static Uuid algorithmToCipher(SymmetricCipher::Algorithm algo);
|
||||
static Algorithm cipherToAlgorithm(Uuid cipher);
|
||||
static Uuid algorithmToCipher(Algorithm algo);
|
||||
static int algorithmIvSize(Algorithm algo);
|
||||
static Mode algorithmMode(Algorithm algo);
|
||||
|
||||
private:
|
||||
static SymmetricCipherBackend* createBackend(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
||||
@@ -84,6 +90,7 @@ private:
|
||||
|
||||
const QScopedPointer<SymmetricCipherBackend> m_backend;
|
||||
bool m_initialized;
|
||||
Algorithm m_algo;
|
||||
|
||||
Q_DISABLE_COPY(SymmetricCipher)
|
||||
};
|
||||
|
||||
@@ -46,6 +46,9 @@ int SymmetricCipherGcrypt::gcryptAlgo(SymmetricCipher::Algorithm algo)
|
||||
case SymmetricCipher::Salsa20:
|
||||
return GCRY_CIPHER_SALSA20;
|
||||
|
||||
case SymmetricCipher::ChaCha20:
|
||||
return GCRY_CIPHER_CHACHA20;
|
||||
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return -1;
|
||||
|
||||
Reference in New Issue
Block a user