File indexing completed on 2024-04-28 04:44:10
0001 /** 0002 * Copyright (C) 2006-2007 Brad Hards <bradh@frogmouth.net> 0003 * 0004 * Redistribution and use in source and binary forms, with or without 0005 * modification, are permitted provided that the following conditions 0006 * are met: 0007 * 0008 * 1. Redistributions of source code must retain the above copyright 0009 * notice, this list of conditions and the following disclaimer. 0010 * 2. Redistributions in binary form must reproduce the above copyright 0011 * notice, this list of conditions and the following disclaimer in the 0012 * documentation and/or other materials provided with the distribution. 0013 * 0014 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 0015 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 0016 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 0017 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 0018 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 0019 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 0020 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 0021 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 0022 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 0023 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 0024 */ 0025 0026 #include <QtCrypto> 0027 #include <QtTest/QtTest> 0028 0029 #include <cstdlib> 0030 0031 #ifdef QT_STATICPLUGIN 0032 #include "import_plugins.h" 0033 #endif 0034 0035 // qt did not introduce qputenv until 4.4, so we'll keep a copy here for 4.2 0036 // compat 0037 bool my_qputenv(const char *varName, const QByteArray &value) 0038 { 0039 #if defined(_MSC_VER) && _MSC_VER >= 1400 0040 return _putenv_s(varName, value.constData()) == 0; 0041 #else 0042 QByteArray buffer(varName); 0043 buffer += "="; 0044 buffer += value; 0045 return putenv(qstrdup(buffer.constData())) == 0; 0046 #endif 0047 } 0048 0049 static int qca_setenv(const char *name, const char *value, int overwrite) 0050 { 0051 if (!overwrite && qEnvironmentVariableIsSet(name)) 0052 return 0; 0053 0054 if (my_qputenv(name, QByteArray(value))) 0055 return 0; // success 0056 else 0057 return 1; // error 0058 } 0059 0060 // Note; in a real application you get this from a user, but this 0061 // is a useful trick for a unit test. 0062 // See the qcatool application or keyloader and eventhandler examples 0063 // for how to do this properly. 0064 class PGPPassphraseProvider : public QObject 0065 { 0066 Q_OBJECT 0067 public: 0068 PGPPassphraseProvider(QObject *parent = nullptr) 0069 : QObject(parent) 0070 { 0071 connect(&m_handler, &QCA::EventHandler::eventReady, this, &PGPPassphraseProvider::eh_eventReady); 0072 m_handler.start(); 0073 } 0074 0075 private Q_SLOTS: 0076 void eh_eventReady(int id, const QCA::Event &event) 0077 { 0078 if (event.type() == QCA::Event::Password) { 0079 QCA::SecureArray pass("start"); 0080 m_handler.submitPassword(id, pass); 0081 } else { 0082 m_handler.reject(id); 0083 } 0084 } 0085 0086 private: 0087 QCA::EventHandler m_handler; 0088 }; 0089 0090 class PGPPassphraseProviderThread : public QCA::SyncThread 0091 { 0092 Q_OBJECT 0093 public: 0094 ~PGPPassphraseProviderThread() override 0095 { 0096 stop(); 0097 } 0098 0099 protected: 0100 void atStart() override 0101 { 0102 prov = new PGPPassphraseProvider; 0103 } 0104 0105 void atEnd() override 0106 { 0107 delete prov; 0108 } 0109 0110 private: 0111 PGPPassphraseProvider *prov; 0112 }; 0113 0114 class PgpUnitTest : public QObject 0115 { 0116 Q_OBJECT 0117 0118 private Q_SLOTS: 0119 void initTestCase(); 0120 void cleanupTestCase(); 0121 void testKeyRing(); 0122 void testMessageSign(); 0123 void testClearsign(); 0124 void testDetachedSign(); 0125 void testSignaturesWithExpiredSubkeys(); 0126 void testEncryptionWithExpiredSubkeys(); 0127 }; 0128 0129 void PgpUnitTest::initTestCase() 0130 { 0131 // Change current directory to executable directory 0132 // it is need to find keys*_work directories 0133 if (!QCoreApplication::applicationDirPath().isEmpty()) 0134 QDir::setCurrent(QCoreApplication::applicationDirPath()); 0135 } 0136 0137 void PgpUnitTest::cleanupTestCase() 0138 { 0139 } 0140 0141 void PgpUnitTest::testKeyRing() 0142 { 0143 QCA::Initializer *qcaInit = new QCA::Initializer; 0144 0145 // We test a small keyring - I downloaded a publically available one from 0146 QByteArray oldGNUPGHOME = qgetenv("GNUPGHOME"); 0147 // the Amsterdam Internet Exchange. 0148 if (qca_setenv("GNUPGHOME", "./keys1_work", 1) != 0) { 0149 QFAIL("Expected to be able to set the GNUPGHOME environment variable, but couldn't"); 0150 } 0151 0152 // activate the KeyStoreManager 0153 QCA::KeyStoreManager::start(); 0154 0155 if (QCA::isSupported(QStringList(QStringLiteral("keystorelist")), QStringLiteral("qca-gnupg"))) { 0156 QCA::KeyStoreManager keyManager(this); 0157 keyManager.waitForBusyFinished(); 0158 QStringList storeIds = keyManager.keyStores(); 0159 QVERIFY(storeIds.contains(QStringLiteral("qca-gnupg"))); 0160 0161 QCA::KeyStore pgpStore(QStringLiteral("qca-gnupg"), &keyManager); 0162 QVERIFY(pgpStore.isValid()); 0163 QCOMPARE(pgpStore.name(), QStringLiteral("GnuPG Keyring")); 0164 QCOMPARE(pgpStore.type(), QCA::KeyStore::PGPKeyring); 0165 QCOMPARE(pgpStore.id(), QStringLiteral("qca-gnupg")); 0166 QCOMPARE(pgpStore.isReadOnly(), false); 0167 QCOMPARE(pgpStore.holdsTrustedCertificates(), false); 0168 QCOMPARE(pgpStore.holdsIdentities(), true); 0169 QCOMPARE(pgpStore.holdsPGPPublicKeys(), true); 0170 0171 QList<QCA::KeyStoreEntry> keylist = pgpStore.entryList(); 0172 QCOMPARE(keylist.count(), 6); 0173 QStringList nameList; 0174 foreach (const QCA::KeyStoreEntry key, keylist) { 0175 QCOMPARE(key.isNull(), false); 0176 QCOMPARE(key.type(), QCA::KeyStoreEntry::TypePGPPublicKey); 0177 QCOMPARE(key.id().length(), 16); // 16 hex digits 0178 QVERIFY(key.keyBundle().isNull()); 0179 QVERIFY(key.certificate().isNull()); 0180 QVERIFY(key.crl().isNull()); 0181 QVERIFY(key.pgpSecretKey().isNull()); 0182 QCOMPARE(key.pgpPublicKey().isNull(), false); 0183 0184 // We accumulate the names, and check them next 0185 nameList << key.name(); 0186 } 0187 QVERIFY(nameList.contains(QStringLiteral("Steven Bakker <steven.bakker@ams-ix.net>"))); 0188 QVERIFY(nameList.contains(QStringLiteral("Romeo Zwart <rz@ams-ix.net>"))); 0189 QVERIFY(nameList.contains(QStringLiteral("Arien Vijn <arien.vijn@ams-ix.net>"))); 0190 QVERIFY(nameList.contains(QStringLiteral("Niels Bakker <niels.bakker@ams-ix.net>"))); 0191 QVERIFY(nameList.contains(QStringLiteral("Henk Steenman <Henk.Steenman@ams-ix.net>"))); 0192 QVERIFY(nameList.contains(QStringLiteral("Geert Nijpels <geert.nijpels@ams-ix.net>"))); 0193 0194 // TODO: We should test removeEntry() and writeEntry() here. 0195 } 0196 0197 delete qcaInit; 0198 qcaInit = new QCA::Initializer; 0199 0200 // We now test an empty keyring 0201 if (qca_setenv("GNUPGHOME", "./keys2_work", 1) != 0) { 0202 QFAIL("Expected to be able to set the GNUPGHOME environment variable, but couldn't"); 0203 } 0204 0205 QCA::KeyStoreManager::start(); 0206 0207 if (QCA::isSupported(QStringList(QStringLiteral("keystorelist")), QStringLiteral("qca-gnupg"))) { 0208 QCA::KeyStoreManager keyManager(this); 0209 keyManager.waitForBusyFinished(); 0210 QStringList storeIds = keyManager.keyStores(); 0211 QVERIFY(storeIds.contains(QStringLiteral("qca-gnupg"))); 0212 0213 QCA::KeyStore pgpStore(QStringLiteral("qca-gnupg"), &keyManager); 0214 0215 QList<QCA::KeyStoreEntry> keylist = pgpStore.entryList(); 0216 QCOMPARE(keylist.count(), 0); 0217 // TODO: We should test removeEntry() and writeEntry() here. 0218 } 0219 0220 if (false == oldGNUPGHOME.isNull()) { 0221 qca_setenv("GNUPGHOME", oldGNUPGHOME.data(), 1); 0222 } 0223 0224 delete qcaInit; 0225 } 0226 0227 void PgpUnitTest::testMessageSign() 0228 { 0229 QCA::Initializer qcaInit; 0230 0231 // event handling cannot be used in the same thread as synchronous calls 0232 // which might require event handling. let's put our event handler in 0233 // a side thread so that we can write the unit test synchronously. 0234 PGPPassphraseProviderThread thread; 0235 thread.start(); 0236 0237 // This keyring has a private / public key pair 0238 QByteArray oldGNUPGHOME = qgetenv("GNUPGHOME"); 0239 if (0 != qca_setenv("GNUPGHOME", "./keys3_work", 1)) { 0240 QFAIL("Expected to be able to set the GNUPGHOME environment variable, but couldn't"); 0241 } 0242 0243 // activate the KeyStoreManager 0244 QCA::KeyStoreManager::start(); 0245 0246 QCA::KeyStoreManager keyManager(this); 0247 keyManager.waitForBusyFinished(); 0248 0249 if (QCA::isSupported(QStringList(QStringLiteral("openpgp")), QStringLiteral("qca-gnupg")) || 0250 QCA::isSupported(QStringList(QStringLiteral("keystorelist")), QStringLiteral("qca-gnupg"))) { 0251 QStringList storeIds = keyManager.keyStores(); 0252 QVERIFY(storeIds.contains(QStringLiteral("qca-gnupg"))); 0253 0254 QCA::KeyStore pgpStore(QStringLiteral("qca-gnupg"), &keyManager); 0255 QVERIFY(pgpStore.isValid()); 0256 0257 QList<QCA::KeyStoreEntry> keylist = pgpStore.entryList(); 0258 QCOMPARE(keylist.count(), 1); 0259 0260 const QCA::KeyStoreEntry &myPGPKey = keylist.at(0); 0261 QCOMPARE(myPGPKey.isNull(), false); 0262 QCOMPARE(myPGPKey.name(), 0263 QStringLiteral("Qca Test Key (This key is only for QCA unit tests) <qca@example.com>")); 0264 QCOMPARE(myPGPKey.type(), QCA::KeyStoreEntry::TypePGPSecretKey); 0265 QCOMPARE(myPGPKey.id(), QStringLiteral("9E946237DAFCCFF4")); 0266 QVERIFY(myPGPKey.keyBundle().isNull()); 0267 QVERIFY(myPGPKey.certificate().isNull()); 0268 QVERIFY(myPGPKey.crl().isNull()); 0269 QCOMPARE(myPGPKey.pgpSecretKey().isNull(), false); 0270 QCOMPARE(myPGPKey.pgpPublicKey().isNull(), false); 0271 0272 // first make the SecureMessageKey 0273 QCA::SecureMessageKey key; 0274 key.setPGPSecretKey(myPGPKey.pgpSecretKey()); 0275 QVERIFY(key.havePrivate()); 0276 0277 // our data to sign 0278 QByteArray plain = "Hello, world"; 0279 0280 // let's do it 0281 QCA::OpenPGP pgp; 0282 QCA::SecureMessage msg(&pgp); 0283 msg.setSigner(key); 0284 msg.setFormat(QCA::SecureMessage::Ascii); 0285 msg.startSign(QCA::SecureMessage::Message); 0286 msg.update(plain); 0287 msg.end(); 0288 msg.waitForFinished(5000); 0289 0290 #if 0 0291 QString str = QCA::KeyStoreManager::diagnosticText(); 0292 QCA::KeyStoreManager::clearDiagnosticText(); 0293 QStringList lines = str.split('\n', Qt::SkipEmptyParts); 0294 for(int n = 0; n < lines.count(); ++n) 0295 fprintf(stderr, "keystore: %s\n", qPrintable(lines[n])); 0296 0297 QString out = msg.diagnosticText(); 0298 QStringList msglines = out.split('\n', Qt::SkipEmptyParts); 0299 for(int n = 0; n < msglines.count(); ++n) 0300 fprintf(stderr, "message: %s\n", qPrintable(msglines[n])); 0301 #endif 0302 QByteArray messageData; 0303 if (msg.success()) { 0304 messageData = msg.read(); 0305 } else { 0306 qDebug() << "Failure:" << msg.errorCode(); 0307 QFAIL("Failed to sign in Message format"); 0308 } 0309 // qDebug() << "Message format data:" << messageData; 0310 0311 // OK, now lets verify that the result will verify. 0312 // let's do it 0313 QCA::OpenPGP pgp2; 0314 QCA::SecureMessage msg2(&pgp2); 0315 msg2.setFormat(QCA::SecureMessage::Ascii); 0316 msg2.startVerify(); 0317 msg2.update(messageData); 0318 msg2.end(); 0319 msg2.waitForFinished(5000); 0320 0321 QVERIFY(msg2.verifySuccess()); 0322 0323 if (msg2.success()) { 0324 QCOMPARE(msg2.read(), plain); 0325 } else { 0326 qDebug() << "Failure:" << msg2.errorCode(); 0327 QFAIL("Failed to verify message"); 0328 } 0329 0330 // why is this here? 0331 if (false == oldGNUPGHOME.isNull()) { 0332 qca_setenv("GNUPGHOME", oldGNUPGHOME.data(), 1); 0333 } 0334 0335 // now test that if we corrupt the message, it no longer 0336 // verifies correctly. 0337 messageData.replace('T', 't'); 0338 messageData.replace('w', 'W'); 0339 QCA::SecureMessage msg3(&pgp2); 0340 msg3.setFormat(QCA::SecureMessage::Ascii); 0341 msg3.startVerify(); 0342 msg3.update(messageData); 0343 msg3.end(); 0344 msg3.waitForFinished(5000); 0345 0346 QCOMPARE(msg3.verifySuccess(), false); 0347 QCOMPARE(msg3.errorCode(), QCA::SecureMessage::ErrorUnknown); 0348 } 0349 0350 if (false == oldGNUPGHOME.isNull()) { 0351 qca_setenv("GNUPGHOME", oldGNUPGHOME.data(), 1); 0352 } 0353 } 0354 0355 void PgpUnitTest::testClearsign() 0356 { 0357 QCA::Initializer qcaInit; 0358 0359 // event handling cannot be used in the same thread as synchronous calls 0360 // which might require event handling. let's put our event handler in 0361 // a side thread so that we can write the unit test synchronously. 0362 PGPPassphraseProviderThread thread; 0363 thread.start(); 0364 0365 // This keyring has a private / public key pair 0366 QByteArray oldGNUPGHOME = qgetenv("GNUPGHOME"); 0367 if (0 != qca_setenv("GNUPGHOME", "./keys3_work", 1)) { 0368 QFAIL("Expected to be able to set the GNUPGHOME environment variable, but couldn't"); 0369 } 0370 0371 // activate the KeyStoreManager 0372 QCA::KeyStoreManager::start(); 0373 0374 QCA::KeyStoreManager keyManager(this); 0375 keyManager.waitForBusyFinished(); 0376 0377 if (QCA::isSupported(QStringList(QStringLiteral("openpgp")), QStringLiteral("qca-gnupg")) || 0378 QCA::isSupported(QStringList(QStringLiteral("keystorelist")), QStringLiteral("qca-gnupg"))) { 0379 QStringList storeIds = keyManager.keyStores(); 0380 QVERIFY(storeIds.contains(QStringLiteral("qca-gnupg"))); 0381 0382 QCA::KeyStore pgpStore(QStringLiteral("qca-gnupg"), &keyManager); 0383 QVERIFY(pgpStore.isValid()); 0384 0385 QList<QCA::KeyStoreEntry> keylist = pgpStore.entryList(); 0386 QCOMPARE(keylist.count(), 1); 0387 0388 const QCA::KeyStoreEntry &myPGPKey = keylist.at(0); 0389 QCOMPARE(myPGPKey.isNull(), false); 0390 QCOMPARE(myPGPKey.name(), 0391 QStringLiteral("Qca Test Key (This key is only for QCA unit tests) <qca@example.com>")); 0392 QCOMPARE(myPGPKey.type(), QCA::KeyStoreEntry::TypePGPSecretKey); 0393 QCOMPARE(myPGPKey.id(), QStringLiteral("9E946237DAFCCFF4")); 0394 QVERIFY(myPGPKey.keyBundle().isNull()); 0395 QVERIFY(myPGPKey.certificate().isNull()); 0396 QVERIFY(myPGPKey.crl().isNull()); 0397 QCOMPARE(myPGPKey.pgpSecretKey().isNull(), false); 0398 QCOMPARE(myPGPKey.pgpPublicKey().isNull(), false); 0399 0400 // first make the SecureMessageKey 0401 QCA::SecureMessageKey key; 0402 key.setPGPSecretKey(myPGPKey.pgpSecretKey()); 0403 QVERIFY(key.havePrivate()); 0404 0405 // our data to sign 0406 QByteArray plain = "Hello, world"; 0407 0408 // let's do it 0409 QCA::OpenPGP pgp; 0410 QCA::SecureMessage msg(&pgp); 0411 msg.setSigner(key); 0412 msg.setFormat(QCA::SecureMessage::Ascii); 0413 msg.startSign(QCA::SecureMessage::Clearsign); 0414 msg.update(plain); 0415 msg.end(); 0416 msg.waitForFinished(5000); 0417 0418 #if 0 0419 QString str = QCA::KeyStoreManager::diagnosticText(); 0420 QCA::KeyStoreManager::clearDiagnosticText(); 0421 QStringList lines = str.split('\n', Qt::SkipEmptyParts); 0422 for(int n = 0; n < lines.count(); ++n) 0423 fprintf(stderr, "keystore: %s\n", qPrintable(lines[n])); 0424 0425 QString out = msg.diagnosticText(); 0426 QStringList msglines = out.split('\n', Qt::SkipEmptyParts); 0427 for(int n = 0; n < msglines.count(); ++n) 0428 fprintf(stderr, "message: %s\n", qPrintable(msglines[n])); 0429 #endif 0430 0431 QByteArray clearsignedData; 0432 if (msg.success()) { 0433 clearsignedData = msg.read(); 0434 } else { 0435 qDebug() << "Failure:" << msg.errorCode(); 0436 QFAIL("Failed to clearsign"); 0437 } 0438 0439 // OK, now lets verify that the result will verify. 0440 // let's do it 0441 QCA::OpenPGP pgp2; 0442 QCA::SecureMessage msg2(&pgp2); 0443 msg2.setFormat(QCA::SecureMessage::Ascii); 0444 msg2.startVerify(); 0445 msg2.update(clearsignedData); 0446 msg2.end(); 0447 msg2.waitForFinished(5000); 0448 0449 QVERIFY(msg2.verifySuccess()); 0450 0451 if (msg2.success()) { 0452 // The trimmed() call is needed because clearsigning 0453 // trashes whitespace 0454 QCOMPARE(msg2.read().trimmed(), plain.trimmed()); 0455 } else { 0456 qDebug() << "Failure:" << msg2.errorCode(); 0457 QFAIL("Failed to verify clearsigned message"); 0458 } 0459 } 0460 0461 if (false == oldGNUPGHOME.isNull()) { 0462 qca_setenv("GNUPGHOME", oldGNUPGHOME.data(), 1); 0463 } 0464 } 0465 0466 void PgpUnitTest::testDetachedSign() 0467 { 0468 QCA::Initializer qcaInit; 0469 0470 // event handling cannot be used in the same thread as synchronous calls 0471 // which might require event handling. let's put our event handler in 0472 // a side thread so that we can write the unit test synchronously. 0473 PGPPassphraseProviderThread thread; 0474 thread.start(); 0475 0476 // This keyring has a private / public key pair 0477 QByteArray oldGNUPGHOME = qgetenv("GNUPGHOME"); 0478 if (0 != qca_setenv("GNUPGHOME", "./keys3_work", 1)) { 0479 QFAIL("Expected to be able to set the GNUPGHOME environment variable, but couldn't"); 0480 } 0481 0482 // activate the KeyStoreManager 0483 QCA::KeyStoreManager::start(); 0484 0485 QCA::KeyStoreManager keyManager(this); 0486 keyManager.waitForBusyFinished(); 0487 0488 if (QCA::isSupported(QStringList(QStringLiteral("openpgp")), QStringLiteral("qca-gnupg")) || 0489 QCA::isSupported(QStringList(QStringLiteral("keystorelist")), QStringLiteral("qca-gnupg"))) { 0490 QStringList storeIds = keyManager.keyStores(); 0491 QVERIFY(storeIds.contains(QStringLiteral("qca-gnupg"))); 0492 0493 QCA::KeyStore pgpStore(QStringLiteral("qca-gnupg"), &keyManager); 0494 QVERIFY(pgpStore.isValid()); 0495 0496 QList<QCA::KeyStoreEntry> keylist = pgpStore.entryList(); 0497 QCOMPARE(keylist.count(), 1); 0498 0499 const QCA::KeyStoreEntry &myPGPKey = keylist.at(0); 0500 QCOMPARE(myPGPKey.isNull(), false); 0501 QCOMPARE(myPGPKey.name(), 0502 QStringLiteral("Qca Test Key (This key is only for QCA unit tests) <qca@example.com>")); 0503 QCOMPARE(myPGPKey.type(), QCA::KeyStoreEntry::TypePGPSecretKey); 0504 QCOMPARE(myPGPKey.id(), QStringLiteral("9E946237DAFCCFF4")); 0505 QVERIFY(myPGPKey.keyBundle().isNull()); 0506 QVERIFY(myPGPKey.certificate().isNull()); 0507 QVERIFY(myPGPKey.crl().isNull()); 0508 QCOMPARE(myPGPKey.pgpSecretKey().isNull(), false); 0509 QCOMPARE(myPGPKey.pgpPublicKey().isNull(), false); 0510 0511 // first make the SecureMessageKey 0512 QCA::SecureMessageKey key; 0513 key.setPGPSecretKey(myPGPKey.pgpSecretKey()); 0514 QVERIFY(key.havePrivate()); 0515 0516 // our data to sign 0517 QByteArray plain = "Hello, world"; 0518 0519 // let's do it 0520 QCA::OpenPGP pgp; 0521 QCA::SecureMessage msg(&pgp); 0522 msg.setSigner(key); 0523 msg.setFormat(QCA::SecureMessage::Ascii); 0524 msg.startSign(QCA::SecureMessage::Detached); 0525 msg.update(plain); 0526 msg.end(); 0527 msg.waitForFinished(5000); 0528 0529 #if 0 0530 QString str = QCA::KeyStoreManager::diagnosticText(); 0531 QCA::KeyStoreManager::clearDiagnosticText(); 0532 QStringList lines = str.split('\n', Qt::SkipEmptyParts); 0533 for(int n = 0; n < lines.count(); ++n) 0534 fprintf(stderr, "keystore: %s\n", qPrintable(lines[n])); 0535 0536 QString out = msg.diagnosticText(); 0537 QStringList msglines = out.split('\n', Qt::SkipEmptyParts); 0538 for(int n = 0; n < msglines.count(); ++n) 0539 fprintf(stderr, "message: %s\n", qPrintable(msglines[n])); 0540 #endif 0541 0542 QByteArray detachedSignature; 0543 if (msg.success()) { 0544 detachedSignature = msg.signature(); 0545 } else { 0546 qDebug() << "Failure:" << msg.errorCode(); 0547 QFAIL("Failed to create detached signature"); 0548 } 0549 0550 // qDebug() << "result:" << detachedSignature; 0551 0552 // OK, now lets verify that the resulting signature will verify. 0553 // let's do it 0554 QCA::OpenPGP pgp2; 0555 QCA::SecureMessage msg2(&pgp2); 0556 msg2.setFormat(QCA::SecureMessage::Ascii); 0557 msg2.startVerify(detachedSignature); 0558 msg2.update(plain); 0559 msg2.end(); 0560 msg2.waitForFinished(2000); 0561 0562 QVERIFY(msg2.verifySuccess()); 0563 0564 // If the message is different, it shouldn't verify any more 0565 QCA::SecureMessage msg3(&pgp2); 0566 msg3.setFormat(QCA::SecureMessage::Ascii); 0567 msg3.startVerify(detachedSignature); 0568 msg3.update(plain + '1'); 0569 msg3.end(); 0570 msg3.waitForFinished(2000); 0571 0572 QCOMPARE(msg3.verifySuccess(), false); 0573 0574 QCOMPARE(msg3.errorCode(), QCA::SecureMessage::ErrorUnknown); 0575 } 0576 0577 // Restore things to the way they were.... 0578 if (false == oldGNUPGHOME.isNull()) { 0579 qca_setenv("GNUPGHOME", oldGNUPGHOME.data(), 1); 0580 } 0581 } 0582 0583 void PgpUnitTest::testSignaturesWithExpiredSubkeys() 0584 { 0585 // This previously failing test tests signatures from 0586 // keys with expired subkeys, while assuring no loss 0587 // of functionality. 0588 0589 QCA::Initializer qcaInit; 0590 0591 PGPPassphraseProviderThread thread; 0592 thread.start(); 0593 0594 QByteArray oldGNUPGHOME = qgetenv("GNUPGHOME"); 0595 if (qca_setenv("GNUPGHOME", "./keys4_expired_subkeys_work", 1) != 0) { 0596 QFAIL("Expected to be able to set the GNUPGHOME environment variable, but couldn't"); 0597 } 0598 0599 QCA::KeyStoreManager::start(); 0600 0601 QCA::KeyStoreManager keyManager(this); 0602 keyManager.waitForBusyFinished(); 0603 0604 if (QCA::isSupported(QStringList(QStringLiteral("openpgp")), QStringLiteral("qca-gnupg")) || 0605 QCA::isSupported(QStringList(QStringLiteral("keystorelist")), QStringLiteral("qca-gnupg"))) { 0606 QStringList storeIds = keyManager.keyStores(); 0607 QVERIFY(storeIds.contains(QStringLiteral("qca-gnupg"))); 0608 0609 QCA::KeyStore pgpStore(QStringLiteral("qca-gnupg"), &keyManager); 0610 QVERIFY(pgpStore.isValid()); 0611 0612 QList<QCA::KeyStoreEntry> keylist = pgpStore.entryList(); 0613 0614 QCA::KeyStoreEntry validKey; 0615 foreach (const QCA::KeyStoreEntry key, keylist) { 0616 if (key.id() == QLatin1String("DD773CA7E4E22769")) { 0617 validKey = key; 0618 } 0619 } 0620 0621 QCOMPARE(validKey.isNull(), false); 0622 QCOMPARE(validKey.name(), QStringLiteral("QCA Test Key (Unit test key for expired subkeys) <qca@example.com>")); 0623 QCOMPARE(validKey.type(), QCA::KeyStoreEntry::TypePGPSecretKey); 0624 QCOMPARE(validKey.id(), QStringLiteral("DD773CA7E4E22769")); 0625 QCOMPARE(validKey.pgpSecretKey().isNull(), false); 0626 QCOMPARE(validKey.pgpPublicKey().isNull(), false); 0627 0628 // Create a signature with the non-expired primary key first 0629 QByteArray validMessage("Non-expired key signature"); 0630 0631 QCA::SecureMessageKey secKey; 0632 secKey.setPGPSecretKey(validKey.pgpSecretKey()); 0633 QVERIFY(secKey.havePrivate()); 0634 0635 QCA::OpenPGP pgp; 0636 QCA::SecureMessage msg(&pgp); 0637 msg.setSigner(secKey); 0638 msg.setFormat(QCA::SecureMessage::Ascii); 0639 msg.startSign(QCA::SecureMessage::Clearsign); 0640 msg.update(validMessage); 0641 msg.end(); 0642 msg.waitForFinished(5000); 0643 0644 QVERIFY(msg.success()); 0645 0646 QByteArray nonExpiredKeySignature = msg.read(); 0647 0648 // Verify it 0649 QCA::OpenPGP pgp1; 0650 QCA::SecureMessage msg1(&pgp1); 0651 msg1.setFormat(QCA::SecureMessage::Ascii); 0652 msg1.startVerify(); 0653 msg1.update(nonExpiredKeySignature); 0654 msg1.end(); 0655 msg1.waitForFinished(5000); 0656 0657 QByteArray signedResult = msg1.read(); 0658 0659 QVERIFY(msg1.verifySuccess()); 0660 QCOMPARE(signedResult.trimmed(), validMessage.trimmed()); 0661 0662 // Test signature made by the expired subkey 0663 QByteArray expiredKeySignature( 0664 "-----BEGIN PGP SIGNED MESSAGE-----\n" 0665 "Hash: SHA1\n" 0666 "\n" 0667 "Expired signature\n" 0668 "-----BEGIN PGP SIGNATURE-----\n" 0669 "Version: GnuPG v1\n" 0670 "\n" 0671 "iQEcBAEBAgAGBQJTaI2VAAoJEPddwMGvBiCrA18H/RMJxnEnyNERd19ffTdSLvHH\n" 0672 "iwsfTEPmFQSpmCUnmjK2IIMXWTi6ofinGTWEsXKHSTqgytz715Q16OICwnFoeHin\n" 0673 "0SnsTgi5lH5QcJPJ5PqoRwgAy8vhy73EpYrv7zqLom1Qm/9NeGtNZsohjp0ECFEk\n" 0674 "4UmZiJF7u5Yn6hBl9btIPRTjI2FrXjr3Zy8jZijRaZMutnz5VveBHCLu00NvJQkG\n" 0675 "PC/ZvvfGOk9SeaApnrnntfdKJ4geKxoT0+r+Yz1kQ1VKG5Af3JziBwwNID6g/FYv\n" 0676 "cDK2no5KD7lyASAt6veCDXBdUmNatBO5Au9eE0jJwsSV6LZCpEYEcgBsnfDwxls=\n" 0677 "=UM6K\n" 0678 "-----END PGP SIGNATURE-----\n"); 0679 0680 QCA::OpenPGP pgp2; 0681 QCA::SecureMessage msg2(&pgp2); 0682 msg2.setFormat(QCA::SecureMessage::Ascii); 0683 msg2.startVerify(); 0684 msg2.update(expiredKeySignature); 0685 msg2.end(); 0686 msg2.waitForFinished(5000); 0687 0688 QCOMPARE(msg2.verifySuccess(), false); 0689 QCOMPARE(msg2.errorCode(), QCA::SecureMessage::ErrorSignerExpired); 0690 0691 // Test signature made by the revoked subkey 0692 QByteArray revokedKeySignature( 0693 "-----BEGIN PGP SIGNED MESSAGE-----\n" 0694 "Hash: SHA1\n" 0695 "\n" 0696 "Revoked signature\n" 0697 "-----BEGIN PGP SIGNATURE-----\n" 0698 "Version: GnuPG v1\n" 0699 "\n" 0700 "iQEcBAEBAgAGBQJTd2AmAAoJEJwx7xWvfHTUCZMH/310hMg68H9kYWqKO12Qvyl7\n" 0701 "SlkHBxRsD1sKIBM10qxh6262582mbdAbObVCSFlHVR5NU2tDN5B67J9NU2KnwCcq\n" 0702 "Ny7Oj06UGEkZRmGA23BZ78w/xhPr2Xg80lckBIfGWCvezjSoAeOonk4WREpqzSUr\n" 0703 "sXX8iioBh98ySuQp4rtzf1j0sGB2Tui/bZybiLwz+/fBzW9ITSV0OXmeT5BfBhJU\n" 0704 "XXnOBXDwPUrJQPHxpGpX0s7iyElfZ2ws/PNqUJiWdmxqwLnndn1y5UECDC2T0FpC\n" 0705 "erOM27tQeEthdrZTjMjV47p1ilNgNJrMI328ovehABYyobK9UlnFHcUnn/1OFRw=\n" 0706 "=sE1o\n" 0707 "-----END PGP SIGNATURE-----\n"); 0708 0709 QCA::OpenPGP pgp3; 0710 QCA::SecureMessage msg3(&pgp3); 0711 msg3.setFormat(QCA::SecureMessage::Ascii); 0712 msg3.startVerify(); 0713 msg3.update(revokedKeySignature); 0714 msg3.end(); 0715 msg3.waitForFinished(5000); 0716 0717 QCOMPARE(msg3.verifySuccess(), false); 0718 QCOMPARE(msg3.errorCode(), QCA::SecureMessage::ErrorSignerRevoked); 0719 0720 // Test expired signature 0721 QByteArray expiredSignature( 0722 "-----BEGIN PGP SIGNED MESSAGE-----\n" 0723 "Hash: SHA1\n" 0724 "\n" 0725 "Valid key, expired signature\n" 0726 "-----BEGIN PGP SIGNATURE-----\n" 0727 "Version: GnuPG v1\n" 0728 "\n" 0729 "iQEiBAEBAgAMBQJTkjUvBYMAAVGAAAoJEN13PKfk4idpItIH/1BpFQkPm8fQV0bd\n" 0730 "37qaXf7IWr7bsPBcb7NjR9EmB6Zl6wnmSKW9mvKvs0ZJ1HxyHx0yC5UQWsgTj3do\n" 0731 "xGDP4nJvi0L7EDUukZApWu98nFwrnTrLEd+JMwlpYDhtaljq2qQo7u7CsqyoE2cL\n" 0732 "nRuPkc+lRbDMlqGXk2QFPL8Wu7gW/ndJ8nQ0Dq+22q77Hh1PcyFlggTBxhLA4Svk\n" 0733 "Hx2I4bUjaUq5P4g9kFeXx6/0n31FCa+uThSkWWjH1OeLEXrUZSH/8nBWcnJ3IGbt\n" 0734 "W2bmHhTUx3tYt9aHc+1kje2bAT01xN974r+wS/XNT4cWw7ZSSvukEIjjieUMP4eQ\n" 0735 "S/H6RmM=\n" 0736 "=9pAJ\n" 0737 "-----END PGP SIGNATURE-----\n"); 0738 0739 QCA::OpenPGP pgp4; 0740 QCA::SecureMessage msg4(&pgp4); 0741 msg4.setFormat(QCA::SecureMessage::Ascii); 0742 msg4.startVerify(); 0743 msg4.update(expiredSignature); 0744 msg4.end(); 0745 msg4.waitForFinished(5000); 0746 0747 QCOMPARE(msg4.verifySuccess(), false); 0748 QCOMPARE(msg4.errorCode(), QCA::SecureMessage::ErrorSignatureExpired); 0749 } 0750 0751 if (!oldGNUPGHOME.isNull()) { 0752 qca_setenv("GNUPGHOME", oldGNUPGHOME.data(), 1); 0753 } 0754 } 0755 0756 void PgpUnitTest::testEncryptionWithExpiredSubkeys() 0757 { 0758 // This previously failing test tests encrypting to 0759 // keys with expired subkeys, while assuring no loss 0760 // of functionality. 0761 0762 QCA::Initializer qcaInit; 0763 0764 PGPPassphraseProviderThread thread; 0765 thread.start(); 0766 0767 QByteArray oldGNUPGHOME = qgetenv("GNUPGHOME"); 0768 if (qca_setenv("GNUPGHOME", "./keys4_expired_subkeys_work", 1) != 0) { 0769 QFAIL("Expected to be able to set the GNUPGHOME environment variable, but couldn't"); 0770 } 0771 0772 QCA::KeyStoreManager::start(); 0773 0774 QCA::KeyStoreManager keyManager(this); 0775 keyManager.waitForBusyFinished(); 0776 0777 if (QCA::isSupported(QStringList(QStringLiteral("openpgp")), QStringLiteral("qca-gnupg")) || 0778 QCA::isSupported(QStringList(QStringLiteral("keystorelist")), QStringLiteral("qca-gnupg"))) { 0779 QStringList storeIds = keyManager.keyStores(); 0780 QVERIFY(storeIds.contains(QStringLiteral("qca-gnupg"))); 0781 0782 QCA::KeyStore pgpStore(QStringLiteral("qca-gnupg"), &keyManager); 0783 QVERIFY(pgpStore.isValid()); 0784 0785 QList<QCA::KeyStoreEntry> keylist = pgpStore.entryList(); 0786 0787 QCA::KeyStoreEntry validKey; 0788 QCA::KeyStoreEntry expiredKey; 0789 QCA::KeyStoreEntry revokedKey; 0790 foreach (const QCA::KeyStoreEntry key, keylist) { 0791 if (key.id() == QLatin1String("FEF97E4C4C870810")) { 0792 validKey = key; 0793 } else if (key.id() == QLatin1String("DD773CA7E4E22769")) { 0794 expiredKey = key; 0795 } else if (key.id() == QLatin1String("1D6A028CC4F444A9")) { 0796 revokedKey = key; 0797 } 0798 } 0799 0800 QCOMPARE(validKey.isNull(), false); 0801 QCOMPARE(validKey.name(), 0802 QStringLiteral( 0803 "QCA Test Key 2 (Non-expired encryption key with expired encryption subkey) <qca@example.com>")); 0804 QCOMPARE(validKey.type(), QCA::KeyStoreEntry::TypePGPSecretKey); 0805 QCOMPARE(validKey.id(), QStringLiteral("FEF97E4C4C870810")); 0806 QCOMPARE(validKey.pgpSecretKey().isNull(), false); 0807 QCOMPARE(validKey.pgpPublicKey().isNull(), false); 0808 0809 QCOMPARE(expiredKey.isNull(), false); 0810 QCOMPARE(expiredKey.name(), 0811 QStringLiteral("QCA Test Key (Unit test key for expired subkeys) <qca@example.com>")); 0812 QCOMPARE(expiredKey.type(), QCA::KeyStoreEntry::TypePGPSecretKey); 0813 QCOMPARE(expiredKey.id(), QStringLiteral("DD773CA7E4E22769")); 0814 QCOMPARE(expiredKey.pgpSecretKey().isNull(), false); 0815 QCOMPARE(expiredKey.pgpPublicKey().isNull(), false); 0816 0817 QCOMPARE(revokedKey.isNull(), false); 0818 QCOMPARE(revokedKey.name(), QStringLiteral("QCA Test Key (Revoked unit test key) <qca@example.com>")); 0819 QCOMPARE(revokedKey.type(), QCA::KeyStoreEntry::TypePGPSecretKey); 0820 QCOMPARE(revokedKey.id(), QStringLiteral("1D6A028CC4F444A9")); 0821 QCOMPARE(revokedKey.pgpSecretKey().isNull(), false); 0822 QCOMPARE(revokedKey.pgpPublicKey().isNull(), false); 0823 0824 // Test encrypting to a non-expired key first 0825 QByteArray nonExpiredMessage("Encrypting to non-expired subkey"); 0826 0827 QCA::SecureMessageKey key; 0828 key.setPGPPublicKey(validKey.pgpPublicKey()); 0829 0830 QCA::OpenPGP pgp; 0831 QCA::SecureMessage msg(&pgp); 0832 msg.setFormat(QCA::SecureMessage::Ascii); 0833 msg.setRecipient(key); 0834 msg.startEncrypt(); 0835 msg.update(nonExpiredMessage); 0836 msg.end(); 0837 msg.waitForFinished(5000); 0838 0839 QVERIFY(msg.success()); 0840 QByteArray encResult = msg.read(); 0841 0842 // Decrypt and compare it 0843 QCA::OpenPGP pgp1; 0844 QCA::SecureMessage msg1(&pgp1); 0845 msg1.startDecrypt(); 0846 msg1.update(encResult); 0847 msg1.end(); 0848 msg1.waitForFinished(5000); 0849 0850 QVERIFY(msg1.success()); 0851 QByteArray decResult = msg1.read(); 0852 QCOMPARE(decResult, nonExpiredMessage); 0853 0854 // Test encrypting to the expired key 0855 QByteArray expiredMessage("Encrypting to the expired key"); 0856 0857 QCA::SecureMessageKey key2; 0858 key2.setPGPPublicKey(expiredKey.pgpPublicKey()); 0859 0860 QCA::OpenPGP pgp2; 0861 QCA::SecureMessage msg2(&pgp2); 0862 msg2.setFormat(QCA::SecureMessage::Ascii); 0863 msg2.setRecipient(key2); 0864 msg2.startEncrypt(); 0865 msg2.update(expiredMessage); 0866 msg2.end(); 0867 msg2.waitForFinished(5000); 0868 0869 QCOMPARE(msg2.success(), false); 0870 // Note: If gpg worked as expected, msg.errorCode() should 0871 // equal QCA::SecureMessage::ErrorEncryptExpired, but currently 0872 // it omits the reason for failure, so we check for both values. 0873 QVERIFY((msg2.errorCode() == QCA::SecureMessage::ErrorEncryptExpired) || 0874 (msg2.errorCode() == QCA::SecureMessage::ErrorEncryptInvalid)); 0875 0876 // Test encrypting to the revoked key 0877 QByteArray revokedMessage("Encrypting to the revoked key"); 0878 0879 QCA::SecureMessageKey key3; 0880 key3.setPGPPublicKey(expiredKey.pgpPublicKey()); 0881 0882 QCA::OpenPGP pgp3; 0883 QCA::SecureMessage msg3(&pgp3); 0884 msg3.setFormat(QCA::SecureMessage::Ascii); 0885 msg3.setRecipient(key3); 0886 msg3.startEncrypt(); 0887 msg3.update(revokedMessage); 0888 msg3.end(); 0889 msg3.waitForFinished(5000); 0890 0891 QCOMPARE(msg3.success(), false); 0892 // Note: If gpg worked as expected, msg.errorCode() should 0893 // equal QCA::SecureMessage::ErrorEncryptRevoked, but currently 0894 // it omits the reason for failure, so we check for both values. 0895 QVERIFY((msg3.errorCode() == QCA::SecureMessage::ErrorEncryptRevoked) || 0896 (msg3.errorCode() == QCA::SecureMessage::ErrorEncryptInvalid)); 0897 } 0898 0899 if (!oldGNUPGHOME.isNull()) { 0900 qca_setenv("GNUPGHOME", oldGNUPGHOME.data(), 1); 0901 } 0902 } 0903 0904 QTEST_MAIN(PgpUnitTest) 0905 0906 #include "pgpunittest.moc"