mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-12-04 15:39:34 +01:00
Replace all crypto libraries with Botan
Selected the [Botan crypto library](https://github.com/randombit/botan) due to its feature list, maintainer support, availability across all deployment platforms, and ease of use. Also evaluated Crypto++ as a viable candidate, but the additional features of Botan (PKCS#11, TPM, etc) won out. The random number generator received a backend upgrade. Botan prefers hardware-based RNG's and will provide one if available. This is transparent to KeePassXC and a significant improvement over gcrypt. Replaced Argon2 library with built-in Botan implementation that supports i, d, and id. This requires Botan 2.11.0 or higher. Also simplified the parameter test across KDF's. Aligned SymmetricCipher parameters with available modes. All encrypt and decrypt operations are done in-place instead of returning new objects. This allows use of secure vectors in the future with no additional overhead. Took this opportunity to decouple KeeShare from SSH Agent. Removed leftover code from OpenSSHKey and consolidated the SSH Agent code into the same directory. Removed bcrypt and blowfish inserts since they are provided by Botan. Additionally simplified KeeShare settings interface by removing raw certificate byte data from the user interface. KeeShare will be further refactored in a future PR. NOTE: This PR breaks backwards compatibility with KeeShare certificates due to different RSA key storage with Botan. As a result, new "own" certificates will need to be generated and trust re-established. Removed YKChallengeResponseKeyCLI in favor of just using the original implementation with signal/slots. Removed TestRandom stub since it was just faking random numbers and not actually using the backend. TestRandomGenerator now uses the actual RNG. Greatly simplified Secret Service plugin's use of crypto functions with Botan.
This commit is contained in:
@@ -94,8 +94,7 @@ set(testsupport_SOURCES
|
||||
modeltest.cpp
|
||||
FailDevice.cpp
|
||||
mock/MockClock.cpp
|
||||
util/TemporaryFile.cpp
|
||||
stub/TestRandom.cpp)
|
||||
util/TemporaryFile.cpp)
|
||||
add_library(testsupport STATIC ${testsupport_SOURCES})
|
||||
target_link_libraries(testsupport Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Test)
|
||||
|
||||
@@ -162,9 +161,9 @@ if(WITH_XC_AUTOTYPE)
|
||||
set_target_properties(testautotype PROPERTIES ENABLE_EXPORTS ON)
|
||||
endif()
|
||||
|
||||
if(WITH_XC_CRYPTO_SSH)
|
||||
if(WITH_XC_SSHAGENT)
|
||||
add_unit_test(NAME testopensshkey SOURCES TestOpenSSHKey.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
LIBS sshagent ${TEST_LIBRARIES})
|
||||
if(NOT WIN32)
|
||||
add_unit_test(NAME testsshagent SOURCES TestSSHAgent.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
|
||||
@@ -21,9 +21,11 @@
|
||||
#include "browser/BrowserSettings.h"
|
||||
#include "core/Tools.h"
|
||||
#include "crypto/Crypto.h"
|
||||
#include "sodium/crypto_box.h"
|
||||
|
||||
#include <QString>
|
||||
#include <botan/sodium.h>
|
||||
|
||||
using namespace Botan::Sodium;
|
||||
|
||||
QTEST_GUILESS_MAIN(TestBrowser)
|
||||
|
||||
|
||||
@@ -1837,7 +1837,7 @@ void TestCli::testYubiKeyOption()
|
||||
|
||||
bool wouldBlock = false;
|
||||
QByteArray challenge("CLITest");
|
||||
QByteArray response;
|
||||
Botan::secure_vector<char> response;
|
||||
QByteArray expected("\xA2\x3B\x94\x00\xBE\x47\x9A\x30\xA9\xEB\x50\x9B\x85\x56\x5B\x6B\x30\x25\xB4\x8E", 20);
|
||||
|
||||
// Find a key that as configured for this test
|
||||
@@ -1845,7 +1845,7 @@ void TestCli::testYubiKeyOption()
|
||||
for (auto key : keys) {
|
||||
if (YubiKey::instance()->testChallenge(key, &wouldBlock) && !wouldBlock) {
|
||||
YubiKey::instance()->challenge(key, challenge, response);
|
||||
if (response == expected) {
|
||||
if (std::memcmp(response.data(), expected.data(), expected.size())) {
|
||||
pKey = key;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "core/EntrySearcher.h"
|
||||
#include "crypto/Crypto.h"
|
||||
#include "fdosecrets/GcryptMPI.h"
|
||||
#include "crypto/Random.h"
|
||||
#include "fdosecrets/dbus/DBusMgr.h"
|
||||
#include "fdosecrets/objects/Collection.h"
|
||||
#include "fdosecrets/objects/Item.h"
|
||||
@@ -29,69 +29,10 @@
|
||||
|
||||
QTEST_GUILESS_MAIN(TestFdoSecrets)
|
||||
|
||||
void TestFdoSecrets::initTestCase()
|
||||
{
|
||||
QVERIFY(Crypto::init());
|
||||
}
|
||||
|
||||
void TestFdoSecrets::cleanupTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
void TestFdoSecrets::testGcryptMPI()
|
||||
{
|
||||
auto bytes = QByteArray::fromHex(QByteArrayLiteral("DEADBEEF"));
|
||||
|
||||
auto mpi = MpiFromBytes(bytes);
|
||||
auto another = MpiFromHex("DEADBEEF");
|
||||
|
||||
// verify it can parse the bytes in USG mode
|
||||
QVERIFY(mpi.get());
|
||||
QVERIFY(another.get());
|
||||
|
||||
// verify the number is of the correct value
|
||||
QCOMPARE(gcry_mpi_cmp_ui(mpi.get(), 0xdeadbeef), 0);
|
||||
QCOMPARE(gcry_mpi_cmp_ui(another.get(), 0xdeadbeef), 0);
|
||||
|
||||
// verify it can convert back
|
||||
QCOMPARE(MpiToBytes(mpi), bytes);
|
||||
QCOMPARE(MpiToBytes(another), bytes);
|
||||
}
|
||||
|
||||
void TestFdoSecrets::testDhIetf1024Sha256Aes128CbcPkcs7()
|
||||
{
|
||||
auto clientPublic = MpiFromHex("40a0c8d27012c651bf270ebd96890a538"
|
||||
"396fae3852aef69c0c19bae420d667577"
|
||||
"ed471cd8ba5a49ef0ec91b568b95f87f0"
|
||||
"9ec31d271f1699ed140c5b38644c42f60"
|
||||
"ef84b5a6c406e17c07cd3208e5a605626"
|
||||
"a5266153b447529946be2394dd43e5638"
|
||||
"5ffbc4322902c2942391d1a36e8d125dc"
|
||||
"809e3e406a2f5c2dcf39d3da2");
|
||||
auto serverPublic = MpiFromHex("e407997e8b918419cf851cf3345358fdf"
|
||||
"ffb9564a220ac9c3934efd277cea20d17"
|
||||
"467ecdc56e817f75ac39501f38a4a04ff"
|
||||
"64d627e16c09981c7ad876da255b61c8e"
|
||||
"6a8408236c2a4523cfe6961c26dbdfc77"
|
||||
"c1a27a5b425ca71a019e829fae32c0b42"
|
||||
"0e1b3096b48bc2ce9ccab1d1ff13a5eb4"
|
||||
"b263cee30bdb1a57af9bfa93f");
|
||||
auto serverPrivate = MpiFromHex("013f4f3381ef0ca11c4c7363079577b56"
|
||||
"99b238644e0aba47e24bdba6173590216"
|
||||
"4f1e12dd0944800a373e090e63192f53b"
|
||||
"93583e9a9e50bb9d792aafaa3a0f5ae77"
|
||||
"de0c3423f5820848d88ee3bdd01c889f2"
|
||||
"7af58a02f5b6693d422b9d189b300d7b1"
|
||||
"be5076b5795cf8808c31e2e2898368d18"
|
||||
"ab5c26b0ea3480c9aba8154cf");
|
||||
|
||||
std::unique_ptr<FdoSecrets::DhIetf1024Sha256Aes128CbcPkcs7> cipher{new FdoSecrets::DhIetf1024Sha256Aes128CbcPkcs7};
|
||||
|
||||
cipher->initialize(std::move(clientPublic), std::move(serverPublic), std::move(serverPrivate));
|
||||
|
||||
QVERIFY(cipher->isValid());
|
||||
|
||||
QCOMPARE(cipher->m_aesKey.toHex(), QByteArrayLiteral("6b8f5ee55138eac37118508be21e7834"));
|
||||
FdoSecrets::DhIetf1024Sha256Aes128CbcPkcs7 cipher(randomGen()->randomArray(128));
|
||||
QVERIFY(cipher.isValid());
|
||||
}
|
||||
|
||||
void TestFdoSecrets::testCrazyAttributeKey()
|
||||
|
||||
@@ -25,10 +25,6 @@ class TestFdoSecrets : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
void testGcryptMPI();
|
||||
void testDhIetf1024Sha256Aes128CbcPkcs7();
|
||||
void testCrazyAttributeKey();
|
||||
void testSpecialCharsInAttributeValue();
|
||||
|
||||
@@ -35,8 +35,11 @@ void TestKeePass2RandomStream::test()
|
||||
const QByteArray key("\x11\x22\x33\x44\x55\x66\x77\x88");
|
||||
const int Size = 128;
|
||||
|
||||
SymmetricCipher cipher(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt);
|
||||
QVERIFY(cipher.init(CryptoHash::hash(key, CryptoHash::Sha256), KeePass2::INNER_STREAM_SALSA20_IV));
|
||||
SymmetricCipher cipher;
|
||||
QVERIFY(cipher.init(SymmetricCipher::Salsa20,
|
||||
SymmetricCipher::Encrypt,
|
||||
CryptoHash::hash(key, CryptoHash::Sha256),
|
||||
KeePass2::INNER_STREAM_SALSA20_IV));
|
||||
|
||||
const QByteArray data(QByteArray::fromHex("601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5"
|
||||
"2b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6"
|
||||
@@ -45,7 +48,7 @@ void TestKeePass2RandomStream::test()
|
||||
|
||||
QByteArray cipherPad;
|
||||
cipherPad.fill('\0', Size);
|
||||
QVERIFY(cipher.processInPlace(cipherPad));
|
||||
QVERIFY(cipher.process(cipherPad));
|
||||
|
||||
QByteArray cipherData;
|
||||
cipherData.resize(Size);
|
||||
@@ -54,9 +57,9 @@ void TestKeePass2RandomStream::test()
|
||||
cipherData[i] = data[i] ^ cipherPad[i];
|
||||
}
|
||||
|
||||
KeePass2RandomStream randomStream(KeePass2::ProtectedStreamAlgo::Salsa20);
|
||||
KeePass2RandomStream randomStream;
|
||||
bool ok;
|
||||
QVERIFY(randomStream.init(key));
|
||||
QVERIFY(randomStream.init(SymmetricCipher::Salsa20, key));
|
||||
QByteArray randomStreamData;
|
||||
randomStreamData.append(randomStream.process(data.mid(0, 7), &ok));
|
||||
QVERIFY(ok);
|
||||
@@ -70,10 +73,13 @@ void TestKeePass2RandomStream::test()
|
||||
randomStreamData.append(randomStream.process(data.mid(64, 64), &ok));
|
||||
QVERIFY(ok);
|
||||
|
||||
SymmetricCipher cipherEncrypt(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt);
|
||||
QVERIFY(cipherEncrypt.init(CryptoHash::hash(key, CryptoHash::Sha256), KeePass2::INNER_STREAM_SALSA20_IV));
|
||||
QByteArray cipherDataEncrypt = cipherEncrypt.process(data, &ok);
|
||||
QVERIFY(ok);
|
||||
SymmetricCipher cipherEncrypt;
|
||||
QVERIFY(cipherEncrypt.init(SymmetricCipher::Salsa20,
|
||||
SymmetricCipher::Encrypt,
|
||||
CryptoHash::hash(key, CryptoHash::Sha256),
|
||||
KeePass2::INNER_STREAM_SALSA20_IV));
|
||||
QByteArray cipherDataEncrypt = data;
|
||||
QVERIFY(cipherEncrypt.process(cipherDataEncrypt));
|
||||
|
||||
QCOMPARE(randomStreamData.size(), Size);
|
||||
QCOMPARE(cipherData, cipherDataEncrypt);
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
#include "TestOpenSSHKey.h"
|
||||
#include "TestGlobal.h"
|
||||
#include "crypto/Crypto.h"
|
||||
#include "crypto/ssh/BinaryStream.h"
|
||||
#include "crypto/ssh/OpenSSHKey.h"
|
||||
#include "sshagent/BinaryStream.h"
|
||||
#include "sshagent/OpenSSHKey.h"
|
||||
|
||||
QTEST_GUILESS_MAIN(TestOpenSSHKey)
|
||||
|
||||
@@ -434,12 +434,3 @@ void TestOpenSSHKey::testDecryptUTF8()
|
||||
QCOMPARE(key.type(), QString("ssh-ed25519"));
|
||||
QCOMPARE(key.comment(), QString("opensshkey-test-utf8@keepassxc"));
|
||||
}
|
||||
|
||||
void TestOpenSSHKey::testGenerateRSA()
|
||||
{
|
||||
OpenSSHKey key = OpenSSHKey::generate(false);
|
||||
QVERIFY(!key.encrypted());
|
||||
QCOMPARE(key.cipherName(), QString("none"));
|
||||
QCOMPARE(key.type(), QString("ssh-rsa"));
|
||||
QCOMPARE(key.comment(), QString(""));
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ private slots:
|
||||
void testDecryptOpenSSHAES256CTR();
|
||||
void testDecryptRSAAES256CTR();
|
||||
void testDecryptUTF8();
|
||||
void testGenerateRSA();
|
||||
};
|
||||
|
||||
#endif // TESTOPENSSHKEY_H
|
||||
|
||||
@@ -17,64 +17,46 @@
|
||||
|
||||
#include "TestRandomGenerator.h"
|
||||
#include "TestGlobal.h"
|
||||
|
||||
#include "core/Endian.h"
|
||||
#include "core/Global.h"
|
||||
#include "stub/TestRandom.h"
|
||||
#include "crypto/Random.h"
|
||||
|
||||
#include <QTest>
|
||||
|
||||
QTEST_GUILESS_MAIN(TestRandomGenerator)
|
||||
|
||||
void TestRandomGenerator::initTestCase()
|
||||
void TestRandomGenerator::testArray()
|
||||
{
|
||||
m_backend = new RandomBackendPreset();
|
||||
auto ba = randomGen()->randomArray(10);
|
||||
QCOMPARE(ba.size(), 10);
|
||||
QVERIFY(ba != QByteArray(10, '\0'));
|
||||
|
||||
TestRandom::setup(m_backend);
|
||||
}
|
||||
|
||||
void TestRandomGenerator::cleanupTestCase()
|
||||
{
|
||||
TestRandom::teardown();
|
||||
|
||||
m_backend = nullptr;
|
||||
auto ba2 = ba;
|
||||
randomGen()->randomize(ba2);
|
||||
QVERIFY(ba2 != ba);
|
||||
}
|
||||
|
||||
void TestRandomGenerator::testUInt()
|
||||
{
|
||||
QByteArray nextBytes;
|
||||
QVERIFY(randomGen()->randomUInt(0) == 0);
|
||||
QVERIFY(randomGen()->randomUInt(1) == 0);
|
||||
|
||||
nextBytes = Endian::sizedIntToBytes(42, QSysInfo::ByteOrder);
|
||||
m_backend->setNextBytes(nextBytes);
|
||||
QCOMPARE(randomGen()->randomUInt(100), 42U);
|
||||
|
||||
nextBytes = Endian::sizedIntToBytes(117, QSysInfo::ByteOrder);
|
||||
m_backend->setNextBytes(nextBytes);
|
||||
QCOMPARE(randomGen()->randomUInt(100), 17U);
|
||||
|
||||
nextBytes = Endian::sizedIntToBytes(1001, QSysInfo::ByteOrder);
|
||||
m_backend->setNextBytes(nextBytes);
|
||||
QCOMPARE(randomGen()->randomUInt(1), 0U);
|
||||
|
||||
nextBytes.clear();
|
||||
nextBytes.append(Endian::sizedIntToBytes(QUINT32_MAX, QSysInfo::ByteOrder));
|
||||
nextBytes.append(Endian::sizedIntToBytes(QUINT32_MAX - 70000U, QSysInfo::ByteOrder));
|
||||
m_backend->setNextBytes(nextBytes);
|
||||
QCOMPARE(randomGen()->randomUInt(100000U), (QUINT32_MAX - 70000U) % 100000U);
|
||||
|
||||
nextBytes.clear();
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
nextBytes.append(Endian::sizedIntToBytes((QUINT32_MAX / 2U) + 1U + i, QSysInfo::ByteOrder));
|
||||
// Run a bunch of trials creating random numbers to ensure we meet the standard
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
QVERIFY(randomGen()->randomUInt(5) < 5);
|
||||
QVERIFY(randomGen()->randomUInt(100) < 100);
|
||||
QVERIFY(randomGen()->randomUInt(100000U) < 100000U);
|
||||
QVERIFY(randomGen()->randomUInt((QUINT32_MAX / 2U) + 1U) < QUINT32_MAX / 2U + 1U);
|
||||
}
|
||||
nextBytes.append(Endian::sizedIntToBytes(QUINT32_MAX / 2U, QSysInfo::ByteOrder));
|
||||
m_backend->setNextBytes(nextBytes);
|
||||
QCOMPARE(randomGen()->randomUInt((QUINT32_MAX / 2U) + 1U), QUINT32_MAX / 2U);
|
||||
}
|
||||
|
||||
void TestRandomGenerator::testUIntRange()
|
||||
{
|
||||
QByteArray nextBytes;
|
||||
|
||||
nextBytes = Endian::sizedIntToBytes(42, QSysInfo::ByteOrder);
|
||||
m_backend->setNextBytes(nextBytes);
|
||||
QCOMPARE(randomGen()->randomUIntRange(100, 200), 142U);
|
||||
// Run a bunch of trials to ensure we stay within the range
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
auto rand = randomGen()->randomUIntRange(100, 200);
|
||||
QVERIFY(rand >= 100);
|
||||
QVERIFY(rand < 200);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,24 +18,16 @@
|
||||
#ifndef KEEPASSX_TESTRANDOMGENERATOR_H
|
||||
#define KEEPASSX_TESTRANDOMGENERATOR_H
|
||||
|
||||
#include "crypto/Random.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class RandomBackendPreset;
|
||||
|
||||
class TestRandomGenerator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
void testArray();
|
||||
void testUInt();
|
||||
void testUIntRange();
|
||||
|
||||
private:
|
||||
RandomBackendPreset* m_backend;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_TESTRANDOMGENERATOR_H
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
#ifndef TESTSSHAGENT_H
|
||||
#define TESTSSHAGENT_H
|
||||
|
||||
#include "crypto/ssh/OpenSSHKey.h"
|
||||
#include "sshagent/OpenSSHKey.h"
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
#include <QTemporaryFile>
|
||||
#include <QUuid>
|
||||
|
||||
class TestSSHAgent : public QObject
|
||||
{
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
#include "TestSharing.h"
|
||||
#include "TestGlobal.h"
|
||||
#include "stub/TestRandom.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QSignalSpy>
|
||||
@@ -29,11 +28,14 @@
|
||||
#include "core/Metadata.h"
|
||||
#include "crypto/Crypto.h"
|
||||
#include "crypto/Random.h"
|
||||
#include "crypto/ssh/OpenSSHKey.h"
|
||||
#include "format/KeePass2Writer.h"
|
||||
#include "keeshare/KeeShareSettings.h"
|
||||
#include "keys/PasswordKey.h"
|
||||
|
||||
#include <botan/pkcs8.h>
|
||||
#include <botan/rsa.h>
|
||||
#include <botan/x509_key.h>
|
||||
|
||||
#include <format/KeePass2Reader.h>
|
||||
|
||||
QTEST_GUILESS_MAIN(TestSharing)
|
||||
@@ -50,59 +52,6 @@ void TestSharing::initTestCase()
|
||||
QVERIFY(Crypto::init());
|
||||
}
|
||||
|
||||
void TestSharing::cleanupTestCase()
|
||||
{
|
||||
TestRandom::teardown();
|
||||
}
|
||||
|
||||
void TestSharing::testIdempotentDatabaseWriting()
|
||||
{
|
||||
QScopedPointer<Database> db(new Database());
|
||||
auto key = QSharedPointer<CompositeKey>::create();
|
||||
key->addKey(QSharedPointer<PasswordKey>::create("password"));
|
||||
db->setKey(key);
|
||||
|
||||
Group* sharingGroup = new Group();
|
||||
sharingGroup->setName("SharingGroup");
|
||||
sharingGroup->setUuid(QUuid::createUuid());
|
||||
sharingGroup->setParent(db->rootGroup());
|
||||
|
||||
Entry* entry1 = new Entry();
|
||||
entry1->setUuid(QUuid::createUuid());
|
||||
entry1->beginUpdate();
|
||||
entry1->setTitle("Entry1");
|
||||
entry1->endUpdate();
|
||||
entry1->setGroup(sharingGroup);
|
||||
|
||||
Entry* entry2 = new Entry();
|
||||
entry2->setUuid(QUuid::createUuid());
|
||||
entry2->beginUpdate();
|
||||
entry2->setTitle("Entry2");
|
||||
entry2->endUpdate();
|
||||
entry2->setGroup(sharingGroup);
|
||||
|
||||
// prevent from changes introduced by randomization
|
||||
TestRandom::setup(new RandomBackendNull());
|
||||
|
||||
QByteArray bufferOriginal;
|
||||
{
|
||||
QBuffer device(&bufferOriginal);
|
||||
device.open(QIODevice::ReadWrite);
|
||||
KeePass2Writer writer;
|
||||
writer.writeDatabase(&device, db.data());
|
||||
}
|
||||
|
||||
QByteArray bufferCopy;
|
||||
{
|
||||
QBuffer device(&bufferCopy);
|
||||
device.open(QIODevice::ReadWrite);
|
||||
KeePass2Writer writer;
|
||||
writer.writeDatabase(&device, db.data());
|
||||
}
|
||||
|
||||
QCOMPARE(bufferCopy, bufferOriginal);
|
||||
}
|
||||
|
||||
void TestSharing::testNullObjects()
|
||||
{
|
||||
const QString empty;
|
||||
@@ -142,11 +91,10 @@ void TestSharing::testNullObjects()
|
||||
void TestSharing::testCertificateSerialization()
|
||||
{
|
||||
QFETCH(KeeShareSettings::Trust, trusted);
|
||||
const OpenSSHKey& key = stubkey();
|
||||
auto key = stubkey();
|
||||
KeeShareSettings::ScopedCertificate original;
|
||||
original.path = "/path";
|
||||
original.certificate = KeeShareSettings::Certificate{OpenSSHKey::serializeToBinary(OpenSSHKey::Public, key),
|
||||
"Some <!> &#_\"\" weird string"};
|
||||
original.certificate = KeeShareSettings::Certificate{key, "Some <!> &#_\"\" weird string"};
|
||||
original.trust = trusted;
|
||||
|
||||
QString buffer;
|
||||
@@ -156,17 +104,18 @@ void TestSharing::testCertificateSerialization()
|
||||
KeeShareSettings::ScopedCertificate::serialize(writer, original);
|
||||
writer.writeEndElement();
|
||||
writer.writeEndDocument();
|
||||
|
||||
QXmlStreamReader reader(buffer);
|
||||
reader.readNextStartElement();
|
||||
QVERIFY(reader.name() == "Certificate");
|
||||
KeeShareSettings::ScopedCertificate restored = KeeShareSettings::ScopedCertificate::deserialize(reader);
|
||||
|
||||
QCOMPARE(restored.certificate.key, original.certificate.key);
|
||||
QCOMPARE(restored.certificate.key->private_key_bits(), original.certificate.key->private_key_bits());
|
||||
QCOMPARE(restored.certificate.signer, original.certificate.signer);
|
||||
QCOMPARE(restored.trust, original.trust);
|
||||
QCOMPARE(restored.path, original.path);
|
||||
|
||||
QCOMPARE(restored.certificate.sshKey().publicParts(), key.publicParts());
|
||||
QCOMPARE(restored.certificate.key->public_key_bits(), key->public_key_bits());
|
||||
}
|
||||
|
||||
void TestSharing::testCertificateSerialization_data()
|
||||
@@ -179,9 +128,9 @@ void TestSharing::testCertificateSerialization_data()
|
||||
|
||||
void TestSharing::testKeySerialization()
|
||||
{
|
||||
const OpenSSHKey& key = stubkey();
|
||||
auto key = stubkey();
|
||||
KeeShareSettings::Key original;
|
||||
original.key = OpenSSHKey::serializeToBinary(OpenSSHKey::Private, key);
|
||||
original.key = key;
|
||||
|
||||
QString buffer;
|
||||
QXmlStreamWriter writer(&buffer);
|
||||
@@ -195,9 +144,8 @@ void TestSharing::testKeySerialization()
|
||||
QVERIFY(reader.name() == "Key");
|
||||
KeeShareSettings::Key restored = KeeShareSettings::Key::deserialize(reader);
|
||||
|
||||
QCOMPARE(restored.key, original.key);
|
||||
QCOMPARE(restored.sshKey().privateParts(), key.privateParts());
|
||||
QCOMPARE(restored.sshKey().type(), key.type());
|
||||
QCOMPARE(restored.key->private_key_bits(), original.key->private_key_bits());
|
||||
QCOMPARE(restored.key->algo_name(), original.key->algo_name());
|
||||
}
|
||||
|
||||
void TestSharing::testReferenceSerialization()
|
||||
@@ -263,31 +211,33 @@ void TestSharing::testSettingsSerialization()
|
||||
|
||||
QCOMPARE(restoredActive.in, importing);
|
||||
QCOMPARE(restoredActive.out, exporting);
|
||||
QCOMPARE(restoredOwn.certificate.key, ownCertificate.key);
|
||||
QCOMPARE(restoredOwn.key.key, ownKey.key);
|
||||
if (ownCertificate.key) {
|
||||
QCOMPARE(restoredOwn.certificate, ownCertificate);
|
||||
}
|
||||
if (ownKey.key) {
|
||||
QCOMPARE(restoredOwn.key, ownKey);
|
||||
}
|
||||
QCOMPARE(restoredForeign.certificates.count(), foreignCertificates.count());
|
||||
for (int i = 0; i < foreignCertificates.count(); ++i) {
|
||||
QCOMPARE(restoredForeign.certificates[i].certificate.key, foreignCertificates[i].certificate.key);
|
||||
QCOMPARE(restoredForeign.certificates[i].certificate, foreignCertificates[i].certificate);
|
||||
}
|
||||
}
|
||||
|
||||
void TestSharing::testSettingsSerialization_data()
|
||||
{
|
||||
const OpenSSHKey& sshKey0 = stubkey(0);
|
||||
auto sshKey0 = stubkey(0);
|
||||
KeeShareSettings::ScopedCertificate certificate0;
|
||||
certificate0.path = "/path/0";
|
||||
certificate0.certificate = KeeShareSettings::Certificate{OpenSSHKey::serializeToBinary(OpenSSHKey::Public, sshKey0),
|
||||
"Some <!> &#_\"\" weird string"};
|
||||
certificate0.certificate = KeeShareSettings::Certificate{sshKey0, "Some <!> &#_\"\" weird string"};
|
||||
certificate0.trust = KeeShareSettings::Trust::Trusted;
|
||||
|
||||
KeeShareSettings::Key key0;
|
||||
key0.key = OpenSSHKey::serializeToBinary(OpenSSHKey::Private, sshKey0);
|
||||
key0.key = sshKey0;
|
||||
|
||||
const OpenSSHKey& sshKey1 = stubkey(1);
|
||||
auto sshKey1 = stubkey(1);
|
||||
KeeShareSettings::ScopedCertificate certificate1;
|
||||
certificate1.path = "/path/1";
|
||||
certificate1.certificate =
|
||||
KeeShareSettings::Certificate{OpenSSHKey::serializeToBinary(OpenSSHKey::Public, sshKey1), "Another "};
|
||||
certificate1.certificate = KeeShareSettings::Certificate{sshKey1, "Another "};
|
||||
certificate1.trust = KeeShareSettings::Trust::Untrusted;
|
||||
|
||||
QTest::addColumn<bool>("importing");
|
||||
@@ -307,13 +257,12 @@ void TestSharing::testSettingsSerialization_data()
|
||||
<< QList<KeeShareSettings::ScopedCertificate>({certificate1});
|
||||
}
|
||||
|
||||
const OpenSSHKey& TestSharing::stubkey(int index)
|
||||
const QSharedPointer<Botan::RSA_PrivateKey> TestSharing::stubkey(int index)
|
||||
{
|
||||
static QMap<int, OpenSSHKey*> keys;
|
||||
static QMap<int, QSharedPointer<Botan::RSA_PrivateKey>> keys;
|
||||
if (!keys.contains(index)) {
|
||||
OpenSSHKey* key = new OpenSSHKey(OpenSSHKey::generate(false));
|
||||
key->setParent(this);
|
||||
keys[index] = key;
|
||||
keys.insert(index,
|
||||
QSharedPointer<Botan::RSA_PrivateKey>(new Botan::RSA_PrivateKey(*randomGen()->getRng(), 2048)));
|
||||
}
|
||||
return *keys[index];
|
||||
return keys[index];
|
||||
}
|
||||
|
||||
@@ -21,16 +21,16 @@
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
|
||||
class OpenSSHKey;
|
||||
|
||||
namespace Botan
|
||||
{
|
||||
class RSA_PrivateKey;
|
||||
}
|
||||
class TestSharing : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
void testIdempotentDatabaseWriting();
|
||||
void testNullObjects();
|
||||
void testCertificateSerialization();
|
||||
void testCertificateSerialization_data();
|
||||
@@ -41,7 +41,7 @@ private slots:
|
||||
void testSettingsSerialization_data();
|
||||
|
||||
private:
|
||||
const OpenSSHKey& stubkey(int iIndex = 0);
|
||||
const QSharedPointer<Botan::RSA_PrivateKey> stubkey(int index = 0);
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_TESTSHARING_H
|
||||
|
||||
@@ -22,9 +22,13 @@
|
||||
#include <QBuffer>
|
||||
|
||||
#include "crypto/Crypto.h"
|
||||
#include "crypto/ssh/OpenSSHKey.h"
|
||||
#include "crypto/Random.h"
|
||||
#include "keeshare/Signature.h"
|
||||
|
||||
#include <botan/pem.h>
|
||||
#include <botan/pkcs8.h>
|
||||
#include <botan/rsa.h>
|
||||
|
||||
QTEST_GUILESS_MAIN(TestSignature)
|
||||
static const char* rsa_2_private = "-----BEGIN RSA PRIVATE KEY-----\n"
|
||||
"MIIEowIBAAKCAQEAwGdladnqFfcDy02Gubx4sdBT8NYEg2YKXfcKLSwca5gV4X7I\n"
|
||||
@@ -107,6 +111,35 @@ static const char* rsa_1_public = "-----BEGIN RSA PUBLIC KEY-----\n"
|
||||
|
||||
static QByteArray data("Some trivial test with a longer .... ................................. longer text");
|
||||
|
||||
QSharedPointer<Botan::Private_Key> loadPrivateKey(const QString& pem)
|
||||
{
|
||||
try {
|
||||
std::string label;
|
||||
auto der = Botan::PEM_Code::decode(pem.toStdString(), label);
|
||||
auto key = new Botan::RSA_PrivateKey(
|
||||
Botan::AlgorithmIdentifier("RSA", Botan::AlgorithmIdentifier::USE_NULL_PARAM), der);
|
||||
return QSharedPointer<Botan::Private_Key>(key);
|
||||
} catch (std::exception& e) {
|
||||
qWarning("Failed to load key: %s", e.what());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
QSharedPointer<Botan::Public_Key> loadPublicKey(const QString& pem)
|
||||
{
|
||||
try {
|
||||
std::string label;
|
||||
auto der = Botan::PEM_Code::decode(pem.toStdString(), label);
|
||||
auto key =
|
||||
new Botan::RSA_PublicKey(Botan::AlgorithmIdentifier("RSA", Botan::AlgorithmIdentifier::USE_NULL_PARAM),
|
||||
std::vector<uint8_t>(der.begin(), der.end()));
|
||||
return QSharedPointer<Botan::Public_Key>(key);
|
||||
} catch (std::exception& e) {
|
||||
qWarning("Failed to load key: %s", e.what());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void TestSignature::initTestCase()
|
||||
{
|
||||
QVERIFY(Crypto::init());
|
||||
@@ -114,85 +147,70 @@ void TestSignature::initTestCase()
|
||||
|
||||
void TestSignature::testSigningOpenSSH_RSA_PrivateOnly()
|
||||
{
|
||||
OpenSSHKey privateKey;
|
||||
privateKey.parsePKCS1PEM(rsa_2_private);
|
||||
privateKey.openKey(QString());
|
||||
QCOMPARE(privateKey.fingerprint(), QString("SHA256:ZAQ/W1QdW59OaIh/0hs3ePl2og5TjXnGX5L0iN7WtNA"));
|
||||
Signature signer;
|
||||
const QString sign = signer.create(data, privateKey);
|
||||
QVERIFY(!sign.isEmpty());
|
||||
auto rsaKey = loadPrivateKey(rsa_2_private);
|
||||
QVERIFY(rsaKey);
|
||||
|
||||
QString sign;
|
||||
Signature::create(data, rsaKey, sign);
|
||||
QCOMPARE(sign, QString("rsa|%1").arg(QString::fromLatin1(rsa_2_sign)));
|
||||
|
||||
Signature verifier;
|
||||
const bool verified = verifier.verify(data, sign, privateKey);
|
||||
const bool verified = Signature::verify(data, rsaKey, sign);
|
||||
QCOMPARE(verified, true);
|
||||
}
|
||||
|
||||
void TestSignature::testSigningOpenSSH_RSA()
|
||||
{
|
||||
OpenSSHKey privateKey;
|
||||
privateKey.parsePKCS1PEM(rsa_2_private);
|
||||
privateKey.openKey(QString());
|
||||
QCOMPARE(privateKey.fingerprint(), QString("SHA256:ZAQ/W1QdW59OaIh/0hs3ePl2og5TjXnGX5L0iN7WtNA"));
|
||||
Signature signer;
|
||||
const QString sign = signer.create(data, privateKey);
|
||||
auto privateKey = loadPrivateKey(rsa_2_private);
|
||||
QVERIFY(privateKey);
|
||||
|
||||
QString sign;
|
||||
Signature::create(data, privateKey, sign);
|
||||
QVERIFY(!sign.isEmpty());
|
||||
|
||||
OpenSSHKey publicKey;
|
||||
publicKey.parsePKCS1PEM(rsa_2_public);
|
||||
publicKey.openKey(QString());
|
||||
QCOMPARE(publicKey.fingerprint(), QString("SHA256:ZAQ/W1QdW59OaIh/0hs3ePl2og5TjXnGX5L0iN7WtNA"));
|
||||
auto publicKey = loadPublicKey(rsa_2_public);
|
||||
QVERIFY(publicKey);
|
||||
|
||||
Signature verifier;
|
||||
const bool verified = verifier.verify(data, sign, publicKey);
|
||||
const bool verified = Signature::verify(data, publicKey, sign);
|
||||
QCOMPARE(verified, true);
|
||||
}
|
||||
|
||||
void TestSignature::testSigningGenerated_RSA_PrivateOnly()
|
||||
{
|
||||
OpenSSHKey privateKey = OpenSSHKey::generate(false);
|
||||
privateKey.openKey(QString());
|
||||
QSharedPointer<Botan::Private_Key> key(new Botan::RSA_PrivateKey(*randomGen()->getRng(), 2048));
|
||||
|
||||
Signature signer;
|
||||
const QString sign = signer.create(data, privateKey);
|
||||
QString sign;
|
||||
Signature::create(data, key, sign);
|
||||
QVERIFY(!sign.isEmpty());
|
||||
|
||||
Signature verifier;
|
||||
const bool verified = verifier.verify(data, sign, privateKey);
|
||||
const bool verified = Signature::verify(data, key, sign);
|
||||
QCOMPARE(verified, true);
|
||||
}
|
||||
|
||||
void TestSignature::testSigningTest_RSA_PrivateOnly()
|
||||
{
|
||||
OpenSSHKey privateKey;
|
||||
privateKey.parsePKCS1PEM(rsa_1_private);
|
||||
privateKey.openKey(QString());
|
||||
QCOMPARE(privateKey.fingerprint(), QString("SHA256:DYdaZciYNxCejr+/8x+OKYxeTU1D5UsuIFUG4PWRFkk"));
|
||||
Signature signer;
|
||||
const QString sign = signer.create(data, privateKey);
|
||||
auto rsaKey = loadPrivateKey(rsa_2_private);
|
||||
QVERIFY(rsaKey);
|
||||
|
||||
QString sign;
|
||||
Signature::create(data, rsaKey, sign);
|
||||
QVERIFY(!sign.isEmpty());
|
||||
|
||||
Signature verifier;
|
||||
const bool verified = verifier.verify(data, sign, privateKey);
|
||||
const bool verified = Signature::verify(data, rsaKey, sign);
|
||||
QCOMPARE(verified, true);
|
||||
}
|
||||
|
||||
void TestSignature::testSigningTest_RSA()
|
||||
{
|
||||
OpenSSHKey privateKey;
|
||||
privateKey.parsePKCS1PEM(rsa_1_private);
|
||||
privateKey.openKey(QString());
|
||||
QCOMPARE(privateKey.fingerprint(), QString("SHA256:DYdaZciYNxCejr+/8x+OKYxeTU1D5UsuIFUG4PWRFkk"));
|
||||
Signature signer;
|
||||
const QString sign = signer.create(data, privateKey);
|
||||
auto privateKey = loadPrivateKey(rsa_1_private);
|
||||
QVERIFY(privateKey);
|
||||
|
||||
QString sign;
|
||||
Signature::create(data, privateKey, sign);
|
||||
QVERIFY(!sign.isEmpty());
|
||||
|
||||
OpenSSHKey publicKey;
|
||||
publicKey.parsePKCS1PEM(rsa_1_public);
|
||||
publicKey.openKey(QString());
|
||||
QCOMPARE(publicKey.fingerprint(), QString("SHA256:DYdaZciYNxCejr+/8x+OKYxeTU1D5UsuIFUG4PWRFkk"));
|
||||
Signature verifier;
|
||||
const bool verified = verifier.verify(data, sign, publicKey);
|
||||
auto publicKey = loadPublicKey(rsa_1_public);
|
||||
QVERIFY(publicKey);
|
||||
|
||||
const bool verified = Signature::verify(data, publicKey, sign);
|
||||
QCOMPARE(verified, true);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "streams/SymmetricCipherStream.h"
|
||||
|
||||
QTEST_GUILESS_MAIN(TestSymmetricCipher)
|
||||
Q_DECLARE_METATYPE(SymmetricCipher::Algorithm);
|
||||
Q_DECLARE_METATYPE(SymmetricCipher::Mode);
|
||||
Q_DECLARE_METATYPE(SymmetricCipher::Direction);
|
||||
|
||||
@@ -35,19 +34,18 @@ void TestSymmetricCipher::initTestCase()
|
||||
QVERIFY(Crypto::init());
|
||||
}
|
||||
|
||||
void TestSymmetricCipher::testAlgorithmToCipher()
|
||||
void TestSymmetricCipher::testCipherUuidToMode()
|
||||
{
|
||||
QCOMPARE(SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm::Aes128), KeePass2::CIPHER_AES128);
|
||||
QCOMPARE(SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm::Aes256), KeePass2::CIPHER_AES256);
|
||||
QCOMPARE(SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm::Twofish), KeePass2::CIPHER_TWOFISH);
|
||||
QCOMPARE(SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm::ChaCha20), KeePass2::CIPHER_CHACHA20);
|
||||
QCOMPARE(SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm::InvalidAlgorithm), QUuid());
|
||||
QCOMPARE(SymmetricCipher::cipherUuidToMode(KeePass2::CIPHER_AES128), SymmetricCipher::Aes128_CBC);
|
||||
QCOMPARE(SymmetricCipher::cipherUuidToMode(KeePass2::CIPHER_AES256), SymmetricCipher::Aes256_CBC);
|
||||
QCOMPARE(SymmetricCipher::cipherUuidToMode(KeePass2::CIPHER_TWOFISH), SymmetricCipher::Twofish_CBC);
|
||||
QCOMPARE(SymmetricCipher::cipherUuidToMode(KeePass2::CIPHER_CHACHA20), SymmetricCipher::ChaCha20);
|
||||
QCOMPARE(SymmetricCipher::cipherUuidToMode(QUuid()), SymmetricCipher::InvalidMode);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
void TestSymmetricCipher::testEncryptionDecryption_data()
|
||||
{
|
||||
QTest::addColumn<SymmetricCipher::Algorithm>("algorithm");
|
||||
QTest::addColumn<SymmetricCipher::Mode>("mode");
|
||||
QTest::addColumn<SymmetricCipher::Direction>("direction");
|
||||
QTest::addColumn<QByteArray>("key");
|
||||
@@ -57,64 +55,57 @@ void TestSymmetricCipher::testEncryptionDecryption_data()
|
||||
|
||||
// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
|
||||
QTest::newRow("AES128-CBC Encryption")
|
||||
<< SymmetricCipher::Aes128
|
||||
<< SymmetricCipher::Cbc
|
||||
<< SymmetricCipher::Aes128_CBC
|
||||
<< SymmetricCipher::Encrypt
|
||||
<< QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c")
|
||||
<< QByteArray::fromHex("000102030405060708090a0b0c0d0e0f")
|
||||
<< QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51")
|
||||
<< QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a")
|
||||
<< QByteArray::fromHex("7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b2");
|
||||
|
||||
QTest::newRow("AES128-CBC Decryption")
|
||||
<< SymmetricCipher::Aes128
|
||||
<< SymmetricCipher::Cbc
|
||||
<< SymmetricCipher::Aes128_CBC
|
||||
<< SymmetricCipher::Decrypt
|
||||
<< QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c")
|
||||
<< QByteArray::fromHex("000102030405060708090a0b0c0d0e0f")
|
||||
<< QByteArray::fromHex("7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b2")
|
||||
<< QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51");
|
||||
<< QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a");
|
||||
|
||||
QTest::newRow("AES256-CBC Encryption")
|
||||
<< SymmetricCipher::Aes256
|
||||
<< SymmetricCipher::Cbc
|
||||
<< SymmetricCipher::Aes256_CBC
|
||||
<< SymmetricCipher::Encrypt
|
||||
<< QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4")
|
||||
<< QByteArray::fromHex("000102030405060708090a0b0c0d0e0f")
|
||||
<< QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51")
|
||||
<< QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a")
|
||||
<< QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d");
|
||||
|
||||
QTest::newRow("AES256-CBC Decryption")
|
||||
<< SymmetricCipher::Aes256
|
||||
<< SymmetricCipher::Cbc
|
||||
<< SymmetricCipher::Aes256_CBC
|
||||
<< SymmetricCipher::Decrypt
|
||||
<< QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4")
|
||||
<< QByteArray::fromHex("000102030405060708090a0b0c0d0e0f")
|
||||
<< QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d")
|
||||
<< QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51");
|
||||
<< QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a");
|
||||
|
||||
QTest::newRow("AES256-CTR Encryption")
|
||||
<< SymmetricCipher::Aes256
|
||||
<< SymmetricCipher::Ctr
|
||||
<< SymmetricCipher::Aes256_CTR
|
||||
<< SymmetricCipher::Encrypt
|
||||
<< QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4")
|
||||
<< QByteArray::fromHex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff")
|
||||
<< QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51")
|
||||
<< QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a")
|
||||
<< QByteArray::fromHex("601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5");
|
||||
|
||||
QTest::newRow("AES256-CTR Decryption")
|
||||
<< SymmetricCipher::Aes256
|
||||
<< SymmetricCipher::Ctr
|
||||
<< SymmetricCipher::Aes256_CTR
|
||||
<< SymmetricCipher::Decrypt
|
||||
<< QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4")
|
||||
<< QByteArray::fromHex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff")
|
||||
<< QByteArray::fromHex("601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5")
|
||||
<< QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51");
|
||||
<< QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a");
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
void TestSymmetricCipher::testEncryptionDecryption()
|
||||
{
|
||||
QFETCH(SymmetricCipher::Algorithm, algorithm);
|
||||
QFETCH(SymmetricCipher::Mode, mode);
|
||||
QFETCH(SymmetricCipher::Direction, direction);
|
||||
QFETCH(QByteArray, key);
|
||||
@@ -122,66 +113,51 @@ void TestSymmetricCipher::testEncryptionDecryption()
|
||||
QFETCH(QByteArray, plainText);
|
||||
QFETCH(QByteArray, cipherText);
|
||||
|
||||
bool ok;
|
||||
SymmetricCipher cipher(algorithm, mode, direction);
|
||||
QVERIFY(cipher.init(key, iv));
|
||||
QCOMPARE(cipher.blockSize(), 16);
|
||||
QCOMPARE(cipher.process(plainText, &ok), cipherText);
|
||||
QVERIFY(ok);
|
||||
QByteArray data = plainText;
|
||||
SymmetricCipher cipher;
|
||||
QVERIFY(cipher.init(mode, direction, key, iv));
|
||||
QVERIFY(cipher.process(data));
|
||||
QCOMPARE(data.left(16), cipherText.left(16));
|
||||
|
||||
if (mode == SymmetricCipher::Cbc) {
|
||||
QBuffer buffer;
|
||||
SymmetricCipherStream stream(&buffer, algorithm, mode, direction);
|
||||
QVERIFY(stream.init(key, iv));
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
QVERIFY(stream.open(QIODevice::WriteOnly));
|
||||
QVERIFY(stream.reset());
|
||||
|
||||
buffer.reset();
|
||||
buffer.buffer().clear();
|
||||
QCOMPARE(stream.write(plainText.left(16)), qint64(16));
|
||||
QCOMPARE(buffer.data(), cipherText.left(16));
|
||||
QVERIFY(stream.reset());
|
||||
// make sure padding is written
|
||||
QCOMPARE(buffer.data().size(), 32);
|
||||
|
||||
buffer.reset();
|
||||
buffer.buffer().clear();
|
||||
QCOMPARE(stream.write(plainText.left(10)), qint64(10));
|
||||
QVERIFY(buffer.data().isEmpty());
|
||||
|
||||
QVERIFY(stream.reset());
|
||||
buffer.reset();
|
||||
buffer.buffer().clear();
|
||||
QCOMPARE(stream.write(plainText.left(10)), qint64(10));
|
||||
stream.close();
|
||||
QCOMPARE(buffer.data().size(), 16);
|
||||
}
|
||||
QBuffer buffer;
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
SymmetricCipherStream stream(&buffer);
|
||||
QVERIFY(stream.init(mode, direction, key, iv));
|
||||
QVERIFY(stream.open(QIODevice::WriteOnly));
|
||||
QCOMPARE(stream.write(plainText.left(16)), qint64(16));
|
||||
stream.close();
|
||||
QCOMPARE(buffer.data().left(16), cipherText.left(16));
|
||||
}
|
||||
|
||||
void TestSymmetricCipher::testAesCbcPadding_data()
|
||||
{
|
||||
QTest::addColumn<SymmetricCipher::Mode>("mode");
|
||||
QTest::addColumn<QByteArray>("key");
|
||||
QTest::addColumn<QByteArray>("iv");
|
||||
QTest::addColumn<QByteArray>("cipherText");
|
||||
QTest::addColumn<QByteArray>("plainText");
|
||||
QTest::addColumn<QByteArray>("padding");
|
||||
|
||||
QTest::newRow("AES128") << QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c")
|
||||
// clang-format off
|
||||
QTest::newRow("AES128") << SymmetricCipher::Aes128_CBC
|
||||
<< QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c")
|
||||
<< QByteArray::fromHex("000102030405060708090a0b0c0d0e0f")
|
||||
<< QByteArray::fromHex("7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b2")
|
||||
<< QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51")
|
||||
<< QByteArray::fromHex("55e21d7100b988ffec32feeafaf23538");
|
||||
|
||||
QTest::newRow("AES256") << QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4")
|
||||
QTest::newRow("AES256") << SymmetricCipher::Aes256_CBC
|
||||
<< QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4")
|
||||
<< QByteArray::fromHex("000102030405060708090a0b0c0d0e0f")
|
||||
<< QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d")
|
||||
<< QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51")
|
||||
<< QByteArray::fromHex("3a3aa5e0213db1a9901f9036cf5102d2");
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
void TestSymmetricCipher::testAesCbcPadding()
|
||||
{
|
||||
QFETCH(SymmetricCipher::Mode, mode);
|
||||
QFETCH(QByteArray, key);
|
||||
QFETCH(QByteArray, iv);
|
||||
QFETCH(QByteArray, cipherText);
|
||||
@@ -192,64 +168,36 @@ void TestSymmetricCipher::testAesCbcPadding()
|
||||
QByteArray cipherTextPadded = cipherText + padding;
|
||||
|
||||
QBuffer buffer(&cipherTextPadded);
|
||||
SymmetricCipherStream stream(&buffer, SymmetricCipher::Aes128, SymmetricCipher::Cbc, SymmetricCipher::Decrypt);
|
||||
QVERIFY(stream.init(key, iv));
|
||||
SymmetricCipherStream stream(&buffer);
|
||||
QVERIFY(stream.init(mode, SymmetricCipher::Decrypt, key, iv));
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
QVERIFY(stream.open(QIODevice::ReadOnly));
|
||||
|
||||
QCOMPARE(stream.read(10), plainText.left(10));
|
||||
buffer.reset();
|
||||
QVERIFY(stream.reset());
|
||||
QVERIFY(stream.init(mode, SymmetricCipher::Decrypt, key, iv));
|
||||
QCOMPARE(stream.read(20), plainText.left(20));
|
||||
buffer.reset();
|
||||
QVERIFY(stream.reset());
|
||||
QVERIFY(stream.init(mode, SymmetricCipher::Decrypt, key, iv));
|
||||
QCOMPARE(stream.read(16), plainText.left(16));
|
||||
buffer.reset();
|
||||
QVERIFY(stream.reset());
|
||||
QVERIFY(stream.init(mode, SymmetricCipher::Decrypt, key, iv));
|
||||
QCOMPARE(stream.read(100), plainText);
|
||||
}
|
||||
|
||||
void TestSymmetricCipher::testInplaceEcb_data()
|
||||
void TestSymmetricCipher::testAesKdf()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("key");
|
||||
QTest::addColumn<QByteArray>("plainText");
|
||||
QTest::addColumn<QByteArray>("cipherText");
|
||||
auto key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
|
||||
auto data = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a");
|
||||
auto result = QByteArray::fromHex("f3eed1bdb5d2a03c064b5a7e3db181f8");
|
||||
|
||||
QTest::newRow("AES128") << QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c")
|
||||
<< QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a")
|
||||
<< QByteArray::fromHex("3ad77bb40d7a3660a89ecaf32466ef97");
|
||||
}
|
||||
QVERIFY(SymmetricCipher::aesKdf(key, 1, data));
|
||||
QCOMPARE(data, result);
|
||||
|
||||
void TestSymmetricCipher::testInplaceEcb()
|
||||
{
|
||||
QFETCH(QByteArray, key);
|
||||
QFETCH(QByteArray, plainText);
|
||||
QFETCH(QByteArray, cipherText);
|
||||
|
||||
SymmetricCipher cipherInPlaceEnc(SymmetricCipher::Aes128, SymmetricCipher::Ecb, SymmetricCipher::Encrypt);
|
||||
QVERIFY(cipherInPlaceEnc.init(key, QByteArray(16, 0)));
|
||||
QCOMPARE(cipherInPlaceEnc.blockSize(), 16);
|
||||
auto data = QByteArray(plainText);
|
||||
QVERIFY(cipherInPlaceEnc.processInPlace(data));
|
||||
QCOMPARE(data, cipherText);
|
||||
|
||||
SymmetricCipher cipherInPlaceDec(SymmetricCipher::Aes128, SymmetricCipher::Ecb, SymmetricCipher::Decrypt);
|
||||
QVERIFY(cipherInPlaceDec.init(key, QByteArray(16, 0)));
|
||||
QCOMPARE(cipherInPlaceDec.blockSize(), 16);
|
||||
QVERIFY(cipherInPlaceDec.processInPlace(data));
|
||||
QCOMPARE(data, plainText);
|
||||
|
||||
SymmetricCipher cipherInPlaceEnc2(SymmetricCipher::Aes128, SymmetricCipher::Ecb, SymmetricCipher::Encrypt);
|
||||
QVERIFY(cipherInPlaceEnc2.init(key, QByteArray(16, 0)));
|
||||
QCOMPARE(cipherInPlaceEnc2.blockSize(), 16);
|
||||
data = QByteArray(plainText);
|
||||
QVERIFY(cipherInPlaceEnc2.processInPlace(data, 100));
|
||||
|
||||
SymmetricCipher cipherInPlaceDec2(SymmetricCipher::Aes128, SymmetricCipher::Ecb, SymmetricCipher::Decrypt);
|
||||
QVERIFY(cipherInPlaceDec2.init(key, QByteArray(16, 0)));
|
||||
QCOMPARE(cipherInPlaceDec2.blockSize(), 16);
|
||||
QVERIFY(cipherInPlaceDec2.processInPlace(data, 100));
|
||||
QCOMPARE(data, plainText);
|
||||
// TODO: Test multiple rounds of AES KDF
|
||||
}
|
||||
|
||||
void TestSymmetricCipher::testTwofish256CbcEncryption()
|
||||
@@ -278,32 +226,30 @@ void TestSymmetricCipher::testTwofish256CbcEncryption()
|
||||
QByteArray::fromHex("957108025BFD57125B40057BC2DE4FE2"),
|
||||
QByteArray::fromHex("6F725C5950133F82EF021A94CADC8508")};
|
||||
|
||||
SymmetricCipher cipher(SymmetricCipher::Twofish, SymmetricCipher::Cbc, SymmetricCipher::Encrypt);
|
||||
SymmetricCipher cipher;
|
||||
|
||||
for (int i = 0; i < keys.size(); ++i) {
|
||||
QVERIFY(cipher.init(keys[i], ivs[i]));
|
||||
QVERIFY(cipher.init(SymmetricCipher::Twofish_CBC, SymmetricCipher::Encrypt, keys[i], ivs[i]));
|
||||
QByteArray ptNext = plainTexts[i];
|
||||
QByteArray ctPrev = ivs[i];
|
||||
QByteArray ctCur;
|
||||
QCOMPARE(cipher.blockSize(), 16);
|
||||
bool ok = false;
|
||||
QCOMPARE(cipher.blockSize(cipher.mode()), 16);
|
||||
for (int j = 0; j < 5000; ++j) {
|
||||
ctCur = cipher.process(ptNext, &ok);
|
||||
if (!ok) {
|
||||
if (!cipher.process(ptNext)) {
|
||||
break;
|
||||
}
|
||||
ctCur = ptNext;
|
||||
ptNext = ctPrev;
|
||||
ctPrev = ctCur;
|
||||
|
||||
ctCur = cipher.process(ptNext, &ok);
|
||||
if (!ok) {
|
||||
if (!cipher.process(ptNext)) {
|
||||
break;
|
||||
}
|
||||
ctCur = ptNext;
|
||||
ptNext = ctPrev;
|
||||
ctPrev = ctCur;
|
||||
}
|
||||
|
||||
QVERIFY(ok);
|
||||
QCOMPARE(ctCur, cipherTexts[i]);
|
||||
}
|
||||
}
|
||||
@@ -334,30 +280,24 @@ void TestSymmetricCipher::testTwofish256CbcDecryption()
|
||||
QByteArray::fromHex("A792AC61E7110C434BC2BBCAB6E53CAE"),
|
||||
QByteArray::fromHex("4C81F5BDC1081170FF96F50B1F76A566")};
|
||||
|
||||
SymmetricCipher cipher(SymmetricCipher::Twofish, SymmetricCipher::Cbc, SymmetricCipher::Decrypt);
|
||||
SymmetricCipher cipher;
|
||||
|
||||
for (int i = 0; i < keys.size(); ++i) {
|
||||
cipher.init(keys[i], ivs[i]);
|
||||
QVERIFY(cipher.init(SymmetricCipher::Twofish_CBC, SymmetricCipher::Decrypt, keys[i], ivs[i]));
|
||||
QByteArray ctNext = cipherTexts[i];
|
||||
QByteArray ptCur;
|
||||
QCOMPARE(cipher.blockSize(), 16);
|
||||
bool ok = false;
|
||||
QCOMPARE(cipher.blockSize(cipher.mode()), 16);
|
||||
for (int j = 0; j < 5000; ++j) {
|
||||
ptCur = cipher.process(ctNext, &ok);
|
||||
if (!ok) {
|
||||
if (!cipher.process(ctNext)) {
|
||||
break;
|
||||
}
|
||||
ctNext = ptCur;
|
||||
|
||||
ptCur = cipher.process(ctNext, &ok);
|
||||
if (!ok) {
|
||||
if (!cipher.process(ctNext)) {
|
||||
break;
|
||||
}
|
||||
ctNext = ptCur;
|
||||
}
|
||||
|
||||
QVERIFY(ok);
|
||||
QCOMPARE(ptCur, plainTexts[i]);
|
||||
QCOMPARE(ctNext, plainTexts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,20 +307,22 @@ void TestSymmetricCipher::testSalsa20()
|
||||
|
||||
QByteArray key = QByteArray::fromHex("F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112");
|
||||
QByteArray iv = QByteArray::fromHex("0000000000000000");
|
||||
bool ok;
|
||||
|
||||
SymmetricCipher cipher(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt);
|
||||
QVERIFY(cipher.init(key, iv));
|
||||
SymmetricCipher cipher;
|
||||
QVERIFY(cipher.init(SymmetricCipher::Salsa20, SymmetricCipher::Encrypt, key, iv));
|
||||
|
||||
QByteArray cipherTextA;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
cipherTextA.append(cipher.process(QByteArray(64, '\0'), &ok));
|
||||
QVERIFY(ok);
|
||||
QByteArray data(64, '\0');
|
||||
QVERIFY(cipher.process(data));
|
||||
cipherTextA.append(data);
|
||||
}
|
||||
cipher.reset();
|
||||
|
||||
QByteArray cipherTextB = cipher.process(QByteArray(512, '\0'), &ok);
|
||||
QVERIFY(ok);
|
||||
// Re-initialize
|
||||
QVERIFY(cipher.init(SymmetricCipher::Salsa20, SymmetricCipher::Encrypt, key, iv));
|
||||
|
||||
QByteArray cipherTextB(512, '\0');
|
||||
QVERIFY(cipher.process(cipherTextB));
|
||||
cipher.reset();
|
||||
|
||||
QByteArray expectedCipherText1;
|
||||
@@ -421,50 +363,52 @@ void TestSymmetricCipher::testSalsa20()
|
||||
void TestSymmetricCipher::testChaCha20()
|
||||
{
|
||||
// https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7
|
||||
bool ok;
|
||||
|
||||
{
|
||||
QByteArray key = QByteArray::fromHex("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
QByteArray iv = QByteArray::fromHex("0000000000000000");
|
||||
SymmetricCipher cipher(SymmetricCipher::ChaCha20, SymmetricCipher::Stream, SymmetricCipher::Encrypt);
|
||||
QVERIFY(cipher.init(key, iv));
|
||||
QCOMPARE(cipher.process(QByteArray(64, 0), &ok),
|
||||
SymmetricCipher cipher;
|
||||
QByteArray data(64, 0);
|
||||
QVERIFY(cipher.init(SymmetricCipher::ChaCha20, SymmetricCipher::Encrypt, key, iv));
|
||||
QVERIFY(cipher.process(data));
|
||||
QCOMPARE(data,
|
||||
QByteArray::fromHex("76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7"
|
||||
"724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"));
|
||||
QVERIFY(ok);
|
||||
}
|
||||
|
||||
{
|
||||
QByteArray key = QByteArray::fromHex("0000000000000000000000000000000000000000000000000000000000000001");
|
||||
QByteArray iv = QByteArray::fromHex("0000000000000000");
|
||||
SymmetricCipher cipher(SymmetricCipher::ChaCha20, SymmetricCipher::Stream, SymmetricCipher::Encrypt);
|
||||
QVERIFY(cipher.init(key, iv));
|
||||
QCOMPARE(cipher.process(QByteArray(64, 0), &ok),
|
||||
SymmetricCipher cipher;
|
||||
QByteArray data(64, 0);
|
||||
QVERIFY(cipher.init(SymmetricCipher::ChaCha20, SymmetricCipher::Encrypt, key, iv));
|
||||
QVERIFY(cipher.process(data));
|
||||
QCOMPARE(data,
|
||||
QByteArray::fromHex("4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a"
|
||||
"5d1e7e20d42af2c53d792b1c43fea817e9ad275ae546963"));
|
||||
QVERIFY(ok);
|
||||
}
|
||||
|
||||
{
|
||||
QByteArray key = QByteArray::fromHex("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
QByteArray iv = QByteArray::fromHex("0000000000000001");
|
||||
SymmetricCipher cipher(SymmetricCipher::ChaCha20, SymmetricCipher::Stream, SymmetricCipher::Encrypt);
|
||||
QVERIFY(cipher.init(key, iv));
|
||||
QCOMPARE(cipher.process(QByteArray(60, 0), &ok),
|
||||
SymmetricCipher cipher;
|
||||
QByteArray data(60, 0);
|
||||
QVERIFY(cipher.init(SymmetricCipher::ChaCha20, SymmetricCipher::Encrypt, key, iv));
|
||||
QVERIFY(cipher.process(data));
|
||||
QCOMPARE(data,
|
||||
QByteArray::fromHex("de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a050278a708452"
|
||||
"7214f73efc7fa5b5277062eb7a0433e445f41e3"));
|
||||
QVERIFY(ok);
|
||||
}
|
||||
|
||||
{
|
||||
QByteArray key = QByteArray::fromHex("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
QByteArray iv = QByteArray::fromHex("0100000000000000");
|
||||
SymmetricCipher cipher(SymmetricCipher::ChaCha20, SymmetricCipher::Stream, SymmetricCipher::Encrypt);
|
||||
QVERIFY(cipher.init(key, iv));
|
||||
QCOMPARE(cipher.process(QByteArray(64, 0), &ok),
|
||||
SymmetricCipher cipher;
|
||||
QByteArray data(64, 0);
|
||||
QVERIFY(cipher.init(SymmetricCipher::ChaCha20, SymmetricCipher::Encrypt, key, iv));
|
||||
QVERIFY(cipher.process(data));
|
||||
QCOMPARE(data,
|
||||
QByteArray::fromHex("ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca"
|
||||
"8ad6426194a88545ddc497a0b466e7d6bbdb0041b2f586b"));
|
||||
QVERIFY(ok);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,8 +421,8 @@ void TestSymmetricCipher::testPadding()
|
||||
QBuffer buffer;
|
||||
buffer.open(QIODevice::ReadWrite);
|
||||
|
||||
SymmetricCipherStream streamEnc(&buffer, SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt);
|
||||
QVERIFY(streamEnc.init(key, iv));
|
||||
SymmetricCipherStream streamEnc(&buffer);
|
||||
QVERIFY(streamEnc.init(SymmetricCipher::Aes256_CBC, SymmetricCipher::Encrypt, key, iv));
|
||||
streamEnc.open(QIODevice::WriteOnly);
|
||||
streamEnc.write(plainText);
|
||||
streamEnc.close();
|
||||
@@ -486,8 +430,8 @@ void TestSymmetricCipher::testPadding()
|
||||
// make sure padding is written
|
||||
QCOMPARE(buffer.buffer().size(), 16);
|
||||
|
||||
SymmetricCipherStream streamDec(&buffer, SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt);
|
||||
QVERIFY(streamDec.init(key, iv));
|
||||
SymmetricCipherStream streamDec(&buffer);
|
||||
QVERIFY(streamDec.init(SymmetricCipher::Aes256_CBC, SymmetricCipher::Decrypt, key, iv));
|
||||
streamDec.open(QIODevice::ReadOnly);
|
||||
QByteArray decrypted = streamDec.readAll();
|
||||
QCOMPARE(decrypted, plainText);
|
||||
@@ -500,8 +444,8 @@ void TestSymmetricCipher::testStreamReset()
|
||||
|
||||
QBuffer buffer;
|
||||
QVERIFY(buffer.open(QIODevice::WriteOnly));
|
||||
SymmetricCipherStream writer(&buffer, SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt);
|
||||
QVERIFY(writer.init(key, iv));
|
||||
SymmetricCipherStream writer(&buffer);
|
||||
QVERIFY(writer.init(SymmetricCipher::Aes256_CBC, SymmetricCipher::Encrypt, key, iv));
|
||||
QVERIFY(writer.open(QIODevice::WriteOnly));
|
||||
QCOMPARE(writer.write(QByteArray(4, 'Z')), qint64(4));
|
||||
// test if reset() and close() write only one block
|
||||
|
||||
@@ -27,13 +27,12 @@ class TestSymmetricCipher : public QObject
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void testAlgorithmToCipher();
|
||||
void testCipherUuidToMode();
|
||||
void testEncryptionDecryption_data();
|
||||
void testEncryptionDecryption();
|
||||
void testAesCbcPadding_data();
|
||||
void testAesCbcPadding();
|
||||
void testInplaceEcb_data();
|
||||
void testInplaceEcb();
|
||||
void testAesKdf();
|
||||
void testTwofish256CbcEncryption();
|
||||
void testTwofish256CbcDecryption();
|
||||
void testSalsa20();
|
||||
|
||||
@@ -88,5 +88,5 @@ void TestYubiKeyChallengeResponse::testKeyChallenge()
|
||||
|
||||
QByteArray ba("UnitTest");
|
||||
QVERIFY(key->challenge(ba));
|
||||
QCOMPARE(key->rawKey().size(), 20);
|
||||
QCOMPARE(key->rawKey().size(), 20UL);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "core/Config.h"
|
||||
#include "core/Tools.h"
|
||||
#include "crypto/Crypto.h"
|
||||
#include "crypto/Random.h"
|
||||
#include "gui/Application.h"
|
||||
#include "gui/DatabaseTabWidget.h"
|
||||
#include "gui/DatabaseWidget.h"
|
||||
@@ -160,49 +161,22 @@ void TestGuiFdoSecrets::initTestCase()
|
||||
VERIFY(m_plugin);
|
||||
m_mainWindow->show();
|
||||
|
||||
auto key = QByteArray::fromHex("e407997e8b918419cf851cf3345358fdf"
|
||||
"ffb9564a220ac9c3934efd277cea20d17"
|
||||
"467ecdc56e817f75ac39501f38a4a04ff"
|
||||
"64d627e16c09981c7ad876da255b61c8e"
|
||||
"6a8408236c2a4523cfe6961c26dbdfc77"
|
||||
"c1a27a5b425ca71a019e829fae32c0b42"
|
||||
"0e1b3096b48bc2ce9ccab1d1ff13a5eb4"
|
||||
"b263cee30bdb1a57af9bfa93f");
|
||||
m_clientCipher.reset(new DhIetf1024Sha256Aes128CbcPkcs7(key));
|
||||
|
||||
// Load the NewDatabase.kdbx file into temporary storage
|
||||
QFile sourceDbFile(QStringLiteral(KEEPASSX_TEST_DATA_DIR "/NewDatabase.kdbx"));
|
||||
VERIFY(sourceDbFile.open(QIODevice::ReadOnly));
|
||||
VERIFY(Tools::readAllFromDevice(&sourceDbFile, m_dbData));
|
||||
sourceDbFile.close();
|
||||
|
||||
// set keys for session encryption
|
||||
m_serverPublic = MpiFromHex("e407997e8b918419cf851cf3345358fdf"
|
||||
"ffb9564a220ac9c3934efd277cea20d17"
|
||||
"467ecdc56e817f75ac39501f38a4a04ff"
|
||||
"64d627e16c09981c7ad876da255b61c8e"
|
||||
"6a8408236c2a4523cfe6961c26dbdfc77"
|
||||
"c1a27a5b425ca71a019e829fae32c0b42"
|
||||
"0e1b3096b48bc2ce9ccab1d1ff13a5eb4"
|
||||
"b263cee30bdb1a57af9bfa93f");
|
||||
m_serverPrivate = MpiFromHex("013f4f3381ef0ca11c4c7363079577b56"
|
||||
"99b238644e0aba47e24bdba6173590216"
|
||||
"4f1e12dd0944800a373e090e63192f53b"
|
||||
"93583e9a9e50bb9d792aafaa3a0f5ae77"
|
||||
"de0c3423f5820848d88ee3bdd01c889f2"
|
||||
"7af58a02f5b6693d422b9d189b300d7b1"
|
||||
"be5076b5795cf8808c31e2e2898368d18"
|
||||
"ab5c26b0ea3480c9aba8154cf");
|
||||
// use the same cipher to do the client side encryption, but exchange the position of client/server keys
|
||||
m_cipher.reset(new DhIetf1024Sha256Aes128CbcPkcs7);
|
||||
VERIFY(m_cipher->initialize(MpiFromBytes(MpiToBytes(m_serverPublic)),
|
||||
MpiFromHex("30d18c6b328bac970c05bda6af2e708b9"
|
||||
"d6bbbb6dc136c1a2d96e870fabc86ad74"
|
||||
"1846a26a4197f32f65ea2e7580ad2afe3"
|
||||
"dd5d6c1224b8368b0df2cd75d520a9ff9"
|
||||
"7fe894cc7da71b7bd285b4633359c16c8"
|
||||
"d341f822fa4f0fdf59b5d3448658c46a2"
|
||||
"a86dbb14ff85823873f8a259ccc52bbb8"
|
||||
"2b5a4c2a75447982553b42221"),
|
||||
MpiFromHex("84aafe9c9f356f7762307f4d791acb59e"
|
||||
"8e3fd562abdbb481d0587f8400ad6c51d"
|
||||
"af561a1beb9a22c8cd4d2807367c5787b"
|
||||
"2e06d631ccbb5194b6bb32211583ce688"
|
||||
"f9c2cebc22a9e4d494d12ebdd570c61a1"
|
||||
"62a94e88561d25ccd0415339d1f59e1b0"
|
||||
"6bc6b6b5fde46e23b2410eb034be390d3"
|
||||
"2407ec7ae90f0831f24afd5ac")));
|
||||
|
||||
// set a fake dbus client all the time so we can freely access DBusMgr anywhere
|
||||
m_client.reset(new FakeClient(m_plugin->dbus().data()));
|
||||
m_plugin->dbus()->overrideClient(m_client);
|
||||
@@ -875,7 +849,7 @@ void TestGuiFdoSecrets::testItemCreate()
|
||||
// secrets
|
||||
{
|
||||
DBUS_GET(ss, item->GetSecret(QDBusObjectPath(sess->path())));
|
||||
auto decrypted = m_cipher->decrypt(ss.unmarshal(m_plugin->dbus()));
|
||||
auto decrypted = m_clientCipher->decrypt(ss.unmarshal(m_plugin->dbus()));
|
||||
COMPARE(decrypted.value, QByteArrayLiteral("Password"));
|
||||
}
|
||||
|
||||
@@ -1075,7 +1049,7 @@ void TestGuiFdoSecrets::testItemSecret()
|
||||
// plain text secret
|
||||
{
|
||||
DBUS_GET(encrypted, item->GetSecret(QDBusObjectPath(sess->path())));
|
||||
auto ss = m_cipher->decrypt(encrypted.unmarshal(m_plugin->dbus()));
|
||||
auto ss = m_clientCipher->decrypt(encrypted.unmarshal(m_plugin->dbus()));
|
||||
COMPARE(ss.contentType, TEXT_PLAIN);
|
||||
COMPARE(ss.value, entry->password().toUtf8());
|
||||
}
|
||||
@@ -1087,7 +1061,7 @@ void TestGuiFdoSecrets::testItemSecret()
|
||||
VERIFY(spyShowNotification.isValid());
|
||||
|
||||
DBUS_GET(encrypted, item->GetSecret(QDBusObjectPath(sess->path())));
|
||||
auto ss = m_cipher->decrypt(encrypted.unmarshal(m_plugin->dbus()));
|
||||
auto ss = m_clientCipher->decrypt(encrypted.unmarshal(m_plugin->dbus()));
|
||||
COMPARE(ss.contentType, TEXT_PLAIN);
|
||||
COMPARE(ss.value, entry->password().toUtf8());
|
||||
|
||||
@@ -1107,7 +1081,7 @@ void TestGuiFdoSecrets::testItemSecret()
|
||||
ss.contentType = TEXT_PLAIN;
|
||||
ss.value = "NewPassword";
|
||||
ss.session = QDBusObjectPath(sess->path());
|
||||
auto encrypted = m_cipher->encrypt(ss.unmarshal(m_plugin->dbus()));
|
||||
auto encrypted = m_clientCipher->encrypt(ss.unmarshal(m_plugin->dbus()));
|
||||
DBUS_VERIFY(item->SetSecret(encrypted.marshal()));
|
||||
|
||||
COMPARE(entry->password().toUtf8(), ss.value);
|
||||
@@ -1119,12 +1093,12 @@ void TestGuiFdoSecrets::testItemSecret()
|
||||
expected.contentType = APPLICATION_OCTET_STREAM;
|
||||
expected.value = QByteArrayLiteral("NewPasswordBinary");
|
||||
expected.session = QDBusObjectPath(sess->path());
|
||||
DBUS_VERIFY(item->SetSecret(m_cipher->encrypt(expected.unmarshal(m_plugin->dbus())).marshal()));
|
||||
DBUS_VERIFY(item->SetSecret(m_clientCipher->encrypt(expected.unmarshal(m_plugin->dbus())).marshal()));
|
||||
|
||||
COMPARE(entry->password(), QStringLiteral(""));
|
||||
|
||||
DBUS_GET(encrypted, item->GetSecret(QDBusObjectPath(sess->path())));
|
||||
auto ss = m_cipher->decrypt(encrypted.unmarshal(m_plugin->dbus()));
|
||||
auto ss = m_clientCipher->decrypt(encrypted.unmarshal(m_plugin->dbus()));
|
||||
COMPARE(ss.contentType, expected.contentType);
|
||||
COMPARE(ss.value, expected.value);
|
||||
}
|
||||
@@ -1195,7 +1169,7 @@ void TestGuiFdoSecrets::testItemLockState()
|
||||
"text/plain",
|
||||
}
|
||||
.unmarshal(m_plugin->dbus());
|
||||
auto encrypted = m_cipher->encrypt(secret).marshal();
|
||||
auto encrypted = m_clientCipher->encrypt(secret).marshal();
|
||||
|
||||
// when access confirmation is disabled, item is unlocked when the collection is unlocked
|
||||
FdoSecrets::settings()->setConfirmAccessItem(false);
|
||||
@@ -1364,13 +1338,8 @@ QSharedPointer<SessionProxy> TestGuiFdoSecrets::openSession(const QSharedPointer
|
||||
|
||||
return getProxy<SessionProxy>(sessPath);
|
||||
} else if (algo == DhIetf1024Sha256Aes128CbcPkcs7::Algorithm) {
|
||||
|
||||
DhIetf1024Sha256Aes128CbcPkcs7::fixNextServerKeys(MpiFromBytes(MpiToBytes(m_serverPrivate)),
|
||||
MpiFromBytes(MpiToBytes(m_serverPublic)));
|
||||
|
||||
DBUS_GET2(output, sessPath, service->OpenSession(algo, QDBusVariant(m_cipher->m_publicKey)));
|
||||
|
||||
COMPARE(qvariant_cast<QByteArray>(output.variant()), MpiToBytes(m_serverPublic));
|
||||
DBUS_GET2(output, sessPath, service->OpenSession(algo, QDBusVariant(m_clientCipher->negotiationOutput())));
|
||||
m_clientCipher->updateClientPublicKey(output.variant().toByteArray());
|
||||
return getProxy<SessionProxy>(sessPath);
|
||||
}
|
||||
QTest::qFail("Unsupported algorithm", __FILE__, __LINE__);
|
||||
@@ -1412,7 +1381,7 @@ QSharedPointer<ItemProxy> TestGuiFdoSecrets::createItem(const QSharedPointer<Ses
|
||||
ss.session = QDBusObjectPath(sess->path());
|
||||
ss.value = pass.toLocal8Bit();
|
||||
ss.contentType = "plain/text";
|
||||
auto encrypted = m_cipher->encrypt(ss.unmarshal(m_plugin->dbus())).marshal();
|
||||
auto encrypted = m_clientCipher->encrypt(ss.unmarshal(m_plugin->dbus())).marshal();
|
||||
|
||||
DBUS_GET2(itemPath, promptPath, coll->CreateItem(properties, encrypted, replace));
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <QSharedPointer>
|
||||
#include <QString>
|
||||
|
||||
#include "fdosecrets/GcryptMPI.h"
|
||||
#include "fdosecrets/dbus/DBusTypes.h"
|
||||
|
||||
class MainWindow;
|
||||
@@ -139,10 +138,7 @@ private:
|
||||
QPointer<FdoSecretsPlugin> m_plugin;
|
||||
QSharedPointer<FdoSecrets::DBusClient> m_client;
|
||||
|
||||
// For DH session tests
|
||||
GcryptMPI m_serverPrivate;
|
||||
GcryptMPI m_serverPublic;
|
||||
std::unique_ptr<FdoSecrets::DhIetf1024Sha256Aes128CbcPkcs7> m_cipher;
|
||||
QScopedPointer<FdoSecrets::DhIetf1024Sha256Aes128CbcPkcs7> m_clientCipher;
|
||||
|
||||
QByteArray m_dbData;
|
||||
QScopedPointer<TemporaryFile> m_dbFile;
|
||||
|
||||
@@ -27,13 +27,13 @@ MockChallengeResponseKey::~MockChallengeResponseKey()
|
||||
{
|
||||
}
|
||||
|
||||
QByteArray MockChallengeResponseKey::rawKey() const
|
||||
{
|
||||
return m_challenge + m_secret;
|
||||
}
|
||||
|
||||
bool MockChallengeResponseKey::challenge(const QByteArray& challenge)
|
||||
{
|
||||
m_challenge = challenge;
|
||||
|
||||
auto response = m_challenge + m_secret;
|
||||
m_key.resize(response.size());
|
||||
std::copy(response.begin(), response.end(), m_key.data());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ public:
|
||||
explicit MockChallengeResponseKey(const QByteArray& secret);
|
||||
Q_DISABLE_COPY(MockChallengeResponseKey);
|
||||
~MockChallengeResponseKey() override;
|
||||
QByteArray rawKey() const override;
|
||||
bool challenge(const QByteArray& challenge) override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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 "TestRandom.h"
|
||||
#include "TestGlobal.h"
|
||||
|
||||
RandomBackendPreset::RandomBackendPreset()
|
||||
: m_bytesIndex(0)
|
||||
{
|
||||
}
|
||||
|
||||
void RandomBackendPreset::randomize(void* data, int len)
|
||||
{
|
||||
QVERIFY(len <= (m_nextBytes.size() - m_bytesIndex));
|
||||
|
||||
char* charData = reinterpret_cast<char*>(data);
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
charData[i] = m_nextBytes[m_bytesIndex + i];
|
||||
}
|
||||
|
||||
m_bytesIndex += len;
|
||||
}
|
||||
|
||||
void RandomBackendPreset::setNextBytes(const QByteArray& nextBytes)
|
||||
{
|
||||
m_nextBytes = nextBytes;
|
||||
m_bytesIndex = 0;
|
||||
}
|
||||
|
||||
void TestRandom::setup(RandomBackend* backend)
|
||||
{
|
||||
Random::setInstance(backend);
|
||||
}
|
||||
|
||||
void TestRandom::teardown()
|
||||
{
|
||||
Random::resetInstance();
|
||||
}
|
||||
|
||||
void RandomBackendNull::randomize(void* data, int len)
|
||||
{
|
||||
char* charData = reinterpret_cast<char*>(data);
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
charData[i] = '\0';
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_TESTRANDOM_H
|
||||
#define KEEPASSXC_TESTRANDOM_H
|
||||
|
||||
#include "crypto/Random.h"
|
||||
|
||||
class RandomBackendPreset : public RandomBackend
|
||||
{
|
||||
public:
|
||||
RandomBackendPreset();
|
||||
void randomize(void* data, int len) override;
|
||||
void setNextBytes(const QByteArray& nextBytes);
|
||||
|
||||
private:
|
||||
QByteArray m_nextBytes;
|
||||
int m_bytesIndex;
|
||||
};
|
||||
|
||||
class RandomBackendNull : public RandomBackend
|
||||
{
|
||||
public:
|
||||
void randomize(void* data, int len) override;
|
||||
};
|
||||
|
||||
class TestRandom : public Random
|
||||
{
|
||||
public:
|
||||
static void setup(RandomBackend* backend);
|
||||
static void teardown();
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_TESTRANDOM_H
|
||||
Reference in New Issue
Block a user