keepassxc/src/crypto/Crypto.cpp
Jonathan White ebc0b3ff5f
Add support for Botan3 (#8994)
Fix Botan 3 build (#9388)

* SymmetricCipher: Fix Botan 3 build

Botan commit 819cf8fe6278a19b8266f449228f02fc28a4f784 changed Botan::Cipher_Dir to be a scoped enumeration, so the users must be adapted.

This change causes no issues with Botan 2 because normal enumeration values can also be referred to the same way scoped enumeration values are accessed.

* Auto detect Botan3

* AsyncTask: Do not use `std::result_of`

`std::result_of` was deprecated in C++17 and then it was subsequently removed in C++20. One could use `std::invoke_result_t`, but let Qt figure out the return type instead.

* Collapse Botan2 and Botan3 find package into one

* Update COPYING
2023-05-07 23:19:18 -04:00

276 lines
10 KiB
C++

/*
* Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
*
* 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 "Crypto.h"
#include "config-keepassx.h"
#include "crypto/CryptoHash.h"
#include "crypto/SymmetricCipher.h"
#include <botan/version.h>
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