373 lines
12 KiB
C++
373 lines
12 KiB
C++
/*!
|
|
* \brief Unit tests for \ref SecureStorage
|
|
*
|
|
* \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany
|
|
*/
|
|
|
|
#include <QCoreApplication>
|
|
#include <QFile>
|
|
#include <QtTest>
|
|
|
|
#include "FileDestination.h"
|
|
#include "SecureStorage.h"
|
|
|
|
#include "asn1/CVCertificate.h"
|
|
|
|
using namespace governikus;
|
|
|
|
Q_DECLARE_METATYPE(SecureStorage::TlsSuite)
|
|
Q_DECLARE_METATYPE(QSsl::SslProtocol)
|
|
|
|
class test_SecureStorage
|
|
: public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
private:
|
|
QStringList loadCommentList(const QString& pCommentName)
|
|
{
|
|
const QString& path = FileDestination::getPath("config.json");
|
|
if (!QFile::exists(path))
|
|
{
|
|
qCritical() << "SecureStorage not found";
|
|
return QStringList();
|
|
}
|
|
|
|
QFile configFile(path);
|
|
if (!configFile.open(QIODevice::ReadOnly | QIODevice::Text))
|
|
{
|
|
qCritical() << "Wasn't able to open SecureStorage";
|
|
return QStringList();
|
|
}
|
|
|
|
QJsonParseError parseError;
|
|
QJsonDocument document = QJsonDocument::fromJson(configFile.readAll(), &parseError);
|
|
configFile.close();
|
|
if (parseError.error != QJsonParseError::NoError)
|
|
{
|
|
qCritical() << "Parse error while reading SecureStorage on position" << parseError.offset << ':' << parseError.errorString();
|
|
return QStringList();
|
|
}
|
|
|
|
const QJsonValue commentValues = document.object().value(pCommentName);
|
|
if (commentValues == QJsonValue::Undefined || !commentValues.isArray())
|
|
{
|
|
qCritical() << "Wasn't able to find the array" << pCommentName;
|
|
return QStringList();
|
|
}
|
|
|
|
QStringList comments;
|
|
const auto& commentValueArray = commentValues.toArray();
|
|
for (const QJsonValue& comment : commentValueArray)
|
|
{
|
|
if (comment.isString())
|
|
{
|
|
comments += comment.toString();
|
|
}
|
|
else
|
|
{
|
|
qCritical() << "At least one entry of" << pCommentName << "is not a string";
|
|
return QStringList();
|
|
}
|
|
}
|
|
|
|
return comments;
|
|
}
|
|
|
|
|
|
private Q_SLOTS:
|
|
void testGetCVRootCertificatesUnique()
|
|
{
|
|
const auto secureStorage = Env::getSingleton<SecureStorage>();
|
|
static const int EXPECTED_CERTIFICATE_COUNT = 13;
|
|
|
|
QVector<QSharedPointer<const CVCertificate> > cvcs = CVCertificate::fromHex(secureStorage->getCVRootCertificates(true))
|
|
+ CVCertificate::fromHex(secureStorage->getCVRootCertificates(false));
|
|
const int count = cvcs.count();
|
|
QCOMPARE(count, EXPECTED_CERTIFICATE_COUNT);
|
|
|
|
const QStringList comments = loadCommentList("_comment_2") + loadCommentList("_comment_4");
|
|
QCOMPARE(comments.size(), count);
|
|
|
|
// Check that each certificate has a unique car/chr.
|
|
for (int j = 0; j < count; ++j)
|
|
{
|
|
for (int i = 0; i < j; ++i)
|
|
{
|
|
const CVCertificateBody& bodyI = cvcs[i]->getBody();
|
|
const QByteArray carI = bodyI.getCertificationAuthorityReference();
|
|
const QByteArray chrI = bodyI.getCertificateHolderReference();
|
|
|
|
const CVCertificateBody& bodyJ = cvcs[j]->getBody();
|
|
const QByteArray carJ = bodyJ.getCertificationAuthorityReference();
|
|
const QByteArray chrJ = bodyJ.getCertificateHolderReference();
|
|
|
|
if (carI == carJ && chrI == chrJ)
|
|
{
|
|
qWarning() << "Found 2 certificates with the same name with comments:" << comments[j] << comments[i];
|
|
}
|
|
|
|
QVERIFY(carI != carJ || chrI != chrJ);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void testGetCVRootCertificateNames_data()
|
|
{
|
|
QTest::addColumn<int>("certificateCount");
|
|
QTest::addColumn<bool>("isProductive");
|
|
QTest::addColumn<QString>("commentName");
|
|
|
|
QTest::newRow("production") << 4 << true << "_comment_2";
|
|
QTest::newRow("test") << 9 << false << "_comment_4";
|
|
}
|
|
|
|
|
|
void testGetCVRootCertificateNames()
|
|
{
|
|
QFETCH(int, certificateCount);
|
|
QFETCH(bool, isProductive);
|
|
QFETCH(QString, commentName);
|
|
|
|
const auto secureStorage = Env::getSingleton<SecureStorage>();
|
|
|
|
QVector<QSharedPointer<const CVCertificate> > cvcs = CVCertificate::fromHex(secureStorage->getCVRootCertificates(isProductive));
|
|
QCOMPARE(cvcs.count(), certificateCount);
|
|
|
|
const QStringList& comments = loadCommentList(commentName);
|
|
QCOMPARE(comments.count(), certificateCount);
|
|
|
|
for (int index = 0; index < certificateCount; ++index)
|
|
{
|
|
const QString commentLine = comments[index];
|
|
|
|
const QStringList carAndChr = commentLine.split(QStringLiteral("_"));
|
|
QVERIFY(carAndChr.size() == 1 || carAndChr.size() == 2);
|
|
|
|
const QByteArray carInComment = carAndChr.first().toUtf8();
|
|
const QByteArray carInCertificate = cvcs.at(index)->getBody().getCertificationAuthorityReference();
|
|
const QByteArray chrInComment = carAndChr.size() == 1 ? carAndChr.first().toUtf8() : carAndChr.last().toUtf8();
|
|
const QByteArray chrInCertificate = cvcs.at(index)->getBody().getCertificateHolderReference();
|
|
|
|
if (carInComment != carInCertificate || chrInComment != chrInCertificate)
|
|
{
|
|
qWarning() << "Wrong certificate information at index" << index << "with comment" << commentLine;
|
|
}
|
|
|
|
QCOMPARE(carInComment, carInCertificate);
|
|
QCOMPARE(chrInComment, chrInCertificate);
|
|
}
|
|
}
|
|
|
|
|
|
void testGetUpdateCertificate_data()
|
|
{
|
|
QTest::addColumn<int>("index");
|
|
QTest::addColumn<QString>("subjectInfo");
|
|
QTest::addColumn<QString>("issuerInfo");
|
|
QTest::addColumn<QString>("expiryDate");
|
|
|
|
QTest::newRow("production") << 0 << "appl.governikus-asp.de" << "TeleSec ServerPass Class 2 CA" << "2020-12-06T23:59:59Z";
|
|
QTest::newRow("ci") << 1 << "*.govkg.de" << "TeleSec ServerPass Class 2 CA" << "2021-03-12T23:59:59Z";
|
|
}
|
|
|
|
|
|
void testGetUpdateCertificate()
|
|
{
|
|
const auto secureStorage = Env::getSingleton<SecureStorage>();
|
|
const auto& certificates = secureStorage->getUpdateCertificates();
|
|
QCOMPARE(certificates.count(), 2);
|
|
|
|
QFETCH(int, index);
|
|
QFETCH(QString, subjectInfo);
|
|
QFETCH(QString, issuerInfo);
|
|
QFETCH(QString, expiryDate);
|
|
|
|
QVERIFY(certificates.count() - index > 0);
|
|
|
|
const auto& cert = certificates.at(index);
|
|
QCOMPARE(cert.subjectInfo(QSslCertificate::CommonName).at(0), subjectInfo);
|
|
QCOMPARE(cert.issuerInfo(QSslCertificate::CommonName).at(0), issuerInfo);
|
|
QCOMPARE(cert.expiryDate(), QDateTime::fromString(expiryDate, Qt::ISODate));
|
|
}
|
|
|
|
|
|
void testGetSelfAuthentication()
|
|
{
|
|
const auto secureStorage = Env::getSingleton<SecureStorage>();
|
|
QVERIFY(secureStorage->getSelfAuthenticationUrl(false).isValid());
|
|
QVERIFY(secureStorage->getSelfAuthenticationUrl(true).isValid());
|
|
}
|
|
|
|
|
|
void testGetUpdateServerBaseUrl()
|
|
{
|
|
const auto secureStorage = Env::getSingleton<SecureStorage>();
|
|
QVERIFY(secureStorage->getUpdateServerBaseUrl().isValid());
|
|
}
|
|
|
|
|
|
void testWhitelistServerBaseUrl()
|
|
{
|
|
const auto secureStorage = Env::getSingleton<SecureStorage>();
|
|
QVERIFY(secureStorage->getWhitelistServerBaseUrl().isValid());
|
|
}
|
|
|
|
|
|
void testAppcast()
|
|
{
|
|
const auto secureStorage = Env::getSingleton<SecureStorage>();
|
|
QCOMPARE(secureStorage->getAppcastUpdateUrl(), QUrl("https://appl.governikus-asp.de/ausweisapp2/Appcast.json"));
|
|
QCOMPARE(secureStorage->getAppcastBetaUpdateUrl(), QUrl("https://appl.governikus-asp.de/ausweisapp2/beta/Appcast.json"));
|
|
}
|
|
|
|
|
|
void testMinStaticKeySizes()
|
|
{
|
|
const auto secureStorage = Env::getSingleton<SecureStorage>();
|
|
QCOMPARE(secureStorage->getMinimumStaticKeySize(QSsl::KeyAlgorithm::Rsa), 2000);
|
|
QCOMPARE(secureStorage->getMinimumStaticKeySize(QSsl::KeyAlgorithm::Dsa), 2000);
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0))
|
|
QCOMPARE(secureStorage->getMinimumStaticKeySize(QSsl::KeyAlgorithm::Dh), 2000);
|
|
#endif
|
|
QCOMPARE(secureStorage->getMinimumStaticKeySize(QSsl::KeyAlgorithm::Ec), 224);
|
|
}
|
|
|
|
|
|
void testMinEphemeralKeySizes()
|
|
{
|
|
const auto secureStorage = Env::getSingleton<SecureStorage>();
|
|
QCOMPARE(secureStorage->getMinimumEphemeralKeySize(QSsl::KeyAlgorithm::Rsa), 2000);
|
|
QCOMPARE(secureStorage->getMinimumEphemeralKeySize(QSsl::KeyAlgorithm::Dsa), 2000);
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0))
|
|
QCOMPARE(secureStorage->getMinimumEphemeralKeySize(QSsl::KeyAlgorithm::Dh), 2000);
|
|
#endif
|
|
QCOMPARE(secureStorage->getMinimumEphemeralKeySize(QSsl::KeyAlgorithm::Ec), 224);
|
|
}
|
|
|
|
|
|
void testSignatureAlgorithms()
|
|
{
|
|
const auto secureStorage = Env::getSingleton<SecureStorage>();
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
|
|
QSKIP("SignatureAlgorithms not supported");
|
|
#endif
|
|
|
|
const auto& tlsSettings = secureStorage->getTlsConfig();
|
|
QCOMPARE(tlsSettings.getSignatureAlgorithms().size(), 12);
|
|
QCOMPARE(tlsSettings.getSignatureAlgorithms().constFirst(), QByteArray("RSA+SHA512"));
|
|
QCOMPARE(tlsSettings.getSignatureAlgorithms().constLast(), QByteArray("ECDSA+SHA224"));
|
|
}
|
|
|
|
|
|
void testSignatureAlgorithmsPsk()
|
|
{
|
|
const auto secureStorage = Env::getSingleton<SecureStorage>();
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
|
|
QSKIP("SignatureAlgorithms not supported");
|
|
#endif
|
|
|
|
const auto& tlsSettings = secureStorage->getTlsConfig(SecureStorage::TlsSuite::PSK);
|
|
QCOMPARE(tlsSettings.getSignatureAlgorithms().size(), 4);
|
|
QCOMPARE(tlsSettings.getSignatureAlgorithms().constFirst(), QByteArray("RSA+SHA512"));
|
|
QCOMPARE(tlsSettings.getSignatureAlgorithms().constLast(), QByteArray("RSA+SHA224"));
|
|
}
|
|
|
|
|
|
void testSignatureAlgorithmsRemoteReader()
|
|
{
|
|
const auto secureStorage = Env::getSingleton<SecureStorage>();
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
|
|
QSKIP("SignatureAlgorithms not supported");
|
|
#endif
|
|
|
|
const auto& config = secureStorage->getTlsConfigRemote();
|
|
QCOMPARE(config.getSignatureAlgorithms().size(), 3);
|
|
|
|
const auto& configPairing = secureStorage->getTlsConfigRemote();
|
|
QCOMPARE(configPairing.getSignatureAlgorithms().size(), 3);
|
|
}
|
|
|
|
|
|
void orderOfCiphers()
|
|
{
|
|
const auto secureStorage = Env::getSingleton<SecureStorage>();
|
|
const auto& ciphersForwardSecrecy = secureStorage->getTlsConfig().getCiphers();
|
|
QCOMPARE(ciphersForwardSecrecy.first(), QSslCipher("ECDHE-ECDSA-AES256-GCM-SHA384"));
|
|
QCOMPARE(ciphersForwardSecrecy.last(), QSslCipher("DHE-RSA-AES128-SHA256"));
|
|
|
|
const auto& ciphersPsk = secureStorage->getTlsConfig(SecureStorage::TlsSuite::PSK).getCiphers();
|
|
QVERIFY(ciphersPsk.count() > 0);
|
|
QCOMPARE(ciphersPsk.first(), QSslCipher("RSA-PSK-AES256-GCM-SHA384"));
|
|
QCOMPARE(ciphersPsk.last(), QSslCipher("RSA-PSK-AES256-CBC-SHA"));
|
|
|
|
const auto& ciphersEc = secureStorage->getTlsConfig().getEllipticCurves();
|
|
QCOMPARE(ciphersEc.count(), 6);
|
|
QCOMPARE(ciphersEc.first(), QSslEllipticCurve::fromLongName("brainpoolP512r1"));
|
|
QCOMPARE(ciphersEc.last(), QSslEllipticCurve::fromLongName("secp224r1"));
|
|
|
|
const auto& ciphersEcRemoteReader = secureStorage->getTlsConfigRemote().getEllipticCurves();
|
|
QCOMPARE(ciphersEcRemoteReader.count(), 6);
|
|
QCOMPARE(ciphersEcRemoteReader.first(), QSslEllipticCurve::fromLongName("brainpoolP512r1"));
|
|
QCOMPARE(ciphersEcRemoteReader.last(), QSslEllipticCurve::fromLongName("secp224r1"));
|
|
|
|
const auto& ciphersEcRemoteReaderPairing = secureStorage->getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getEllipticCurves();
|
|
QCOMPARE(ciphersEcRemoteReaderPairing.count(), 0);
|
|
|
|
const auto& ciphersRemoteReader = secureStorage->getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getCiphers();
|
|
QVERIFY(ciphersRemoteReader.count() > 0);
|
|
QCOMPARE(ciphersRemoteReader.first(), QSslCipher("RSA-PSK-AES256-GCM-SHA384"));
|
|
QCOMPARE(ciphersRemoteReader.last(), QSslCipher("RSA-PSK-AES256-CBC-SHA"));
|
|
}
|
|
|
|
|
|
void getSslProtocolVersion_data()
|
|
{
|
|
QTest::addColumn<SecureStorage::TlsSuite>("suite");
|
|
QTest::addColumn<QSsl::SslProtocol>("protocol");
|
|
|
|
QTest::newRow("default") << SecureStorage::TlsSuite::DEFAULT << QSsl::SslProtocol::TlsV1_2;
|
|
QTest::newRow("PSK") << SecureStorage::TlsSuite::PSK << QSsl::SslProtocol::TlsV1_2;
|
|
}
|
|
|
|
|
|
void getSslProtocolVersion()
|
|
{
|
|
QFETCH(SecureStorage::TlsSuite, suite);
|
|
QFETCH(QSsl::SslProtocol, protocol);
|
|
|
|
const auto secureStorage = Env::getSingleton<SecureStorage>();
|
|
|
|
QCOMPARE(secureStorage->getTlsConfig(suite).getProtocolVersion(), protocol);
|
|
}
|
|
|
|
|
|
void getConfiguration_data()
|
|
{
|
|
QTest::addColumn<QSslConfiguration>("configuration");
|
|
QTest::addColumn<int>("cipherSize");
|
|
|
|
const auto secureStorage = Env::getSingleton<SecureStorage>();
|
|
|
|
QTest::newRow("ciphers non PSK") << secureStorage->getTlsConfig().getConfiguration() << 12;
|
|
QTest::newRow("ciphers for PSK") << secureStorage->getTlsConfig(SecureStorage::TlsSuite::PSK).getConfiguration() << 5;
|
|
QTest::newRow("remote reader") << secureStorage->getTlsConfigRemote().getConfiguration() << 7;
|
|
QTest::newRow("remote reader pairing") << secureStorage->getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getConfiguration() << 5;
|
|
}
|
|
|
|
|
|
void getConfiguration()
|
|
{
|
|
QFETCH(QSslConfiguration, configuration);
|
|
QFETCH(int, cipherSize);
|
|
|
|
QCOMPARE(configuration.ciphers().size(), cipherSize);
|
|
}
|
|
|
|
|
|
};
|
|
|
|
QTEST_GUILESS_MAIN(test_SecureStorage)
|
|
#include "test_SecureStorage.moc"
|