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:
angelsl
2017-11-13 02:23:01 +08:00
committed by Jonathan White
parent 4532108678
commit 6a0d05e1ef
23 changed files with 616 additions and 25 deletions

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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)
};

View File

@@ -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;