File indexing completed on 2024-04-28 04:43:38

0001 /*
0002  Copyright (C) 2007 Justin Karneges <justin@affinix.com>
0003 
0004  Permission is hereby granted, free of charge, to any person obtaining a copy
0005  of this software and associated documentation files (the "Software"), to deal
0006  in the Software without restriction, including without limitation the rights
0007  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
0008  copies of the Software, and to permit persons to whom the Software is
0009  furnished to do so, subject to the following conditions:
0010 
0011  The above copyright notice and this permission notice shall be included in
0012  all copies or substantial portions of the Software.
0013 
0014  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
0017  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
0018  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
0019  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0020 */
0021 
0022 #include <QFileDialog>
0023 #include <QMessageBox>
0024 #include <QtCore>
0025 #include <QtCrypto>
0026 #include <QtGui>
0027 
0028 #include "certitem.h"
0029 #include "certviewdlg.h"
0030 #include "keyselectdlg.h"
0031 #include "pkcs11configdlg/pkcs11configdlg.h"
0032 #include "ui_mainwin.h"
0033 
0034 #ifdef QT_STATICPLUGIN
0035 #include "import_plugins.h"
0036 #endif
0037 
0038 #define VERSION "1.0.0"
0039 
0040 class Icons
0041 {
0042 public:
0043     QPixmap cert, crl, keybundle, pgppub, pgpsec;
0044 };
0045 
0046 Icons *g_icons = 0;
0047 
0048 //----------------------------------------------------------------------------
0049 // Operation
0050 //----------------------------------------------------------------------------
0051 class Operation : public QObject
0052 {
0053     Q_OBJECT
0054 public:
0055     Operation(QObject *parent = 0)
0056         : QObject(parent)
0057     {
0058     }
0059 
0060 Q_SIGNALS:
0061     void error(const QString &str);
0062 };
0063 
0064 static QString validityToString(QCA::Validity v)
0065 {
0066     QString s;
0067     switch (v) {
0068     case QCA::ValidityGood:
0069         s = Operation::tr("Validated");
0070         break;
0071     case QCA::ErrorRejected:
0072         s = Operation::tr("Root CA is marked to reject the specified purpose");
0073         break;
0074     case QCA::ErrorUntrusted:
0075         s = Operation::tr("Certificate not trusted for the required purpose");
0076         break;
0077     case QCA::ErrorSignatureFailed:
0078         s = Operation::tr("Invalid signature");
0079         break;
0080     case QCA::ErrorInvalidCA:
0081         s = Operation::tr("Invalid CA certificate");
0082         break;
0083     case QCA::ErrorInvalidPurpose:
0084         s = Operation::tr("Invalid certificate purpose");
0085         break;
0086     case QCA::ErrorSelfSigned:
0087         s = Operation::tr("Certificate is self-signed");
0088         break;
0089     case QCA::ErrorRevoked:
0090         s = Operation::tr("Certificate has been revoked");
0091         break;
0092     case QCA::ErrorPathLengthExceeded:
0093         s = Operation::tr("Maximum certificate chain length exceeded");
0094         break;
0095     case QCA::ErrorExpired:
0096         s = Operation::tr("Certificate has expired");
0097         break;
0098     case QCA::ErrorExpiredCA:
0099         s = Operation::tr("CA has expired");
0100         break;
0101     case QCA::ErrorValidityUnknown:
0102     default:
0103         s = Operation::tr("General certificate validation error");
0104         break;
0105     }
0106     return s;
0107 }
0108 
0109 static QString smErrorToString(QCA::SecureMessage::Error e)
0110 {
0111     QString s;
0112     switch (e) {
0113     case QCA::SecureMessage::ErrorPassphrase:
0114         s = Operation::tr("Invalid passphrase.");
0115         break;
0116     case QCA::SecureMessage::ErrorFormat:
0117         s = Operation::tr("Bad input format.");
0118         break;
0119     case QCA::SecureMessage::ErrorSignerExpired:
0120         s = Operation::tr("Signer key is expired.");
0121         break;
0122     case QCA::SecureMessage::ErrorSignerInvalid:
0123         s = Operation::tr("Signer key is invalid.");
0124         break;
0125     case QCA::SecureMessage::ErrorEncryptExpired:
0126         s = Operation::tr("Encrypting key is expired.");
0127         break;
0128     case QCA::SecureMessage::ErrorEncryptUntrusted:
0129         s = Operation::tr("Encrypting key is untrusted.");
0130         break;
0131     case QCA::SecureMessage::ErrorEncryptInvalid:
0132         s = Operation::tr("Encrypting key is invalid.");
0133         break;
0134     case QCA::SecureMessage::ErrorNeedCard:
0135         s = Operation::tr("Card was needed but not found.");
0136         break;
0137     case QCA::SecureMessage::ErrorCertKeyMismatch:
0138         s = Operation::tr("Certificate and private key don't match.");
0139         break;
0140     case QCA::SecureMessage::ErrorUnknown:
0141     default:
0142         s = Operation::tr("General error.");
0143         break;
0144     }
0145     return s;
0146 }
0147 
0148 static QString smsIdentityToString(const QCA::SecureMessageSignature &sig)
0149 {
0150     QString s;
0151     switch (sig.identityResult()) {
0152     case QCA::SecureMessageSignature::Valid:
0153         break;
0154     case QCA::SecureMessageSignature::InvalidSignature:
0155         s = Operation::tr("Invalid signature");
0156         break;
0157     case QCA::SecureMessageSignature::InvalidKey:
0158         s = Operation::tr("Invalid key: %1").arg(validityToString(sig.keyValidity()));
0159         break;
0160     case QCA::SecureMessageSignature::NoKey:
0161         s = Operation::tr("Key not found");
0162         break;
0163     default: // this should not really be possible
0164         s = Operation::tr("Unknown");
0165         break;
0166     }
0167     return s;
0168 }
0169 
0170 class SignOperation : public Operation
0171 {
0172     Q_OBJECT
0173 private:
0174     QByteArray             in;
0175     CertItemStore         *store;
0176     int                    id;
0177     QCA::CMS              *cms;
0178     CertItemPrivateLoader *loader;
0179     QCA::SecureMessage    *msg;
0180     int                    pending;
0181 
0182 public:
0183     SignOperation(const QByteArray &_in, CertItemStore *_store, int _id, QCA::CMS *_cms, QObject *parent = 0)
0184         : Operation(parent)
0185         , in(_in)
0186         , store(_store)
0187         , id(_id)
0188         , cms(_cms)
0189         , msg(0)
0190     {
0191         loader = new CertItemPrivateLoader(store, this);
0192         connect(loader, SIGNAL(finished()), SLOT(loader_finished()));
0193         loader->start(id);
0194     }
0195 
0196 Q_SIGNALS:
0197     void loadError();
0198     void finished(const QString &sig);
0199 
0200 private Q_SLOTS:
0201     void loader_finished()
0202     {
0203         QCA::PrivateKey privateKey = loader->privateKey();
0204         delete loader;
0205         loader = 0;
0206 
0207         if (privateKey.isNull()) {
0208             emit loadError();
0209             return;
0210         }
0211 
0212         CertItem item = store->itemFromId(id);
0213 
0214         QCA::SecureMessageKey signer;
0215         signer.setX509CertificateChain(item.certificateChain());
0216         signer.setX509PrivateKey(privateKey);
0217 
0218         msg = new QCA::SecureMessage(cms);
0219         connect(msg, SIGNAL(bytesWritten(int)), SLOT(msg_bytesWritten(int)));
0220         connect(msg, SIGNAL(finished()), SLOT(msg_finished()));
0221         msg->setFormat(QCA::SecureMessage::Ascii);
0222         msg->setSigner(signer);
0223         msg->startSign(QCA::SecureMessage::Detached);
0224 
0225         pending = 0;
0226         update();
0227     }
0228 
0229     void update()
0230     {
0231         QByteArray buf = in.mid(0, 16384 - pending); // 16k chunks
0232         in             = in.mid(buf.size());
0233         pending += buf.size();
0234         msg->update(buf);
0235     }
0236 
0237     void msg_bytesWritten(int x)
0238     {
0239         pending -= x;
0240 
0241         if (in.isEmpty() && pending == 0)
0242             msg->end();
0243         else
0244             update();
0245     }
0246 
0247     void msg_finished()
0248     {
0249         if (!msg->success()) {
0250             QString str = smErrorToString(msg->errorCode());
0251             delete msg;
0252             msg = 0;
0253             emit error(tr("Error during sign operation.\nReason: %1").arg(str));
0254             return;
0255         }
0256 
0257         QByteArray result = msg->signature();
0258         delete msg;
0259         msg = 0;
0260         emit finished(QString::fromLatin1(result));
0261     }
0262 };
0263 
0264 class VerifyOperation : public Operation
0265 {
0266     Q_OBJECT
0267 private:
0268     QByteArray          in, sig;
0269     QCA::CMS           *cms;
0270     QCA::SecureMessage *msg;
0271     int                 pending;
0272 
0273 public:
0274     QCA::SecureMessageSignature signer;
0275 
0276     VerifyOperation(const QByteArray &_in, const QByteArray &_sig, QCA::CMS *_cms, QObject *parent = 0)
0277         : Operation(parent)
0278         , in(_in)
0279         , sig(_sig)
0280         , cms(_cms)
0281         , msg(0)
0282     {
0283         msg = new QCA::SecureMessage(cms);
0284         connect(msg, SIGNAL(bytesWritten(int)), SLOT(msg_bytesWritten(int)));
0285         connect(msg, SIGNAL(finished()), SLOT(msg_finished()));
0286         msg->setFormat(QCA::SecureMessage::Ascii);
0287         msg->startVerify(sig);
0288 
0289         pending = 0;
0290         update();
0291     }
0292 
0293 Q_SIGNALS:
0294     void finished();
0295 
0296 private Q_SLOTS:
0297     void update()
0298     {
0299         QByteArray buf = in.mid(0, 16384 - pending); // 16k chunks
0300         in             = in.mid(buf.size());
0301         pending += buf.size();
0302         msg->update(buf);
0303     }
0304 
0305     void msg_bytesWritten(int x)
0306     {
0307         pending -= x;
0308 
0309         if (in.isEmpty() && pending == 0)
0310             msg->end();
0311         else
0312             update();
0313     }
0314 
0315     void msg_finished()
0316     {
0317         if (!msg->success()) {
0318             QString str = smErrorToString(msg->errorCode());
0319             delete msg;
0320             msg = 0;
0321             emit error(tr("Error during verify operation.\nReason: %1").arg(str));
0322             return;
0323         }
0324 
0325         signer = msg->signer();
0326         delete msg;
0327         msg = 0;
0328 
0329         if (signer.identityResult() != QCA::SecureMessageSignature::Valid) {
0330             QString str = smsIdentityToString(signer);
0331             emit    error(tr("Verification failed!\nReason: %1").arg(str));
0332             return;
0333         }
0334 
0335         emit finished();
0336     }
0337 };
0338 
0339 //----------------------------------------------------------------------------
0340 // MainWin
0341 //----------------------------------------------------------------------------
0342 static QString get_fingerprint(const QCA::Certificate &cert)
0343 {
0344     QString hex = QCA::Hash("sha1").hashToString(cert.toDER());
0345     QString out;
0346     for (int n = 0; n < hex.count(); ++n) {
0347         if (n != 0 && n % 2 == 0)
0348             out += ':';
0349         out += hex[n];
0350     }
0351     return out;
0352 }
0353 
0354 class MainWin : public QMainWindow
0355 {
0356     Q_OBJECT
0357 private:
0358     Ui_MainWin       ui;
0359     CertItemStore   *users, *roots;
0360     QCA::CMS        *cms;
0361     Operation       *op;
0362     QAction         *actionView, *actionRename, *actionRemove;
0363     QCA::Certificate self_signed_verify_cert;
0364     int              auto_import_req_id;
0365 
0366 public:
0367     MainWin(QWidget *parent = 0)
0368         : QMainWindow(parent)
0369         , op(0)
0370         , auto_import_req_id(-1)
0371     {
0372         ui.setupUi(this);
0373 
0374         g_icons            = new Icons;
0375         g_icons->cert      = QPixmap(":/gfx/icons/cert16.png");
0376         g_icons->crl       = QPixmap(":/gfx/icons/crl16.png");
0377         g_icons->keybundle = QPixmap(":/gfx/icons/keybundle16.png");
0378         g_icons->pgppub    = QPixmap(":/gfx/icons/publickey16.png");
0379         g_icons->pgpsec    = QPixmap(":/gfx/icons/keypair16.png");
0380         if (g_icons->cert.isNull() || g_icons->crl.isNull() || g_icons->keybundle.isNull() ||
0381             g_icons->pgppub.isNull() || g_icons->pgpsec.isNull())
0382             printf("Warning: not all icons loaded\n");
0383 
0384         users = new CertItemStore(this);
0385         roots = new CertItemStore(this);
0386 
0387         setIcons(users);
0388         setIcons(roots);
0389 
0390         connect(users, SIGNAL(addSuccess(int, int)), SLOT(users_addSuccess(int, int)));
0391         connect(users, SIGNAL(addFailed(int)), SLOT(users_addFailed(int)));
0392 
0393         actionView   = new QAction(tr("&View"), this);
0394         actionRename = new QAction(tr("Re&name"), this);
0395         actionRemove = new QAction(tr("Rem&ove"), this);
0396 
0397         connect(ui.actionLoadIdentityFile, SIGNAL(triggered()), SLOT(load_file()));
0398         connect(ui.actionLoadIdentityEntry, SIGNAL(triggered()), SLOT(load_device()));
0399         connect(ui.actionLoadAuthority, SIGNAL(triggered()), SLOT(load_root()));
0400         connect(ui.actionConfigurePkcs11, SIGNAL(triggered()), SLOT(mod_config()));
0401         connect(ui.actionQuit, SIGNAL(triggered()), SLOT(close()));
0402         connect(ui.actionAbout, SIGNAL(triggered()), SLOT(about()));
0403         connect(ui.pb_sign, SIGNAL(clicked()), SLOT(do_sign()));
0404         connect(ui.pb_verify, SIGNAL(clicked()), SLOT(do_verify()));
0405 
0406         connect(actionView, SIGNAL(triggered()), SLOT(item_view()));
0407         connect(actionRename, SIGNAL(triggered()), SLOT(item_rename()));
0408         connect(actionRemove, SIGNAL(triggered()), SLOT(item_remove()));
0409 
0410         ui.pb_sign->setEnabled(false);
0411 
0412         ui.lv_users->setModel(users);
0413         connect(ui.lv_users->selectionModel(),
0414                 SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
0415                 SLOT(users_selectionChanged(const QItemSelection &, const QItemSelection &)));
0416 
0417         ui.lv_users->setContextMenuPolicy(Qt::CustomContextMenu);
0418         connect(ui.lv_users,
0419                 SIGNAL(customContextMenuRequested(const QPoint &)),
0420                 SLOT(users_customContextMenuRequested(const QPoint &)));
0421 
0422         ui.lv_authorities->setModel(roots);
0423 
0424         ui.lv_authorities->setContextMenuPolicy(Qt::CustomContextMenu);
0425         connect(ui.lv_authorities,
0426                 SIGNAL(customContextMenuRequested(const QPoint &)),
0427                 SLOT(roots_customContextMenuRequested(const QPoint &)));
0428 
0429         cms = new QCA::CMS(this);
0430 
0431         QStringList ulist, rlist;
0432         {
0433             QSettings settings("Affinix", "CMS Signer");
0434             ulist = settings.value("users").toStringList();
0435             rlist = settings.value("roots").toStringList();
0436         }
0437 
0438         users->load(ulist);
0439         roots->load(rlist);
0440     }
0441 
0442     ~MainWin()
0443     {
0444         QStringList ulist = users->save();
0445         QStringList rlist = roots->save();
0446 
0447         QSettings settings("Affinix", "CMS Signer");
0448         settings.setValue("users", ulist);
0449         settings.setValue("roots", rlist);
0450 
0451         delete g_icons;
0452         g_icons = 0;
0453     }
0454 
0455     void setIcons(CertItemStore *store)
0456     {
0457         store->setIcon(CertItemStore::IconCert, g_icons->cert);
0458         store->setIcon(CertItemStore::IconCrl, g_icons->crl);
0459         store->setIcon(CertItemStore::IconKeyBundle, g_icons->keybundle);
0460         store->setIcon(CertItemStore::IconPgpPub, g_icons->pgppub);
0461         store->setIcon(CertItemStore::IconPgpSec, g_icons->pgpsec);
0462     }
0463 
0464     QCA::CertificateCollection allCerts()
0465     {
0466         QCA::CertificateCollection col;
0467 
0468         // system store
0469         col += QCA::systemStore();
0470 
0471         // additional roots configured in application
0472         foreach (const CertItem &i, roots->items())
0473             col.addCertificate(i.certificateChain().primary());
0474 
0475         // user chains
0476         foreach (const CertItem &i, users->items()) {
0477             foreach (const QCA::Certificate &cert, i.certificateChain())
0478                 col.addCertificate(cert);
0479         }
0480 
0481         return col;
0482     }
0483 
0484     QCA::CertificateChain complete(const QCA::CertificateChain &chain)
0485     {
0486         return chain.complete(allCerts().certificates());
0487     }
0488 
0489 private Q_SLOTS:
0490     void load_file()
0491     {
0492         QString fileName =
0493             QFileDialog::getOpenFileName(this, tr("Open File"), QString(), tr("X.509 Identities (*.p12 *.pfx)"));
0494         if (fileName.isEmpty())
0495             return;
0496 
0497         setEnabled(false);
0498         users->addFromFile(fileName);
0499     }
0500 
0501     void load_device()
0502     {
0503         KeySelectDlg *w = new KeySelectDlg(this);
0504         w->setAttribute(Qt::WA_DeleteOnClose, true);
0505         w->setWindowModality(Qt::WindowModal);
0506         connect(
0507             w, SIGNAL(selected(const QCA::KeyStoreEntry &)), SLOT(load_device_finished(const QCA::KeyStoreEntry &)));
0508         connect(w,
0509                 SIGNAL(viewCertificate(const QCA::CertificateChain &)),
0510                 SLOT(keyselect_viewCertificate(const QCA::CertificateChain &)));
0511         w->setIcon(KeySelectDlg::IconCert, g_icons->cert);
0512         w->setIcon(KeySelectDlg::IconCrl, g_icons->crl);
0513         w->setIcon(KeySelectDlg::IconKeyBundle, g_icons->keybundle);
0514         w->setIcon(KeySelectDlg::IconPgpPub, g_icons->pgppub);
0515         w->setIcon(KeySelectDlg::IconPgpSec, g_icons->pgpsec);
0516         w->show();
0517     }
0518 
0519     void load_device_finished(const QCA::KeyStoreEntry &entry)
0520     {
0521         users->addFromKeyStore(entry);
0522     }
0523 
0524     void load_root()
0525     {
0526         QString fileName =
0527             QFileDialog::getOpenFileName(this, tr("Open File"), QString(), tr("X.509 Certificates (*.pem *.crt)"));
0528         if (fileName.isEmpty())
0529             return;
0530 
0531         QCA::Certificate cert = QCA::Certificate::fromPEMFile(fileName);
0532         if (cert.isNull()) {
0533             QMessageBox::information(this, tr("Error"), tr("Error opening certificate file."));
0534             return;
0535         }
0536 
0537         roots->addUser(cert);
0538     }
0539 
0540     void users_addSuccess(int req_id, int id)
0541     {
0542         if (req_id == auto_import_req_id) {
0543             auto_import_req_id = -1;
0544 
0545             CertItem i = users->itemFromId(id);
0546 
0547             QMessageBox::information(this,
0548                                      tr("User added"),
0549                                      tr("This signature was made by a previously unknown user, and so the "
0550                                         "user has now been added to the keyring as \"%1\".")
0551                                          .arg(i.name()));
0552 
0553             verify_next();
0554             return;
0555         }
0556 
0557         ui.lv_users->selectionModel()->select(users->index(users->rowFromId(id)),
0558                                               QItemSelectionModel::Clear | QItemSelectionModel::Select |
0559                                                   QItemSelectionModel::Current);
0560 
0561         setEnabled(true);
0562     }
0563 
0564     void users_addFailed(int req_id)
0565     {
0566         Q_UNUSED(req_id);
0567 
0568         setEnabled(true);
0569     }
0570 
0571     void mod_config()
0572     {
0573         if (!Pkcs11ConfigDlg::isSupported()) {
0574             QMessageBox::information(
0575                 this, tr("Error"), tr("No provider available supporting standard PKCS#11 configuration."));
0576             return;
0577         }
0578 
0579         Pkcs11ConfigDlg *w = new Pkcs11ConfigDlg(this);
0580         w->setAttribute(Qt::WA_DeleteOnClose, true);
0581         w->setWindowModality(Qt::WindowModal);
0582         w->show();
0583     }
0584 
0585     void users_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
0586     {
0587         Q_UNUSED(deselected);
0588 
0589         int at = -1;
0590         if (!selected.indexes().isEmpty()) {
0591             QModelIndex index = selected.indexes().first();
0592             at                = index.row();
0593         }
0594 
0595         bool usable = false;
0596         if (at != -1 && users->itemFromRow(at).isUsable())
0597             usable = true;
0598 
0599         if (usable && !ui.pb_sign->isEnabled())
0600             ui.pb_sign->setEnabled(true);
0601         else if (!usable && ui.pb_sign->isEnabled())
0602             ui.pb_sign->setEnabled(false);
0603     }
0604 
0605     void item_view()
0606     {
0607         if (ui.lv_users->hasFocus()) {
0608             QItemSelection selection = ui.lv_users->selectionModel()->selection();
0609             if (selection.indexes().isEmpty())
0610                 return;
0611             QModelIndex index = selection.indexes().first();
0612             users_view(index.row());
0613         } else // lv_authorities
0614         {
0615             QItemSelection selection = ui.lv_authorities->selectionModel()->selection();
0616             if (selection.indexes().isEmpty())
0617                 return;
0618             QModelIndex index = selection.indexes().first();
0619             roots_view(index.row());
0620         }
0621     }
0622 
0623     void item_rename()
0624     {
0625         if (ui.lv_users->hasFocus()) {
0626             QItemSelection selection = ui.lv_users->selectionModel()->selection();
0627             if (selection.indexes().isEmpty())
0628                 return;
0629             QModelIndex index = selection.indexes().first();
0630             users_rename(index.row());
0631         } else // lv_authorities
0632         {
0633             QItemSelection selection = ui.lv_authorities->selectionModel()->selection();
0634             if (selection.indexes().isEmpty())
0635                 return;
0636             QModelIndex index = selection.indexes().first();
0637             roots_rename(index.row());
0638         }
0639     }
0640 
0641     void item_remove()
0642     {
0643         if (ui.lv_users->hasFocus()) {
0644             QItemSelection selection = ui.lv_users->selectionModel()->selection();
0645             if (selection.indexes().isEmpty())
0646                 return;
0647             QModelIndex index = selection.indexes().first();
0648             users_remove(index.row());
0649         } else // lv_authorities
0650         {
0651             QItemSelection selection = ui.lv_authorities->selectionModel()->selection();
0652             if (selection.indexes().isEmpty())
0653                 return;
0654             QModelIndex index = selection.indexes().first();
0655             roots_remove(index.row());
0656         }
0657     }
0658 
0659     void users_view(int at)
0660     {
0661         CertItem     i = users->itemFromRow(at);
0662         CertViewDlg *w = new CertViewDlg(complete(i.certificateChain()), this);
0663         w->setAttribute(Qt::WA_DeleteOnClose, true);
0664         w->show();
0665     }
0666 
0667     void users_rename(int at)
0668     {
0669         QModelIndex index = users->index(at);
0670         ui.lv_users->setFocus();
0671         ui.lv_users->setCurrentIndex(index);
0672         ui.lv_users->selectionModel()->select(
0673             index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
0674         ui.lv_users->edit(index);
0675     }
0676 
0677     void users_remove(int at)
0678     {
0679         users->removeItem(users->idFromRow(at));
0680     }
0681 
0682     void roots_view(int at)
0683     {
0684         CertItem     i = roots->itemFromRow(at);
0685         CertViewDlg *w = new CertViewDlg(complete(i.certificateChain()), this);
0686         w->setAttribute(Qt::WA_DeleteOnClose, true);
0687         w->show();
0688     }
0689 
0690     void roots_rename(int at)
0691     {
0692         QModelIndex index = roots->index(at);
0693         ui.lv_authorities->setFocus();
0694         ui.lv_authorities->setCurrentIndex(index);
0695         ui.lv_authorities->selectionModel()->select(
0696             index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
0697         ui.lv_authorities->edit(index);
0698     }
0699 
0700     void roots_remove(int at)
0701     {
0702         roots->removeItem(roots->idFromRow(at));
0703     }
0704 
0705     void keyselect_viewCertificate(const QCA::CertificateChain &chain)
0706     {
0707         CertViewDlg *w = new CertViewDlg(complete(chain), (QWidget *)sender());
0708         w->setAttribute(Qt::WA_DeleteOnClose, true);
0709         w->show();
0710     }
0711 
0712     void users_customContextMenuRequested(const QPoint &pos)
0713     {
0714         QItemSelection selection = ui.lv_users->selectionModel()->selection();
0715         if (selection.indexes().isEmpty())
0716             return;
0717 
0718         QMenu menu(this);
0719         menu.addAction(actionView);
0720         menu.addAction(actionRename);
0721         menu.addAction(actionRemove);
0722         menu.exec(ui.lv_users->viewport()->mapToGlobal(pos));
0723     }
0724 
0725     void roots_customContextMenuRequested(const QPoint &pos)
0726     {
0727         QItemSelection selection = ui.lv_authorities->selectionModel()->selection();
0728         if (selection.indexes().isEmpty())
0729             return;
0730 
0731         QMenu menu(this);
0732         menu.addAction(actionView);
0733         menu.addAction(actionRename);
0734         menu.addAction(actionRemove);
0735         menu.exec(ui.lv_authorities->viewport()->mapToGlobal(pos));
0736     }
0737 
0738     void do_sign()
0739     {
0740         QItemSelection selection = ui.lv_users->selectionModel()->selection();
0741         if (selection.indexes().isEmpty())
0742             return;
0743         QModelIndex index = selection.indexes().first();
0744         int         at    = index.row();
0745 
0746         setEnabled(false);
0747 
0748         op = new SignOperation(ui.te_data->toPlainText().toUtf8(), users, users->idFromRow(at), cms, this);
0749         connect(op, SIGNAL(loadError()), SLOT(sign_loadError()));
0750         connect(op, SIGNAL(finished(const QString &)), SLOT(sign_finished(const QString &)));
0751         connect(op, SIGNAL(error(const QString &)), SLOT(sign_error(const QString &)));
0752     }
0753 
0754     void do_verify()
0755     {
0756         // prepare root certs
0757         QCA::CertificateCollection col;
0758 
0759         // system store
0760         col += QCA::systemStore();
0761 
0762         // additional roots configured in application
0763         foreach (const CertItem &i, roots->items())
0764             col.addCertificate(i.certificateChain().primary());
0765 
0766         // consider self-signed users as roots
0767         // (it is therefore not possible with this application to
0768         // have people in your keyring that you don't trust)
0769         foreach (const CertItem &i, users->items()) {
0770             QCA::Certificate cert = i.certificateChain().primary();
0771             if (cert.isSelfSigned())
0772                 col.addCertificate(cert);
0773         }
0774 
0775         // the self signed verify cert, if applicable
0776         if (!self_signed_verify_cert.isNull()) {
0777             col.addCertificate(self_signed_verify_cert);
0778             self_signed_verify_cert = QCA::Certificate();
0779         }
0780 
0781         cms->setTrustedCertificates(col);
0782 
0783         setEnabled(false);
0784 
0785         op = new VerifyOperation(ui.te_data->toPlainText().toUtf8(), ui.te_sig->toPlainText().toUtf8(), cms, this);
0786         connect(op, SIGNAL(finished()), SLOT(verify_finished()));
0787         connect(op, SIGNAL(error(const QString &)), SLOT(verify_error(const QString &)));
0788     }
0789 
0790     void about()
0791     {
0792         int     ver = qcaVersion();
0793         int     maj = (ver >> 16) & 0xff;
0794         int     min = (ver >> 8) & 0xff;
0795         int     bug = ver & 0xff;
0796         QString verstr;
0797         verstr.sprintf("%d.%d.%d", maj, min, bug);
0798 
0799         QString str;
0800         str += tr("CMS Signer version %1 by Justin Karneges").arg(VERSION) + '\n';
0801         str += tr("A simple tool for creating and verifying digital signatures.") + '\n';
0802         str += '\n';
0803         str += tr("Using QCA version %1").arg(verstr) + '\n';
0804         str += '\n';
0805         str += tr("Icons by Jason Kim") + '\n';
0806 
0807         QCA::ProviderList list = QCA::providers();
0808         foreach (QCA::Provider *p, list) {
0809             QString credit = p->credit();
0810             if (!credit.isEmpty()) {
0811                 str += '\n';
0812                 str += credit;
0813             }
0814         }
0815 
0816         QMessageBox::about(this, tr("About CMS Signer"), str);
0817     }
0818 
0819     void sign_loadError()
0820     {
0821         delete op;
0822         op = 0;
0823 
0824         setEnabled(true);
0825     }
0826 
0827     void sign_finished(const QString &sig)
0828     {
0829         delete op;
0830         op = 0;
0831 
0832         ui.te_sig->setPlainText(sig);
0833 
0834         setEnabled(true);
0835     }
0836 
0837     void sign_error(const QString &msg)
0838     {
0839         delete op;
0840         op = 0;
0841 
0842         setEnabled(true);
0843 
0844         QMessageBox::information(this, tr("Error"), msg);
0845     }
0846 
0847     void verify_finished()
0848     {
0849         QCA::SecureMessageSignature signer = ((VerifyOperation *)op)->signer;
0850         delete op;
0851         op = 0;
0852 
0853         // import the cert?
0854         QCA::SecureMessageKey skey = signer.key();
0855         if (!skey.isNull()) {
0856             QCA::CertificateChain chain = skey.x509CertificateChain();
0857 
0858             int             at    = -1;
0859             QList<CertItem> items = users->items();
0860             for (int n = 0; n < items.count(); ++n) {
0861                 const CertItem &i = items[n];
0862                 if (i.certificateChain().primary() == chain.primary()) {
0863                     at = n;
0864                     break;
0865                 }
0866             }
0867 
0868             // add
0869             if (at == -1) {
0870                 auto_import_req_id = users->addUser(chain);
0871                 return;
0872             }
0873             // update
0874             else {
0875                 users->updateChain(users->idFromRow(at), chain);
0876             }
0877         }
0878 
0879         verify_next();
0880     }
0881 
0882     void verify_next()
0883     {
0884         setEnabled(true);
0885 
0886         QMessageBox::information(this, tr("Verify"), tr("Signature verified successfully."));
0887     }
0888 
0889     void verify_error(const QString &msg)
0890     {
0891         QCA::SecureMessageSignature signer = ((VerifyOperation *)op)->signer;
0892         delete op;
0893         op = 0;
0894 
0895         QCA::SecureMessageKey skey = signer.key();
0896         if (signer.keyValidity() == QCA::ErrorSelfSigned && !skey.isNull()) {
0897             QCA::CertificateChain chain = skey.x509CertificateChain();
0898             if (chain.count() == 1 && chain.primary().isSelfSigned()) {
0899                 QCA::Certificate cert = chain.primary();
0900 
0901                 int ret = QMessageBox::warning(
0902                     this,
0903                     tr("Self-signed certificate"),
0904                     tr("<qt>The signature is made by an unknown user, and the certificate is self-signed.<br>\n"
0905                        "<br>\n"
0906                        "<nobr>Common Name: %1</nobr><br>\n"
0907                        "<nobr>SHA1 Fingerprint: %2</nobr><br>\n"
0908                        "<br>\n"
0909                        "Trust the certificate?</qt>")
0910                         .arg(cert.commonName(), get_fingerprint(cert)),
0911                     QMessageBox::Yes | QMessageBox::No,
0912                     QMessageBox::No);
0913 
0914                 if (ret == QMessageBox::Yes) {
0915                     self_signed_verify_cert = cert;
0916                     do_verify();
0917                     return;
0918                 }
0919             }
0920         }
0921 
0922         setEnabled(true);
0923 
0924         QMessageBox::information(this, tr("Error"), msg);
0925     }
0926 };
0927 
0928 int main(int argc, char **argv)
0929 {
0930     QCA::Initializer qcaInit;
0931     QApplication     qapp(argc, argv);
0932 
0933     qapp.setApplicationName(MainWin::tr("CMS Signer"));
0934 
0935     if (!QCA::isSupported("cert,crl,cms")) {
0936         QMessageBox::critical(
0937             0,
0938             qapp.applicationName() + ": " + MainWin::tr("Error"),
0939             MainWin::tr(
0940                 "No support for CMS is available.  Please install an appropriate QCA plugin, such as qca-ossl."));
0941         return 1;
0942     }
0943 
0944     QCA::KeyStoreManager::start();
0945 
0946     MainWin mainWin;
0947     mainWin.show();
0948     return qapp.exec();
0949 }
0950 
0951 #include "main.moc"