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"