File indexing completed on 2024-05-19 05:22:50
0001 /* 0002 This file is part of libkleopatra's test suite. 0003 SPDX-FileCopyrightText: 2022 Sandro Knauß <knauss@kde.org> 0004 SPDX-FileCopyrightText: 2023 g10 Code GmbH 0005 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "testhelpers.h" 0011 0012 #include <Libkleo/Chrono> 0013 #include <Libkleo/ExpiryChecker> 0014 #include <Libkleo/ExpiryCheckerSettings> 0015 #include <Libkleo/Formatting> 0016 #include <Libkleo/KeyCache> 0017 0018 #include <QDebug> 0019 #include <QProcess> 0020 #include <QRegularExpression> 0021 #include <QSignalSpy> 0022 #include <QTemporaryDir> 0023 #include <QTest> 0024 0025 using namespace Kleo; 0026 using namespace GpgME; 0027 0028 Q_DECLARE_METATYPE(GpgME::Key) 0029 0030 using days = Kleo::chrono::days; 0031 0032 class FakeTimeProvider : public Kleo::TimeProvider 0033 { 0034 public: 0035 explicit FakeTimeProvider(const QDateTime &dateTime) 0036 : mCurrentDate{dateTime.date()} 0037 , mCurrentTime{dateTime.toSecsSinceEpoch()} 0038 { 0039 } 0040 0041 time_t currentTime() const override 0042 { 0043 return mCurrentTime; 0044 } 0045 0046 QDate currentDate() const override 0047 { 0048 return mCurrentDate; 0049 } 0050 0051 Qt::TimeSpec timeSpec() const override 0052 { 0053 // use UTC to avoid test failures caused by "wrong" local timezone 0054 return Qt::UTC; 0055 } 0056 0057 private: 0058 QDate mCurrentDate; 0059 time_t mCurrentTime; 0060 }; 0061 0062 class ExpiryCheckerTest : public QObject 0063 { 0064 Q_OBJECT 0065 0066 private Q_SLOTS: 0067 void initTestCase() 0068 { 0069 qRegisterMetaType<ExpiryChecker::ExpiryInformation>(); 0070 0071 mGnupgHome = QTest::qExtractTestData(QStringLiteral("/fixtures/expirycheckertest")); 0072 qputenv("GNUPGHOME", mGnupgHome->path().toLocal8Bit()); 0073 0074 // hold a reference to the key cache to avoid rebuilding while the test is running 0075 mKeyCache = KeyCache::instance(); 0076 // make sure that the key cache has been populated 0077 (void)mKeyCache->keys(); 0078 } 0079 0080 void cleanupTestCase() 0081 { 0082 // verify that nobody else holds a reference to the key cache 0083 QVERIFY(mKeyCache.use_count() == 1); 0084 mKeyCache.reset(); 0085 0086 (void)QProcess::execute(QStringLiteral("gpgconf"), {"--kill", "all"}); 0087 0088 mGnupgHome.reset(); 0089 qunsetenv("GNUPGHOME"); 0090 } 0091 0092 void errorHandling_data() 0093 { 0094 QTest::addColumn<GpgME::Key>("key"); 0095 QTest::addColumn<ExpiryChecker::CheckFlags>("checkFlags"); 0096 QTest::addColumn<ExpiryChecker::ExpirationStatus>("expectedStatus"); 0097 0098 QTest::newRow("invalid key") // 0099 << GpgME::Key{} // 0100 << ExpiryChecker::CheckFlags{ExpiryChecker::EncryptionKey} // 0101 << ExpiryChecker::InvalidKey; 0102 QTest::newRow("invalid flags - no flags") // 0103 << testKey("test@kolab.org", GpgME::OpenPGP) // 0104 << ExpiryChecker::CheckFlags{} // 0105 << ExpiryChecker::InvalidCheckFlags; 0106 QTest::newRow("invalid flags - no usage flags") // 0107 << testKey("test@kolab.org", GpgME::OpenPGP) // 0108 << ExpiryChecker::CheckFlags{ExpiryChecker::OwnKey | ExpiryChecker::CheckChain} // 0109 << ExpiryChecker::InvalidCheckFlags; 0110 } 0111 0112 void errorHandling() 0113 { 0114 QFETCH(GpgME::Key, key); 0115 QFETCH(ExpiryChecker::CheckFlags, checkFlags); 0116 QFETCH(ExpiryChecker::ExpirationStatus, expectedStatus); 0117 0118 ExpiryChecker checker(ExpiryCheckerSettings{days{1}, days{1}, days{1}, days{1}}); 0119 QTest::ignoreMessage(QtWarningMsg, QRegularExpression{QStringLiteral("checkKey called with")}); 0120 const auto result = checker.checkKey(key, checkFlags); 0121 QCOMPARE(result.expiration.certificate, key); 0122 QCOMPARE(result.expiration.status, expectedStatus); 0123 } 0124 0125 void valid_data() 0126 { 0127 QTest::addColumn<GpgME::Key>("key"); 0128 QTest::addColumn<QDateTime>("fakedate"); 0129 // use dates between creation date and expiration date (if there is one) of the test keys/certificates 0130 QTest::newRow("neverExpire") << testKey("test@kolab.org", GpgME::OpenPGP) << QDateTime{{2012, 1, 1}, {}, QTimeZone::UTC}; 0131 QTest::newRow("openpgp") << testKey("alice@autocrypt.example", GpgME::OpenPGP) << QDateTime{{2020, 1, 1}, {}, QTimeZone::UTC}; 0132 QTest::newRow("smime") << testKey("test@example.com", GpgME::CMS) << QDateTime{{2012, 1, 1}, {}, QTimeZone::UTC}; 0133 } 0134 0135 void valid() 0136 { 0137 QFETCH(GpgME::Key, key); 0138 QFETCH(QDateTime, fakedate); 0139 0140 ExpiryChecker checker(ExpiryCheckerSettings{days{1}, days{1}, days{1}, days{1}}); 0141 checker.setTimeProviderForTest(std::make_shared<FakeTimeProvider>(fakedate)); 0142 QSignalSpy spy(&checker, &ExpiryChecker::expiryMessage); 0143 0144 const auto result = checker.checkKey(key, ExpiryChecker::EncryptionKey); 0145 QCOMPARE(result.checkFlags, ExpiryChecker::EncryptionKey); 0146 QCOMPARE(result.expiration.certificate, key); 0147 QCOMPARE(result.expiration.status, ExpiryChecker::NotNearExpiry); 0148 QCOMPARE(spy.count(), 0); 0149 } 0150 0151 void expired_data() 0152 { 0153 QTest::addColumn<GpgME::Key>("key"); 0154 QTest::addColumn<ExpiryChecker::CheckFlags>("checkFlags"); 0155 QTest::addColumn<QDateTime>("fakedate"); 0156 QTest::addColumn<Kleo::chrono::days>("expectedDuration"); 0157 QTest::addColumn<ExpiryChecker::ExpiryInformation>("expiryInfo"); 0158 QTest::addColumn<QString>("msg"); 0159 0160 QTest::newRow("openpgp - other; 0 days ago") // 0161 << testKey("alice@autocrypt.example", GpgME::OpenPGP) // 0162 << ExpiryChecker::CheckFlags{ExpiryChecker::EncryptionKey} // 0163 << QDateTime{{2021, 1, 21}, {23, 59, 59}, QTimeZone::UTC} // the last second of the day the key expired 0164 << days{0} // 0165 << ExpiryChecker::OtherKeyExpired 0166 << QStringLiteral( 0167 "<p>The OpenPGP key for</p><p align=center><b>alice@autocrypt.example</b> (KeyID 0xF231550C4F47E38E)</p><p>expired less than a day " 0168 "ago.</p>"); 0169 QTest::newRow("openpgp - own; 1 day ago") // 0170 << testKey("alice@autocrypt.example", GpgME::OpenPGP) // 0171 << ExpiryChecker::CheckFlags{ExpiryChecker::OwnEncryptionKey} // 0172 << QDateTime{{2021, 1, 22}, {}, QTimeZone::UTC} // the day after the expiration date of the key 0173 << days{1} // 0174 << ExpiryChecker::OwnKeyExpired 0175 << QStringLiteral( 0176 "<p>Your OpenPGP encryption key</p><p align=center><b>alice@autocrypt.example</b> (KeyID 0xF231550C4F47E38E)</p><p>expired yesterday.</p>"); 0177 QTest::newRow("openpgp - own signing; 2 days ago") // 0178 << testKey("alice@autocrypt.example", GpgME::OpenPGP) // 0179 << ExpiryChecker::CheckFlags{ExpiryChecker::OwnSigningKey} // 0180 << QDateTime{{2021, 1, 23}, {}, QTimeZone::UTC} // the second day after the expiration date of the key 0181 << days{2} // 0182 << ExpiryChecker::OwnKeyExpired 0183 << QStringLiteral( 0184 "<p>Your OpenPGP signing key</p><p align=center><b>alice@autocrypt.example</b> (KeyID 0xF231550C4F47E38E)</p><p>expired 2 days " 0185 "ago.</p>"); 0186 0187 QTest::newRow("smime - other; 0 days ago") // 0188 << testKey("test@example.com", GpgME::CMS) // 0189 << ExpiryChecker::CheckFlags{ExpiryChecker::EncryptionKey} // 0190 << QDateTime{{2013, 3, 25}, {23, 59, 59}, QTimeZone::UTC} // the last second of the day the key expired 0191 << days{0} // 0192 << ExpiryChecker::OtherKeyExpired 0193 << QStringLiteral( 0194 "<p>The S/MIME certificate for</p><p align=center><b>CN=unittest cert,EMAIL=test@example.com,O=KDAB,C=US</b> (serial " 0195 "number 00D345203A186385C9)</p><p>expired less than a day ago.</p>"); 0196 QTest::newRow("smime - own; 1 day ago") // 0197 << testKey("test@example.com", GpgME::CMS) // 0198 << ExpiryChecker::CheckFlags{ExpiryChecker::OwnEncryptionKey} // 0199 << QDateTime{{2013, 3, 26}, {}, QTimeZone::UTC} // the day after the expiration date of the key 0200 << days{1} // 0201 << ExpiryChecker::OwnKeyExpired 0202 << QStringLiteral( 0203 "<p>Your S/MIME encryption certificate</p><p align=center><b>CN=unittest cert,EMAIL=test@example.com,O=KDAB,C=US</b> " 0204 "(serial number 00D345203A186385C9)</p><p>expired yesterday.</p>"); 0205 QTest::newRow("smime - own signing; 2 days ago") // 0206 << testKey("test@example.com", GpgME::CMS) // 0207 << ExpiryChecker::CheckFlags{ExpiryChecker::OwnSigningKey} // 0208 << QDateTime{{2013, 3, 27}, {}, QTimeZone::UTC} // the second day after the expiration date of the key 0209 << days{2} // 0210 << ExpiryChecker::OwnKeyExpired 0211 << QStringLiteral( 0212 "<p>Your S/MIME signing certificate</p><p align=center><b>CN=unittest cert,EMAIL=test@example.com,O=KDAB,C=US</b> " 0213 "(serial number 00D345203A186385C9)</p><p>expired 2 days ago.</p>"); 0214 } 0215 0216 void expired() 0217 { 0218 QFETCH(GpgME::Key, key); 0219 QFETCH(ExpiryChecker::CheckFlags, checkFlags); 0220 QFETCH(QDateTime, fakedate); 0221 QFETCH(Kleo::chrono::days, expectedDuration); 0222 QFETCH(ExpiryChecker::ExpiryInformation, expiryInfo); 0223 QFETCH(QString, msg); 0224 0225 { 0226 ExpiryChecker checker(ExpiryCheckerSettings{days{1}, days{1}, days{1}, days{1}}); 0227 checker.setTimeProviderForTest(std::make_shared<FakeTimeProvider>(fakedate)); 0228 QSignalSpy spy(&checker, &ExpiryChecker::expiryMessage); 0229 const auto result = checker.checkKey(key, checkFlags); 0230 QCOMPARE(result.checkFlags, checkFlags); 0231 QCOMPARE(result.expiration.certificate, key); 0232 QCOMPARE(result.expiration.status, ExpiryChecker::Expired); 0233 QCOMPARE(result.expiration.duration, expectedDuration); 0234 QCOMPARE(spy.count(), 1); 0235 QList<QVariant> arguments = spy.takeFirst(); 0236 QCOMPARE(arguments.at(0).value<GpgME::Key>().keyID(), key.keyID()); 0237 QCOMPARE(arguments.at(1).toString(), msg); 0238 QCOMPARE(arguments.at(2).value<ExpiryChecker::ExpiryInformation>(), expiryInfo); 0239 } 0240 } 0241 0242 void nearexpiry_data() 0243 { 0244 QTest::addColumn<GpgME::Key>("key"); 0245 QTest::addColumn<QDateTime>("fakedate"); 0246 QTest::addColumn<Kleo::chrono::days>("expectedDuration"); 0247 QTest::addColumn<QString>("msg"); 0248 QTest::addColumn<QString>("msgOwnKey"); 0249 QTest::addColumn<QString>("msgOwnSigningKey"); 0250 0251 // use the day 5 days before the expiration date of the test keys/certificates as fake date 0252 QTest::newRow("openpgp") 0253 << testKey("alice@autocrypt.example", GpgME::OpenPGP) // 0254 << QDateTime{{2021, 1, 16}, {}, QTimeZone::UTC} // 0255 << days{5} 0256 << QStringLiteral( 0257 "<p>The OpenPGP key for</p><p align=center><b>alice@autocrypt.example</b> (KeyID 0xF231550C4F47E38E)</p><p>expires in 5 days.</p>") 0258 << QStringLiteral( 0259 "<p>Your OpenPGP encryption key</p><p align=center><b>alice@autocrypt.example</b> (KeyID 0xF231550C4F47E38E)</p><p>expires in 5 " 0260 "days.</p>") 0261 << QStringLiteral( 0262 "<p>Your OpenPGP signing key</p><p align=center><b>alice@autocrypt.example</b> (KeyID 0xF231550C4F47E38E)</p><p>expires in 5 " 0263 "days.</p>"); 0264 QTest::newRow("smime") << testKey("test@example.com", GpgME::CMS) // 0265 << QDateTime{{2013, 3, 20}, {}, QTimeZone::UTC} // 0266 << days{5} 0267 << QStringLiteral( 0268 "<p>The S/MIME certificate for</p><p align=center><b>CN=unittest cert,EMAIL=test@example.com,O=KDAB,C=US</b> (serial " 0269 "number 00D345203A186385C9)</p><p>expires in 5 days.</p>") 0270 << QStringLiteral( 0271 "<p>Your S/MIME encryption certificate</p><p align=center><b>CN=unittest cert,EMAIL=test@example.com,O=KDAB,C=US</b> " 0272 "(serial number 00D345203A186385C9)</p><p>expires in 5 days.</p>") 0273 << QStringLiteral( 0274 "<p>Your S/MIME signing certificate</p><p align=center><b>CN=unittest cert,EMAIL=test@example.com,O=KDAB,C=US</b> " 0275 "(serial number 00D345203A186385C9)</p><p>expires in 5 days.</p>"); 0276 } 0277 0278 void nearexpiry() 0279 { 0280 QFETCH(GpgME::Key, key); 0281 QFETCH(QDateTime, fakedate); 0282 QFETCH(Kleo::chrono::days, expectedDuration); 0283 QFETCH(QString, msg); 0284 QFETCH(QString, msgOwnKey); 0285 QFETCH(QString, msgOwnSigningKey); 0286 0287 { 0288 ExpiryChecker checker(ExpiryCheckerSettings{days{1}, days{10}, days{1}, days{1}}); 0289 checker.setTimeProviderForTest(std::make_shared<FakeTimeProvider>(fakedate)); 0290 QSignalSpy spy(&checker, &ExpiryChecker::expiryMessage); 0291 // Test if the correct threshold is taken 0292 { 0293 const auto result = checker.checkKey(key, ExpiryChecker::EncryptionKey); 0294 QCOMPARE(result.checkFlags, ExpiryChecker::EncryptionKey); 0295 QCOMPARE(result.expiration.certificate, key); 0296 QCOMPARE(result.expiration.status, ExpiryChecker::ExpiresSoon); 0297 QCOMPARE(result.expiration.duration, expectedDuration); 0298 QCOMPARE(spy.count(), 1); 0299 } 0300 { 0301 const auto result = checker.checkKey(key, ExpiryChecker::OwnEncryptionKey); 0302 QCOMPARE(result.checkFlags, ExpiryChecker::OwnEncryptionKey); 0303 QCOMPARE(result.expiration.certificate, key); 0304 QCOMPARE(result.expiration.status, ExpiryChecker::NotNearExpiry); 0305 QCOMPARE(result.expiration.duration, expectedDuration); 0306 QCOMPARE(spy.count(), 1); 0307 } 0308 { 0309 const auto result = checker.checkKey(key, ExpiryChecker::OwnSigningKey); 0310 QCOMPARE(result.checkFlags, ExpiryChecker::OwnSigningKey); 0311 QCOMPARE(result.expiration.certificate, key); 0312 QCOMPARE(result.expiration.status, ExpiryChecker::NotNearExpiry); 0313 QCOMPARE(result.expiration.duration, expectedDuration); 0314 QCOMPARE(spy.count(), 1); 0315 } 0316 QList<QVariant> arguments = spy.takeFirst(); 0317 QCOMPARE(arguments.at(0).value<GpgME::Key>().keyID(), key.keyID()); 0318 QCOMPARE(arguments.at(1).toString(), msg); 0319 QCOMPARE(arguments.at(2).value<ExpiryChecker::ExpiryInformation>(), ExpiryChecker::OtherKeyNearExpiry); 0320 } 0321 { 0322 ExpiryChecker checker(ExpiryCheckerSettings{days{10}, days{1}, days{1}, days{1}}); 0323 checker.setTimeProviderForTest(std::make_shared<FakeTimeProvider>(fakedate)); 0324 QSignalSpy spy(&checker, &ExpiryChecker::expiryMessage); 0325 // Test if the correct treshold is taken 0326 checker.checkKey(key, ExpiryChecker::EncryptionKey); 0327 checker.checkKey(key, ExpiryChecker::OwnEncryptionKey); 0328 QCOMPARE(spy.count(), 1); 0329 QList<QVariant> arguments = spy.takeFirst(); 0330 QCOMPARE(arguments.at(0).value<GpgME::Key>().keyID(), key.keyID()); 0331 QCOMPARE(arguments.at(1).toString(), msgOwnKey); 0332 QCOMPARE(arguments.at(2).value<ExpiryChecker::ExpiryInformation>(), ExpiryChecker::OwnKeyNearExpiry); 0333 } 0334 { 0335 ExpiryChecker checker(ExpiryCheckerSettings{days{10}, days{1}, days{1}, days{1}}); 0336 checker.setTimeProviderForTest(std::make_shared<FakeTimeProvider>(fakedate)); 0337 QSignalSpy spy(&checker, &ExpiryChecker::expiryMessage); 0338 // Test if the correct treshold is taken 0339 checker.checkKey(key, ExpiryChecker::EncryptionKey); 0340 checker.checkKey(key, ExpiryChecker::OwnSigningKey); 0341 QCOMPARE(spy.count(), 1); 0342 QList<QVariant> arguments = spy.takeFirst(); 0343 QCOMPARE(arguments.at(0).value<GpgME::Key>().keyID(), key.keyID()); 0344 QCOMPARE(arguments.at(1).toString(), msgOwnSigningKey); 0345 QCOMPARE(arguments.at(2).value<ExpiryChecker::ExpiryInformation>(), ExpiryChecker::OwnKeyNearExpiry); 0346 } 0347 } 0348 0349 void expiringEncryptionSubkey_data() 0350 { 0351 QTest::addColumn<GpgME::Key>("key"); 0352 QTest::addColumn<ExpiryChecker::CheckFlags>("checkFlags"); 0353 QTest::addColumn<QDateTime>("fakedate"); 0354 QTest::addColumn<ExpiryChecker::ExpirationStatus>("expectedStatus"); 0355 QTest::addColumn<Kleo::chrono::days>("expectedDuration"); 0356 0357 QTest::newRow("valid - sign") // 0358 << testKey("encr-expires@example.net", GpgME::OpenPGP) // 0359 << ExpiryChecker::CheckFlags{ExpiryChecker::OwnSigningKey} // 0360 << QDateTime{{2023, 4, 18}, {}, QTimeZone::UTC} // 9 days before expiration of encryption subkey 0361 << ExpiryChecker::NotNearExpiry // 0362 << days{0}; // ignored 0363 QTest::newRow("valid - encrypt to self") // 0364 << testKey("encr-expires@example.net", GpgME::OpenPGP) // 0365 << ExpiryChecker::CheckFlags{ExpiryChecker::OwnEncryptionKey} // 0366 << QDateTime{{2023, 4, 18}, {}, QTimeZone::UTC} // 9 days before expiration of encryption subkey 0367 << ExpiryChecker::NotNearExpiry // 0368 << days{0}; // ignored 0369 QTest::newRow("valid - encrypt to others") // 0370 << testKey("encr-expires@example.net", GpgME::OpenPGP) // 0371 << ExpiryChecker::CheckFlags{ExpiryChecker::EncryptionKey} // 0372 << QDateTime{{2023, 4, 18}, {}, QTimeZone::UTC} // 9 days before expiration of encryption subkey 0373 << ExpiryChecker::NotNearExpiry // 0374 << days{0}; // ignored 0375 QTest::newRow("near expiry - sign") // 0376 << testKey("encr-expires@example.net", GpgME::OpenPGP) // 0377 << ExpiryChecker::CheckFlags{ExpiryChecker::OwnSigningKey} // 0378 << QDateTime{{2023, 4, 26}, {}, QTimeZone::UTC} // 1 day before expiration of encryption subkey 0379 << ExpiryChecker::NotNearExpiry // signing key doesn't expire 0380 << days{0}; // ignored 0381 QTest::newRow("near expiry - encrypt to self") // 0382 << testKey("encr-expires@example.net", GpgME::OpenPGP) // 0383 << ExpiryChecker::CheckFlags{ExpiryChecker::OwnEncryptionKey} // 0384 << QDateTime{{2023, 4, 26}, {}, QTimeZone::UTC} // 1 day before expiration of encryption subkey 0385 << ExpiryChecker::ExpiresSoon // 0386 << days{1}; 0387 QTest::newRow("near expiry - encrypt to others") // 0388 << testKey("encr-expires@example.net", GpgME::OpenPGP) // 0389 << ExpiryChecker::CheckFlags{ExpiryChecker::EncryptionKey} // 0390 << QDateTime{{2023, 4, 26}, {}, QTimeZone::UTC} // 1 day before expiration of encryption subkey 0391 << ExpiryChecker::ExpiresSoon // 0392 << days{1}; 0393 QTest::newRow("expired - sign") // 0394 << testKey("encr-expires@example.net", GpgME::OpenPGP) // 0395 << ExpiryChecker::CheckFlags{ExpiryChecker::OwnSigningKey} // 0396 << QDateTime{{2023, 4, 28}, {}, QTimeZone::UTC} // 1 day after expiration of encryption subkey 0397 << ExpiryChecker::NotNearExpiry // signing key doesn't expire 0398 << days{0}; // ignored 0399 QTest::newRow("expired - encrypt to self") // 0400 << testKey("encr-expires@example.net", GpgME::OpenPGP) // 0401 << ExpiryChecker::CheckFlags{ExpiryChecker::OwnEncryptionKey} // 0402 << QDateTime{{2023, 4, 28}, {}, QTimeZone::UTC} // 1 day after expiration of encryption subkey 0403 << ExpiryChecker::Expired // 0404 << days{1}; 0405 QTest::newRow("expired - encrypt to others") // 0406 << testKey("encr-expires@example.net", GpgME::OpenPGP) // 0407 << ExpiryChecker::CheckFlags{ExpiryChecker::EncryptionKey} // 0408 << QDateTime{{2023, 4, 28}, {}, QTimeZone::UTC} // 1 day after expiration of encryption subkey 0409 << ExpiryChecker::Expired // 0410 << days{1}; 0411 } 0412 0413 void expiringEncryptionSubkey() 0414 { 0415 QFETCH(GpgME::Key, key); 0416 QFETCH(ExpiryChecker::CheckFlags, checkFlags); 0417 QFETCH(QDateTime, fakedate); 0418 QFETCH(ExpiryChecker::ExpirationStatus, expectedStatus); 0419 QFETCH(Kleo::chrono::days, expectedDuration); 0420 0421 ExpiryChecker checker(ExpiryCheckerSettings{days{5}, days{5}, days{5}, days{5}}); 0422 checker.setTimeProviderForTest(std::make_shared<FakeTimeProvider>(fakedate)); 0423 const auto result = checker.checkKey(key, checkFlags); 0424 QCOMPARE(result.checkFlags, checkFlags); 0425 QCOMPARE(result.expiration.certificate, key); 0426 QCOMPARE(result.expiration.status, expectedStatus); 0427 if (expectedStatus != ExpiryChecker::NotNearExpiry) { 0428 // duration is undefined if status is NotNearExpiry 0429 QCOMPARE(result.expiration.duration, expectedDuration); 0430 } 0431 } 0432 0433 void notExpiringEncryptionSubkey_data() 0434 { 0435 QTest::addColumn<GpgME::Key>("key"); 0436 QTest::addColumn<ExpiryChecker::CheckFlags>("checkFlags"); 0437 QTest::addColumn<QDateTime>("fakedate"); 0438 QTest::addColumn<ExpiryChecker::ExpirationStatus>("expectedStatus"); 0439 QTest::addColumn<Kleo::chrono::days>("expectedDuration"); 0440 0441 QTest::newRow("valid - sign") // 0442 << testKey("expires@example.net", GpgME::OpenPGP) // 0443 << ExpiryChecker::CheckFlags{ExpiryChecker::OwnSigningKey} // 0444 << QDateTime{{2023, 4, 24}, {}, QTimeZone::UTC} // 9 days before expiration of primary key 0445 << ExpiryChecker::NotNearExpiry // 0446 << days{0}; // ignored 0447 QTest::newRow("valid - encrypt to self") // 0448 << testKey("expires@example.net", GpgME::OpenPGP) // 0449 << ExpiryChecker::CheckFlags{ExpiryChecker::OwnEncryptionKey} // 0450 << QDateTime{{2023, 4, 24}, {}, QTimeZone::UTC} // 9 days before expiration of primary key 0451 << ExpiryChecker::NotNearExpiry // 0452 << days{0}; // ignored 0453 QTest::newRow("valid - encrypt to others") // 0454 << testKey("expires@example.net", GpgME::OpenPGP) // 0455 << ExpiryChecker::CheckFlags{ExpiryChecker::EncryptionKey} // 0456 << QDateTime{{2023, 4, 24}, {}, QTimeZone::UTC} // 9 days before expiration of primary key 0457 << ExpiryChecker::NotNearExpiry // 0458 << days{0}; // ignored 0459 QTest::newRow("near expiry - sign") // 0460 << testKey("expires@example.net", GpgME::OpenPGP) // 0461 << ExpiryChecker::CheckFlags{ExpiryChecker::OwnSigningKey} // 0462 << QDateTime{{2023, 5, 2}, {}, QTimeZone::UTC} // 1 day before expiration of primary key 0463 << ExpiryChecker::ExpiresSoon // 0464 << days{1}; 0465 QTest::newRow("near expiry - encrypt to self") // 0466 << testKey("expires@example.net", GpgME::OpenPGP) // 0467 << ExpiryChecker::CheckFlags{ExpiryChecker::OwnEncryptionKey} // 0468 << QDateTime{{2023, 5, 2}, {}, QTimeZone::UTC} // 1 day before expiration of primary key 0469 << ExpiryChecker::ExpiresSoon // 0470 << days{1}; 0471 QTest::newRow("near expiry - encrypt to others") // 0472 << testKey("expires@example.net", GpgME::OpenPGP) // 0473 << ExpiryChecker::CheckFlags{ExpiryChecker::EncryptionKey} // 0474 << QDateTime{{2023, 5, 2}, {}, QTimeZone::UTC} // 1 day before expiration of primary key 0475 << ExpiryChecker::ExpiresSoon // 0476 << days{1}; 0477 QTest::newRow("expired - sign") // 0478 << testKey("expires@example.net", GpgME::OpenPGP) // 0479 << ExpiryChecker::CheckFlags{ExpiryChecker::OwnSigningKey} // 0480 << QDateTime{{2023, 5, 4}, {}, QTimeZone::UTC} // 1 day after expiration of primary key 0481 << ExpiryChecker::Expired // 0482 << days{1}; 0483 QTest::newRow("expired - encrypt to self") // 0484 << testKey("expires@example.net", GpgME::OpenPGP) // 0485 << ExpiryChecker::CheckFlags{ExpiryChecker::OwnEncryptionKey} // 0486 << QDateTime{{2023, 5, 4}, {}, QTimeZone::UTC} // 1 day after expiration of primary key 0487 << ExpiryChecker::Expired // 0488 << days{1}; 0489 QTest::newRow("expired - encrypt to others") // 0490 << testKey("expires@example.net", GpgME::OpenPGP) // 0491 << ExpiryChecker::CheckFlags{ExpiryChecker::EncryptionKey} // 0492 << QDateTime{{2023, 5, 4}, {}, QTimeZone::UTC} // 1 day after expiration of primary key 0493 << ExpiryChecker::Expired // 0494 << days{1}; 0495 } 0496 0497 void notExpiringEncryptionSubkey() 0498 { 0499 QFETCH(GpgME::Key, key); 0500 QFETCH(ExpiryChecker::CheckFlags, checkFlags); 0501 QFETCH(QDateTime, fakedate); 0502 QFETCH(ExpiryChecker::ExpirationStatus, expectedStatus); 0503 QFETCH(Kleo::chrono::days, expectedDuration); 0504 0505 ExpiryChecker checker(ExpiryCheckerSettings{days{5}, days{5}, days{5}, days{5}}); 0506 checker.setTimeProviderForTest(std::make_shared<FakeTimeProvider>(fakedate)); 0507 const auto result = checker.checkKey(key, checkFlags); 0508 QCOMPARE(result.checkFlags, checkFlags); 0509 QCOMPARE(result.expiration.certificate, key); 0510 QCOMPARE(result.expiration.status, expectedStatus); 0511 if (expectedStatus != ExpiryChecker::NotNearExpiry) { 0512 // duration is undefined if status is NotNearExpiry 0513 QCOMPARE(result.expiration.duration, expectedDuration); 0514 } 0515 } 0516 0517 void certificateChain_data() 0518 { 0519 QTest::addColumn<GpgME::Key>("key"); 0520 QTest::addColumn<ExpiryChecker::CheckFlags>("checkFlags"); 0521 QTest::addColumn<QDateTime>("fakedate"); 0522 QTest::addColumn<ExpiryChecker::ExpirationStatus>("expectedStatus"); 0523 QTest::addColumn<Kleo::chrono::days>("expectedDuration"); 0524 QTest::addColumn<int>("expectedChainResults"); 0525 QTest::addColumn<GpgME::Key>("expectedChainCertificate"); 0526 QTest::addColumn<ExpiryChecker::ExpirationStatus>("expectedChainStatus"); 0527 QTest::addColumn<Kleo::chrono::days>("expectedChainDuration"); 0528 QTest::addColumn<int>("emissions"); 0529 QTest::addColumn<QByteArray>("keyID"); 0530 QTest::addColumn<QString>("msg"); 0531 0532 QTest::newRow("certificate near expiry; issuer okay") // 0533 << testKey("3193786A48BDF2D4D20B8FC6501F4DE8BE231B05", GpgME::CMS) // 0534 << ExpiryChecker::CheckFlags{ExpiryChecker::CertificationKey | ExpiryChecker::CheckChain} // 0535 << QDateTime{{2019, 6, 19}, {}, QTimeZone::UTC} // 5 days before expiration date of the certificate 0536 << ExpiryChecker::ExpiresSoon // 0537 << days{5} // 0538 << 0 // no expired or expiring certificates in issuer chain 0539 << Key{} // ignored 0540 << ExpiryChecker::ExpirationStatus{} // ignored 0541 << days{} // ignored 0542 << 1 // expect 1 signal emission because of a 2-certificate chain with 1 cert near expiry 0543 << QByteArray{"501F4DE8BE231B05"} // first signal emission references the certificate 0544 << QStringLiteral( 0545 "<p>The S/MIME certificate for</p><p align=center><b>CN=AddTrust External CA Root,OU=AddTrust External TTP Network,O=AddTrust AB,C=SE</b> " 0546 "(serial number 51260A931CE27F9CC3A55F79E072AE82)</p><p>expires in 5 days.</p>"); 0547 QTest::newRow("certificate near expiry; issuer not checked") // 0548 << testKey("3193786A48BDF2D4D20B8FC6501F4DE8BE231B05", GpgME::CMS) // 0549 << ExpiryChecker::CheckFlags{ExpiryChecker::CertificationKey} // 0550 << QDateTime{{2019, 6, 19}, {}, QTimeZone::UTC} // 5 days before expiration date of the certificate 0551 << ExpiryChecker::ExpiresSoon // 0552 << days{5} // 0553 << 0 // issuer chain not checked 0554 << Key{} // ignored 0555 << ExpiryChecker::ExpirationStatus{} // ignored 0556 << days{} // ignored 0557 << 1 // expect 1 signal emission because certificate is near expiry 0558 << QByteArray{"501F4DE8BE231B05"} // signal emission references the certificate 0559 << QStringLiteral( 0560 "<p>The S/MIME certificate for</p><p align=center><b>CN=AddTrust External CA Root,OU=AddTrust External TTP Network,O=AddTrust AB,C=SE</b> " 0561 "(serial number 51260A931CE27F9CC3A55F79E072AE82)</p><p>expires in 5 days.</p>"); 0562 QTest::newRow("certificate okay; issuer near expiry") // 0563 << testKey("9E99817D12280C9677674430492EDA1DCE2E4C63", GpgME::CMS) // 0564 << ExpiryChecker::CheckFlags{ExpiryChecker::CertificationKey | ExpiryChecker::CheckChain} // 0565 << QDateTime{{2019, 6, 19}, {}, QTimeZone::UTC} // 5 days before expiration date of the issuer certificate 0566 << ExpiryChecker::NotNearExpiry // 0567 << days{346} // 0568 << 1 // one expiring certificate in issuer chain 0569 << testKey("3193786A48BDF2D4D20B8FC6501F4DE8BE231B05", GpgME::CMS) // 0570 << ExpiryChecker::ExpiresSoon // 0571 << days{5} // 0572 << 1 // expect 1 signal emission because of a 2-certificate chain with 1 cert near expiry 0573 << QByteArray{"501F4DE8BE231B05"} // first signal emission references the isser certificate 0574 << QStringLiteral( 0575 "<p>The intermediate CA certificate</p><p align=center><b>CN=AddTrust External CA Root,OU=AddTrust External TTP Network,O=AddTrust " 0576 "AB,C=SE</b></p><p>for S/MIME certificate</p><p align=center><b>CN=UTN - DATACorp SGC,L=Salt Lake " 0577 "City,SP=UT,OU=http://www.usertrust.com,O=The USERTRUST Network,C=US</b> (serial number 46EAF096054CC5E3FA65EA6E9F42C664)</p><p>expires in " 0578 "5 days.</p>"); 0579 QTest::newRow("certificate okay; issuer not checked") // 0580 << testKey("9E99817D12280C9677674430492EDA1DCE2E4C63", GpgME::CMS) // 0581 << ExpiryChecker::CheckFlags{ExpiryChecker::CertificationKey} // 0582 << QDateTime{{2019, 6, 19}, {}, QTimeZone::UTC} // 5 days before expiration date of the issuer certificate 0583 << ExpiryChecker::NotNearExpiry // 0584 << days{346} // 0585 << 0 // issuer chain not checked 0586 << Key{} // ignored 0587 << ExpiryChecker::ExpirationStatus{} // ignored 0588 << days{} // ignored 0589 << 0 // expect 0 signal emission because certificate is not near expiry 0590 << QByteArray{} // 0591 << QString{}; 0592 QTest::newRow("certificate near expiry; issuer expired") // 0593 << testKey("9E99817D12280C9677674430492EDA1DCE2E4C63", GpgME::CMS) // 0594 << ExpiryChecker::CheckFlags{ExpiryChecker::CertificationKey | ExpiryChecker::CheckChain} // 0595 << QDateTime{{2020, 5, 25}, {}, QTimeZone::UTC} // 5 days before expiration date of the certificate 0596 << ExpiryChecker::ExpiresSoon // 0597 << days{5} // 0598 << 1 // one expired certificate in issuer chain 0599 << testKey("3193786A48BDF2D4D20B8FC6501F4DE8BE231B05", GpgME::CMS) // 0600 << ExpiryChecker::Expired // 0601 << days{336} // 0602 << 2 // expect 2 signal emissions because both certificates in the 2-certificate chain are either expired or near expiry 0603 << QByteArray{"492EDA1DCE2E4C63"} // first signal emission references the certificate 0604 << QStringLiteral( 0605 "<p>The S/MIME certificate for</p><p align=center><b>CN=UTN - DATACorp SGC,L=Salt Lake City,SP=UT,OU=http://www.usertrust.com,O=The " 0606 "USERTRUST Network,C=US</b> (serial number 46EAF096054CC5E3FA65EA6E9F42C664)</p><p>expires in 5 days.</p>"); 0607 QTest::newRow("certificate near expiry; issuer not checked") 0608 << testKey("9E99817D12280C9677674430492EDA1DCE2E4C63", GpgME::CMS) // 0609 << ExpiryChecker::CheckFlags{ExpiryChecker::CertificationKey} // 0610 << QDateTime{{2020, 5, 25}, {}, QTimeZone::UTC} // 5 days before expiration date of the certificate 0611 << ExpiryChecker::ExpiresSoon // 0612 << days{5} // 0613 << 0 // issuer chain not checked 0614 << Key{} // ignored 0615 << ExpiryChecker::ExpirationStatus{} // ignored 0616 << days{} // ignored 0617 << 1 // expect 1 signal emission because certificate is near expiry 0618 << QByteArray{"492EDA1DCE2E4C63"} // first signal emission references the certificate 0619 << QStringLiteral( 0620 "<p>The S/MIME certificate for</p><p align=center><b>CN=UTN - DATACorp SGC,L=Salt Lake City,SP=UT,OU=http://www.usertrust.com,O=The " 0621 "USERTRUST Network,C=US</b> (serial number 46EAF096054CC5E3FA65EA6E9F42C664)</p><p>expires in 5 days.</p>"); 0622 } 0623 0624 void certificateChain() 0625 { 0626 QFETCH(GpgME::Key, key); 0627 QFETCH(ExpiryChecker::CheckFlags, checkFlags); 0628 QFETCH(QDateTime, fakedate); 0629 QFETCH(ExpiryChecker::ExpirationStatus, expectedStatus); 0630 QFETCH(Kleo::chrono::days, expectedDuration); 0631 QFETCH(int, expectedChainResults); 0632 QFETCH(GpgME::Key, expectedChainCertificate); 0633 QFETCH(ExpiryChecker::ExpirationStatus, expectedChainStatus); 0634 QFETCH(Kleo::chrono::days, expectedChainDuration); 0635 QFETCH(int, emissions); 0636 QFETCH(QByteArray, keyID); 0637 QFETCH(QString, msg); 0638 0639 { 0640 ExpiryChecker checker(ExpiryCheckerSettings{days{1}, days{10}, days{10}, days{10}}); 0641 checker.setTimeProviderForTest(std::make_shared<FakeTimeProvider>(fakedate)); 0642 QSignalSpy spy(&checker, &ExpiryChecker::expiryMessage); 0643 const auto result = checker.checkKey(key, checkFlags); 0644 QCOMPARE(result.checkFlags, checkFlags); 0645 QCOMPARE(result.expiration.certificate, key); 0646 QCOMPARE(result.expiration.status, expectedStatus); 0647 QCOMPARE(result.expiration.duration, expectedDuration); 0648 QCOMPARE(result.chainExpiration.size(), expectedChainResults); 0649 if (result.chainExpiration.size() > 0) { 0650 const auto issuerExpiration = result.chainExpiration.front(); 0651 QCOMPARE(issuerExpiration.status, expectedChainStatus); 0652 QCOMPARE(issuerExpiration.duration, expectedChainDuration); 0653 } 0654 QCOMPARE(spy.count(), emissions); 0655 if (emissions > 0) { 0656 QList<QVariant> arguments = spy.takeFirst(); 0657 QCOMPARE(arguments.at(0).value<GpgME::Key>().keyID(), keyID); 0658 QCOMPARE(arguments.at(1).toString(), msg); 0659 QCOMPARE(arguments.at(2).value<ExpiryChecker::ExpiryInformation>(), ExpiryChecker::OtherKeyNearExpiry); 0660 } 0661 } 0662 } 0663 0664 void noSuitableSubkey_data() 0665 { 0666 QTest::addColumn<GpgME::Key>("key"); 0667 QTest::addColumn<ExpiryChecker::CheckFlags>("checkFlags"); 0668 0669 QTest::newRow("OpenPGP; no encryption subkey") // 0670 << testKey("sign-only@example.net", GpgME::OpenPGP) // sign-only key 0671 << ExpiryChecker::CheckFlags{ExpiryChecker::EncryptionKey}; 0672 QTest::newRow("S/MIME; no encryption key") // 0673 << testKey("3193786A48BDF2D4D20B8FC6501F4DE8BE231B05", GpgME::CMS) // certification-only key 0674 << ExpiryChecker::CheckFlags{ExpiryChecker::EncryptionKey}; 0675 QTest::newRow("S/MIME; no signing key") // 0676 << testKey("3193786A48BDF2D4D20B8FC6501F4DE8BE231B05", GpgME::CMS) // certification-only key 0677 << ExpiryChecker::CheckFlags{ExpiryChecker::SigningKey}; 0678 } 0679 0680 void noSuitableSubkey() 0681 { 0682 QFETCH(GpgME::Key, key); 0683 QFETCH(ExpiryChecker::CheckFlags, checkFlags); 0684 0685 ExpiryChecker checker(ExpiryCheckerSettings{days{1}, days{1}, days{1}, days{1}}); 0686 const auto result = checker.checkKey(key, checkFlags); 0687 QCOMPARE(result.expiration.certificate, key); 0688 QCOMPARE(result.expiration.status, ExpiryChecker::NoSuitableSubkey); 0689 } 0690 0691 private: 0692 // OpenPGP keys 0693 // 0694 // pub rsa2048 2009-11-13 [SC] 0695 // 1BA323932B3FAA826132C79E8D9860C58F246DE6 0696 // uid [ultimate] unittest key (no password) <test@kolab.org> 0697 // sub rsa2048 2009-11-13 [E] 0698 // 0699 // pub ed25519 2019-01-22 [SC] [expired: 2021-01-21] 0700 // EB85BB5FA33A75E15E944E63F231550C4F47E38E 0701 // uid [ expired] alice@autocrypt.example 0702 // sub cv25519 2019-01-22 [E] [expired: 2021-01-21] 0703 // 0704 // pub ed25519 2023-04-17 [SC] 0705 // C1218845DEEDA5432198FA7AF78A0834BB3C4A16 0706 // uid [ultimate] encr-expires@example.net 0707 // sub cv25519 2023-04-17 [E] [expires: 2023-04-27] 0708 // 0709 // pub ed25519 2023-05-02 [SC] [expires: 2023-05-03] 0710 // C3607CB03C13FDC6CB0384649358227B5DD4D260 0711 // uid [ultimate] expires@example.net 0712 // sub cv25519 2023-05-02 [E] 0713 // 0714 // pub ed25519 2023-05-02 [SC] [expires: 2023-05-03] 0715 // 26C9EEEA094AC00FDA0FFC1384EFDDEEC99C022F 0716 // uid [ultimate] sign-only@example.net 0717 // 0718 // 0719 // S/MIME certificates 0720 // 0721 // ID: 0x212B49DC 0722 // S/N: 00D345203A186385C9 0723 // (dec): 15223609549285197257 0724 // Issuer: /CN=unittest cert/O=KDAB/C=US/EMail=test@example.com 0725 // Subject: /CN=unittest cert/O=KDAB/C=US/EMail=test@example.com 0726 // validity: 2010-06-29 13:48:23 through 2013-03-25 13:48:23 0727 // key type: rsa1024 0728 // chain length: unlimited 0729 // sha1 fpr: 24:D2:FC:A2:2E:B3:B8:0A:1E:37:71:D1:4C:C6:58:E3:21:2B:49:DC 0730 // sha2 fpr: 62:4B:A4:B8:7D:8F:99:AA:6B:46:E3:C8:C5:BE:BF:30:29:B6:EC:4E:CC:7D:1F:9F:A8:39:B6:CE:03:6F:C7:FB 0731 // 0732 // S/MIME certificates building a circular chain 0733 // 0734 // ID: 0xBE231B05 0735 // S/N: 51260A931CE27F9CC3A55F79E072AE82 0736 // (dec): 107864989418777835411218143713715990146 0737 // Issuer: /CN=UTN - DATACorp SGC/OU=http:\x2f\x2fwww.usertrust.com/O=The USERTRUST Network/L=Salt Lake City/ST=UT/C=US 0738 // Subject: /CN=AddTrust External CA Root/OU=AddTrust External TTP Network/O=AddTrust AB/C=SE 0739 // validity: 2005-06-07 08:09:10 through 2019-06-24 19:06:30 0740 // key type: rsa2048 0741 // key usage: certSign crlSign 0742 // ext key usage: ms-serverGatedCrypto (suggested), serverGatedCrypto.ns (suggested) 0743 // chain length: unlimited 0744 // sha1 fpr: 31:93:78:6A:48:BD:F2:D4:D2:0B:8F:C6:50:1F:4D:E8:BE:23:1B:05 0745 // sha2 fpr: 92:5E:4B:37:2B:A3:2E:5E:87:30:22:84:B2:D7:C9:DF:BF:82:00:FF:CB:A0:D1:66:03:A1:A0:6F:F7:6C:D3:53 0746 // 0747 // ID: 0xCE2E4C63 0748 // S/N: 46EAF096054CC5E3FA65EA6E9F42C664 0749 // (dec): 94265836834010752231943569188608722532 0750 // Issuer: /CN=AddTrust External CA Root/OU=AddTrust External TTP Network/O=AddTrust AB/C=SE 0751 // Subject: /CN=UTN - DATACorp SGC/OU=http:\x2f\x2fwww.usertrust.com/O=The USERTRUST Network/L=Salt Lake City/ST=UT/C=US 0752 // validity: 2005-06-07 08:09:10 through 2020-05-30 10:48:38 0753 // key type: rsa2048 0754 // key usage: certSign crlSign 0755 // ext key usage: ms-serverGatedCrypto (suggested), serverGatedCrypto.ns (suggested) 0756 // policies: 2.5.29.32.0:N: 0757 // chain length: unlimited 0758 // sha1 fpr: 9E:99:81:7D:12:28:0C:96:77:67:44:30:49:2E:DA:1D:CE:2E:4C:63 0759 // sha2 fpr: 21:3F:AD:03:B1:C5:23:47:E9:A8:0F:29:9A:F0:89:9B:CA:FF:3F:62:B3:4E:B0:60:66:F4:D7:EE:A5:EE:1A:73 0760 0761 Key testKey(const char *pattern, Protocol protocol = UnknownProtocol) 0762 { 0763 const std::vector<Key> keys = KeyCache::instance()->findByEMailAddress(pattern); 0764 for (const auto &key : keys) { 0765 if (protocol == UnknownProtocol || key.protocol() == protocol) { 0766 return key; 0767 } 0768 } 0769 const auto key = KeyCache::instance()->findByKeyIDOrFingerprint(pattern); 0770 if (key.isNull()) { 0771 qWarning() << "No" << Formatting::displayName(protocol) << "test key found for" << pattern; 0772 } 0773 return key; 0774 } 0775 0776 private: 0777 QSharedPointer<QTemporaryDir> mGnupgHome; 0778 std::shared_ptr<const KeyCache> mKeyCache; 0779 }; 0780 0781 QTEST_MAIN(ExpiryCheckerTest) 0782 #include "expirycheckertest.moc"