/* * Copyright (C) 2021 KeePassXC Team * Copyright (C) 2010 Felix Geyer * * 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 . */ #include "Crypto.h" #include "config-keepassx.h" #include "crypto/CryptoHash.h" #include "crypto/SymmetricCipher.h" #include namespace { QString g_cryptoError; bool testSha256() { if (CryptoHash::hash("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", CryptoHash::Sha256) != QByteArray::fromHex("248D6A61D20638B8E5C026930C3E6039A33CE45964FF2167F6ECEDD419DB06C1")) { g_cryptoError = "SHA-256 mismatch."; return false; } return true; } bool testSha512() { if (CryptoHash::hash("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", CryptoHash::Sha512) != QByteArray::fromHex("204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c335" "96fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445")) { g_cryptoError = "SHA-512 mismatch."; return false; } return true; } bool testAes256Cbc() { QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d7781" "1f352c073b6108d72d9810a30914dff4"); QByteArray iv = QByteArray::fromHex("000102030405060708090a0b0c0d0e0f"); QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a" "ae2d8a571e03ac9c9eb76fac45af8e51"); QByteArray cipherText = QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd6" "9cfc4e967edb808d679f777bc6702c7d"); QByteArray data = plainText; SymmetricCipher aes256; if (!aes256.init(SymmetricCipher::Aes256_CBC, SymmetricCipher::Encrypt, key, iv)) { g_cryptoError = aes256.errorString(); return false; } if (!aes256.process(data)) { g_cryptoError = aes256.errorString(); return false; } if (data != cipherText) { g_cryptoError = "AES-256 CBC encryption mismatch."; return false; } if (!aes256.init(SymmetricCipher::Aes256_CBC, SymmetricCipher::Decrypt, key, iv)) { g_cryptoError = aes256.errorString(); return false; } if (!aes256.process(data)) { g_cryptoError = aes256.errorString(); return false; } if (data != plainText) { g_cryptoError = "AES-256 CBC decryption mismatch."; return false; } return true; } bool testAesKdf() { QByteArray key = QByteArray::fromHex("000102030405060708090A0B0C0D0E0F" "101112131415161718191A1B1C1D1E1F"); QByteArray plainText = QByteArray::fromHex("00112233445566778899AABBCCDDEEFF"); QByteArray cipherText = QByteArray::fromHex("8EA2B7CA516745BFEAFC49904B496089"); if (!SymmetricCipher::aesKdf(key, 1, plainText)) { g_cryptoError = "AES KDF Failed."; } if (plainText != cipherText) { g_cryptoError = "AES KDF encryption mismatch."; return false; } return true; } bool testTwofish() { QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d7781" "1f352c073b6108d72d9810a30914dff4"); QByteArray iv = QByteArray::fromHex("000102030405060708090a0b0c0d0e0f"); QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a" "ae2d8a571e03ac9c9eb76fac45af8e51"); QByteArray cipherText = QByteArray::fromHex("e0227c3cc80f3cb1b2ed847cc6f57d3c" "657b1e7960b30fb7c8d62e72ae37c3a0"); QByteArray data = plainText; SymmetricCipher twofish; if (!twofish.init(SymmetricCipher::Twofish_CBC, SymmetricCipher::Encrypt, key, iv)) { g_cryptoError = twofish.errorString(); return false; } if (!twofish.process(data)) { g_cryptoError = twofish.errorString(); return false; } if (data != cipherText) { g_cryptoError = "Twofish encryption mismatch."; return false; } if (!twofish.init(SymmetricCipher::Twofish_CBC, SymmetricCipher::Decrypt, key, iv)) { g_cryptoError = twofish.errorString(); return false; } if (!twofish.process(data)) { g_cryptoError = twofish.errorString(); return false; } if (data != plainText) { g_cryptoError = "Twofish encryption mismatch."; return false; } return true; } bool testSalsa20() { QByteArray salsa20Key = QByteArray::fromHex("F3F4F5F6F7F8F9FAFBFCFDFEFF000102" "030405060708090A0B0C0D0E0F101112"); QByteArray salsa20iv = QByteArray::fromHex("0000000000000000"); QByteArray salsa20Plain = QByteArray::fromHex("00000000000000000000000000000000"); QByteArray salsa20Cipher = QByteArray::fromHex("B4C0AFA503BE7FC29A62058166D56F8F"); QByteArray data = salsa20Plain; SymmetricCipher salsa20Stream; if (!salsa20Stream.init(SymmetricCipher::Salsa20, SymmetricCipher::Encrypt, salsa20Key, salsa20iv)) { g_cryptoError = salsa20Stream.errorString(); return false; } if (!salsa20Stream.process(data)) { g_cryptoError = salsa20Stream.errorString(); return false; } if (data != salsa20Cipher) { g_cryptoError = "Salsa20 stream cipher encrypt mismatch."; return false; } if (!salsa20Stream.init(SymmetricCipher::Salsa20, SymmetricCipher::Decrypt, salsa20Key, salsa20iv)) { g_cryptoError = salsa20Stream.errorString(); return false; } if (!salsa20Stream.process(data)) { g_cryptoError = salsa20Stream.errorString(); return false; } if (data != salsa20Plain) { g_cryptoError = "Salsa20 stream cipher decrypt mismatch."; return false; } return true; } bool testChaCha20() { QByteArray chacha20Key = QByteArray::fromHex("00000000000000000000000000000000" "00000000000000000000000000000000"); QByteArray chacha20iv = QByteArray::fromHex("0000000000000000"); QByteArray chacha20Plain = QByteArray::fromHex("0000000000000000000000000000000000000000000000000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000"); QByteArray chacha20Cipher = QByteArray::fromHex("76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7" "da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"); QByteArray data = chacha20Plain; SymmetricCipher chacha20Stream; if (!chacha20Stream.init(SymmetricCipher::ChaCha20, SymmetricCipher::Encrypt, chacha20Key, chacha20iv)) { g_cryptoError = chacha20Stream.errorString(); return false; } if (!chacha20Stream.process(data)) { g_cryptoError = chacha20Stream.errorString(); return false; } if (data != chacha20Cipher) { g_cryptoError = "ChaCha20 stream cipher encrypt mismatch."; return false; } if (!chacha20Stream.init(SymmetricCipher::ChaCha20, SymmetricCipher::Decrypt, chacha20Key, chacha20iv)) { g_cryptoError = chacha20Stream.errorString(); return false; } if (!chacha20Stream.process(data)) { g_cryptoError = chacha20Stream.errorString(); return false; } if (data != chacha20Plain) { g_cryptoError = "ChaCha20 stream cipher decrypt mismatch."; return false; } return true; } } // namespace namespace Crypto { bool init() { #ifdef WITH_XC_BOTAN3 unsigned int version_major = 3, min_version_minor = 0; QString versionString = "3.x"; #else unsigned int version_major = 2, min_version_minor = 11; QString versionString = "2.11.x"; #endif if (Botan::version_major() != version_major || Botan::version_minor() < min_version_minor) { g_cryptoError = QObject::tr("Botan library must be at least %1, found %2.%3.%4") .arg(versionString) .arg(Botan::version_major()) .arg(Botan::version_minor()) .arg(Botan::version_patch()); return false; } return testSha256() && testSha512() && testAes256Cbc() && testAesKdf() && testTwofish() && testSalsa20() && testChaCha20(); } QString errorString() { return g_cryptoError; } QString debugInfo() { QString debugInfo = QObject::tr("Cryptographic libraries:").append("\n"); debugInfo.append(QString("- Botan %1.%2.%3\n") .arg(Botan::version_major()) .arg(Botan::version_minor()) .arg(Botan::version_patch())); return debugInfo; } } // namespace Crypto