Do some basic self-checks when initializing the crypto backend.

This commit is contained in:
Felix Geyer 2014-06-15 11:17:40 +02:00
parent b417bf9187
commit 0d6117bf4c
23 changed files with 143 additions and 27 deletions

View file

@ -21,7 +21,12 @@
#include <gcrypt.h>
#include "config-keepassx.h"
#include "crypto/CryptoHash.h"
#include "crypto/SymmetricCipher.h"
bool Crypto::m_initalized(false);
QString Crypto::m_errorStr;
#if !defined(GCRYPT_VERSION_NUMBER) || (GCRYPT_VERSION_NUMBER < 0x010600)
static int gcry_qt_mutex_init(void** p_sys)
@ -64,11 +69,11 @@ Crypto::Crypto()
{
}
void Crypto::init()
bool Crypto::init()
{
if (m_initalized) {
qWarning("Crypto::init: already initalized");
return;
return true;
}
// libgcrypt >= 1.6 doesn't allow custom thread callbacks anymore.
@ -78,7 +83,19 @@ void Crypto::init()
gcry_check_version(0);
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
if (!checkAlgorithms()) {
return false;
}
// has to be set before testing Crypto classes
m_initalized = true;
if (!selfTest()) {
m_initalized = false;
return false;
}
return true;
}
bool Crypto::initalized()
@ -86,7 +103,89 @@ bool Crypto::initalized()
return m_initalized;
}
bool Crypto::selfTest()
QString Crypto::errorString()
{
return m_errorStr;
}
bool Crypto::backendSelfTest()
{
return (gcry_control(GCRYCTL_SELFTEST) == 0);
}
bool Crypto::checkAlgorithms()
{
if (gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_TEST_ALGO, Q_NULLPTR, Q_NULLPTR) != 0) {
m_errorStr = "GCRY_CIPHER_AES256 not found.";
qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr));
return false;
}
if (gcry_cipher_algo_info(GCRY_CIPHER_TWOFISH, GCRYCTL_TEST_ALGO, Q_NULLPTR, Q_NULLPTR) != 0) {
m_errorStr = "GCRY_CIPHER_TWOFISH not found.";
qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr));
return false;
}
#ifdef GCRYPT_HAS_SALSA20
if (gcry_cipher_algo_info(GCRY_CIPHER_SALSA20, GCRYCTL_TEST_ALGO, Q_NULLPTR, Q_NULLPTR) != 0) {
m_errorStr = "GCRY_CIPHER_SALSA20 not found.";
qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr));
return false;
}
#endif
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;
}
return true;
}
bool Crypto::selfTest()
{
QByteArray sha256Test = CryptoHash::hash("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
CryptoHash::Sha256);
if (sha256Test != QByteArray::fromHex("248D6A61D20638B8E5C026930C3E6039A33CE45964FF2167F6ECEDD419DB06C1")) {
m_errorStr = "SHA-256 mismatch.";
qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr));
return false;
}
QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
QByteArray iv = QByteArray::fromHex("000102030405060708090a0b0c0d0e0f");
QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a");
plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51"));
QByteArray cipherText = QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd6");
cipherText.append(QByteArray::fromHex("9cfc4e967edb808d679f777bc6702c7d"));
SymmetricCipher aes256Encrypt(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt, key, iv);
if (aes256Encrypt.process(plainText) != cipherText) {
m_errorStr = "AES-256 encryption mismatch.";
qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr));
return false;
}
SymmetricCipher aes256Descrypt(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt, key, iv);
if (aes256Descrypt.process(cipherText) != plainText) {
m_errorStr = "AES-256 decryption mismatch.";
qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr));
return false;
}
QByteArray salsa20Key = QByteArray::fromHex("F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112");
QByteArray salsa20iv = QByteArray::fromHex("0000000000000000");
QByteArray salsa20Plain = QByteArray::fromHex("00000000000000000000000000000000");
QByteArray salsa20Cipher = QByteArray::fromHex("B4C0AFA503BE7FC29A62058166D56F8F");
SymmetricCipher salsa20Stream(SymmetricCipher::Salsa20, SymmetricCipher::Stream,
SymmetricCipher::Encrypt, salsa20Key, salsa20iv);
if (salsa20Stream.process(salsa20Plain) != salsa20Cipher) {
m_errorStr = "Salsa20 stream cipher mismatch.";
qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr));
return false;
}
return true;
}

View file

@ -18,18 +18,25 @@
#ifndef KEEPASSX_CRYPTO_H
#define KEEPASSX_CRYPTO_H
#include <QString>
#include "core/Global.h"
class Crypto
{
public:
static void init();
static bool init();
static bool initalized();
static bool selfTest();
static bool backendSelfTest();
static QString errorString();
private:
Crypto();
static bool checkAlgorithms();
static bool selfTest();
static bool m_initalized;
static QString m_errorStr;
};
#endif // KEEPASSX_CRYPTO_H

View file

@ -25,6 +25,7 @@
#include "crypto/Crypto.h"
#include "gui/Application.h"
#include "gui/MainWindow.h"
#include "gui/MessageBox.h"
int main(int argc, char** argv)
{
@ -38,7 +39,14 @@ int main(int argc, char** argv)
// don't set organizationName as that changes the return value of
// QDesktopServices::storageLocation(QDesktopServices::DataLocation)
Crypto::init();
if (!Crypto::init()) {
QString error = QCoreApplication::translate("Main",
"Fatal error while testing the cryptographic functions.");
error.append("\n");
error.append(Crypto::errorString());
MessageBox::critical(Q_NULLPTR, QCoreApplication::translate("Main", "KeePassX - Error"), error);
return 1;
}
QCommandLineParser parser;
parser.setApplicationDescription(QCoreApplication::translate("main", "KeePassX - cross-platform password manager"));