File indexing completed on 2024-05-05 04:45:26

0001 /*
0002  * Copyright (C) 2005-2007  Justin Karneges <justin@affinix.com>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Lesser General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Lesser General Public
0015  * License along with this library; if not, write to the Free Software
0016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
0017  *
0018  */
0019 
0020 #include <QtCrypto>
0021 
0022 #include <QCoreApplication>
0023 #include <QDebug>
0024 #include <QDir>
0025 #include <QFile>
0026 #include <QFileInfo>
0027 #include <QTextStream>
0028 #include <QTimer>
0029 
0030 #ifdef QT_STATICPLUGIN
0031 #include "import_plugins.h"
0032 #endif
0033 
0034 const char *const APPNAME = "qcatool";
0035 const char *const EXENAME = "qcatool";
0036 const char *const VERSION = QCA_VERSION_STR;
0037 
0038 static QStringList wrapstring(const QString &str, int width)
0039 {
0040     QStringList out;
0041     QString     simp = str.simplified();
0042     QString     rest = simp;
0043     while (true) {
0044         int lastSpace = -1;
0045         int n;
0046         for (n = 0; n < rest.length(); ++n) {
0047             if (rest[n].isSpace())
0048                 lastSpace = n;
0049             if (n == width)
0050                 break;
0051         }
0052         if (n == rest.length()) {
0053             out += rest;
0054             break;
0055         }
0056 
0057         QString line;
0058         if (lastSpace != -1) {
0059             line = rest.mid(0, lastSpace);
0060             rest = rest.mid(lastSpace + 1);
0061         } else {
0062             line = rest.mid(0, n);
0063             rest = rest.mid(n);
0064         }
0065         out += line;
0066     }
0067     return out;
0068 }
0069 
0070 class StreamLogger : public QCA::AbstractLogDevice
0071 {
0072     Q_OBJECT
0073 public:
0074     StreamLogger(QTextStream &stream)
0075         : QCA::AbstractLogDevice(QStringLiteral("Stream logger"))
0076         , _stream(stream)
0077     {
0078         QCA::logger()->registerLogDevice(this);
0079     }
0080 
0081     ~StreamLogger() override
0082     {
0083         QCA::logger()->unregisterLogDevice(name());
0084     }
0085 
0086     void logTextMessage(const QString &message, enum QCA::Logger::Severity severity) override
0087     {
0088         _stream << now() << " " << severityName(severity) << " " << message << Qt::endl;
0089     }
0090 
0091     void logBinaryMessage(const QByteArray &blob, enum QCA::Logger::Severity severity) override
0092     {
0093         Q_UNUSED(blob);
0094         _stream << now() << " " << severityName(severity) << " "
0095                 << "Binary blob not implemented yet" << Qt::endl;
0096     }
0097 
0098 private:
0099     inline const char *severityName(enum QCA::Logger::Severity severity)
0100     {
0101         if (severity <= QCA::Logger::Debug) {
0102             return s_severityNames[severity];
0103         } else {
0104             return s_severityNames[QCA::Logger::Debug + 1];
0105         }
0106     }
0107 
0108     inline QString now()
0109     {
0110         static QString format = QStringLiteral("yyyy-MM-dd hh:mm:ss");
0111         return QDateTime::currentDateTime().toString(format);
0112     }
0113 
0114 private:
0115     static const char *s_severityNames[];
0116     QTextStream       &_stream;
0117 };
0118 
0119 const char *StreamLogger::s_severityNames[] = {"Q", "M", "A", "C", "E", "W", "N", "I", "D", "U"};
0120 
0121 static void output_plugin_diagnostic_text()
0122 {
0123     QString str = QCA::pluginDiagnosticText();
0124     QCA::clearPluginDiagnosticText();
0125     if (str[str.length() - 1] == QLatin1Char('\n'))
0126         str.truncate(str.length() - 1);
0127     const QStringList lines = str.split(QLatin1Char('\n'), Qt::KeepEmptyParts);
0128     for (int n = 0; n < lines.count(); ++n)
0129         fprintf(stderr, "plugin: %s\n", qPrintable(lines[n]));
0130 }
0131 
0132 static void output_keystore_diagnostic_text()
0133 {
0134     QString str = QCA::KeyStoreManager::diagnosticText();
0135     QCA::KeyStoreManager::clearDiagnosticText();
0136     if (str[str.length() - 1] == QLatin1Char('\n'))
0137         str.truncate(str.length() - 1);
0138     const QStringList lines = str.split(QLatin1Char('\n'), Qt::KeepEmptyParts);
0139     for (int n = 0; n < lines.count(); ++n)
0140         fprintf(stderr, "keystore: %s\n", qPrintable(lines[n]));
0141 }
0142 
0143 static void output_message_diagnostic_text(QCA::SecureMessage *msg)
0144 {
0145     QString str = msg->diagnosticText();
0146     if (str[str.length() - 1] == QLatin1Char('\n'))
0147         str.truncate(str.length() - 1);
0148     const QStringList lines = str.split(QLatin1Char('\n'), Qt::KeepEmptyParts);
0149     for (int n = 0; n < lines.count(); ++n)
0150         fprintf(stderr, "message: %s\n", qPrintable(lines[n]));
0151 }
0152 
0153 class AnimatedKeyGen : public QObject
0154 {
0155     Q_OBJECT
0156 public:
0157     static QCA::PrivateKey makeKey(QCA::PKey::Type type, int bits, QCA::DLGroupSet set)
0158     {
0159         AnimatedKeyGen kg;
0160         kg.type = type;
0161         kg.bits = bits;
0162         kg.set  = set;
0163         QEventLoop eventLoop;
0164         kg.eventLoop = &eventLoop;
0165         QTimer::singleShot(0, &kg, &AnimatedKeyGen::start);
0166         eventLoop.exec();
0167         QCA::PrivateKey key = kg.key;
0168         return key;
0169     }
0170 
0171 private:
0172     QCA::PKey::Type   type;
0173     int               bits;
0174     QCA::DLGroupSet   set;
0175     QEventLoop       *eventLoop;
0176     QCA::KeyGenerator gen;
0177     QCA::DLGroup      group;
0178     QCA::PrivateKey   key;
0179     QTimer            t;
0180     int               x;
0181 
0182     AnimatedKeyGen()
0183     {
0184         gen.setBlockingEnabled(false);
0185         connect(&gen, &QCA::KeyGenerator::finished, this, &AnimatedKeyGen::gen_finished);
0186         connect(&t, &QTimer::timeout, this, &AnimatedKeyGen::t_timeout);
0187     }
0188 
0189 private Q_SLOTS:
0190     void start()
0191     {
0192         printf("Generating Key ...  ");
0193         fflush(stdout);
0194         x = 0;
0195         t.start(125);
0196 
0197         if (type == QCA::PKey::RSA)
0198             gen.createRSA(bits);
0199         else
0200             gen.createDLGroup(set);
0201     }
0202 
0203     void gen_finished()
0204     {
0205         if (type == QCA::PKey::DSA || type == QCA::PKey::DH) {
0206             if (group.isNull()) {
0207                 group = gen.dlGroup();
0208 
0209                 if (type == QCA::PKey::DSA)
0210                     gen.createDSA(group);
0211                 else
0212                     gen.createDH(group);
0213                 return;
0214             }
0215         }
0216 
0217         key = gen.key();
0218 
0219         printf("\b");
0220         if (!key.isNull())
0221             printf("Done\n");
0222         else
0223             printf("Error\n");
0224 
0225         eventLoop->exit();
0226     }
0227 
0228     void t_timeout()
0229     {
0230         if (x == 0)
0231             printf("\b/");
0232         else if (x == 1)
0233             printf("\b-");
0234         else if (x == 2)
0235             printf("\b\\");
0236         else if (x == 3)
0237             printf("\b|");
0238         fflush(stdout);
0239 
0240         ++x;
0241         x %= 4;
0242     }
0243 };
0244 
0245 class KeyStoreMonitor : public QObject
0246 {
0247     Q_OBJECT
0248 public:
0249     static void monitor()
0250     {
0251         KeyStoreMonitor monitor;
0252         QEventLoop      eventLoop;
0253         monitor.eventLoop = &eventLoop;
0254         QTimer::singleShot(0, &monitor, &KeyStoreMonitor::start);
0255         eventLoop.exec();
0256     }
0257 
0258 private:
0259     QEventLoop            *eventLoop;
0260     QCA::KeyStoreManager  *ksm;
0261     QList<QCA::KeyStore *> keyStores;
0262     QCA::ConsolePrompt    *prompt;
0263 
0264 private Q_SLOTS:
0265     void start()
0266     {
0267         // user can quit the monitoring by pressing enter
0268         printf("Monitoring keystores, press 'q' to quit.\n");
0269         prompt = new QCA::ConsolePrompt(this);
0270         connect(prompt, &QCA::ConsolePrompt::finished, this, &KeyStoreMonitor::prompt_finished);
0271         prompt->getChar();
0272 
0273         // kick off the subsystem
0274         QCA::KeyStoreManager::start();
0275 
0276         // setup keystore manager for monitoring
0277         ksm = new QCA::KeyStoreManager(this);
0278         connect(ksm, &QCA::KeyStoreManager::keyStoreAvailable, this, &KeyStoreMonitor::ks_available);
0279         foreach (const QString &keyStoreId, ksm->keyStores())
0280             ks_available(keyStoreId);
0281     }
0282 
0283     void ks_available(const QString &keyStoreId)
0284     {
0285         QCA::KeyStore *ks = new QCA::KeyStore(keyStoreId, ksm);
0286         connect(ks, &QCA::KeyStore::updated, this, &KeyStoreMonitor::ks_updated);
0287         connect(ks, &QCA::KeyStore::unavailable, this, &KeyStoreMonitor::ks_unavailable);
0288         keyStores += ks;
0289 
0290         printf("  available:   %s\n", qPrintable(ks->name()));
0291     }
0292 
0293     void ks_updated()
0294     {
0295         QCA::KeyStore *ks = (QCA::KeyStore *)sender();
0296 
0297         printf("  updated:     %s\n", qPrintable(ks->name()));
0298     }
0299 
0300     void ks_unavailable()
0301     {
0302         QCA::KeyStore *ks = (QCA::KeyStore *)sender();
0303 
0304         printf("  unavailable: %s\n", qPrintable(ks->name()));
0305         keyStores.removeAll(ks);
0306         delete ks;
0307     }
0308 
0309     void prompt_finished()
0310     {
0311         QChar c = prompt->resultChar();
0312         if (c == QLatin1Char('q') || c == QLatin1Char('Q')) {
0313             eventLoop->exit();
0314             return;
0315         }
0316         prompt->getChar();
0317     }
0318 };
0319 
0320 class PassphrasePrompt : public QObject
0321 {
0322     Q_OBJECT
0323 public:
0324     class Item
0325     {
0326     public:
0327         QString    promptStr;
0328         int        id;
0329         QCA::Event event;
0330     };
0331 
0332     QCA::EventHandler   handler;
0333     bool                allowPrompt;
0334     bool                warned;
0335     bool                have_pass;
0336     bool                used_pass;
0337     QCA::SecureArray    pass;
0338     QCA::ConsolePrompt *prompt;
0339     int                 prompt_id;
0340     QCA::Event          prompt_event;
0341     QList<Item>         pending;
0342     bool                auto_accept;
0343 
0344     QCA::KeyStoreManager   ksm;
0345     QList<QCA::KeyStore *> keyStores;
0346 
0347     PassphrasePrompt()
0348         : handler(this)
0349         , ksm(this)
0350     {
0351         allowPrompt = true;
0352         warned      = false;
0353         have_pass   = false;
0354         auto_accept = false;
0355 
0356         prompt = nullptr;
0357 
0358         connect(&handler, &QCA::EventHandler::eventReady, this, &PassphrasePrompt::ph_eventReady);
0359         handler.start();
0360 
0361         connect(&ksm, &QCA::KeyStoreManager::keyStoreAvailable, this, &PassphrasePrompt::ks_available);
0362         foreach (const QString &keyStoreId, ksm.keyStores())
0363             ks_available(keyStoreId);
0364     }
0365 
0366     ~PassphrasePrompt() override
0367     {
0368         qDeleteAll(keyStores);
0369 
0370         if (prompt) {
0371             handler.reject(prompt_id);
0372             delete prompt;
0373         }
0374 
0375         while (!pending.isEmpty())
0376             handler.reject(pending.takeFirst().id);
0377     }
0378 
0379     void setExplicitPassword(const QCA::SecureArray &_pass)
0380     {
0381         have_pass = true;
0382         used_pass = false;
0383         pass      = _pass;
0384     }
0385 
0386 private Q_SLOTS:
0387     void ph_eventReady(int id, const QCA::Event &e)
0388     {
0389         if (have_pass) {
0390             // only allow using an explicit passphrase once
0391             if (used_pass) {
0392                 handler.reject(id);
0393                 return;
0394             }
0395             used_pass = true;
0396             handler.submitPassword(id, pass);
0397             return;
0398         }
0399 
0400         if (!allowPrompt) {
0401             if (!have_pass && !warned) {
0402                 warned = true;
0403                 fprintf(stderr, "Error: no passphrase specified (use '--pass=' for none).\n");
0404             }
0405 
0406             handler.reject(id);
0407             return;
0408         }
0409 
0410         if (e.type() == QCA::Event::Password) {
0411             QString type = QStringLiteral("password");
0412             if (e.passwordStyle() == QCA::Event::StylePassphrase)
0413                 type = QStringLiteral("passphrase");
0414             else if (e.passwordStyle() == QCA::Event::StylePIN)
0415                 type = QStringLiteral("PIN");
0416 
0417             QString str;
0418             if (e.source() == QCA::Event::KeyStore) {
0419                 QString            name;
0420                 QCA::KeyStoreEntry entry = e.keyStoreEntry();
0421                 if (!entry.isNull()) {
0422                     name = entry.name();
0423                 } else {
0424                     if (e.keyStoreInfo().type() == QCA::KeyStore::SmartCard)
0425                         name = QStringLiteral("the '") + e.keyStoreInfo().name() + QStringLiteral("' token");
0426                     else
0427                         name = e.keyStoreInfo().name();
0428                 }
0429                 str = QStringLiteral("Enter %1 for %2").arg(type, name);
0430             } else if (!e.fileName().isEmpty())
0431                 str = QStringLiteral("Enter %1 for %2").arg(type, e.fileName());
0432             else
0433                 str = QStringLiteral("Enter %1").arg(type);
0434 
0435             if (!prompt) {
0436                 prompt = new QCA::ConsolePrompt(this);
0437                 connect(prompt, &QCA::ConsolePrompt::finished, this, &PassphrasePrompt::prompt_finished);
0438                 prompt_id    = id;
0439                 prompt_event = e;
0440                 prompt->getHidden(str);
0441             } else {
0442                 Item i;
0443                 i.promptStr = str;
0444                 i.id        = id;
0445                 i.event     = e;
0446                 pending += i;
0447             }
0448         } else if (e.type() == QCA::Event::Token) {
0449             // even though we're being prompted for a missing token,
0450             //   we should still check if the token is present, due to
0451             //   a possible race between insert and token request.
0452             bool found = false;
0453 
0454             // token-only
0455             if (e.keyStoreEntry().isNull()) {
0456                 foreach (QCA::KeyStore *ks, keyStores) {
0457                     if (ks->id() == e.keyStoreInfo().id()) {
0458                         found = true;
0459                         break;
0460                     }
0461                 }
0462             }
0463             // token-entry
0464             else {
0465                 QCA::KeyStoreEntry kse = e.keyStoreEntry();
0466 
0467                 QCA::KeyStore *ks = nullptr;
0468                 foreach (QCA::KeyStore *i, keyStores) {
0469                     if (i->id() == e.keyStoreInfo().id()) {
0470                         ks = i;
0471                         break;
0472                     }
0473                 }
0474                 if (ks) {
0475                     QList<QCA::KeyStoreEntry> list = ks->entryList();
0476                     foreach (const QCA::KeyStoreEntry &e, list) {
0477                         if (e.id() == kse.id() && kse.isAvailable()) {
0478                             found = true;
0479                             break;
0480                         }
0481                     }
0482                 }
0483             }
0484             if (found) {
0485                 // auto-accept
0486                 handler.tokenOkay(id);
0487                 return;
0488             }
0489 
0490             QCA::KeyStoreEntry entry = e.keyStoreEntry();
0491             QString            name;
0492             if (!entry.isNull()) {
0493                 name = QStringLiteral("Please make ") + entry.name() + QStringLiteral(" (of ") + entry.storeName() +
0494                     QStringLiteral(") available");
0495             } else {
0496                 name = QStringLiteral("Please insert the '") + e.keyStoreInfo().name() + QStringLiteral("' token");
0497             }
0498 
0499             QString str = QStringLiteral("%1 and press Enter (or 'q' to cancel) ...").arg(name);
0500 
0501             if (!prompt) {
0502                 fprintf(stderr, "%s\n", qPrintable(str));
0503                 prompt = new QCA::ConsolePrompt(this);
0504                 connect(prompt, &QCA::ConsolePrompt::finished, this, &PassphrasePrompt::prompt_finished);
0505                 prompt_id    = id;
0506                 prompt_event = e;
0507                 prompt->getChar();
0508             } else {
0509                 Item i;
0510                 i.promptStr = str;
0511                 i.id        = id;
0512                 i.event     = e;
0513                 pending += i;
0514             }
0515         } else
0516             handler.reject(id);
0517     }
0518 
0519     void prompt_finished()
0520     {
0521         if (prompt_event.type() == QCA::Event::Password) {
0522             handler.submitPassword(prompt_id, prompt->result());
0523         } else {
0524             if (auto_accept) {
0525                 auto_accept = false;
0526                 handler.tokenOkay(prompt_id);
0527             } else {
0528                 QChar c = prompt->resultChar();
0529                 if (c == QLatin1Char('\r') || c == QLatin1Char('\n'))
0530                     handler.tokenOkay(prompt_id);
0531                 else if (c == QLatin1Char('q') || c == QLatin1Char('Q'))
0532                     handler.reject(prompt_id);
0533                 else {
0534                     // retry
0535                     prompt->getChar();
0536                     return;
0537                 }
0538             }
0539         }
0540 
0541         if (!pending.isEmpty()) {
0542             Item i       = pending.takeFirst();
0543             prompt_id    = i.id;
0544             prompt_event = i.event;
0545             if (i.event.type() == QCA::Event::Password) {
0546                 prompt->getHidden(i.promptStr);
0547             } else // Token
0548             {
0549                 fprintf(stderr, "%s\n", qPrintable(i.promptStr));
0550                 prompt->getChar();
0551             }
0552         } else {
0553             delete prompt;
0554             prompt = nullptr;
0555         }
0556     }
0557 
0558     void ks_available(const QString &keyStoreId)
0559     {
0560         QCA::KeyStore *ks = new QCA::KeyStore(keyStoreId, &ksm);
0561         connect(ks, &QCA::KeyStore::updated, this, &PassphrasePrompt::ks_updated);
0562         connect(ks, &QCA::KeyStore::unavailable, this, &PassphrasePrompt::ks_unavailable);
0563         keyStores += ks;
0564         ks->startAsynchronousMode();
0565 
0566         // are we currently in a token-only prompt?
0567         if (prompt && prompt_event.type() == QCA::Event::Token && prompt_event.keyStoreEntry().isNull()) {
0568             // was the token we're looking for just inserted?
0569             if (prompt_event.keyStoreInfo().id() == keyStoreId) {
0570                 fprintf(stderr, "Token inserted!  Continuing...\n");
0571 
0572                 // auto-accept
0573                 auto_accept = true;
0574                 prompt_finished();
0575             }
0576         }
0577     }
0578 
0579     void ks_unavailable()
0580     {
0581         QCA::KeyStore *ks = (QCA::KeyStore *)sender();
0582         keyStores.removeAll(ks);
0583         delete ks;
0584     }
0585 
0586     void ks_updated()
0587     {
0588         QCA::KeyStore *ks = (QCA::KeyStore *)sender();
0589 
0590         // are we currently in a token-entry prompt?
0591         if (prompt && prompt_event.type() == QCA::Event::Token && !prompt_event.keyStoreEntry().isNull()) {
0592             QCA::KeyStoreEntry kse = prompt_event.keyStoreEntry();
0593 
0594             // was the token of the entry we're looking for updated?
0595             if (prompt_event.keyStoreInfo().id() == ks->id()) {
0596                 // is the entry available?
0597                 bool                      avail = false;
0598                 QList<QCA::KeyStoreEntry> list  = ks->entryList();
0599                 foreach (const QCA::KeyStoreEntry &e, list) {
0600                     if (e.id() == kse.id()) {
0601                         avail = kse.isAvailable();
0602                         break;
0603                     }
0604                 }
0605                 if (avail) {
0606                     fprintf(stderr, "Entry available!  Continuing...\n");
0607 
0608                     // auto-accept
0609                     auto_accept = true;
0610                     prompt_finished();
0611                 }
0612             }
0613         }
0614     }
0615 };
0616 
0617 class PassphrasePromptThread : public QCA::SyncThread
0618 {
0619     Q_OBJECT
0620 public:
0621     PassphrasePrompt *pp;
0622 
0623     PassphrasePromptThread()
0624     {
0625         start();
0626     }
0627 
0628     ~PassphrasePromptThread() override
0629     {
0630         stop();
0631     }
0632 
0633 protected:
0634     void atStart() override
0635     {
0636         pp = new PassphrasePrompt;
0637     }
0638 
0639     void atEnd() override
0640     {
0641         delete pp;
0642     }
0643 };
0644 
0645 static bool promptForNewPassphrase(QCA::SecureArray *result)
0646 {
0647     QCA::ConsolePrompt prompt;
0648     prompt.getHidden(QStringLiteral("Enter new passphrase"));
0649     prompt.waitForFinished();
0650     QCA::SecureArray out = prompt.result();
0651 
0652     prompt.getHidden(QStringLiteral("Confirm new passphrase"));
0653     prompt.waitForFinished();
0654 
0655     if (prompt.result() != out) {
0656         fprintf(stderr, "Error: confirmation does not match original entry.\n");
0657         return false;
0658     }
0659     *result = out;
0660     return true;
0661 }
0662 
0663 static void ksm_start_and_wait()
0664 {
0665     // activate the KeyStoreManager and block until ready
0666     QCA::KeyStoreManager::start();
0667     {
0668         QCA::KeyStoreManager ksm;
0669         ksm.waitForBusyFinished();
0670     }
0671 }
0672 
0673 static QString line_encode(const QString &in)
0674 {
0675     QString out;
0676     for (const QChar &c : in) {
0677         if (c == QLatin1Char('\\'))
0678             out += QStringLiteral("\\\\");
0679         else if (c == QLatin1Char('\n'))
0680             out += QStringLiteral("\\n");
0681         else
0682             out += c;
0683     }
0684     return out;
0685 }
0686 
0687 static QString line_decode(const QString &in)
0688 {
0689     QString out;
0690     for (int n = 0; n < in.length(); ++n) {
0691         if (in[n] == QLatin1Char('\\')) {
0692             if (n + 1 < in.length()) {
0693                 if (in[n + 1] == QLatin1Char('\\'))
0694                     out += QLatin1Char('\\');
0695                 else if (in[n + 1] == QLatin1Char('n'))
0696                     out += QLatin1Char('\n');
0697                 ++n;
0698             }
0699         } else
0700             out += in[n];
0701     }
0702     return out;
0703 }
0704 
0705 static QString make_ksentry_string(const QString &id)
0706 {
0707     QString out;
0708     out += QStringLiteral("QCATOOL_KEYSTOREENTRY_1\n");
0709     out += line_encode(id) + QLatin1Char('\n');
0710     return out;
0711 }
0712 
0713 /*static bool write_ksentry_file(const QString &id, const QString &fileName)
0714 {
0715     QFile f(fileName);
0716     if(!f.open(QFile::WriteOnly | QFile::Truncate))
0717         return false;
0718     f.write(make_ksentry_string(id).toUtf8());
0719     return true;
0720 }*/
0721 
0722 static QString read_ksentry_file(const QString &fileName)
0723 {
0724     QString out;
0725 
0726     QFile f(fileName);
0727     if (!f.open(QFile::ReadOnly))
0728         return out;
0729     QTextStream ts(&f);
0730     int         linenum = 0;
0731     while (!ts.atEnd()) {
0732         QString line = ts.readLine();
0733         if (linenum == 0) {
0734             if (line != QLatin1String("QCATOOL_KEYSTOREENTRY_1"))
0735                 return out;
0736         } else {
0737             out = line_decode(line);
0738             break;
0739         }
0740         ++linenum;
0741     }
0742     return out;
0743 }
0744 
0745 static bool is_pem_file(const QString &fileName)
0746 {
0747     QFile f(fileName);
0748     if (!f.open(QFile::ReadOnly))
0749         return false;
0750     QTextStream ts(&f);
0751     if (!ts.atEnd()) {
0752         QString line = ts.readLine();
0753         if (line.startsWith(QLatin1String("-----BEGIN")))
0754             return true;
0755     }
0756     return false;
0757 }
0758 
0759 static QByteArray read_der_file(const QString &fileName)
0760 {
0761     QFile f(fileName);
0762     if (!f.open(QFile::ReadOnly))
0763         return QByteArray();
0764     return f.readAll();
0765 }
0766 
0767 class InfoType
0768 {
0769 public:
0770     QCA::CertificateInfoType type;
0771     QString                  varname;
0772     QString                  shortname;
0773     QString                  name;
0774     QString                  desc;
0775 
0776     InfoType()
0777     {
0778     }
0779 
0780     InfoType(const QCA::CertificateInfoType &_type,
0781              const QString                  &_varname,
0782              const QString                  &_shortname,
0783              const QString                  &_name,
0784              const QString                  &_desc)
0785         : type(_type)
0786         , varname(_varname)
0787         , shortname(_shortname)
0788         , name(_name)
0789         , desc(_desc)
0790     {
0791     }
0792 };
0793 
0794 static QList<InfoType> makeInfoTypeList(bool legacyEmail = false)
0795 {
0796     QList<InfoType> out;
0797     out += InfoType(QCA::CommonName,
0798                     QStringLiteral("CommonName"),
0799                     QStringLiteral("CN"),
0800                     QStringLiteral("Common Name (CN)"),
0801                     QStringLiteral("Full name, domain, anything"));
0802     out += InfoType(
0803         QCA::Email, QStringLiteral("Email"), QLatin1String(""), QStringLiteral("Email Address"), QLatin1String(""));
0804     if (legacyEmail)
0805         out += InfoType(QCA::EmailLegacy,
0806                         QStringLiteral("EmailLegacy"),
0807                         QLatin1String(""),
0808                         QStringLiteral("PKCS#9 Email Address"),
0809                         QLatin1String(""));
0810     out += InfoType(QCA::Organization,
0811                     QStringLiteral("Organization"),
0812                     QStringLiteral("O"),
0813                     QStringLiteral("Organization (O)"),
0814                     QStringLiteral("Company, group, etc"));
0815     out += InfoType(QCA::OrganizationalUnit,
0816                     QStringLiteral("OrganizationalUnit"),
0817                     QStringLiteral("OU"),
0818                     QStringLiteral("Organizational Unit (OU)"),
0819                     QStringLiteral("Division/branch of organization"));
0820     out += InfoType(QCA::Locality,
0821                     QStringLiteral("Locality"),
0822                     QLatin1String(""),
0823                     QStringLiteral("Locality (L)"),
0824                     QStringLiteral("City, shire, part of a state"));
0825     out += InfoType(QCA::State,
0826                     QStringLiteral("State"),
0827                     QLatin1String(""),
0828                     QStringLiteral("State (ST)"),
0829                     QStringLiteral("State within the country"));
0830     out += InfoType(QCA::Country,
0831                     QStringLiteral("Country"),
0832                     QStringLiteral("C"),
0833                     QStringLiteral("Country Code (C)"),
0834                     QStringLiteral("2-letter code"));
0835     out += InfoType(QCA::IncorporationLocality,
0836                     QStringLiteral("IncorporationLocality"),
0837                     QLatin1String(""),
0838                     QStringLiteral("Incorporation Locality"),
0839                     QStringLiteral("For EV certificates"));
0840     out += InfoType(QCA::IncorporationState,
0841                     QStringLiteral("IncorporationState"),
0842                     QLatin1String(""),
0843                     QStringLiteral("Incorporation State"),
0844                     QStringLiteral("For EV certificates"));
0845     out += InfoType(QCA::IncorporationCountry,
0846                     QStringLiteral("IncorporationCountry"),
0847                     QLatin1String(""),
0848                     QStringLiteral("Incorporation Country"),
0849                     QStringLiteral("For EV certificates"));
0850     out += InfoType(QCA::URI, QStringLiteral("URI"), QLatin1String(""), QStringLiteral("URI"), QLatin1String(""));
0851     out += InfoType(QCA::DNS,
0852                     QStringLiteral("DNS"),
0853                     QLatin1String(""),
0854                     QStringLiteral("Domain Name"),
0855                     QStringLiteral("Domain (dnsName)"));
0856     out += InfoType(QCA::IPAddress,
0857                     QStringLiteral("IPAddress"),
0858                     QLatin1String(""),
0859                     QStringLiteral("IP Adddress"),
0860                     QLatin1String(""));
0861     out += InfoType(QCA::XMPP,
0862                     QStringLiteral("XMPP"),
0863                     QLatin1String(""),
0864                     QStringLiteral("XMPP Address (JID)"),
0865                     QStringLiteral("From RFC 3920 (id-on-xmppAddr)"));
0866     return out;
0867 }
0868 
0869 class MyConstraintType
0870 {
0871 public:
0872     QCA::ConstraintType type;
0873     QString             varname;
0874     QString             name;
0875     QString             desc;
0876 
0877     MyConstraintType()
0878     {
0879     }
0880 
0881     MyConstraintType(const QCA::ConstraintType &_type,
0882                      const QString             &_varname,
0883                      const QString             &_name,
0884                      const QString             &_desc)
0885         : type(_type)
0886         , varname(_varname)
0887         , name(_name)
0888         , desc(_desc)
0889     {
0890     }
0891 };
0892 
0893 static QList<MyConstraintType> makeConstraintTypeList()
0894 {
0895     QList<MyConstraintType> out;
0896     out += MyConstraintType(QCA::DigitalSignature,
0897                             QStringLiteral("DigitalSignature"),
0898                             QStringLiteral("Digital Signature"),
0899                             QStringLiteral("Can be used for signing"));
0900     out += MyConstraintType(QCA::NonRepudiation,
0901                             QStringLiteral("NonRepudiation"),
0902                             QStringLiteral("Non-Repudiation"),
0903                             QStringLiteral("Usage is legally binding"));
0904     out += MyConstraintType(QCA::KeyEncipherment,
0905                             QStringLiteral("KeyEncipherment"),
0906                             QStringLiteral("Key Encipherment"),
0907                             QStringLiteral("Can encrypt other keys"));
0908     out += MyConstraintType(QCA::DataEncipherment,
0909                             QStringLiteral("DataEncipherment"),
0910                             QStringLiteral("Data Encipherment"),
0911                             QStringLiteral("Can encrypt arbitrary data"));
0912     out += MyConstraintType(QCA::KeyAgreement,
0913                             QStringLiteral("KeyAgreement"),
0914                             QStringLiteral("Key Agreement"),
0915                             QStringLiteral("Can perform key agreement (DH)"));
0916     out += MyConstraintType(QCA::KeyCertificateSign,
0917                             QStringLiteral("KeyCertificateSign"),
0918                             QStringLiteral("Certificate Sign"),
0919                             QStringLiteral("Can sign other certificates"));
0920     out += MyConstraintType(
0921         QCA::CRLSign, QStringLiteral("CRLSign"), QStringLiteral("CRL Sign"), QStringLiteral("Can sign CRLs"));
0922     out += MyConstraintType(QCA::EncipherOnly,
0923                             QStringLiteral("EncipherOnly"),
0924                             QStringLiteral("Encipher Only"),
0925                             QStringLiteral("Can be used for encrypting"));
0926     out += MyConstraintType(QCA::DecipherOnly,
0927                             QStringLiteral("DecipherOnly"),
0928                             QStringLiteral("Decipher Only"),
0929                             QStringLiteral("Can be used for decrypting"));
0930     out += MyConstraintType(QCA::ServerAuth,
0931                             QStringLiteral("ServerAuth"),
0932                             QStringLiteral("Server Authentication"),
0933                             QStringLiteral("TLS Server"));
0934     out += MyConstraintType(QCA::ClientAuth,
0935                             QStringLiteral("ClientAuth"),
0936                             QStringLiteral("Client Authentication"),
0937                             QStringLiteral("TLS Client"));
0938     out += MyConstraintType(
0939         QCA::CodeSigning, QStringLiteral("CodeSigning"), QStringLiteral("Code Signing"), QLatin1String(""));
0940     out += MyConstraintType(QCA::EmailProtection,
0941                             QStringLiteral("EmailProtection"),
0942                             QStringLiteral("Email Protection"),
0943                             QStringLiteral("S/MIME"));
0944     out += MyConstraintType(
0945         QCA::IPSecEndSystem, QStringLiteral("IPSecEndSystem"), QStringLiteral("IPSec End-System"), QLatin1String(""));
0946     out += MyConstraintType(
0947         QCA::IPSecTunnel, QStringLiteral("IPSecTunnel"), QStringLiteral("IPSec Tunnel"), QLatin1String(""));
0948     out +=
0949         MyConstraintType(QCA::IPSecUser, QStringLiteral("IPSecUser"), QStringLiteral("IPSec User"), QLatin1String(""));
0950     out += MyConstraintType(
0951         QCA::TimeStamping, QStringLiteral("TimeStamping"), QStringLiteral("Time Stamping"), QLatin1String(""));
0952     out += MyConstraintType(
0953         QCA::OCSPSigning, QStringLiteral("OCSPSigning"), QStringLiteral("OCSP Signing"), QLatin1String(""));
0954     return out;
0955 }
0956 
0957 const char *crlEntryReasonToString(QCA::CRLEntry::Reason r)
0958 {
0959     switch (r) {
0960     case QCA::CRLEntry::Unspecified:
0961         return "Unspecified";
0962     case QCA::CRLEntry::KeyCompromise:
0963         return "KeyCompromise";
0964     case QCA::CRLEntry::CACompromise:
0965         return "CACompromise";
0966     case QCA::CRLEntry::AffiliationChanged:
0967         return "AffiliationChanged";
0968     case QCA::CRLEntry::Superseded:
0969         return "Superseded";
0970     case QCA::CRLEntry::CessationOfOperation:
0971         return "CessationOfOperation";
0972     case QCA::CRLEntry::CertificateHold:
0973         return "CertificateHold";
0974     case QCA::CRLEntry::RemoveFromCRL:
0975         return "RemoveFromCRL";
0976     case QCA::CRLEntry::PrivilegeWithdrawn:
0977         return "PrivilegeWithdrawn";
0978     case QCA::CRLEntry::AACompromise:
0979         return "AACompromise";
0980     default:
0981         return "Unknown";
0982     }
0983 }
0984 
0985 static bool validOid(const QString &in)
0986 {
0987     for (const QChar &c : in) {
0988         if (!c.isDigit() && c != QLatin1Char('.'))
0989             return false;
0990     }
0991     return true;
0992 }
0993 
0994 class ValidityLength
0995 {
0996 public:
0997     int years, months, days;
0998 };
0999 
1000 static int vl_getnext(const QString &in, int offset = 0)
1001 {
1002     if (offset >= in.length())
1003         return in.length();
1004 
1005     int  n = offset;
1006     bool lookForNonDigit;
1007 
1008     if (in[n].isDigit())
1009         lookForNonDigit = true;
1010     else
1011         lookForNonDigit = false;
1012 
1013     for (++n; n < in.length(); ++n) {
1014         if (in[n].isDigit() != lookForNonDigit)
1015             break;
1016     }
1017     return n;
1018 }
1019 
1020 static QStringList vl_getparts(const QString &in)
1021 {
1022     QStringList out;
1023     int         offset = 0;
1024     while (true) {
1025         int n = vl_getnext(in, offset);
1026         if (n == offset)
1027             break;
1028         out += in.mid(offset, n - offset);
1029         offset = n;
1030     }
1031     return out;
1032 }
1033 
1034 static bool parseValidityLength(const QString &in, ValidityLength *vl)
1035 {
1036     vl->years  = -1;
1037     vl->months = -1;
1038     vl->days   = -1;
1039 
1040     QStringList parts = vl_getparts(in);
1041     while (true) {
1042         // first part should be a number
1043         if (parts.count() < 1)
1044             break;
1045         QString str = parts.takeFirst();
1046         bool    ok;
1047         int     x = str.toInt(&ok);
1048         if (!ok)
1049             return false;
1050 
1051         // next part should be 1 letter plus any amount of space
1052         if (parts.count() < 1)
1053             return false;
1054         str = parts.takeFirst();
1055         if (!str[0].isLetter())
1056             return false;
1057         str = str.trimmed(); // remove space
1058 
1059         if (str == QLatin1String("y")) {
1060             if (vl->years != -1)
1061                 return false;
1062             vl->years = x;
1063         }
1064         if (str == QLatin1String("m")) {
1065             if (vl->months != -1)
1066                 return false;
1067             vl->months = x;
1068         }
1069         if (str == QLatin1String("d")) {
1070             if (vl->days != -1)
1071                 return false;
1072             vl->days = x;
1073         }
1074     }
1075 
1076     if (vl->years == -1)
1077         vl->years = 0;
1078     if (vl->months == -1)
1079         vl->months = 0;
1080     if (vl->days == -1)
1081         vl->days = 0;
1082 
1083     return true;
1084 }
1085 
1086 static QString prompt_for(const QString &prompt)
1087 {
1088     printf("%s: ", prompt.toLatin1().data());
1089     fflush(stdout);
1090     QByteArray result(256, 0);
1091     if (fgets((char *)result.data(), result.size(), stdin))
1092         return QString::fromLocal8Bit(result).trimmed();
1093     else
1094         return QString();
1095 }
1096 
1097 static QCA::CertificateOptions promptForCertAttributes(bool advanced, bool req)
1098 {
1099     QCA::CertificateOptions opts;
1100 
1101     if (advanced) {
1102         if (!req) {
1103             while (true) {
1104                 QString str = prompt_for(
1105                     QStringLiteral("Create an end user ('user') certificate or a CA ('ca') certificate? [user]"));
1106                 if (str.isEmpty())
1107                     str = QStringLiteral("user");
1108                 if (str != QLatin1String("user") && str != QLatin1String("ca")) {
1109                     printf("'%s' is not a valid entry.\n", qPrintable(str));
1110                     continue;
1111                 }
1112 
1113                 if (str == QLatin1String("ca"))
1114                     opts.setAsCA();
1115                 break;
1116             }
1117             printf("\n");
1118 
1119             while (true) {
1120                 QString         str = prompt_for(QStringLiteral("Serial Number"));
1121                 QCA::BigInteger num;
1122                 if (str.isEmpty() || !num.fromString(str)) {
1123                     printf("'%s' is not a valid entry.\n", qPrintable(str));
1124                     continue;
1125                 }
1126 
1127                 opts.setSerialNumber(num);
1128                 break;
1129             }
1130             printf("\n");
1131         }
1132 
1133         {
1134             QCA::CertificateInfoOrdered info;
1135             printf(
1136                 "Choose the information attributes to add to the certificate.  They will be\n"
1137                 "added in the order they are entered.\n\n");
1138             printf("Available information attributes:\n");
1139             QList<InfoType> list = makeInfoTypeList();
1140             for (int n = 0; n < list.count(); ++n) {
1141                 const InfoType &i = list[n];
1142                 char            c = 'a' + n;
1143                 printf("  %c) %-32s        %s\n", c, qPrintable(i.name), qPrintable(i.desc));
1144             }
1145             printf("\n");
1146             while (true) {
1147                 int index;
1148                 while (true) {
1149                     QString str = prompt_for(QStringLiteral("Select an attribute to add, or enter to move on"));
1150                     if (str.isEmpty()) {
1151                         index = -1;
1152                         break;
1153                     }
1154                     if (str.length() == 1) {
1155                         index = str[0].toLatin1() - 'a';
1156                         if (index >= 0 && index < list.count())
1157                             break;
1158                     }
1159                     printf("'%s' is not a valid entry.\n", qPrintable(str));
1160                 }
1161                 if (index == -1)
1162                     break;
1163 
1164                 QString val = prompt_for(list[index].name);
1165                 info += QCA::CertificateInfoPair(list[index].type, val);
1166                 printf("Added attribute.\n\n");
1167             }
1168             opts.setInfoOrdered(info);
1169         }
1170 
1171         {
1172             QCA::Constraints constraints;
1173             printf("\n");
1174             printf("Choose the constraint attributes to add to the certificate.\n\n");
1175             printf("Available attributes:\n");
1176             QList<MyConstraintType> list = makeConstraintTypeList();
1177             for (int n = 0; n < list.count(); ++n) {
1178                 const MyConstraintType &i = list[n];
1179                 char                    c = 'a' + n;
1180                 printf("  %c) %-32s        %s\n", c, qPrintable(i.name), qPrintable(i.desc));
1181             }
1182             printf("\n");
1183             printf("If no constraints are added, then the certificate may be used for any purpose.\n\n");
1184             while (true) {
1185                 int index;
1186                 while (true) {
1187                     QString str = prompt_for(QStringLiteral("Select an attribute to add, or enter to move on"));
1188                     if (str.isEmpty()) {
1189                         index = -1;
1190                         break;
1191                     }
1192                     if (str.length() == 1) {
1193                         index = str[0].toLatin1() - 'a';
1194                         if (index >= 0 && index < list.count())
1195                             break;
1196                     }
1197                     printf("'%s' is not a valid entry.\n\n", qPrintable(str));
1198                 }
1199                 if (index == -1)
1200                     break;
1201 
1202                 if (constraints.contains(list[index].type)) {
1203                     printf("You have already added '%s'.\n\n", qPrintable(list[index].name));
1204                     continue;
1205                 }
1206 
1207                 constraints += list[index].type;
1208                 printf("Added attribute.\n\n");
1209             }
1210             opts.setConstraints(constraints);
1211         }
1212 
1213         {
1214             QStringList policies;
1215             printf("\n");
1216             printf(
1217                 "Are there any policy OID attributes that you wish to add?  Use the dotted\n"
1218                 "string format.\n\n");
1219             while (true) {
1220                 QString str = prompt_for(QStringLiteral("Enter a policy OID to add, or enter to move on"));
1221                 if (str.isEmpty())
1222                     break;
1223                 if (!validOid(str)) {
1224                     printf("'%s' is not a valid entry.\n\n", qPrintable(str));
1225                     continue;
1226                 }
1227                 if (policies.contains(str)) {
1228                     printf("You have already added '%s'.\n\n", qPrintable(str));
1229                     continue;
1230                 }
1231 
1232                 policies += str;
1233                 printf("Added attribute.\n\n");
1234             }
1235             opts.setPolicies(policies);
1236         }
1237 
1238         printf("\n");
1239     } else {
1240         QCA::CertificateInfo info;
1241         info.insert(QCA::CommonName, prompt_for(QStringLiteral("Common Name")));
1242         info.insert(QCA::Country, prompt_for(QStringLiteral("Country Code (2 letters)")));
1243         info.insert(QCA::Organization, prompt_for(QStringLiteral("Organization")));
1244         info.insert(QCA::Email, prompt_for(QStringLiteral("Email")));
1245         opts.setInfo(info);
1246 
1247         printf("\n");
1248     }
1249 
1250     if (!req) {
1251         while (true) {
1252             QString str = prompt_for(QStringLiteral("How long should the certificate be valid? (e.g. '1y2m3d')"));
1253             ValidityLength vl;
1254             if (!parseValidityLength(str, &vl)) {
1255                 printf("'%s' is not a valid entry.\n\n", qPrintable(str));
1256                 continue;
1257             }
1258 
1259             if (vl.years == 0 && vl.months == 0 && vl.days == 0) {
1260                 printf("The certificate must be valid for at least one day.\n\n");
1261                 continue;
1262             }
1263 
1264             QDateTime start = QDateTime::currentDateTimeUtc();
1265             QDateTime end   = start;
1266             if (vl.years > 0)
1267                 end = end.addYears(vl.years);
1268             if (vl.months > 0)
1269                 end = end.addMonths(vl.months);
1270             if (vl.days > 0)
1271                 end = end.addDays(vl.days);
1272             opts.setValidityPeriod(start, end);
1273 
1274             QStringList parts;
1275             if (vl.years > 0)
1276                 parts += QStringLiteral("%1 year(s)").arg(vl.years);
1277             if (vl.months > 0)
1278                 parts += QStringLiteral("%1 month(s)").arg(vl.months);
1279             if (vl.days > 0)
1280                 parts += QStringLiteral("%1 day(s)").arg(vl.days);
1281             QString out;
1282             if (parts.count() == 1)
1283                 out = parts[0];
1284             else if (parts.count() == 2)
1285                 out = parts[0] + QStringLiteral(" and ") + parts[1];
1286             else if (parts.count() == 3)
1287                 out = parts[0] + QStringLiteral(", ") + parts[1] + QStringLiteral(", and ") + parts[2];
1288             printf("Certificate will be valid for %s.\n", qPrintable(out));
1289             break;
1290         }
1291         printf("\n");
1292     }
1293 
1294     return opts;
1295 }
1296 
1297 // qsettings seems to give us a string type for both bool and int (and
1298 //   possibly others, but those are the only two we care about here).
1299 //   in order to figure out what is actually a bool or an int, we need
1300 //   to examine the string.  so for the functions below, we convert
1301 //   the variant to a string, and then inspect it to see if it looks
1302 //   like a bool or an int.
1303 
1304 static bool string_is_bool(const QString &in)
1305 {
1306     QString lc = in.toLower();
1307     if (lc == QLatin1String("true") || lc == QLatin1String("false"))
1308         return true;
1309     return false;
1310 }
1311 
1312 static bool string_is_int(const QString &in)
1313 {
1314     bool ok;
1315     in.toInt(&ok);
1316     return ok;
1317 }
1318 
1319 static bool variant_is_bool(const QVariant &in)
1320 {
1321     if (in.canConvert<QString>() && string_is_bool(in.toString()))
1322         return true;
1323     return false;
1324 }
1325 
1326 static bool variant_is_int(const QVariant &in)
1327 {
1328     if (in.canConvert<QString>() && string_is_int(in.toString()))
1329         return true;
1330     return false;
1331 }
1332 
1333 static QString prompt_for_string(const QString &prompt, const QString &def = QString())
1334 {
1335     printf("%s", prompt.toLatin1().data());
1336     fflush(stdout);
1337     QByteArray result(256, 0);
1338     if (!fgets((char *)result.data(), result.size(), stdin))
1339         return QString();
1340     if (result[result.length() - 1] == '\n')
1341         result.truncate(result.length() - 1);
1342     // empty input -> use default
1343     if (result.isEmpty())
1344         return def;
1345     // trimmed input could result in an empty value, but in that case
1346     //   it is treated as if the user wishes to submit an empty value.
1347     return QString::fromLocal8Bit(result).trimmed();
1348 }
1349 
1350 static int prompt_for_int(const QString &prompt, int def = 0)
1351 {
1352     while (true) {
1353         QString str = prompt_for_string(prompt);
1354         if (str.isEmpty())
1355             return def;
1356         bool ok;
1357         int  x = str.toInt(&ok);
1358         if (ok)
1359             return x;
1360         printf("'%s' is not a valid entry.\n\n", qPrintable(str));
1361     }
1362 }
1363 
1364 static bool partial_compare_nocase(const QString &in, const QString &target, int min = 1)
1365 {
1366     if (in.length() >= min && in.length() <= target.length() && target.mid(0, in.length()).toLower() == in.toLower())
1367         return true;
1368     return false;
1369 }
1370 
1371 static bool prompt_for_bool(const QString &prompt, bool def = false)
1372 {
1373     while (true) {
1374         QString str = prompt_for_string(prompt);
1375         if (str.isEmpty())
1376             return def;
1377         if (partial_compare_nocase(str, QStringLiteral("true")))
1378             return true;
1379         else if (partial_compare_nocase(str, QStringLiteral("false")))
1380             return false;
1381         printf("'%s' is not a valid entry.\n\n", qPrintable(str));
1382     }
1383 }
1384 
1385 static bool prompt_for_yesno(const QString &prompt, bool def = false)
1386 {
1387     while (true) {
1388         QString str = prompt_for_string(prompt);
1389         if (str.isEmpty())
1390             return def;
1391         if (partial_compare_nocase(str, QStringLiteral("yes")))
1392             return true;
1393         else if (partial_compare_nocase(str, QStringLiteral("no")))
1394             return false;
1395         printf("'%s' is not a valid entry.\n\n", qPrintable(str));
1396     }
1397 }
1398 
1399 static QString prompt_for_slotevent_method(const QString &prompt, const QString &def = QString())
1400 {
1401     while (true) {
1402         QString str = prompt_for_string(prompt);
1403         if (str.isEmpty())
1404             return def;
1405         if (partial_compare_nocase(str, QStringLiteral("auto")))
1406             return QStringLiteral("auto");
1407         else if (partial_compare_nocase(str, QStringLiteral("trigger")))
1408             return QStringLiteral("trigger");
1409         else if (partial_compare_nocase(str, QStringLiteral("poll")))
1410             return QStringLiteral("poll");
1411         printf("'%s' is not a valid entry.\n\n", qPrintable(str));
1412     }
1413 }
1414 
1415 static QVariantMap provider_config_edit_generic(const QVariantMap &in)
1416 {
1417     QVariantMap                            config = in;
1418     QMutableMapIterator<QString, QVariant> it(config);
1419     while (it.hasNext()) {
1420         it.next();
1421         QString var = it.key();
1422         if (var == QLatin1String("formtype"))
1423             continue;
1424         QVariant val = it.value();
1425 
1426         // fields must be bool, int, or string
1427         QVariant newval;
1428         QString  prompt = QStringLiteral("%1: [%2] ").arg(var, val.toString());
1429         if (variant_is_bool(val))
1430             newval = prompt_for_bool(QStringLiteral("bool   ") + prompt, val.toBool());
1431         else if (variant_is_int(val))
1432             newval = prompt_for_int(QStringLiteral("int    ") + prompt, val.toInt());
1433         else if (val.canConvert<QString>())
1434             newval = prompt_for_string(QStringLiteral("string ") + prompt, val.toString());
1435         else
1436             continue; // skip bogus fields
1437 
1438         it.setValue(newval);
1439     }
1440 
1441     return config;
1442 }
1443 
1444 class Pkcs11ProviderConfig
1445 {
1446 public:
1447     bool    allow_protected_authentication;
1448     bool    cert_private;
1449     bool    enabled;
1450     QString library;
1451     QString name;
1452     int     private_mask;
1453     QString slotevent_method;
1454     int     slotevent_timeout;
1455 
1456     Pkcs11ProviderConfig()
1457         : allow_protected_authentication(true)
1458         , cert_private(false)
1459         , enabled(false)
1460         , private_mask(0)
1461         , slotevent_method(QStringLiteral("auto"))
1462         , slotevent_timeout(0)
1463     {
1464     }
1465 
1466     QVariantMap toVariantMap() const
1467     {
1468         QVariantMap out;
1469         out[QStringLiteral("allow_protected_authentication")] = allow_protected_authentication;
1470         out[QStringLiteral("cert_private")]                   = cert_private;
1471         out[QStringLiteral("enabled")]                        = enabled;
1472         out[QStringLiteral("library")]                        = library;
1473         out[QStringLiteral("name")]                           = name;
1474         out[QStringLiteral("private_mask")]                   = private_mask;
1475         out[QStringLiteral("slotevent_method")]               = slotevent_method;
1476         out[QStringLiteral("slotevent_timeout")]              = slotevent_timeout;
1477         return out;
1478     }
1479 
1480     bool fromVariantMap(const QVariantMap &in)
1481     {
1482         allow_protected_authentication = in[QStringLiteral("allow_protected_authentication")].toBool();
1483         cert_private                   = in[QStringLiteral("cert_private")].toBool();
1484         enabled                        = in[QStringLiteral("enabled")].toBool();
1485         library                        = in[QStringLiteral("library")].toString();
1486         name                           = in[QStringLiteral("name")].toString();
1487         private_mask                   = in[QStringLiteral("private_mask")].toInt();
1488         slotevent_method               = in[QStringLiteral("slotevent_method")].toString();
1489         slotevent_timeout              = in[QStringLiteral("slotevent_timeout")].toInt();
1490         return true;
1491     }
1492 };
1493 
1494 class Pkcs11Config
1495 {
1496 public:
1497     bool                        allow_load_rootca;
1498     bool                        allow_protected_authentication;
1499     int                         log_level;
1500     int                         pin_cache;
1501     QList<Pkcs11ProviderConfig> providers;
1502 
1503     QVariantMap orig_config;
1504 
1505     Pkcs11Config()
1506         : allow_load_rootca(false)
1507         , allow_protected_authentication(true)
1508         , log_level(0)
1509         , pin_cache(-1)
1510     {
1511     }
1512 
1513     QVariantMap toVariantMap() const
1514     {
1515         QVariantMap out = orig_config;
1516 
1517         // form type
1518         out[QStringLiteral("formtype")] = QLatin1String("http://affinix.com/qca/forms/qca-pkcs11#1.0");
1519 
1520         // base settings
1521         out[QStringLiteral("allow_load_rootca")]              = allow_load_rootca;
1522         out[QStringLiteral("allow_protected_authentication")] = allow_protected_authentication;
1523         out[QStringLiteral("log_level")]                      = log_level;
1524         out[QStringLiteral("pin_cache")]                      = pin_cache;
1525 
1526         // provider settings (always write at least 10 providers)
1527         for (int n = 0; n < 10 || n < providers.count(); ++n) {
1528             QString prefix = QString::asprintf("provider_%02d_", n);
1529 
1530             Pkcs11ProviderConfig provider;
1531             if (n < providers.count())
1532                 provider = providers[n];
1533 
1534             QVariantMap                     subconfig = provider.toVariantMap();
1535             QMapIterator<QString, QVariant> it(subconfig);
1536             while (it.hasNext()) {
1537                 it.next();
1538                 out.insert(prefix + it.key(), it.value());
1539             }
1540         }
1541 
1542         return out;
1543     }
1544 
1545     bool fromVariantMap(const QVariantMap &in)
1546     {
1547         if (in[QStringLiteral("formtype")] != QLatin1String("http://affinix.com/qca/forms/qca-pkcs11#1.0"))
1548             return false;
1549 
1550         allow_load_rootca              = in[QStringLiteral("allow_load_rootca")].toBool();
1551         allow_protected_authentication = in[QStringLiteral("allow_protected_authentication")].toBool();
1552         log_level                      = in[QStringLiteral("log_level")].toInt();
1553         pin_cache                      = in[QStringLiteral("pin_cache")].toInt();
1554 
1555         for (int n = 0;; ++n) {
1556             QString prefix = QString::asprintf("provider_%02d_", n);
1557 
1558             // collect all key/values with this prefix into a
1559             //   a separate container, leaving out the prefix
1560             //   from the keys.
1561             QVariantMap                     subconfig;
1562             QMapIterator<QString, QVariant> it(in);
1563             while (it.hasNext()) {
1564                 it.next();
1565                 if (it.key().startsWith(prefix))
1566                     subconfig.insert(it.key().mid(prefix.length()), it.value());
1567             }
1568 
1569             // if there are no config items with this prefix, we're done
1570             if (subconfig.isEmpty())
1571                 break;
1572 
1573             Pkcs11ProviderConfig provider;
1574             if (!provider.fromVariantMap(subconfig))
1575                 return false;
1576 
1577             // skip unnamed entries
1578             if (provider.name.isEmpty())
1579                 continue;
1580 
1581             // skip duplicate entries
1582             bool have_name_already = false;
1583             foreach (const Pkcs11ProviderConfig &i, providers) {
1584                 if (i.name == provider.name) {
1585                     have_name_already = true;
1586                     break;
1587                 }
1588             }
1589             if (have_name_already)
1590                 continue;
1591 
1592             providers += provider;
1593         }
1594 
1595         orig_config = in;
1596         return true;
1597     }
1598 };
1599 
1600 static QVariantMap provider_config_edit_pkcs11(const QVariantMap &in)
1601 {
1602     Pkcs11Config config;
1603     if (!config.fromVariantMap(in)) {
1604         fprintf(stderr, "Error: unable to parse PKCS#11 provider configuration.\n");
1605         return QVariantMap();
1606     }
1607 
1608     while (true) {
1609         printf("\n");
1610         printf("Global settings:\n");
1611         printf("  Allow loading of root CAs: %s\n", config.allow_load_rootca ? "Yes" : "No");
1612         printf("  Allow protected authentication: %s\n", config.allow_protected_authentication ? "Yes" : "No");
1613         QString str;
1614         if (config.pin_cache == -1)
1615             str = QStringLiteral("No limit");
1616         else
1617             str = QStringLiteral("%1 seconds").arg(config.pin_cache);
1618         printf("  Maximum PIN cache time: %s\n", qPrintable(str));
1619         printf("  Log level: %d\n", config.log_level);
1620         printf("\n");
1621         printf("PKCS#11 modules:\n");
1622         if (!config.providers.isEmpty()) {
1623             foreach (const Pkcs11ProviderConfig &provider, config.providers)
1624                 printf("  %s\n", qPrintable(provider.name));
1625         } else
1626             printf("  (None)\n");
1627         printf("\n");
1628         printf("Actions:\n");
1629         printf("  a) Edit global settings\n");
1630         printf("  b) Add PKCS#11 module\n");
1631         printf("  c) Edit PKCS#11 module\n");
1632         printf("  d) Remove PKCS#11 module\n");
1633         printf("\n");
1634 
1635         int index;
1636         while (true) {
1637             QString str = prompt_for(QStringLiteral("Select an action, or enter to quit"));
1638             if (str.isEmpty()) {
1639                 index = -1;
1640                 break;
1641             }
1642             if (str.length() == 1) {
1643                 index = str[0].toLatin1() - 'a';
1644                 if (index >= 0 && index < 4)
1645                     break;
1646             }
1647             printf("'%s' is not a valid entry.\n\n", qPrintable(str));
1648         }
1649         if (index == -1)
1650             break;
1651 
1652         if (index == 0) {
1653             printf("\n");
1654 
1655             QString prompt;
1656             prompt = QStringLiteral("Allow loading of root CAs: [%1] ")
1657                          .arg(config.allow_load_rootca ? QStringLiteral("Yes") : QStringLiteral("No"));
1658             config.allow_load_rootca = prompt_for_yesno(prompt, config.allow_load_rootca);
1659             prompt                   = QStringLiteral("Allow protected authentication: [%1] ")
1660                          .arg(config.allow_protected_authentication ? QStringLiteral("Yes") : QStringLiteral("No"));
1661             config.allow_protected_authentication = prompt_for_yesno(prompt, config.allow_protected_authentication);
1662             prompt = QStringLiteral("Maximum PIN cache time in seconds (-1 for no limit): [%1] ").arg(config.pin_cache);
1663             config.pin_cache = prompt_for_int(prompt, config.pin_cache);
1664             prompt           = QStringLiteral("Log level: [%1] ").arg(config.log_level);
1665             config.log_level = prompt_for_int(prompt, config.log_level);
1666         } else // 1, 2, 3
1667         {
1668             int at = -1;
1669 
1670             // for edit/remove, need to select provider
1671             if (index == 2 || index == 3) {
1672                 printf("\nWhich PKCS#11 module?\n");
1673                 for (int n = 0; n < config.providers.count(); ++n) {
1674                     const Pkcs11ProviderConfig &provider = config.providers[n];
1675                     char                        c        = 'a' + n;
1676                     printf("  %c) %s\n", c, qPrintable(provider.name));
1677                 }
1678                 printf("\n");
1679 
1680                 int index;
1681                 while (true) {
1682                     QString str = prompt_for(QStringLiteral("Select a module, or enter to go back"));
1683                     if (str.isEmpty()) {
1684                         index = -1;
1685                         break;
1686                     }
1687                     if (str.length() == 1) {
1688                         index = str[0].toLatin1() - 'a';
1689                         if (index >= 0 && index < config.providers.count())
1690                             break;
1691                     }
1692                     printf("'%s' is not a valid entry.\n", qPrintable(str));
1693                 }
1694 
1695                 // exit?
1696                 if (index == -1)
1697                     continue;
1698 
1699                 at = index;
1700             }
1701 
1702             // edit the entry
1703             if (index == 1 || index == 2) {
1704                 Pkcs11ProviderConfig provider;
1705                 if (index == 2) // edit
1706                     provider = config.providers[at];
1707                 provider.enabled = true;
1708                 printf("\n");
1709 
1710                 QString prompt;
1711 
1712                 // prompt for unique name
1713                 while (true) {
1714                     if (index == 1)
1715                         prompt = QStringLiteral("Unique friendly name: ");
1716                     else
1717                         prompt = QStringLiteral("Unique friendly name: [%1] ").arg(provider.name);
1718                     provider.name = prompt_for_string(prompt, provider.name);
1719 
1720                     if (provider.name.isEmpty()) {
1721                         printf("The friendly name cannot be blank.\n\n");
1722                         continue;
1723                     }
1724 
1725                     bool have_name_already = false;
1726                     for (int n = 0; n < config.providers.count(); ++n) {
1727                         const Pkcs11ProviderConfig &i = config.providers[n];
1728 
1729                         // skip checking against the entry we are editing
1730                         if (at != -1 && n == at)
1731                             continue;
1732 
1733                         if (i.name == provider.name) {
1734                             have_name_already = true;
1735                             break;
1736                         }
1737                     }
1738                     if (have_name_already) {
1739                         printf("This name is already used by another module.\n\n");
1740                         continue;
1741                     }
1742 
1743                     break;
1744                 }
1745 
1746                 // prompt for library file
1747                 QString last;
1748                 while (true) {
1749                     if (index == 1)
1750                         prompt = QStringLiteral("Library filename: ");
1751                     else
1752                         prompt = QStringLiteral("Library filename: [%1] ").arg(provider.library);
1753                     provider.library = prompt_for_string(prompt, provider.library);
1754 
1755                     if (provider.library.isEmpty()) {
1756                         printf("The library filename cannot be blank.\n\n");
1757                         continue;
1758                     }
1759 
1760                     if (last != provider.library && !QFile::exists(provider.library)) {
1761                         last = provider.library;
1762                         printf("'%s' does not exist.\nPress enter again if you really want this.\n\n",
1763                                qPrintable(provider.library));
1764                         continue;
1765                     }
1766 
1767                     break;
1768                 }
1769 
1770                 prompt =
1771                     QStringLiteral("Allow protected authentication: [%1] ")
1772                         .arg(provider.allow_protected_authentication ? QStringLiteral("Yes") : QStringLiteral("No"));
1773                 provider.allow_protected_authentication =
1774                     prompt_for_yesno(prompt, provider.allow_protected_authentication);
1775                 prompt = QStringLiteral("Provider stores certificates as private objects: [%1] ")
1776                              .arg(provider.cert_private ? QStringLiteral("Yes") : QStringLiteral("No"));
1777                 provider.cert_private = prompt_for_yesno(prompt, provider.cert_private);
1778                 printf("\n");
1779                 printf("Provider private key mask:\n");
1780                 printf("    0        Determine automatically.\n");
1781                 printf("    1        Use sign.\n");
1782                 printf("    2        Use sign recover.\n");
1783                 printf("    4        Use decrypt.\n");
1784                 printf("    8        Use unwrap.\n");
1785                 prompt                = QStringLiteral("Mask value: [%1] ").arg(provider.private_mask);
1786                 provider.private_mask = prompt_for_int(prompt, provider.private_mask);
1787                 printf("\n");
1788                 printf("Slot event method:\n");
1789                 printf("    auto     Determine automatically.\n");
1790                 printf("    trigger  Use trigger.\n");
1791                 printf("    poll     Use poll.\n");
1792                 prompt                    = QStringLiteral("Method value: [%1] ").arg(provider.slotevent_method);
1793                 provider.slotevent_method = prompt_for_slotevent_method(prompt, provider.slotevent_method);
1794                 if (provider.slotevent_method == QLatin1String("poll")) {
1795                     prompt =
1796                         QStringLiteral("Poll timeout (0 for no preference): [%1] ").arg(provider.slotevent_timeout);
1797                     provider.slotevent_timeout = prompt_for_int(prompt, provider.slotevent_timeout);
1798                 } else
1799                     provider.slotevent_timeout = 0;
1800 
1801                 if (index == 1)
1802                     config.providers += provider;
1803                 else // 2
1804                     config.providers[at] = provider;
1805             }
1806             // remove the entry
1807             else // 3
1808             {
1809                 config.providers.removeAt(at);
1810             }
1811         }
1812     }
1813 
1814     return config.toVariantMap();
1815 }
1816 
1817 static QVariantMap provider_config_edit(const QVariantMap &in)
1818 {
1819     // see if we have a configurator for a known form type
1820     if (in[QStringLiteral("formtype")] == QLatin1String("http://affinix.com/qca/forms/qca-pkcs11#1.0"))
1821         return provider_config_edit_pkcs11(in);
1822 
1823     // otherwise, use the generic configurator
1824     return provider_config_edit_generic(in);
1825 }
1826 
1827 static QString get_fingerprint(const QCA::Certificate &cert, const QString &hashType)
1828 {
1829     QString hex = QCA::Hash(hashType).hashToString(cert.toDER());
1830     QString out;
1831     for (int n = 0; n < hex.count(); ++n) {
1832         if (n != 0 && n % 2 == 0)
1833             out += QLatin1Char(':');
1834         out += hex[n];
1835     }
1836     return out;
1837 }
1838 
1839 static QString kstype_to_string(QCA::KeyStore::Type _type)
1840 {
1841     QString type;
1842     switch (_type) {
1843     case QCA::KeyStore::System:
1844         type = QStringLiteral("Sys ");
1845         break;
1846     case QCA::KeyStore::User:
1847         type = QStringLiteral("User");
1848         break;
1849     case QCA::KeyStore::Application:
1850         type = QStringLiteral("App ");
1851         break;
1852     case QCA::KeyStore::SmartCard:
1853         type = QStringLiteral("Card");
1854         break;
1855     case QCA::KeyStore::PGPKeyring:
1856         type = QStringLiteral("PGP ");
1857         break;
1858     default:
1859         type = QStringLiteral("XXXX");
1860         break;
1861     }
1862     return type;
1863 }
1864 
1865 static QString ksentrytype_to_string(QCA::KeyStoreEntry::Type _type)
1866 {
1867     QString type;
1868     switch (_type) {
1869     case QCA::KeyStoreEntry::TypeKeyBundle:
1870         type = QStringLiteral("Key ");
1871         break;
1872     case QCA::KeyStoreEntry::TypeCertificate:
1873         type = QStringLiteral("Cert");
1874         break;
1875     case QCA::KeyStoreEntry::TypeCRL:
1876         type = QStringLiteral("CRL ");
1877         break;
1878     case QCA::KeyStoreEntry::TypePGPSecretKey:
1879         type = QStringLiteral("PSec");
1880         break;
1881     case QCA::KeyStoreEntry::TypePGPPublicKey:
1882         type = QStringLiteral("PPub");
1883         break;
1884     default:
1885         type = QStringLiteral("XXXX");
1886         break;
1887     }
1888     return type;
1889 }
1890 
1891 static void try_print_info(const char *name, const QStringList &values)
1892 {
1893     if (!values.isEmpty()) {
1894         QString value = values.join(QStringLiteral(", "));
1895         printf("   %s: %s\n", name, value.toUtf8().data());
1896     }
1897 }
1898 
1899 static void print_info(const char *title, const QCA::CertificateInfo &info)
1900 {
1901     QList<InfoType> list = makeInfoTypeList();
1902     printf("%s\n", title);
1903     foreach (const InfoType &t, list)
1904         try_print_info(qPrintable(t.name), info.values(t.type));
1905 }
1906 
1907 static void print_info_ordered(const char *title, const QCA::CertificateInfoOrdered &info)
1908 {
1909     QList<InfoType> list = makeInfoTypeList(true);
1910     printf("%s\n", title);
1911     foreach (const QCA::CertificateInfoPair &pair, info) {
1912         QCA::CertificateInfoType type = pair.type();
1913         QString                  name;
1914         int                      at = -1;
1915         for (int n = 0; n < list.count(); ++n) {
1916             if (list[n].type == type) {
1917                 at = n;
1918                 break;
1919             }
1920         }
1921 
1922         // known type?
1923         if (at != -1) {
1924             name = list[at].name;
1925         } else {
1926             if (pair.type().section() == QCA::CertificateInfoType::DN)
1927                 name = QStringLiteral("DN:") + pair.type().id();
1928             else
1929                 name = QStringLiteral("AN:") + pair.type().id();
1930         }
1931 
1932         printf("   %s: %s\n", qPrintable(name), pair.value().toUtf8().data());
1933     }
1934 }
1935 
1936 static QString constraint_to_string(const QCA::ConstraintType &t)
1937 {
1938     QList<MyConstraintType> list = makeConstraintTypeList();
1939     for (int n = 0; n < list.count(); ++n) {
1940         if (list[n].type == t)
1941             return list[n].name;
1942     }
1943     return t.id();
1944 }
1945 
1946 static QString sigalgo_to_string(QCA::SignatureAlgorithm algo)
1947 {
1948     QString str;
1949     switch (algo) {
1950     case QCA::EMSA1_SHA1:
1951         str = QStringLiteral("EMSA1(SHA1)");
1952         break;
1953     case QCA::EMSA3_SHA1:
1954         str = QStringLiteral("EMSA3(SHA1)");
1955         break;
1956     case QCA::EMSA3_MD5:
1957         str = QStringLiteral("EMSA3(MD5)");
1958         break;
1959     case QCA::EMSA3_MD2:
1960         str = QStringLiteral("EMSA3(MD2)");
1961         break;
1962     case QCA::EMSA3_RIPEMD160:
1963         str = QStringLiteral("EMSA3(RIPEMD160)");
1964         break;
1965     case QCA::EMSA3_Raw:
1966         str = QStringLiteral("EMSA3(raw)");
1967         break;
1968     default:
1969         str = QStringLiteral("Unknown");
1970         break;
1971     }
1972     return str;
1973 }
1974 
1975 static void print_cert(const QCA::Certificate &cert, bool ordered = false)
1976 {
1977     printf("Serial Number: %s\n", qPrintable(cert.serialNumber().toString()));
1978 
1979     if (ordered) {
1980         print_info_ordered("Subject", cert.subjectInfoOrdered());
1981         print_info_ordered("Issuer", cert.issuerInfoOrdered());
1982     } else {
1983         print_info("Subject", cert.subjectInfo());
1984         print_info("Issuer", cert.issuerInfo());
1985     }
1986 
1987     printf("Validity\n");
1988     printf("   Not before: %s\n", qPrintable(cert.notValidBefore().toString()));
1989     printf("   Not after:  %s\n", qPrintable(cert.notValidAfter().toString()));
1990 
1991     printf("Constraints\n");
1992     QCA::Constraints constraints = cert.constraints();
1993     int              n;
1994     if (!constraints.isEmpty()) {
1995         for (n = 0; n < constraints.count(); ++n)
1996             printf("   %s\n", qPrintable(constraint_to_string(constraints[n])));
1997     } else
1998         printf("   No constraints\n");
1999 
2000     printf("Policies\n");
2001     QStringList policies = cert.policies();
2002     if (!policies.isEmpty()) {
2003         for (n = 0; n < policies.count(); ++n)
2004             printf("   %s\n", qPrintable(policies[n]));
2005     } else
2006         printf("   No policies\n");
2007 
2008     QByteArray id;
2009     printf("Issuer Key ID: ");
2010     id = cert.issuerKeyId();
2011     if (!id.isEmpty())
2012         printf("%s\n", qPrintable(QCA::arrayToHex(id)));
2013     else
2014         printf("None\n");
2015 
2016     printf("Subject Key ID: ");
2017     id = cert.subjectKeyId();
2018     if (!id.isEmpty())
2019         printf("%s\n", qPrintable(QCA::arrayToHex(id)));
2020     else
2021         printf("None\n");
2022 
2023     printf("CA: %s\n", cert.isCA() ? "Yes" : "No");
2024     printf("Signature Algorithm: %s\n", qPrintable(sigalgo_to_string(cert.signatureAlgorithm())));
2025 
2026     QCA::PublicKey key = cert.subjectPublicKey();
2027     printf("Public Key:\n%s", key.toPEM().toLatin1().data());
2028 
2029     printf("SHA1 Fingerprint: %s\n", qPrintable(get_fingerprint(cert, QStringLiteral("sha1"))));
2030     printf("MD5 Fingerprint: %s\n", qPrintable(get_fingerprint(cert, QStringLiteral("md5"))));
2031 }
2032 
2033 static void print_certreq(const QCA::CertificateRequest &cert, bool ordered = false)
2034 {
2035     if (ordered)
2036         print_info_ordered("Subject", cert.subjectInfoOrdered());
2037     else
2038         print_info("Subject", cert.subjectInfo());
2039 
2040     printf("Constraints\n");
2041     QCA::Constraints constraints = cert.constraints();
2042     int              n;
2043     if (!constraints.isEmpty()) {
2044         for (n = 0; n < constraints.count(); ++n)
2045             printf("   %s\n", qPrintable(constraint_to_string(constraints[n])));
2046     } else
2047         printf("   No constraints\n");
2048 
2049     printf("Policies\n");
2050     QStringList policies = cert.policies();
2051     if (!policies.isEmpty()) {
2052         for (n = 0; n < policies.count(); ++n)
2053             printf("   %s\n", qPrintable(policies[n]));
2054     } else
2055         printf("   No policies\n");
2056 
2057     printf("CA: %s\n", cert.isCA() ? "Yes" : "No");
2058     printf("Signature Algorithm: %s\n", qPrintable(sigalgo_to_string(cert.signatureAlgorithm())));
2059 
2060     QCA::PublicKey key = cert.subjectPublicKey();
2061     printf("Public Key:\n%s", key.toPEM().toLatin1().data());
2062 }
2063 
2064 static void print_crl(const QCA::CRL &crl, bool ordered = false)
2065 {
2066     if (ordered)
2067         print_info_ordered("Issuer", crl.issuerInfoOrdered());
2068     else
2069         print_info("Issuer", crl.issuerInfo());
2070 
2071     int num = crl.number();
2072     if (num != -1)
2073         printf("Number: %d\n", num);
2074 
2075     printf("Validity\n");
2076     printf("   This update: %s\n", qPrintable(crl.thisUpdate().toString()));
2077     printf("   Next update: %s\n", qPrintable(crl.nextUpdate().toString()));
2078 
2079     QByteArray id;
2080     printf("Issuer Key ID: ");
2081     id = crl.issuerKeyId();
2082     if (!id.isEmpty())
2083         printf("%s\n", qPrintable(QCA::arrayToHex(id)));
2084     else
2085         printf("None\n");
2086 
2087     printf("Signature Algorithm: %s\n", qPrintable(sigalgo_to_string(crl.signatureAlgorithm())));
2088 
2089     QList<QCA::CRLEntry> revokedList = crl.revoked();
2090     foreach (const QCA::CRLEntry &entry, revokedList) {
2091         printf("   %s: %s, %s\n",
2092                qPrintable(entry.serialNumber().toString()),
2093                crlEntryReasonToString(entry.reason()),
2094                qPrintable(entry.time().toString()));
2095     }
2096 }
2097 
2098 static QString format_pgp_fingerprint(const QString &in)
2099 {
2100     QString out;
2101     bool    first = true;
2102     for (int n = 0; n + 3 < in.length(); n += 4) {
2103         if (!first)
2104             out += QLatin1Char(' ');
2105         else
2106             first = false;
2107         out += in.mid(n, 4).toUpper();
2108     }
2109     return out;
2110 }
2111 
2112 static void print_pgp(const QCA::PGPKey &key)
2113 {
2114     printf("Key ID: %s\n", qPrintable(key.keyId()));
2115     printf("User IDs:\n");
2116     foreach (const QString &s, key.userIds())
2117         printf("   %s\n", qPrintable(s));
2118     printf("Validity\n");
2119     printf("   Not before: %s\n", qPrintable(key.creationDate().toString()));
2120     if (!key.expirationDate().isNull())
2121         printf("   Not after:  %s\n", qPrintable(key.expirationDate().toString()));
2122     else
2123         printf("   Not after:  (no expiration)\n");
2124     printf("In Keyring: %s\n", key.inKeyring() ? "Yes" : "No");
2125     printf("Secret Key: %s\n", key.isSecret() ? "Yes" : "No");
2126     printf("Trusted:    %s\n", key.isTrusted() ? "Yes" : "No");
2127     printf("Fingerprint: %s\n", qPrintable(format_pgp_fingerprint(key.fingerprint())));
2128 }
2129 
2130 static QString validityToString(QCA::Validity v)
2131 {
2132     QString s;
2133     switch (v) {
2134     case QCA::ValidityGood:
2135         s = QStringLiteral("Validated");
2136         break;
2137     case QCA::ErrorRejected:
2138         s = QStringLiteral("Root CA is marked to reject the specified purpose");
2139         break;
2140     case QCA::ErrorUntrusted:
2141         s = QStringLiteral("Certificate not trusted for the required purpose");
2142         break;
2143     case QCA::ErrorSignatureFailed:
2144         s = QStringLiteral("Invalid signature");
2145         break;
2146     case QCA::ErrorInvalidCA:
2147         s = QStringLiteral("Invalid CA certificate");
2148         break;
2149     case QCA::ErrorInvalidPurpose:
2150         s = QStringLiteral("Invalid certificate purpose");
2151         break;
2152     case QCA::ErrorSelfSigned:
2153         s = QStringLiteral("Certificate is self-signed");
2154         break;
2155     case QCA::ErrorRevoked:
2156         s = QStringLiteral("Certificate has been revoked");
2157         break;
2158     case QCA::ErrorPathLengthExceeded:
2159         s = QStringLiteral("Maximum certificate chain length exceeded");
2160         break;
2161     case QCA::ErrorExpired:
2162         s = QStringLiteral("Certificate has expired");
2163         break;
2164     case QCA::ErrorExpiredCA:
2165         s = QStringLiteral("CA has expired");
2166         break;
2167     case QCA::ErrorValidityUnknown:
2168     default:
2169         s = QStringLiteral("General certificate validation error");
2170         break;
2171     }
2172     return s;
2173 }
2174 
2175 static QString smIdentityResultToString(QCA::SecureMessageSignature::IdentityResult r)
2176 {
2177     QString str;
2178     switch (r) {
2179     case QCA::SecureMessageSignature::Valid:
2180         str = QStringLiteral("Valid");
2181         break;
2182     case QCA::SecureMessageSignature::InvalidSignature:
2183         str = QStringLiteral("InvalidSignature");
2184         break;
2185     case QCA::SecureMessageSignature::InvalidKey:
2186         str = QStringLiteral("InvalidKey");
2187         break;
2188     case QCA::SecureMessageSignature::NoKey:
2189         str = QStringLiteral("NoKey");
2190         break;
2191     default:
2192         str = QStringLiteral("Unknown");
2193     }
2194     return str;
2195 }
2196 
2197 static QString smErrorToString(QCA::SecureMessage::Error e)
2198 {
2199     QMap<QCA::SecureMessage::Error, QString> map;
2200     map[QCA::SecureMessage::ErrorPassphrase]       = QStringLiteral("ErrorPassphrase");
2201     map[QCA::SecureMessage::ErrorFormat]           = QStringLiteral("ErrorFormat");
2202     map[QCA::SecureMessage::ErrorSignerExpired]    = QStringLiteral("ErrorSignerExpired");
2203     map[QCA::SecureMessage::ErrorSignerInvalid]    = QStringLiteral("ErrorSignerInvalid");
2204     map[QCA::SecureMessage::ErrorEncryptExpired]   = QStringLiteral("ErrorEncryptExpired");
2205     map[QCA::SecureMessage::ErrorEncryptUntrusted] = QStringLiteral("ErrorEncryptUntrusted");
2206     map[QCA::SecureMessage::ErrorEncryptInvalid]   = QStringLiteral("ErrorEncryptInvalid");
2207     map[QCA::SecureMessage::ErrorNeedCard]         = QStringLiteral("ErrorNeedCard");
2208     map[QCA::SecureMessage::ErrorCertKeyMismatch]  = QStringLiteral("ErrorCertKeyMismatch");
2209     map[QCA::SecureMessage::ErrorUnknown]          = QStringLiteral("ErrorUnknown");
2210     return map[e];
2211 }
2212 
2213 static void smDisplaySignatures(const QList<QCA::SecureMessageSignature> &signers)
2214 {
2215     foreach (const QCA::SecureMessageSignature &signer, signers) {
2216         QCA::SecureMessageSignature::IdentityResult r = signer.identityResult();
2217         fprintf(stderr, "IdentityResult: %s\n", qPrintable(smIdentityResultToString(r)));
2218 
2219         QCA::SecureMessageKey key = signer.key();
2220         if (!key.isNull()) {
2221             if (key.type() == QCA::SecureMessageKey::PGP) {
2222                 QCA::PGPKey pub = key.pgpPublicKey();
2223                 fprintf(stderr, "From: %s (%s)\n", qPrintable(pub.primaryUserId()), qPrintable(pub.keyId()));
2224             } else {
2225                 QCA::Certificate     cert = key.x509CertificateChain().primary();
2226                 QString              emailStr;
2227                 QCA::CertificateInfo info = cert.subjectInfo();
2228                 if (info.contains(QCA::Email))
2229                     emailStr = QStringLiteral(" (%1)").arg(info.value(QCA::Email));
2230                 fprintf(stderr, "From: %s%s\n", qPrintable(cert.commonName()), qPrintable(emailStr));
2231             }
2232         }
2233     }
2234 }
2235 
2236 static const char *mime_signpart =
2237     "Content-Type: text/plain; charset=UTF-8\r\n"
2238     "Content-Transfer-Encoding: 8bit\r\n"
2239     "\r\n"
2240     "%1";
2241 
2242 static const char *mime_signed =
2243     "Content-Type: multipart/signed;\r\n"
2244     "   micalg=%1;\r\n"
2245     "   boundary=QCATOOL-0001;\r\n"
2246     "   protocol=\"application/pkcs7-signature\"\r\n"
2247     "\r\n"
2248     "\r\n"
2249     "--QCATOOL-0001\r\n"
2250     "%2\r\n"
2251     "--QCATOOL-0001\r\n"
2252     "Content-Transfer-Encoding: base64\r\n"
2253     "Content-Type: application/pkcs7-signature;\r\n"
2254     "   name=smime.p7s\r\n"
2255     "Content-Disposition: attachment;\r\n"
2256     "   filename=smime.p7s\r\n"
2257     "\r\n"
2258     "%3\r\n"
2259     "\r\n"
2260     "--QCATOOL-0001--\r\n";
2261 
2262 static const char *mime_enveloped =
2263     "Mime-Version: 1.0\r\n"
2264     "Content-Transfer-Encoding: base64\r\n"
2265     "Content-Type: application/pkcs7-mime;\r\n"
2266     "   name=smime.p7m;\r\n"
2267     "   smime-type=enveloped-data\r\n"
2268     "Content-Disposition: attachment;\r\n"
2269     "   filename=smime.p7m\r\n"
2270     "\r\n"
2271     "%1\r\n";
2272 
2273 static QString add_cr(const QString &in)
2274 {
2275     QString out = in;
2276     int     at  = 0;
2277     while (true) {
2278         at = out.indexOf(QLatin1Char('\n'), at);
2279         if (at == -1)
2280             break;
2281         if (at - 1 >= 0 && out[at - 1] != QLatin1Char('\r')) {
2282             out.insert(at, QLatin1Char('\r'));
2283             ++at;
2284         }
2285         ++at;
2286     }
2287     return out;
2288 }
2289 
2290 static QString rem_cr(const QString &in)
2291 {
2292     QString out = in;
2293     out.replace(QLatin1String("\r\n"), QLatin1String("\n"));
2294     return out;
2295 }
2296 
2297 static int indexOf_newline(const QString &in, int offset = 0)
2298 {
2299     for (int n = offset; n < in.length(); ++n) {
2300         if (n + 1 < in.length() && in[n] == QLatin1Char('\r') && in[n + 1] == QLatin1Char('\n'))
2301             return n;
2302         if (in[n] == QLatin1Char('\n'))
2303             return n;
2304     }
2305     return -1;
2306 }
2307 
2308 static int indexOf_doublenewline(const QString &in, int offset = 0)
2309 {
2310     int at = -1;
2311     while (true) {
2312         int n = indexOf_newline(in, offset);
2313         if (n == -1)
2314             return -1;
2315 
2316         if (at != -1) {
2317             if (n == offset)
2318                 break;
2319         }
2320 
2321         at = n;
2322         if (in[n] == QLatin1Char('\n'))
2323             offset = n + 1;
2324         else
2325             offset = n + 2;
2326     }
2327     return at;
2328 }
2329 
2330 // this is so gross
2331 static int newline_len(const QString &in, int offset = 0)
2332 {
2333     if (in[offset] == QLatin1Char('\r'))
2334         return 2;
2335     else
2336         return 1;
2337 }
2338 
2339 // all of this mime stuff is a total hack
2340 static QString open_mime_envelope(const QString &in)
2341 {
2342     int n = indexOf_doublenewline(in);
2343     if (n == -1)
2344         return QString();
2345     return in.mid(n + (newline_len(in, n) * 2)); // good lord
2346 }
2347 
2348 static bool open_mime_data_sig(const QString &in, QString *data, QString *sig)
2349 {
2350     int n = in.indexOf(QLatin1String("boundary="));
2351     if (n == -1)
2352         return false;
2353     n += 9;
2354     int i = indexOf_newline(in, n);
2355     if (i == -1)
2356         return false;
2357     QString boundary;
2358     QString bregion = in.mid(n, i - n);
2359     n               = bregion.indexOf(QLatin1Char(';'));
2360     if (n != -1)
2361         boundary = bregion.mid(0, n);
2362     else
2363         boundary = bregion;
2364 
2365     if (boundary[0] == QLatin1Char('\"'))
2366         boundary.remove(0, 1);
2367     if (boundary[boundary.length() - 1] == QLatin1Char('\"'))
2368         boundary.remove(boundary.length() - 1, 1);
2369     // printf("boundary: [%s]\n", qPrintable(boundary));
2370     QString boundary_end = QStringLiteral("--") + boundary;
2371     boundary             = QStringLiteral("--") + boundary;
2372 
2373     QString work = open_mime_envelope(in);
2374     // printf("work: [%s]\n", qPrintable(work));
2375 
2376     n = work.indexOf(boundary);
2377     if (n == -1)
2378         return false;
2379     n += boundary.length();
2380     i = indexOf_newline(work, n);
2381     if (i == -1)
2382         return false;
2383     n += newline_len(work, i);
2384     int data_start = n;
2385 
2386     n = work.indexOf(boundary, data_start);
2387     if (n == -1)
2388         return false;
2389     int data_end = n;
2390 
2391     n = data_end + boundary.length();
2392     i = indexOf_newline(work, n);
2393     if (i == -1)
2394         return false;
2395     n += newline_len(work, i);
2396     int next = n;
2397 
2398     QString tmp_data = work.mid(data_start, data_end - data_start);
2399     n                = work.indexOf(boundary_end, next);
2400     if (n == -1)
2401         return false;
2402     QString tmp_sig = work.mid(next, n - next);
2403 
2404     // nuke some newlines
2405     if (tmp_data.right(2) == QLatin1String("\r\n"))
2406         tmp_data.truncate(tmp_data.length() - 2);
2407     else if (tmp_data.right(1) == QLatin1String("\n"))
2408         tmp_data.truncate(tmp_data.length() - 1);
2409     if (tmp_sig.right(2) == QLatin1String("\r\n"))
2410         tmp_sig.truncate(tmp_sig.length() - 2);
2411     else if (tmp_sig.right(1) == QLatin1String("\n"))
2412         tmp_sig.truncate(tmp_sig.length() - 1);
2413 
2414     tmp_sig = open_mime_envelope(tmp_sig);
2415 
2416     *data = tmp_data;
2417     *sig  = tmp_sig;
2418     return true;
2419 }
2420 
2421 static QString idHash(const QString &id)
2422 {
2423     // hash the id and take the rightmost 4 hex characters
2424     return QCA::Hash(QStringLiteral("md5")).hashToString(id.toUtf8()).right(4);
2425 }
2426 
2427 // first = ids, second = names
2428 static QPair<QStringList, QStringList> getKeyStoreStrings(const QStringList &list, QCA::KeyStoreManager *ksm)
2429 {
2430     QPair<QStringList, QStringList> out;
2431     for (int n = 0; n < list.count(); ++n) {
2432         QCA::KeyStore ks(list[n], ksm);
2433         out.first.append(idHash(ks.id()));
2434         out.second.append(ks.name());
2435     }
2436     return out;
2437 }
2438 
2439 static QPair<QStringList, QStringList> getKeyStoreEntryStrings(const QList<QCA::KeyStoreEntry> &list)
2440 {
2441     QPair<QStringList, QStringList> out;
2442     for (int n = 0; n < list.count(); ++n) {
2443         out.first.append(idHash(list[n].id()));
2444         out.second.append(list[n].name());
2445     }
2446     return out;
2447 }
2448 
2449 static QList<int> getPartialMatches(const QStringList &list, const QString &str)
2450 {
2451     QList<int> out;
2452     for (int n = 0; n < list.count(); ++n) {
2453         if (list[n].contains(str, Qt::CaseInsensitive))
2454             out += n;
2455     }
2456     return out;
2457 }
2458 
2459 static int findByString(const QPair<QStringList, QStringList> &in, const QString &str)
2460 {
2461     // exact id match
2462     int n = in.first.indexOf(str);
2463     if (n != -1)
2464         return n;
2465 
2466     // partial id match
2467     QList<int> ret = getPartialMatches(in.first, str);
2468     if (!ret.isEmpty())
2469         return ret.first();
2470 
2471     // partial name match
2472     ret = getPartialMatches(in.second, str);
2473     if (!ret.isEmpty())
2474         return ret.first();
2475 
2476     return -1;
2477 }
2478 
2479 static QString getKeyStore(const QString &name)
2480 {
2481     QCA::KeyStoreManager ksm;
2482     QStringList          storeList = ksm.keyStores();
2483     int                  n         = findByString(getKeyStoreStrings(storeList, &ksm), name);
2484     if (n != -1)
2485         return storeList[n];
2486     return QString();
2487 }
2488 
2489 static QCA::KeyStoreEntry getKeyStoreEntry(QCA::KeyStore *store, const QString &name)
2490 {
2491     QList<QCA::KeyStoreEntry> list = store->entryList();
2492     int                       n    = findByString(getKeyStoreEntryStrings(list), name);
2493     if (n != -1)
2494         return list[n];
2495     return QCA::KeyStoreEntry();
2496 }
2497 
2498 // here are a bunch of get_Foo functions for the various types
2499 
2500 // E - generic entry
2501 // K - private key
2502 // C - cert
2503 // X - keybundle
2504 // P - pgp public key
2505 // S - pgp secret key
2506 
2507 // in all cases but K, the store:obj notation can be used.  if there
2508 //   is no colon present, then we treat the input as a filename. we
2509 //   try the file as an exported passive entry id, and if the type
2510 //   is C or X, we'll fall back to regular files if necessary.
2511 
2512 static QCA::KeyStoreEntry get_E(const QString &name, bool nopassiveerror = false)
2513 {
2514     QCA::KeyStoreEntry entry;
2515 
2516     QCA::KeyStoreManager::start();
2517 
2518     int n = name.indexOf(QLatin1Char(':'));
2519     if (n != -1) {
2520         ksm_start_and_wait();
2521 
2522         // store:obj lookup
2523         QString storeName  = name.mid(0, n);
2524         QString objectName = name.mid(n + 1);
2525 
2526         QCA::KeyStoreManager ksm;
2527         QCA::KeyStore        store(getKeyStore(storeName), &ksm);
2528         if (!store.isValid()) {
2529             fprintf(stderr, "Error: no such store [%s].\n", qPrintable(storeName));
2530             return entry;
2531         }
2532 
2533         entry = getKeyStoreEntry(&store, objectName);
2534         if (entry.isNull()) {
2535             fprintf(stderr, "Error: no such object [%s].\n", qPrintable(objectName));
2536             return entry;
2537         }
2538     } else {
2539         // exported id
2540         QString serialized = read_ksentry_file(name);
2541         entry              = QCA::KeyStoreEntry(serialized);
2542         if (entry.isNull()) {
2543             if (!nopassiveerror)
2544                 fprintf(stderr, "Error: invalid/unknown entry [%s].\n", qPrintable(name));
2545             return entry;
2546         }
2547     }
2548 
2549     return entry;
2550 }
2551 
2552 static QCA::PrivateKey get_K(const QString &name)
2553 {
2554     QCA::PrivateKey key;
2555 
2556     int n = name.indexOf(QLatin1Char(':'));
2557     if (n != -1) {
2558         fprintf(stderr, "Error: cannot use store:obj notation for raw private keys.\n");
2559         return key;
2560     }
2561 
2562     if (is_pem_file(name))
2563         key = QCA::PrivateKey::fromPEMFile(name);
2564     else
2565         key = QCA::PrivateKey::fromDER(read_der_file(name));
2566     if (key.isNull()) {
2567         fprintf(stderr, "Error: unable to read/process private key file.\n");
2568         return key;
2569     }
2570 
2571     return key;
2572 }
2573 
2574 static QCA::Certificate get_C(const QString &name)
2575 {
2576     QCA::KeyStoreEntry entry = get_E(name, true);
2577     if (!entry.isNull()) {
2578         if (entry.type() != QCA::KeyStoreEntry::TypeCertificate) {
2579             fprintf(stderr, "Error: entry is not a certificate.\n");
2580             return QCA::Certificate();
2581         }
2582         return entry.certificate();
2583     }
2584 
2585     if (!QCA::isSupported("cert")) {
2586         fprintf(stderr, "Error: need 'cert' feature.\n");
2587         return QCA::Certificate();
2588     }
2589 
2590     // try file
2591     QCA::Certificate cert;
2592     if (is_pem_file(name))
2593         cert = QCA::Certificate::fromPEMFile(name);
2594     else
2595         cert = QCA::Certificate::fromDER(read_der_file(name));
2596     if (cert.isNull()) {
2597         fprintf(stderr, "Error: unable to read/process certificate file.\n");
2598         return cert;
2599     }
2600 
2601     return cert;
2602 }
2603 
2604 static QCA::KeyBundle get_X(const QString &name)
2605 {
2606     QCA::KeyStoreEntry entry = get_E(name, true);
2607     if (!entry.isNull()) {
2608         if (entry.type() != QCA::KeyStoreEntry::TypeKeyBundle) {
2609             fprintf(stderr, "Error: entry is not a keybundle.\n");
2610             return QCA::KeyBundle();
2611         }
2612         return entry.keyBundle();
2613     }
2614 
2615     if (!QCA::isSupported("pkcs12")) {
2616         fprintf(stderr, "Error: need 'pkcs12' feature.\n");
2617         return QCA::KeyBundle();
2618     }
2619 
2620     // try file
2621     QCA::KeyBundle key = QCA::KeyBundle::fromFile(name);
2622     if (key.isNull()) {
2623         fprintf(stderr, "Error: unable to read/process keybundle file.\n");
2624         return key;
2625     }
2626 
2627     return key;
2628 }
2629 
2630 static QCA::PGPKey get_P(const QString &name)
2631 {
2632     QCA::KeyStoreEntry entry = get_E(name, true);
2633     if (!entry.isNull()) {
2634         if (entry.type() != QCA::KeyStoreEntry::TypePGPPublicKey &&
2635             entry.type() != QCA::KeyStoreEntry::TypePGPSecretKey) {
2636             fprintf(stderr, "Error: entry is not a pgp public key.\n");
2637             return QCA::PGPKey();
2638         }
2639         return entry.pgpPublicKey();
2640     }
2641 
2642     // try file
2643     QCA::PGPKey key = QCA::PGPKey::fromFile(name);
2644     if (key.isNull()) {
2645         fprintf(stderr, "Error: unable to read/process pgp key file.\n");
2646         return key;
2647     }
2648 
2649     return key;
2650 }
2651 
2652 static QPair<QCA::PGPKey, QCA::PGPKey> get_S(const QString &name, bool noerror = false)
2653 {
2654     QPair<QCA::PGPKey, QCA::PGPKey> key;
2655     QCA::KeyStoreEntry              entry = get_E(name, true);
2656     if (!entry.isNull()) {
2657         if (entry.type() != QCA::KeyStoreEntry::TypePGPSecretKey) {
2658             if (!noerror)
2659                 fprintf(stderr, "Error: entry is not a pgp secret key.\n");
2660             return key;
2661         }
2662 
2663         key.first  = entry.pgpSecretKey();
2664         key.second = entry.pgpPublicKey();
2665         return key;
2666     }
2667     return key;
2668 }
2669 
2670 static void usage()
2671 {
2672     printf("%s: simple qca utility\n", APPNAME);
2673     printf("usage: %s (options) [command]\n", EXENAME);
2674     printf(" options: --pass=x, --newpass=x, --nonroots=x, --roots=x, --nosys,\n");
2675     printf("          --noprompt, --ordered, --debug, --log-file=x, --log-level=n,\n");
2676     printf("          --nobundle\n");
2677     printf("\n");
2678     printf(" help|--help|-h                        This help text\n");
2679     printf(" version|--version|-v                  Print version information\n");
2680     printf(" plugins                               List available plugins\n");
2681     printf(" config [command]\n");
2682     printf("   save [provider]                     Save default provider config\n");
2683     printf("   edit [provider]                     Edit provider config\n");
2684     printf(" key [command]\n");
2685     printf("   make rsa|dsa [bits]                 Create a key pair\n");
2686     printf("   changepass [K]                      Add/change/remove passphrase of a key\n");
2687     printf(" cert [command]\n");
2688     printf("   makereq [K]                         Create certificate request (CSR)\n");
2689     printf("   makeself [K]                        Create self-signed certificate\n");
2690     printf("   makereqadv [K]                      Advanced version of 'makereq'\n");
2691     printf("   makeselfadv [K]                     Advanced version of 'makeself'\n");
2692     printf("   validate [C]                        Validate certificate\n");
2693     printf(" keybundle [command]\n");
2694     printf("   make [K] [C]                        Create a keybundle\n");
2695     printf("   extract [X]                         Extract certificate(s) and key\n");
2696     printf("   changepass [X]                      Change passphrase of a keybundle\n");
2697     printf(" keystore [command]\n");
2698     printf("   list-stores                         List all available keystores\n");
2699     printf("   list [storeName]                    List content of a keystore\n");
2700     printf("   monitor                             Monitor for keystore availability\n");
2701     printf("   export [E]                          Export a keystore entry's content\n");
2702     printf("   exportref [E]                       Export a keystore entry reference\n");
2703     printf("   addkb [storeName] [cert.p12]        Add a keybundle into a keystore\n");
2704     printf("   addpgp [storeName] [key.asc]        Add a PGP key into a keystore\n");
2705     printf("   remove [E]                          Remove an object from a keystore\n");
2706     printf(" show [command]\n");
2707     printf("   cert [C]                            Examine a certificate\n");
2708     printf("   req [req.pem]                       Examine a certificate request (CSR)\n");
2709     printf("   crl [crl.pem]                       Examine a certificate revocation list\n");
2710     printf("   kb [X]                              Examine a keybundle\n");
2711     printf("   pgp [P|S]                           Examine a PGP key\n");
2712     printf(" message [command]\n");
2713     printf("   sign pgp|pgpdetach|smime [X|S]      Sign a message\n");
2714     printf("   encrypt pgp|smime [C|P]             Encrypt a message\n");
2715     printf("   signencrypt [S] [P]                 PGP sign & encrypt a message\n");
2716     printf("   verify pgp|smime                    Verify a message\n");
2717     printf("   decrypt pgp|smime ((X) ...)         Decrypt a message (S/MIME needs X)\n");
2718     printf("   exportcerts                         Export certs from S/MIME message\n");
2719     printf("\n");
2720     printf("Object types: K = private key, C = certificate, X = key bundle,\n");
2721     printf("  P = PGP public key, S = PGP secret key, E = generic entry\n");
2722     printf("\n");
2723     printf("An object must be either a filename or a keystore reference (\"store:obj\").\n");
2724     printf("\n");
2725     printf("Log level is from 0 (quiet) to 8 (debug)\n");
2726     printf("\n");
2727 }
2728 
2729 int main(int argc, char **argv)
2730 {
2731     QCA::Initializer qcaInit;
2732     QCoreApplication app(argc, argv);
2733     QFile            logFile;
2734     QTextStream      logStream(stderr);
2735     StreamLogger     streamLogger(logStream);
2736 
2737     QStringList args;
2738     for (int n = 1; n < argc; ++n)
2739         args.append(QString::fromLocal8Bit(argv[n]));
2740 
2741     if (args.count() < 1) {
2742         usage();
2743         return 1;
2744     }
2745 
2746     bool             have_pass    = false;
2747     bool             have_newpass = false;
2748     QCA::SecureArray pass, newpass;
2749     bool             allowprompt = true;
2750     bool             ordered     = false;
2751     bool             debug       = false;
2752     bool             nosys       = false;
2753     bool             nobundle    = false;
2754     QString          rootsFile, nonRootsFile;
2755 
2756     for (int n = 0; n < args.count(); ++n) {
2757         QString s = args[n];
2758         if (!s.startsWith(QLatin1String("--")))
2759             continue;
2760         QString var;
2761         QString val;
2762         int     x = s.indexOf(QLatin1Char('='));
2763         if (x != -1) {
2764             var = s.mid(2, x - 2);
2765             val = s.mid(x + 1);
2766         } else {
2767             var = s.mid(2);
2768         }
2769 
2770         bool known = true;
2771 
2772         if (var == QLatin1String("pass")) {
2773             have_pass = true;
2774             pass      = val.toUtf8();
2775         } else if (var == QLatin1String("newpass")) {
2776             have_newpass = true;
2777             newpass      = val.toUtf8();
2778         } else if (var == QLatin1String("log-file")) {
2779             logFile.setFileName(val);
2780             logFile.open(QIODevice::Append | QIODevice::Text | QIODevice::Unbuffered);
2781             logStream.setDevice(&logFile);
2782         } else if (var == QLatin1String("log-level")) {
2783             QCA::logger()->setLevel((QCA::Logger::Severity)val.toInt());
2784         } else if (var == QLatin1String("noprompt"))
2785             allowprompt = false;
2786         else if (var == QLatin1String("ordered"))
2787             ordered = true;
2788         else if (var == QLatin1String("debug"))
2789             debug = true;
2790         else if (var == QLatin1String("roots"))
2791             rootsFile = val;
2792         else if (var == QLatin1String("nonroots"))
2793             nonRootsFile = val;
2794         else if (var == QLatin1String("nosys"))
2795             nosys = true;
2796         else if (var == QLatin1String("nobundle"))
2797             nobundle = true;
2798         else
2799             known = false;
2800 
2801         if (known) {
2802             args.removeAt(n);
2803             --n; // adjust position
2804         }
2805     }
2806 
2807     // help
2808     if (args.isEmpty() || args[0] == QLatin1String("help") || args[0] == QLatin1String("--help") ||
2809         args[0] == QLatin1String("-h")) {
2810         usage();
2811         return 0;
2812     }
2813 
2814     // version
2815     if (args[0] == QLatin1String("version") || args[0] == QLatin1String("--version") ||
2816         args[0] == QLatin1String("-v")) {
2817         int ver = qcaVersion();
2818         int maj = (ver >> 16) & 0xff;
2819         int min = (ver >> 8) & 0xff;
2820         int bug = ver & 0xff;
2821         printf("%s version %s by Justin Karneges\n", APPNAME, VERSION);
2822         printf("Using QCA version %d.%d.%d\n", maj, min, bug);
2823         return 0;
2824     }
2825 
2826     // show plugins
2827     if (args[0] == QLatin1String("plugins")) {
2828         QStringList paths = QCA::pluginPaths();
2829         if (!paths.isEmpty()) {
2830             for (int n = 0; n < paths.count(); ++n) {
2831                 printf("  %s\n", qPrintable(QDir::toNativeSeparators(paths[n])));
2832             }
2833         } else
2834             printf("  (none)\n");
2835 
2836         QCA::ProviderList list = QCA::providers();
2837 
2838         if (debug)
2839             output_plugin_diagnostic_text();
2840 
2841         printf("Available Providers:\n");
2842         if (!list.isEmpty()) {
2843             for (int n = 0; n < list.count(); ++n) {
2844                 printf("  %s\n", qPrintable(list[n]->name()));
2845                 QString credit = list[n]->credit();
2846                 if (!credit.isEmpty()) {
2847                     QStringList lines = wrapstring(credit, 74);
2848                     foreach (const QString &s, lines)
2849                         printf("    %s\n", qPrintable(s));
2850                 }
2851                 if (debug) {
2852                     QStringList capabilities = list[n]->features();
2853                     foreach (const QString &capability, capabilities) {
2854                         printf("    *%s", qPrintable(capability));
2855                         if (!QCA::isSupported(qPrintable(capability), list[n]->name())) {
2856                             printf("(NOT supported) - bug");
2857                         }
2858                         printf("\n");
2859                     }
2860                 }
2861             }
2862         } else
2863             printf("  (none)\n");
2864 
2865         QCA::unloadAllPlugins();
2866 
2867         if (debug)
2868             output_plugin_diagnostic_text();
2869 
2870         return 0;
2871     }
2872 
2873     // config stuff
2874     if (args[0] == QLatin1String("config")) {
2875         if (args.count() < 2) {
2876             usage();
2877             return 1;
2878         }
2879 
2880         if (args[1] == QLatin1String("save")) {
2881             if (args.count() < 3) {
2882                 usage();
2883                 return 1;
2884             }
2885 
2886             QString        name = args[2];
2887             QCA::Provider *p    = QCA::findProvider(name);
2888             if (!p) {
2889                 fprintf(stderr, "Error: no such provider '%s'.\n", qPrintable(name));
2890                 return 1;
2891             }
2892 
2893             QVariantMap map1 = p->defaultConfig();
2894             if (map1.isEmpty()) {
2895                 fprintf(stderr, "Error: provider does not support configuration.\n");
2896                 return 1;
2897             }
2898 
2899             // set and save
2900             QCA::setProviderConfig(name, map1);
2901             QCA::saveProviderConfig(name);
2902             printf("Done.\n");
2903             return 0;
2904         } else if (args[1] == QLatin1String("edit")) {
2905             if (args.count() < 3) {
2906                 usage();
2907                 return 1;
2908             }
2909 
2910             QString name = args[2];
2911             if (!QCA::findProvider(name)) {
2912                 fprintf(stderr, "Error: no such provider '%s'.\n", qPrintable(name));
2913                 return 1;
2914             }
2915 
2916             QVariantMap map1 = QCA::getProviderConfig(name);
2917             if (map1.isEmpty()) {
2918                 fprintf(stderr, "Error: provider does not support configuration.\n");
2919                 return 1;
2920             }
2921 
2922             printf("Editing configuration for %s ...\n", qPrintable(name));
2923             printf("Note: to clear a string entry, type whitespace and press enter.\n");
2924 
2925             map1 = provider_config_edit(map1);
2926             if (map1.isEmpty())
2927                 return 1;
2928 
2929             // set and save
2930             QCA::setProviderConfig(name, map1);
2931             QCA::saveProviderConfig(name);
2932             printf("Done.\n");
2933             return 0;
2934         } else {
2935             usage();
2936             return 1;
2937         }
2938     }
2939 
2940     // enable console passphrase prompt
2941     PassphrasePromptThread passphrasePrompt;
2942     if (!allowprompt)
2943         passphrasePrompt.pp->allowPrompt = false;
2944     if (have_pass)
2945         passphrasePrompt.pp->setExplicitPassword(pass);
2946 
2947     if (args[0] == QLatin1String("key")) {
2948         if (args.count() < 2) {
2949             usage();
2950             return 1;
2951         }
2952 
2953         if (args[1] == QLatin1String("make")) {
2954             if (args.count() < 4) {
2955                 usage();
2956                 return 1;
2957             }
2958 
2959             bool genrsa;
2960             int  bits;
2961 
2962             if (args[2] == QLatin1String("rsa")) {
2963                 if (!QCA::isSupported("rsa")) {
2964                     fprintf(stderr, "Error: need 'rsa' feature.\n");
2965                     return 1;
2966                 }
2967 
2968                 genrsa = true;
2969                 bits   = args[3].toInt();
2970                 if (bits < 512) {
2971                     fprintf(stderr, "Error: RSA bits must be at least 512.\n");
2972                     return 1;
2973                 }
2974             } else if (args[2] == QLatin1String("dsa")) {
2975                 if (!QCA::isSupported("dsa")) {
2976                     fprintf(stderr, "Error: need 'dsa' feature.\n");
2977                     return 1;
2978                 }
2979 
2980                 if (!QCA::isSupported("dlgroup")) {
2981                     fprintf(stderr, "Error: need 'dlgroup' feature.\n");
2982                     return 1;
2983                 }
2984 
2985                 genrsa = false;
2986                 bits   = args[3].toInt();
2987                 if (bits != 512 && bits != 768 && bits != 1024) {
2988                     fprintf(stderr, "Error: DSA bits must be 512, 768, or 1024.\n");
2989                     return 1;
2990                 }
2991             } else {
2992                 usage();
2993                 return 1;
2994             }
2995 
2996             if (!allowprompt && !have_newpass) {
2997                 fprintf(stderr, "Error: no passphrase specified (use '--newpass=' for none).\n");
2998                 return 1;
2999             }
3000 
3001             QCA::PrivateKey priv;
3002             QString         pubFileName, privFileName;
3003 
3004             if (genrsa) {
3005                 // note: third arg is bogus, doesn't apply to RSA
3006                 priv         = AnimatedKeyGen::makeKey(QCA::PKey::RSA, bits, QCA::DSA_512);
3007                 pubFileName  = QStringLiteral("rsapub.pem");
3008                 privFileName = QStringLiteral("rsapriv.pem");
3009             } else // dsa
3010             {
3011                 QCA::DLGroupSet set;
3012                 if (bits == 512)
3013                     set = QCA::DSA_512;
3014                 else if (bits == 768)
3015                     set = QCA::DSA_768;
3016                 else // 1024
3017                     set = QCA::DSA_1024;
3018 
3019                 // note: second arg is bogus, doesn't apply to DSA
3020                 priv         = AnimatedKeyGen::makeKey(QCA::PKey::DSA, 0, set);
3021                 pubFileName  = QStringLiteral("dsapub.pem");
3022                 privFileName = QStringLiteral("dsapriv.pem");
3023             }
3024 
3025             if (priv.isNull()) {
3026                 fprintf(stderr, "Error: unable to generate key.\n");
3027                 return 1;
3028             }
3029 
3030             QCA::PublicKey pub = priv.toPublicKey();
3031 
3032             // prompt for new passphrase if necessary
3033             if (!have_newpass) {
3034                 while (!promptForNewPassphrase(&newpass)) { }
3035                 have_newpass = true;
3036             }
3037 
3038             if (pub.toPEMFile(pubFileName))
3039                 printf("Public key saved to %s\n", qPrintable(pubFileName));
3040             else {
3041                 fprintf(stderr, "Error: can't encode/write %s\n", qPrintable(pubFileName));
3042                 return 1;
3043             }
3044 
3045             bool ok;
3046             if (!newpass.isEmpty())
3047                 ok = priv.toPEMFile(privFileName, newpass);
3048             else
3049                 ok = priv.toPEMFile(privFileName);
3050             if (ok)
3051                 printf("Private key saved to %s\n", qPrintable(privFileName));
3052             else {
3053                 fprintf(stderr, "Error: can't encode/write %s\n", qPrintable(privFileName));
3054                 return 1;
3055             }
3056         } else if (args[1] == QLatin1String("changepass")) {
3057             if (args.count() < 3) {
3058                 usage();
3059                 return 1;
3060             }
3061 
3062             QCA::PrivateKey priv = get_K(args[2]);
3063             if (priv.isNull())
3064                 return 1;
3065 
3066             if (!allowprompt && !have_newpass) {
3067                 fprintf(stderr, "Error: no passphrase specified (use '--newpass=' for none).\n");
3068                 return 1;
3069             }
3070 
3071             // prompt for new passphrase if necessary
3072             if (!have_newpass) {
3073                 while (!promptForNewPassphrase(&newpass)) { }
3074                 have_newpass = true;
3075             }
3076 
3077             QString out;
3078             if (!newpass.isEmpty())
3079                 out = priv.toPEM(newpass);
3080             else
3081                 out = priv.toPEM();
3082             if (!out.isEmpty())
3083                 printf("%s", qPrintable(out));
3084             else {
3085                 fprintf(stderr, "Error: can't encode key.\n");
3086                 return 1;
3087             }
3088         } else {
3089             usage();
3090             return 1;
3091         }
3092     } else if (args[0] == QLatin1String("cert")) {
3093         if (args.count() < 2) {
3094             usage();
3095             return 1;
3096         }
3097 
3098         if (args[1] == QLatin1String("makereq") || args[1] == QLatin1String("makereqadv")) {
3099             if (args.count() < 3) {
3100                 usage();
3101                 return 1;
3102             }
3103 
3104             if (!QCA::isSupported("csr")) {
3105                 fprintf(stderr, "Error: need 'csr' feature.\n");
3106                 return 1;
3107             }
3108 
3109             QCA::PrivateKey priv = get_K(args[2]);
3110             if (priv.isNull())
3111                 return 1;
3112 
3113             printf("\n");
3114 
3115             bool advanced = (args[1] == QLatin1String("makereqadv")) ? true : false;
3116 
3117             QCA::CertificateOptions opts = promptForCertAttributes(advanced, true);
3118             QCA::CertificateRequest req(opts, priv);
3119 
3120             QString reqname = QStringLiteral("certreq.pem");
3121             if (req.toPEMFile(reqname))
3122                 printf("Certificate request saved to %s\n", qPrintable(reqname));
3123             else {
3124                 fprintf(stderr, "Error: can't encode/write %s\n", qPrintable(reqname));
3125                 return 1;
3126             }
3127         } else if (args[1] == QLatin1String("makeself") || args[1] == QLatin1String("makeselfadv")) {
3128             if (args.count() < 3) {
3129                 usage();
3130                 return 1;
3131             }
3132 
3133             if (!QCA::isSupported("cert")) {
3134                 fprintf(stderr, "Error: need 'cert' feature.\n");
3135                 return 1;
3136             }
3137 
3138             QCA::PrivateKey priv = get_K(args[2]);
3139             if (priv.isNull())
3140                 return 1;
3141 
3142             printf("\n");
3143 
3144             bool advanced = (args[1] == QLatin1String("makeselfadv")) ? true : false;
3145 
3146             QCA::CertificateOptions opts = promptForCertAttributes(advanced, false);
3147             QCA::Certificate        cert(opts, priv);
3148 
3149             QString certname = QStringLiteral("cert.pem");
3150             if (cert.toPEMFile(certname))
3151                 printf("Certificate saved to %s\n", qPrintable(certname));
3152             else {
3153                 fprintf(stderr, "Error: can't encode/write %s\n", qPrintable(certname));
3154                 return 1;
3155             }
3156         } else if (args[1] == QLatin1String("validate")) {
3157             if (args.count() < 3) {
3158                 usage();
3159                 return 1;
3160             }
3161 
3162             QCA::Certificate target = get_C(args[2]);
3163             if (target.isNull())
3164                 return 1;
3165 
3166             // get roots
3167             QCA::CertificateCollection roots;
3168             if (!nosys)
3169                 roots += QCA::systemStore();
3170             if (!rootsFile.isEmpty())
3171                 roots += QCA::CertificateCollection::fromFlatTextFile(rootsFile);
3172 
3173             // get nonroots
3174             QCA::CertificateCollection nonroots;
3175             if (!nonRootsFile.isEmpty())
3176                 nonroots = QCA::CertificateCollection::fromFlatTextFile(nonRootsFile);
3177 
3178             QCA::Validity v = target.validate(roots, nonroots);
3179             if (v == QCA::ValidityGood)
3180                 printf("Certificate is valid\n");
3181             else {
3182                 printf("Certificate is NOT valid: %s\n", qPrintable(validityToString(v)));
3183                 return 1;
3184             }
3185         } else {
3186             usage();
3187             return 1;
3188         }
3189     } else if (args[0] == QLatin1String("keybundle")) {
3190         if (args.count() < 2) {
3191             usage();
3192             return 1;
3193         }
3194 
3195         if (args[1] == QLatin1String("make")) {
3196             if (args.count() < 4) {
3197                 usage();
3198                 return 1;
3199             }
3200 
3201             if (!QCA::isSupported("pkcs12")) {
3202                 fprintf(stderr, "Error: need 'pkcs12' feature.\n");
3203                 return 1;
3204             }
3205 
3206             QCA::PrivateKey priv = get_K(args[2]);
3207             if (priv.isNull())
3208                 return 1;
3209 
3210             QCA::Certificate cert = get_C(args[3]);
3211             if (cert.isNull())
3212                 return 1;
3213 
3214             // get roots
3215             QCA::CertificateCollection roots;
3216             if (!nosys)
3217                 roots += QCA::systemStore();
3218             if (!rootsFile.isEmpty())
3219                 roots += QCA::CertificateCollection::fromFlatTextFile(rootsFile);
3220 
3221             // get nonroots
3222             QCA::CertificateCollection nonroots;
3223             if (!nonRootsFile.isEmpty())
3224                 nonroots = QCA::CertificateCollection::fromFlatTextFile(nonRootsFile);
3225 
3226             QList<QCA::Certificate> issuer_pool = roots.certificates() + nonroots.certificates();
3227 
3228             QCA::CertificateChain chain;
3229             chain += cert;
3230             chain = chain.complete(issuer_pool);
3231 
3232             QCA::KeyBundle key;
3233             key.setName(chain.primary().commonName());
3234             key.setCertificateChainAndKey(chain, priv);
3235 
3236             if (!allowprompt && !have_newpass) {
3237                 fprintf(stderr, "Error: no passphrase specified (use '--newpass=' for none).\n");
3238                 return 1;
3239             }
3240 
3241             // prompt for new passphrase if necessary
3242             if (!have_newpass) {
3243                 while (!promptForNewPassphrase(&newpass)) { }
3244                 have_newpass = true;
3245             }
3246 
3247             if (newpass.isEmpty()) {
3248                 fprintf(stderr, "Error: keybundles cannot have empty passphrases.\n");
3249                 return 1;
3250             }
3251 
3252             QString newFileName = QStringLiteral("cert.p12");
3253 
3254             if (key.toFile(newFileName, newpass))
3255                 printf("Keybundle saved to %s\n", qPrintable(newFileName));
3256             else {
3257                 fprintf(stderr, "Error: can't encode keybundle.\n");
3258                 return 1;
3259             }
3260         } else if (args[1] == QLatin1String("extract")) {
3261             if (args.count() < 3) {
3262                 usage();
3263                 return 1;
3264             }
3265 
3266             QCA::KeyBundle key = get_X(args[2]);
3267             if (key.isNull())
3268                 return 1;
3269 
3270             QCA::PrivateKey priv        = key.privateKey();
3271             bool            export_priv = priv.canExport();
3272 
3273             if (export_priv) {
3274                 fprintf(stderr, "You will need to create a passphrase for the extracted private key.\n");
3275 
3276                 if (!allowprompt && !have_newpass) {
3277                     fprintf(stderr, "Error: no passphrase specified (use '--newpass=' for none).\n");
3278                     return 1;
3279                 }
3280 
3281                 // prompt for new passphrase if necessary
3282                 if (!have_newpass) {
3283                     while (!promptForNewPassphrase(&newpass)) { }
3284                     have_newpass = true;
3285                 }
3286             }
3287 
3288             printf("Certs: (first is primary)\n");
3289             QCA::CertificateChain chain = key.certificateChain();
3290             for (int n = 0; n < chain.count(); ++n)
3291                 printf("%s", qPrintable(chain[n].toPEM()));
3292             printf("Private Key:\n");
3293             if (export_priv) {
3294                 QString out;
3295                 if (!newpass.isEmpty())
3296                     out = priv.toPEM(newpass);
3297                 else
3298                     out = priv.toPEM();
3299                 printf("%s", qPrintable(out));
3300             } else {
3301                 printf("(Key is not exportable)\n");
3302             }
3303         } else if (args[1] == QLatin1String("changepass")) {
3304             if (args.count() < 3) {
3305                 usage();
3306                 return 1;
3307             }
3308 
3309             QCA::KeyBundle key = get_X(args[2]);
3310             if (key.isNull())
3311                 return 1;
3312 
3313             if (!key.privateKey().canExport()) {
3314                 fprintf(stderr, "Error: private key not exportable.\n");
3315                 return 1;
3316             }
3317 
3318             if (!allowprompt && !have_newpass) {
3319                 fprintf(stderr, "Error: no passphrase specified (use '--newpass=' for none).\n");
3320                 return 1;
3321             }
3322 
3323             // prompt for new passphrase if necessary
3324             if (!have_newpass) {
3325                 while (!promptForNewPassphrase(&newpass)) { }
3326                 have_newpass = true;
3327             }
3328 
3329             if (newpass.isEmpty()) {
3330                 fprintf(stderr, "Error: keybundles cannot have empty passphrases.\n");
3331                 return 1;
3332             }
3333 
3334             QFileInfo fi(args[2]);
3335             QString   newFileName = fi.baseName() + QStringLiteral("_new.p12");
3336 
3337             if (key.toFile(newFileName, newpass))
3338                 printf("Keybundle saved to %s\n", qPrintable(newFileName));
3339             else {
3340                 fprintf(stderr, "Error: can't encode keybundle.\n");
3341                 return 1;
3342             }
3343         } else {
3344             usage();
3345             return 1;
3346         }
3347     } else if (args[0] == QLatin1String("keystore")) {
3348         if (args.count() < 2) {
3349             usage();
3350             return 1;
3351         }
3352 
3353         if (args[1] == QLatin1String("list-stores")) {
3354             ksm_start_and_wait();
3355 
3356             QCA::KeyStoreManager ksm;
3357             QStringList          storeList = ksm.keyStores();
3358 
3359             for (int n = 0; n < storeList.count(); ++n) {
3360                 QCA::KeyStore ks(storeList[n], &ksm);
3361                 QString       type = kstype_to_string(ks.type());
3362                 printf("%s %s [%s]\n", qPrintable(type), qPrintable(idHash(ks.id())), qPrintable(ks.name()));
3363             }
3364 
3365             if (debug)
3366                 output_keystore_diagnostic_text();
3367         } else if (args[1] == QLatin1String("list")) {
3368             if (args.count() < 3) {
3369                 usage();
3370                 return 1;
3371             }
3372 
3373             ksm_start_and_wait();
3374 
3375             QCA::KeyStoreManager ksm;
3376             QCA::KeyStore        store(getKeyStore(args[2]), &ksm);
3377             if (!store.isValid()) {
3378                 if (debug)
3379                     output_keystore_diagnostic_text();
3380 
3381                 fprintf(stderr, "Error: no such store\n");
3382                 return 1;
3383             }
3384 
3385             QList<QCA::KeyStoreEntry> list = store.entryList();
3386             for (int n = 0; n < list.count(); ++n) {
3387                 QCA::KeyStoreEntry i    = list[n];
3388                 QString            type = ksentrytype_to_string(i.type());
3389                 printf("%s %s [%s]\n", qPrintable(type), qPrintable(idHash(i.id())), qPrintable(i.name()));
3390             }
3391 
3392             if (debug)
3393                 output_keystore_diagnostic_text();
3394         } else if (args[1] == QLatin1String("monitor")) {
3395             KeyStoreMonitor::monitor();
3396 
3397             if (debug)
3398                 output_keystore_diagnostic_text();
3399         } else if (args[1] == QLatin1String("export")) {
3400             if (args.count() < 3) {
3401                 usage();
3402                 return 1;
3403             }
3404 
3405             QCA::KeyStoreEntry entry = get_E(args[2]);
3406             if (entry.isNull())
3407                 return 1;
3408 
3409             if (entry.type() == QCA::KeyStoreEntry::TypeCertificate)
3410                 printf("%s", qPrintable(entry.certificate().toPEM()));
3411             else if (entry.type() == QCA::KeyStoreEntry::TypeCRL)
3412                 printf("%s", qPrintable(entry.crl().toPEM()));
3413             else if (entry.type() == QCA::KeyStoreEntry::TypePGPPublicKey ||
3414                      entry.type() == QCA::KeyStoreEntry::TypePGPSecretKey)
3415                 printf("%s", qPrintable(entry.pgpPublicKey().toString()));
3416             else if (entry.type() == QCA::KeyStoreEntry::TypeKeyBundle) {
3417                 fprintf(stderr, "Error: use 'keybundle extract' command instead.\n");
3418                 return 1;
3419             } else {
3420                 fprintf(stderr, "Error: cannot export type '%d'.\n", entry.type());
3421                 return 1;
3422             }
3423         } else if (args[1] == QLatin1String("exportref")) {
3424             if (args.count() < 3) {
3425                 usage();
3426                 return 1;
3427             }
3428 
3429             QCA::KeyStoreEntry entry = get_E(args[2]);
3430             if (entry.isNull())
3431                 return 1;
3432             printf("%s", make_ksentry_string(entry.toString()).toUtf8().data());
3433         } else if (args[1] == QLatin1String("addkb")) {
3434             if (args.count() < 4) {
3435                 usage();
3436                 return 1;
3437             }
3438 
3439             ksm_start_and_wait();
3440 
3441             QCA::KeyStoreManager ksm;
3442             QCA::KeyStore        store(getKeyStore(args[2]), &ksm);
3443             if (!store.isValid()) {
3444                 fprintf(stderr, "Error: no such store\n");
3445                 return 1;
3446             }
3447 
3448             QCA::KeyBundle key = get_X(args[3]);
3449             if (key.isNull())
3450                 return 1;
3451 
3452             if (!store.writeEntry(key).isEmpty())
3453                 printf("Entry written.\n");
3454             else {
3455                 fprintf(stderr, "Error: unable to write entry.\n");
3456                 return 1;
3457             }
3458         } else if (args[1] == QLatin1String("addpgp")) {
3459             if (args.count() < 4) {
3460                 usage();
3461                 return 1;
3462             }
3463 
3464             if (!QCA::isSupported("openpgp")) {
3465                 fprintf(stderr, "Error: need 'openpgp' feature.\n");
3466                 return 1;
3467             }
3468 
3469             ksm_start_and_wait();
3470 
3471             QCA::KeyStoreManager ksm;
3472             QCA::KeyStore        store(getKeyStore(args[2]), &ksm);
3473             if (!store.isValid()) {
3474                 fprintf(stderr, "Error: no such store\n");
3475                 return 1;
3476             }
3477 
3478             QCA::PGPKey pub = QCA::PGPKey::fromFile(args[3]);
3479             if (pub.isNull())
3480                 return 1;
3481 
3482             if (!store.writeEntry(pub).isEmpty())
3483                 printf("Entry written.\n");
3484             else {
3485                 fprintf(stderr, "Error: unable to write entry.\n");
3486                 return 1;
3487             }
3488         } else if (args[1] == QLatin1String("remove")) {
3489             if (args.count() < 3) {
3490                 usage();
3491                 return 1;
3492             }
3493 
3494             QCA::KeyStoreEntry entry = get_E(args[2]);
3495             if (entry.isNull())
3496                 return 1;
3497 
3498             QCA::KeyStoreManager ksm;
3499             QCA::KeyStore        store(entry.storeId(), &ksm);
3500             if (!store.isValid()) {
3501                 fprintf(stderr, "Error: no such store\n");
3502                 return 1;
3503             }
3504 
3505             if (store.removeEntry(entry.id()))
3506                 printf("Entry removed.\n");
3507             else {
3508                 fprintf(stderr, "Error: unable to remove entry.\n");
3509                 return 1;
3510             }
3511         } else {
3512             usage();
3513             return 1;
3514         }
3515     } else if (args[0] == QLatin1String("show")) {
3516         if (args.count() < 2) {
3517             usage();
3518             return 1;
3519         }
3520 
3521         if (args[1] == QLatin1String("cert")) {
3522             if (args.count() < 3) {
3523                 usage();
3524                 return 1;
3525             }
3526 
3527             QCA::Certificate cert = get_C(args[2]);
3528             if (cert.isNull())
3529                 return 1;
3530 
3531             print_cert(cert, ordered);
3532         } else if (args[1] == QLatin1String("req")) {
3533             if (args.count() < 3) {
3534                 usage();
3535                 return 1;
3536             }
3537 
3538             if (!QCA::isSupported("csr")) {
3539                 fprintf(stderr, "Error: need 'csr' feature.\n");
3540                 return 1;
3541             }
3542 
3543             QCA::CertificateRequest req(args[2]);
3544             if (req.isNull()) {
3545                 fprintf(stderr, "Error: can't read/process certificate request file.\n");
3546                 return 1;
3547             }
3548 
3549             print_certreq(req, ordered);
3550         } else if (args[1] == QLatin1String("crl")) {
3551             if (args.count() < 3) {
3552                 usage();
3553                 return 1;
3554             }
3555 
3556             if (!QCA::isSupported("crl")) {
3557                 fprintf(stderr, "Error: need 'crl' feature.\n");
3558                 return 1;
3559             }
3560 
3561             QCA::CRL crl;
3562             if (is_pem_file(args[2]))
3563                 crl = QCA::CRL::fromPEMFile(args[2]);
3564             else
3565                 crl = QCA::CRL::fromDER(read_der_file(args[2]));
3566             if (crl.isNull()) {
3567                 fprintf(stderr, "Error: unable to read/process CRL file.\n");
3568                 return 1;
3569             }
3570 
3571             print_crl(crl, ordered);
3572         } else if (args[1] == QLatin1String("kb")) {
3573             if (args.count() < 3) {
3574                 usage();
3575                 return 1;
3576             }
3577 
3578             QCA::KeyBundle key = get_X(args[2]);
3579             if (key.isNull())
3580                 return 1;
3581 
3582             printf("Keybundle contains %d certificates.  Displaying primary:\n", int(key.certificateChain().count()));
3583             print_cert(key.certificateChain().primary(), ordered);
3584         } else if (args[1] == QLatin1String("pgp")) {
3585             if (args.count() < 3) {
3586                 usage();
3587                 return 1;
3588             }
3589 
3590             // try for secret key, then try public key
3591             QCA::PGPKey key = get_S(args[2], true).first;
3592             if (key.isNull()) {
3593                 key = get_P(args[2]);
3594                 if (key.isNull())
3595                     return 1;
3596             }
3597 
3598             print_pgp(key);
3599         } else {
3600             usage();
3601             return 1;
3602         }
3603     } else if (args[0] == QLatin1String("message")) {
3604         if (args.count() < 2) {
3605             usage();
3606             return 1;
3607         }
3608 
3609         if (args[1] == QLatin1String("sign")) {
3610             if (args.count() < 4) {
3611                 usage();
3612                 return 1;
3613             }
3614 
3615             QCA::SecureMessageSystem    *sms;
3616             QCA::SecureMessageKey        skey;
3617             QCA::SecureMessage::SignMode mode;
3618             bool                         pgp = false;
3619 
3620             if (args[2] == QLatin1String("pgp")) {
3621                 if (!QCA::isSupported("openpgp")) {
3622                     fprintf(stderr, "Error: need 'openpgp' feature.\n");
3623                     return 1;
3624                 }
3625 
3626                 QPair<QCA::PGPKey, QCA::PGPKey> key = get_S(args[3]);
3627                 if (key.first.isNull())
3628                     return 1;
3629 
3630                 sms = new QCA::OpenPGP;
3631                 skey.setPGPSecretKey(key.first);
3632                 mode = QCA::SecureMessage::Clearsign;
3633                 pgp  = true;
3634             } else if (args[2] == QLatin1String("pgpdetach")) {
3635                 if (!QCA::isSupported("openpgp")) {
3636                     fprintf(stderr, "Error: need 'openpgp' feature.\n");
3637                     return 1;
3638                 }
3639 
3640                 QPair<QCA::PGPKey, QCA::PGPKey> key = get_S(args[3]);
3641                 if (key.first.isNull())
3642                     return 1;
3643 
3644                 sms = new QCA::OpenPGP;
3645                 skey.setPGPSecretKey(key.first);
3646                 mode = QCA::SecureMessage::Detached;
3647                 pgp  = true;
3648             } else if (args[2] == QLatin1String("smime")) {
3649                 if (!QCA::isSupported("cms")) {
3650                     fprintf(stderr, "Error: need 'cms' feature.\n");
3651                     return 1;
3652                 }
3653 
3654                 QCA::KeyBundle key = get_X(args[3]);
3655                 if (key.isNull())
3656                     return 1;
3657 
3658                 // get nonroots
3659                 QCA::CertificateCollection nonroots;
3660                 if (!nonRootsFile.isEmpty())
3661                     nonroots = QCA::CertificateCollection::fromFlatTextFile(nonRootsFile);
3662 
3663                 QList<QCA::Certificate> issuer_pool = nonroots.certificates();
3664 
3665                 QCA::CertificateChain chain = key.certificateChain();
3666                 chain                       = chain.complete(issuer_pool);
3667 
3668                 sms = new QCA::CMS;
3669                 skey.setX509CertificateChain(chain);
3670                 skey.setX509PrivateKey(key.privateKey());
3671                 mode = QCA::SecureMessage::Detached;
3672             } else {
3673                 usage();
3674                 return 1;
3675             }
3676 
3677             // read input data from stdin all at once
3678             QByteArray plain;
3679             while (!feof(stdin)) {
3680                 QByteArray block(1024, 0);
3681                 int        n = fread(block.data(), 1, 1024, stdin);
3682                 if (n < 0)
3683                     break;
3684                 block.resize(n);
3685                 plain += block;
3686             }
3687 
3688             // smime envelope
3689             if (!pgp) {
3690                 QString text = add_cr(QString::fromUtf8(plain));
3691                 plain        = QString::fromLatin1(mime_signpart).arg(text).toUtf8();
3692             }
3693 
3694             QCA::SecureMessage *msg = new QCA::SecureMessage(sms);
3695             msg->setSigner(skey);
3696             // pgp should always be ascii
3697             if (pgp)
3698                 msg->setFormat(QCA::SecureMessage::Ascii);
3699             msg->setBundleSignerEnabled(!nobundle);
3700             msg->startSign(mode);
3701             msg->update(plain);
3702             msg->end();
3703             msg->waitForFinished(-1);
3704 
3705             if (debug) {
3706                 output_keystore_diagnostic_text();
3707                 output_message_diagnostic_text(msg);
3708             }
3709 
3710             if (!msg->success()) {
3711                 QString errstr = smErrorToString(msg->errorCode());
3712                 delete msg;
3713                 delete sms;
3714 
3715                 fprintf(stderr, "Error: unable to sign: %s\n", qPrintable(errstr));
3716                 return 1;
3717             }
3718 
3719             QString hashName = msg->hashName();
3720 
3721             QByteArray output;
3722             if (mode == QCA::SecureMessage::Detached)
3723                 output = msg->signature();
3724             else
3725                 output = msg->read();
3726 
3727             delete msg;
3728             delete sms;
3729 
3730             // smime envelope
3731             if (!pgp) {
3732                 QCA::Base64 enc;
3733                 enc.setLineBreaksEnabled(true);
3734                 enc.setLineBreaksColumn(76);
3735                 QString sigtext = add_cr(enc.arrayToString(output));
3736                 QString str     = QString::fromLatin1(mime_signed).arg(hashName, QString::fromUtf8(plain), sigtext);
3737                 output          = str.toUtf8();
3738             }
3739 
3740             printf("%s", output.data());
3741         } else if (args[1] == QLatin1String("encrypt")) {
3742             if (args.count() < 4) {
3743                 usage();
3744                 return 1;
3745             }
3746 
3747             QCA::SecureMessageSystem *sms;
3748             QCA::SecureMessageKey     skey;
3749             bool                      pgp = false;
3750 
3751             if (args[2] == QLatin1String("pgp")) {
3752                 if (!QCA::isSupported("openpgp")) {
3753                     fprintf(stderr, "Error: need 'openpgp' feature.\n");
3754                     return 1;
3755                 }
3756 
3757                 QCA::PGPKey key = get_P(args[3]);
3758                 if (key.isNull())
3759                     return 1;
3760 
3761                 sms = new QCA::OpenPGP;
3762                 skey.setPGPPublicKey(key);
3763                 pgp = true;
3764             } else if (args[2] == QLatin1String("smime")) {
3765                 if (!QCA::isSupported("cms")) {
3766                     fprintf(stderr, "Error: need 'cms' feature.\n");
3767                     return 1;
3768                 }
3769 
3770                 QCA::Certificate cert = get_C(args[3]);
3771                 if (cert.isNull())
3772                     return 1;
3773 
3774                 sms = new QCA::CMS;
3775                 skey.setX509CertificateChain(cert);
3776             } else {
3777                 usage();
3778                 return 1;
3779             }
3780 
3781             // read input data from stdin all at once
3782             QByteArray plain;
3783             while (!feof(stdin)) {
3784                 QByteArray block(1024, 0);
3785                 int        n = fread(block.data(), 1, 1024, stdin);
3786                 if (n < 0)
3787                     break;
3788                 block.resize(n);
3789                 plain += block;
3790             }
3791 
3792             QCA::SecureMessage *msg = new QCA::SecureMessage(sms);
3793             msg->setRecipient(skey);
3794             // pgp should always be ascii
3795             if (pgp)
3796                 msg->setFormat(QCA::SecureMessage::Ascii);
3797             msg->startEncrypt();
3798             msg->update(plain);
3799             msg->end();
3800             msg->waitForFinished(-1);
3801 
3802             if (debug) {
3803                 output_keystore_diagnostic_text();
3804                 output_message_diagnostic_text(msg);
3805             }
3806 
3807             if (!msg->success()) {
3808                 QString errstr = smErrorToString(msg->errorCode());
3809                 delete msg;
3810                 delete sms;
3811                 fprintf(stderr, "Error: unable to encrypt: %s\n", qPrintable(errstr));
3812                 return 1;
3813             }
3814 
3815             QByteArray output = msg->read();
3816             delete msg;
3817             delete sms;
3818 
3819             // smime envelope
3820             if (!pgp) {
3821                 QCA::Base64 enc;
3822                 enc.setLineBreaksEnabled(true);
3823                 enc.setLineBreaksColumn(76);
3824                 QString enctext = add_cr(enc.arrayToString(output));
3825                 QString str     = QString::fromLatin1(mime_enveloped).arg(enctext);
3826                 output          = str.toUtf8();
3827             }
3828 
3829             printf("%s", output.data());
3830         } else if (args[1] == QLatin1String("signencrypt")) {
3831             if (args.count() < 4) {
3832                 usage();
3833                 return 1;
3834             }
3835 
3836             if (!QCA::isSupported("openpgp")) {
3837                 fprintf(stderr, "Error: need 'openpgp' feature.\n");
3838                 return 1;
3839             }
3840 
3841             QCA::SecureMessageSystem *sms;
3842             QCA::SecureMessageKey     skey;
3843             QCA::SecureMessageKey     rkey;
3844 
3845             {
3846                 QPair<QCA::PGPKey, QCA::PGPKey> sec = get_S(args[2]);
3847                 if (sec.first.isNull())
3848                     return 1;
3849 
3850                 QCA::PGPKey pub = get_P(args[3]);
3851                 if (pub.isNull())
3852                     return 1;
3853 
3854                 sms = new QCA::OpenPGP;
3855                 skey.setPGPSecretKey(sec.first);
3856                 rkey.setPGPPublicKey(pub);
3857             }
3858 
3859             // read input data from stdin all at once
3860             QByteArray plain;
3861             while (!feof(stdin)) {
3862                 QByteArray block(1024, 0);
3863                 int        n = fread(block.data(), 1, 1024, stdin);
3864                 if (n < 0)
3865                     break;
3866                 block.resize(n);
3867                 plain += block;
3868             }
3869 
3870             QCA::SecureMessage *msg = new QCA::SecureMessage(sms);
3871             if (!msg->canSignAndEncrypt()) {
3872                 delete msg;
3873                 delete sms;
3874                 fprintf(stderr, "Error: cannot perform integrated sign and encrypt.\n");
3875                 return 1;
3876             }
3877 
3878             msg->setSigner(skey);
3879             msg->setRecipient(rkey);
3880             msg->setFormat(QCA::SecureMessage::Ascii);
3881             msg->startSignAndEncrypt();
3882             msg->update(plain);
3883             msg->end();
3884             msg->waitForFinished(-1);
3885 
3886             if (debug) {
3887                 output_keystore_diagnostic_text();
3888                 output_message_diagnostic_text(msg);
3889             }
3890 
3891             if (!msg->success()) {
3892                 QString errstr = smErrorToString(msg->errorCode());
3893                 delete msg;
3894                 delete sms;
3895                 fprintf(stderr, "Error: unable to sign and encrypt: %s\n", qPrintable(errstr));
3896                 return 1;
3897             }
3898 
3899             QByteArray output = msg->read();
3900             delete msg;
3901             delete sms;
3902 
3903             printf("%s", output.data());
3904         } else if (args[1] == QLatin1String("verify")) {
3905             if (args.count() < 3) {
3906                 usage();
3907                 return 1;
3908             }
3909 
3910             QCA::SecureMessageSystem *sms;
3911             bool                      pgp = false;
3912 
3913             if (args[2] == QLatin1String("pgp")) {
3914                 if (!QCA::isSupported("openpgp")) {
3915                     fprintf(stderr, "Error: need 'openpgp' feature.\n");
3916                     return 1;
3917                 }
3918 
3919                 sms = new QCA::OpenPGP;
3920                 pgp = true;
3921             } else if (args[2] == QLatin1String("smime")) {
3922                 if (!QCA::isSupported("cms")) {
3923                     fprintf(stderr, "Error: need 'cms' feature.\n");
3924                     return 1;
3925                 }
3926 
3927                 // get roots
3928                 QCA::CertificateCollection roots;
3929                 if (!nosys)
3930                     roots += QCA::systemStore();
3931                 if (!rootsFile.isEmpty())
3932                     roots += QCA::CertificateCollection::fromFlatTextFile(rootsFile);
3933 
3934                 // get intermediates and possible signers, in case
3935                 //   the message does not have them.
3936                 QCA::CertificateCollection nonroots;
3937                 if (!nonRootsFile.isEmpty())
3938                     nonroots += QCA::CertificateCollection::fromFlatTextFile(nonRootsFile);
3939 
3940                 sms = new QCA::CMS;
3941                 ((QCA::CMS *)sms)->setTrustedCertificates(roots);
3942                 ((QCA::CMS *)sms)->setUntrustedCertificates(nonroots);
3943             } else {
3944                 usage();
3945                 return 1;
3946             }
3947 
3948             QByteArray data, sig;
3949             QString    smime_text;
3950             {
3951                 // read input data from stdin all at once
3952                 QByteArray plain;
3953                 while (!feof(stdin)) {
3954                     QByteArray block(1024, 0);
3955                     int        n = fread(block.data(), 1, 1024, stdin);
3956                     if (n < 0)
3957                         break;
3958                     block.resize(n);
3959                     plain += block;
3960                 }
3961 
3962                 if (pgp) {
3963                     // pgp can be either a detached signature followed
3964                     //  by data, or an integrated message.
3965 
3966                     // detached signature?
3967                     if (plain.startsWith("-----BEGIN PGP SIGNATURE-----")) {
3968                         QByteArray footer = "-----END PGP SIGNATURE-----\n";
3969                         int        n      = plain.indexOf(footer);
3970                         if (n == -1) {
3971                             delete sms;
3972                             fprintf(stderr, "Error: pgp signature header, but no footer.\n");
3973                             return 1;
3974                         }
3975 
3976                         n += footer.length();
3977                         sig  = plain.mid(0, n);
3978                         data = plain.mid(n);
3979                     } else {
3980                         data = plain;
3981                     }
3982                 } else {
3983                     // smime envelope
3984                     QString in = QString::fromUtf8(plain);
3985                     in         = add_cr(in); // change the line endings?!
3986                     QString str, sigtext;
3987                     if (!open_mime_data_sig(in, &str, &sigtext)) {
3988                         fprintf(stderr, "Error: can't parse message file.\n");
3989                         return 1;
3990                     }
3991 
3992                     data       = str.toUtf8();
3993                     smime_text = str;
3994 
3995                     QCA::Base64 dec;
3996                     dec.setLineBreaksEnabled(true);
3997                     sig = dec.stringToArray(rem_cr(sigtext)).toByteArray();
3998                 }
3999             }
4000 
4001             QCA::SecureMessage *msg = new QCA::SecureMessage(sms);
4002             if (pgp)
4003                 msg->setFormat(QCA::SecureMessage::Ascii);
4004             msg->startVerify(sig);
4005             msg->update(data);
4006             msg->end();
4007             msg->waitForFinished(-1);
4008 
4009             if (debug) {
4010                 output_keystore_diagnostic_text();
4011                 output_message_diagnostic_text(msg);
4012             }
4013 
4014             if (!msg->success()) {
4015                 QString errstr = smErrorToString(msg->errorCode());
4016                 delete msg;
4017                 delete sms;
4018                 fprintf(stderr, "Error: verify failed: %s\n", qPrintable(errstr));
4019                 return 1;
4020             }
4021 
4022             QByteArray output;
4023             if (pgp && sig.isEmpty())
4024                 output = msg->read();
4025 
4026             QList<QCA::SecureMessageSignature> signers = msg->signers();
4027             delete msg;
4028             delete sms;
4029 
4030             // for pgp clearsign, pgp signed (non-detached), and smime,
4031             //   the signed content was inside of the message.  we need
4032             //   to print that content now
4033             if (pgp) {
4034                 printf("%s", output.data());
4035             } else {
4036                 QString str = open_mime_envelope(smime_text);
4037                 printf("%s", str.toUtf8().data());
4038             }
4039 
4040             smDisplaySignatures(signers);
4041 
4042             bool allgood = true;
4043             foreach (const QCA::SecureMessageSignature &signer, signers) {
4044                 if (signer.identityResult() != QCA::SecureMessageSignature::Valid) {
4045                     allgood = false;
4046                     break;
4047                 }
4048             }
4049 
4050             if (!allgood)
4051                 return 1;
4052         } else if (args[1] == QLatin1String("decrypt")) {
4053             if (args.count() < 3) {
4054                 usage();
4055                 return 1;
4056             }
4057 
4058             QCA::SecureMessageSystem *sms;
4059             bool                      pgp = false;
4060 
4061             if (args[2] == QLatin1String("pgp")) {
4062                 if (!QCA::isSupported("openpgp")) {
4063                     fprintf(stderr, "Error: need 'openpgp' feature.\n");
4064                     return 1;
4065                 }
4066 
4067                 ksm_start_and_wait();
4068 
4069                 sms = new QCA::OpenPGP;
4070                 pgp = true;
4071             } else if (args[2] == QLatin1String("smime")) {
4072                 if (args.count() < 4) {
4073                     usage();
4074                     return 1;
4075                 }
4076 
4077                 if (!QCA::isSupported("cms")) {
4078                     fprintf(stderr, "Error: need 'cms' feature.\n");
4079                     return 1;
4080                 }
4081 
4082                 // user can provide many possible decrypt keys
4083                 QList<QCA::KeyBundle> keys;
4084                 for (int n = 3; n < args.count(); ++n) {
4085                     QCA::KeyBundle key = get_X(args[n]);
4086                     if (key.isNull())
4087                         return 1;
4088                     keys += key;
4089                 }
4090 
4091                 sms = new QCA::CMS;
4092 
4093                 QList<QCA::SecureMessageKey> skeys;
4094                 foreach (const QCA::KeyBundle &key, keys) {
4095                     QCA::SecureMessageKey skey;
4096                     skey.setX509CertificateChain(key.certificateChain());
4097                     skey.setX509PrivateKey(key.privateKey());
4098                     skeys += skey;
4099                 }
4100 
4101                 ((QCA::CMS *)sms)->setPrivateKeys(skeys);
4102             } else {
4103                 usage();
4104                 return 1;
4105             }
4106 
4107             // read input data from stdin all at once
4108             QByteArray plain;
4109             while (!feof(stdin)) {
4110                 QByteArray block(1024, 0);
4111                 int        n = fread(block.data(), 1, 1024, stdin);
4112                 if (n < 0)
4113                     break;
4114                 block.resize(n);
4115                 plain += block;
4116             }
4117 
4118             // smime envelope
4119             if (!pgp) {
4120                 QString in  = QString::fromUtf8(plain);
4121                 QString str = open_mime_envelope(in);
4122                 if (str.isEmpty()) {
4123                     delete sms;
4124                     fprintf(stderr, "Error: can't parse message file.\n");
4125                     return 1;
4126                 }
4127 
4128                 QCA::Base64 dec;
4129                 dec.setLineBreaksEnabled(true);
4130                 plain = dec.stringToArray(rem_cr(str)).toByteArray();
4131             }
4132 
4133             QCA::SecureMessage *msg = new QCA::SecureMessage(sms);
4134             if (pgp)
4135                 msg->setFormat(QCA::SecureMessage::Ascii);
4136             msg->startDecrypt();
4137             msg->update(plain);
4138             msg->end();
4139             msg->waitForFinished(-1);
4140 
4141             if (debug) {
4142                 output_keystore_diagnostic_text();
4143                 output_message_diagnostic_text(msg);
4144             }
4145 
4146             if (!msg->success()) {
4147                 QString errstr = smErrorToString(msg->errorCode());
4148                 delete msg;
4149                 delete sms;
4150                 fprintf(stderr, "Error: decrypt failed: %s\n", qPrintable(errstr));
4151                 return 1;
4152             }
4153 
4154             QByteArray output = msg->read();
4155 
4156             QList<QCA::SecureMessageSignature> signers;
4157             bool                               wasSigned = false;
4158             if (msg->wasSigned()) {
4159                 signers   = msg->signers();
4160                 wasSigned = true;
4161             }
4162             delete msg;
4163             delete sms;
4164 
4165             printf("%s", output.data());
4166 
4167             if (wasSigned) {
4168                 fprintf(stderr, "Message was also signed:\n");
4169 
4170                 smDisplaySignatures(signers);
4171 
4172                 bool allgood = true;
4173                 foreach (const QCA::SecureMessageSignature &signer, signers) {
4174                     if (signer.identityResult() != QCA::SecureMessageSignature::Valid) {
4175                         allgood = false;
4176                         break;
4177                     }
4178                 }
4179 
4180                 if (!allgood)
4181                     return 1;
4182             }
4183         } else if (args[1] == QLatin1String("exportcerts")) {
4184             if (!QCA::isSupported("cms")) {
4185                 fprintf(stderr, "Error: need 'cms' feature.\n");
4186                 return 1;
4187             }
4188 
4189             QCA::SecureMessageSystem *sms = new QCA::CMS;
4190 
4191             QByteArray data, sig;
4192             QString    smime_text;
4193             {
4194                 // read input data from stdin all at once
4195                 QByteArray plain;
4196                 while (!feof(stdin)) {
4197                     QByteArray block(1024, 0);
4198                     int        n = fread(block.data(), 1, 1024, stdin);
4199                     if (n < 0)
4200                         break;
4201                     block.resize(n);
4202                     plain += block;
4203                 }
4204 
4205                 // smime envelope
4206                 QString in = QString::fromUtf8(plain);
4207                 QString str, sigtext;
4208                 if (!open_mime_data_sig(in, &str, &sigtext)) {
4209                     delete sms;
4210                     fprintf(stderr, "Error: can't parse message file.\n");
4211                     return 1;
4212                 }
4213 
4214                 data       = str.toUtf8();
4215                 smime_text = str;
4216 
4217                 QCA::Base64 dec;
4218                 dec.setLineBreaksEnabled(true);
4219                 sig = dec.stringToArray(rem_cr(sigtext)).toByteArray();
4220             }
4221 
4222             QCA::SecureMessage *msg = new QCA::SecureMessage(sms);
4223             msg->startVerify(sig);
4224             msg->update(data);
4225             msg->end();
4226             msg->waitForFinished(-1);
4227 
4228             if (debug)
4229                 output_message_diagnostic_text(msg);
4230 
4231             if (!msg->success()) {
4232                 QString errstr = smErrorToString(msg->errorCode());
4233                 delete msg;
4234                 delete sms;
4235                 fprintf(stderr, "Error: export failed: %s\n", qPrintable(errstr));
4236                 return 1;
4237             }
4238 
4239             QList<QCA::SecureMessageSignature> signers = msg->signers();
4240             delete msg;
4241             delete sms;
4242 
4243             // print out all certs of all signers
4244             foreach (const QCA::SecureMessageSignature &signer, signers) {
4245                 QCA::SecureMessageKey key = signer.key();
4246                 if (!key.isNull()) {
4247                     foreach (const QCA::Certificate &c, key.x509CertificateChain())
4248                         printf("%s", qPrintable(c.toPEM()));
4249                 }
4250             }
4251         } else {
4252             usage();
4253             return 1;
4254         }
4255     } else {
4256         usage();
4257         return 1;
4258     }
4259 
4260     return 0;
4261 }
4262 
4263 #include "main.moc"