/* * Copyright (C) 2007 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * */ #include #include #include #include "ui_mainwin.h" #include "prompter.h" #include "mylistview.h" #include "ui_loadstore.h" #include "pkcs11configdlg/pkcs11configdlg.h" #define VERSION "0.0.1" class Icons { public: QPixmap cert, crl, keybundle, pgppub, pgpsec; }; Icons *g_icons = 0; //---------------------------------------------------------------------------- // CertItem //---------------------------------------------------------------------------- class CertItem { public: enum StorageType { File, Entry }; QString name; QCA::CertificateChain chain; bool havePrivate; StorageType storageType; // private storage type bool usable; // storage is accessible QString fileName; QCA::KeyStoreEntry keyStoreEntry; CertItem(); QString toString() const; bool fromString(const QString &in); }; static QString escape(const QString &in) { QString out; for(int n = 0; n < in.length(); ++n) { if(in[n] == '\\') out += "\\\\"; else if(in[n] == ':') out += "\\c"; else if(in[n] == '\n') out += "\\n"; else out += in[n]; } return out; } static QString unescape(const QString &in) { QString out; for(int n = 0; n < in.length(); ++n) { if(in[n] == '\\') { if(n + 1 < in.length()) { ++n; if(in[n] == '\\') out += '\\'; else if(in[n] == 'c') out += ':'; else if(in[n] == 'n') out += '\n'; } } else out += in[n]; } return out; } CertItem::CertItem() : havePrivate(false), storageType(File), usable(false) { } QString CertItem::toString() const { QStringList parts; parts += name; parts += QString::number(chain.count()); foreach(const QCA::Certificate &cert, chain) parts += QCA::Base64().arrayToString(cert.toDER()); if(havePrivate) { if(storageType == File) { parts += "privateFile"; parts += fileName; } else // Entry { parts += "privateEntry"; parts += keyStoreEntry.toString(); } } for(int n = 0; n < parts.count(); ++n) parts[n] = escape(parts[n]); return parts.join(":"); } bool CertItem::fromString(const QString &in) { QStringList parts = in.split(':'); for(int n = 0; n < parts.count(); ++n) parts[n] = unescape(parts[n]); if(parts.count() < 3) return false; name = parts[0]; int chainCount = parts[1].toInt(); if(chainCount < 1 || chainCount > parts.count() - 2) return false; chain.clear(); for(int n = 0; n < chainCount; ++n) { QCA::Certificate cert = QCA::Certificate::fromDER(QCA::Base64().stringToArray(parts[n + 2])); if(cert.isNull()) return false; chain += cert; } int at = chain.count() + 2; if(at < parts.count()) { havePrivate = true; usable = false; if(parts[at] == "privateFile") { storageType = File; fileName = parts[at + 1]; if(QFile::exists(fileName)) usable = true; } else if(parts[at] == "privateEntry") { storageType = Entry; keyStoreEntry = QCA::KeyStoreEntry(parts[at + 1]); if(!keyStoreEntry.isNull()) usable = true; } else return false; } return true; } //---------------------------------------------------------------------------- // CertListModel //---------------------------------------------------------------------------- class CertListModel : public QAbstractListModel { Q_OBJECT public: QList list; CertListModel(QObject *parent = 0) : QAbstractListModel(parent) { } int rowCount(const QModelIndex &parent = QModelIndex()) const { Q_UNUSED(parent); return list.count(); } QVariant data(const QModelIndex &index, int role) const { if(!index.isValid()) return QVariant(); if(index.row() >= list.count()) return QVariant(); if(role == Qt::DisplayRole) return list[index.row()].name; else if(role == Qt::EditRole) return list[index.row()].name; else if(role == Qt::DecorationRole && g_icons) { const CertItem &i = list[index.row()]; if(i.havePrivate) return g_icons->keybundle; else return g_icons->cert; } else return QVariant(); } Qt::ItemFlags flags(const QModelIndex &index) const { if(!index.isValid()) return Qt::ItemIsEnabled; return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; } bool setData(const QModelIndex &index, const QVariant &value, int role) { if(index.isValid() && role == Qt::EditRole) { QString str = value.toString(); list[index.row()].name = str; emit dataChanged(index, index); return true; } return false; } void addItems(const QList &items) { if(items.isEmpty()) return; beginInsertRows(QModelIndex(), list.size(), list.size() + items.count() - 1); list += items; endInsertRows(); } void addItem(const CertItem &i) { beginInsertRows(QModelIndex(), list.size(), list.size()); list += i; endInsertRows(); } void removeItem(int at) { beginRemoveRows(QModelIndex(), at, at); list.removeAt(at); endRemoveRows(); } QString getUniqueName(const QString &name) { int num = 1; while(1) { QString tryname; if(num == 1) tryname = name; else tryname = name + QString(" (%1)").arg(num); bool found = false; foreach(const CertItem &i, list) { if(i.name == tryname) { found = true; break; } } if(!found) return tryname; ++num; } } }; //---------------------------------------------------------------------------- // Operation //---------------------------------------------------------------------------- class Operation : public QObject { Q_OBJECT public: Operation(QObject *parent = 0) : QObject(parent) { } signals: void error(const QString &str); }; static QString validityToString(QCA::Validity v) { QString s; switch(v) { case QCA::ValidityGood: s = Operation::tr("Validated"); break; case QCA::ErrorRejected: s = Operation::tr("Root CA is marked to reject the specified purpose"); break; case QCA::ErrorUntrusted: s = Operation::tr("Certificate not trusted for the required purpose"); break; case QCA::ErrorSignatureFailed: s = Operation::tr("Invalid signature"); break; case QCA::ErrorInvalidCA: s = Operation::tr("Invalid CA certificate"); break; case QCA::ErrorInvalidPurpose: s = Operation::tr("Invalid certificate purpose"); break; case QCA::ErrorSelfSigned: s = Operation::tr("Certificate is self-signed"); break; case QCA::ErrorRevoked: s = Operation::tr("Certificate has been revoked"); break; case QCA::ErrorPathLengthExceeded: s = Operation::tr("Maximum certificate chain length exceeded"); break; case QCA::ErrorExpired: s = Operation::tr("Certificate has expired"); break; case QCA::ErrorExpiredCA: s = Operation::tr("CA has expired"); break; case QCA::ErrorValidityUnknown: default: s = Operation::tr("General certificate validation error"); break; } return s; } static QString smErrorToString(QCA::SecureMessage::Error e) { QMap map; map[QCA::SecureMessage::ErrorPassphrase] = Operation::tr("Invalid passphrase"); map[QCA::SecureMessage::ErrorFormat] = Operation::tr("Bad input format"); map[QCA::SecureMessage::ErrorSignerExpired] = Operation::tr("Signer key is expired"); map[QCA::SecureMessage::ErrorSignerInvalid] = Operation::tr("Signer key is invalid"); map[QCA::SecureMessage::ErrorEncryptExpired] = Operation::tr("Encrypting key is expired"); map[QCA::SecureMessage::ErrorEncryptUntrusted] = Operation::tr("Encrypting key is untrusted"); map[QCA::SecureMessage::ErrorEncryptInvalid] = Operation::tr("Encrypting key is invalid"); map[QCA::SecureMessage::ErrorNeedCard] = Operation::tr("Card was needed but not found"); map[QCA::SecureMessage::ErrorCertKeyMismatch] = Operation::tr("Certificate and private key don't match"); map[QCA::SecureMessage::ErrorUnknown] = Operation::tr("General error"); return map[e]; } class SignOperation : public Operation { Q_OBJECT private: QByteArray in; CertItem *item; QCA::CMS *cms; QCA::KeyLoader *loader; QCA::KeyBundle key; QCA::SecureMessage *msg; public: SignOperation(const QByteArray &_in, CertItem *_item, QCA::CMS *_cms, QObject *parent = 0) : Operation(parent), in(_in), item(_item), cms(_cms), loader(0), msg(0) { if(item->storageType == CertItem::File) { loader = new QCA::KeyLoader(this); connect(loader, SIGNAL(finished()), SLOT(loaded())); loader->loadKeyBundleFromFile(item->fileName); } else // Entry { key = item->keyStoreEntry.keyBundle(); QMetaObject::invokeMethod(this, "do_sign", Qt::QueuedConnection); } } ~SignOperation() { } signals: void finished(const QString &sig); private slots: void loaded() { if(loader->convertResult() != QCA::ConvertGood) { emit error(tr("Error opening key file.")); return; } key = loader->keyBundle(); delete loader; loader = 0; do_sign(); } void do_sign() { //printf("do_sign\n"); QCA::SecureMessageKey signer; signer.setX509CertificateChain(key.certificateChain()); signer.setX509PrivateKey(key.privateKey()); msg = new QCA::SecureMessage(cms); connect(msg, SIGNAL(finished()), SLOT(msg_finished())); msg->setFormat(QCA::SecureMessage::Ascii); msg->setSigner(signer); msg->startSign(QCA::SecureMessage::Detached); update(); } void update() { //printf("update\n"); QByteArray buf = in.mid(0, 16384); // 16k chunks in = in.mid(buf.size()); msg->update(buf); if(in.isEmpty()) msg->end(); else QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); } void msg_finished() { //printf("msg_finished\n"); if(!msg->success()) { QString str = smErrorToString(msg->errorCode()); delete msg; msg = 0; emit error(tr("Error during sign operation.\nReason: %1").arg(str)); return; } QByteArray result = msg->signature(); delete msg; msg = 0; emit finished(QString::fromLatin1(result)); } }; class VerifyOperation : public Operation { Q_OBJECT private: QByteArray in, sig; QCA::CMS *cms; QCA::SecureMessage *msg; public: VerifyOperation(const QByteArray &_in, const QByteArray &_sig, QCA::CMS *_cms, QObject *parent = 0) : Operation(parent), in(_in), sig(_sig), cms(_cms), msg(0) { //printf("do_verify\n"); msg = new QCA::SecureMessage(cms); connect(msg, SIGNAL(finished()), SLOT(msg_finished())); msg->setFormat(QCA::SecureMessage::Ascii); msg->startVerify(sig); QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); } signals: void finished(); private slots: void update() { //printf("update\n"); QByteArray buf = in.mid(0, 16384); // 16k chunks in = in.mid(buf.size()); msg->update(buf); if(in.isEmpty()) msg->end(); else QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); } void msg_finished() { //printf("msg_finished\n"); if(!msg->success()) { QString str = smErrorToString(msg->errorCode()); delete msg; msg = 0; emit error(tr("Error during verify operation.\nReason: %1").arg(str)); return; } QCA::SecureMessageSignature signer = msg->signer(); QCA::SecureMessageSignature::IdentityResult r = signer.identityResult(); delete msg; msg = 0; if(r != QCA::SecureMessageSignature::Valid) { QString str; if(r == QCA::SecureMessageSignature::InvalidSignature) str = tr("Invalid signature"); else if(r == QCA::SecureMessageSignature::InvalidKey) str = tr("Invalid key: %1").arg(validityToString(signer.keyValidity())); else if(r == QCA::SecureMessageSignature::NoKey) str = tr("Key not found"); else // unknown str = tr("Unknown"); emit error(tr("Verification failed!\nReason: %1").arg(str)); return; } emit finished(); } }; QAction *actionView, *actionRename, *actionRemove; MyListView::MyListView(QWidget *parent) : QListView(parent) { } void MyListView::contextMenuEvent(QContextMenuEvent *event) { QItemSelection selection = selectionModel()->selection(); if(selection.indexes().isEmpty()) return; QMenu menu(this); menu.addAction(actionView); menu.addAction(actionRename); menu.addAction(actionRemove); menu.exec(event->globalPos()); } /*static QString entryTypeToString(QCA::KeyStoreEntry::Type type) { QString out; switch(type) { case QCA::KeyStoreEntry::TypeKeyBundle: out = "X"; break; case QCA::KeyStoreEntry::TypeCertificate: out = "C"; break; case QCA::KeyStoreEntry::TypeCRL: out = "R"; break; case QCA::KeyStoreEntry::TypePGPSecretKey: out = "S"; break; case QCA::KeyStoreEntry::TypePGPPublicKey: out = "P"; break; default: out = "U"; break; } return out; }*/ static QPixmap entryTypeToIcon(QCA::KeyStoreEntry::Type type) { QPixmap out; switch(type) { case QCA::KeyStoreEntry::TypeKeyBundle: out = g_icons->keybundle; break; case QCA::KeyStoreEntry::TypeCertificate: out = g_icons->cert; break; case QCA::KeyStoreEntry::TypeCRL: out = g_icons->crl; break; case QCA::KeyStoreEntry::TypePGPSecretKey: out = g_icons->pgpsec; break; case QCA::KeyStoreEntry::TypePGPPublicKey: out = g_icons->pgppub; break; default: break; } return out; } class KeyStoreModel : public QStandardItemModel { Q_OBJECT public: QCA::KeyStoreManager ksm; QList stores; QList storeItems; QList< QList > storeEntries; QList< QList > storeEntryItems; KeyStoreModel(QObject *parent = 0) : QStandardItemModel(parent), ksm(this) { // make sure keystores are started QCA::KeyStoreManager::start(); connect(&ksm, SIGNAL(keyStoreAvailable(const QString &)), SLOT(ks_available(const QString &))); QStringList list = ksm.keyStores(); foreach(const QString &s, list) ks_available(s); } private slots: void ks_available(const QString &keyStoreId) { QCA::KeyStore *ks = new QCA::KeyStore(keyStoreId, &ksm); // TODO: only list non-pgp identity stores //if(!ks->holdsIdentities() || ks->type() == QCA::KeyStore::PGPKeyring) // return; connect(ks, SIGNAL(updated()), SLOT(ks_updated())); connect(ks, SIGNAL(unavailable()), SLOT(ks_unavailable())); stores += ks; ks->startAsynchronousMode(); QStandardItem *item = new QStandardItem(ks->name()); storeItems += item; storeEntries += QList(); storeEntryItems += QList(); appendRow(item); } void ks_updated() { QCA::KeyStore *ks = (QCA::KeyStore *)sender(); int at = stores.indexOf(ks); QList entries = ks->entryList(); // TODO: only list keybundles /*for(int n = 0; n < entries.count(); ++n) { if(entries[n].type() != QCA::KeyStoreEntry::TypeKeyBundle) { entries.removeAt(n); --n; // adjust position } }*/ storeEntries[at] = entries; storeEntryItems[at].clear(); // fake CRL, just to show off the icon /*if(ks->type() == QCA::KeyStore::System) { QStandardItem *item = new QStandardItem(entryTypeToIcon(QCA::KeyStoreEntry::TypeCRL), "Santa's Naughty List"); storeEntryItems[at] += item; storeItems[at]->appendRow(item); }*/ foreach(const QCA::KeyStoreEntry &entry, entries) { QStandardItem *item = new QStandardItem(entryTypeToIcon(entry.type()), entry.name()); storeEntryItems[at] += item; storeItems[at]->appendRow(item); } } void ks_unavailable() { QCA::KeyStore *ks = (QCA::KeyStore *)sender(); Q_UNUSED(ks); // TODO: remove from internal list and display } }; class LoadStore : public QDialog { Q_OBJECT private: Ui_LoadStore ui; KeyStoreModel *model; QCA::KeyStoreEntry cur_entry; public: LoadStore(QWidget *parent = 0) : QDialog(parent) { ui.setupUi(this); ui.lv_stores->header()->hide(); ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); model = new KeyStoreModel(this); ui.lv_stores->setModel(model); connect(ui.lv_stores->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), SLOT(stores_selectionChanged(const QItemSelection &, const QItemSelection &))); } signals: void entrySelected(const QCA::KeyStoreEntry &entry); protected slots: virtual void accept() { QCA::KeyStoreEntry entry = cur_entry; QDialog::accept(); emit entrySelected(entry); } private slots: void stores_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { Q_UNUSED(deselected); bool valid = false; QCA::KeyStoreEntry entry; { QModelIndex index; if(!selected.indexes().isEmpty()) index = selected.indexes().first(); if(index.isValid()) { QModelIndex pindex = index.parent(); // are we clicking on an entry? if(pindex.isValid()) { int store_at = pindex.row(); int entry_at = index.row(); entry = model->storeEntries[store_at][entry_at]; if(entry.type() == QCA::KeyStoreEntry::TypeKeyBundle) valid = true; } } } if(valid) cur_entry = entry; else cur_entry = QCA::KeyStoreEntry(); QPushButton *ok = ui.buttonBox->button(QDialogButtonBox::Ok); if(valid && !ok->isEnabled()) ok->setEnabled(true); else if(!valid && ok->isEnabled()) ok->setEnabled(false); } }; class MyPrompter : public Prompter { Q_OBJECT private: QMap known; public: MyPrompter(QObject *parent = 0) : Prompter(parent) { } protected: virtual QCA::SecureArray knownPassword(const QCA::Event &event) { if(event.source() == QCA::Event::Data && !event.fileName().isEmpty()) return known.value(event.fileName()); else return QCA::SecureArray(); } virtual void userSubmitted(const QCA::SecureArray &password, const QCA::Event &event) { if(event.source() == QCA::Event::Data && !event.fileName().isEmpty()) known[event.fileName()] = password; } }; class MainWin : public QMainWindow { Q_OBJECT private: Ui_MainWin ui; MyPrompter *prompter; QCA::KeyLoader *keyLoader; QString keyLoader_fileName; CertListModel *users, *roots; QCA::CMS *cms; Operation *op; public: MainWin(QWidget *parent = 0) : QMainWindow(parent), keyLoader(0), op(0) { ui.setupUi(this); g_icons = new Icons; g_icons->cert = QPixmap(":/gfx/icons/cert16.png"); g_icons->crl = QPixmap(":/gfx/icons/crl16.png"); g_icons->keybundle = QPixmap(":/gfx/icons/keybundle16.png"); g_icons->pgppub = QPixmap(":/gfx/icons/publickey16.png"); g_icons->pgpsec = QPixmap(":/gfx/icons/keypair16.png"); if(g_icons->cert.isNull() || g_icons->crl.isNull() || g_icons->keybundle.isNull() || g_icons->pgppub.isNull() || g_icons->pgpsec.isNull()) printf("warning: not all icons loaded\n"); actionView = new QAction(tr("&View"), this); actionRename = new QAction(tr("Re&name"), this); actionRemove = new QAction(tr("Rem&ove"), this); // TODO actionView->setEnabled(false); connect(ui.actionLoadIdentityFile, SIGNAL(triggered()), SLOT(load_file())); connect(ui.actionLoadIdentityEntry, SIGNAL(triggered()), SLOT(load_device())); connect(ui.actionLoadAuthority, SIGNAL(triggered()), SLOT(load_root())); connect(ui.actionConfigurePkcs11, SIGNAL(triggered()), SLOT(mod_config())); connect(ui.actionQuit, SIGNAL(triggered()), SLOT(close())); connect(ui.actionAbout, SIGNAL(triggered()), SLOT(about())); connect(ui.pb_sign, SIGNAL(clicked()), SLOT(do_sign())); connect(ui.pb_verify, SIGNAL(clicked()), SLOT(do_verify())); //connect(actionView, SIGNAL(triggered()), SLOT(item_view())); connect(actionRename, SIGNAL(triggered()), SLOT(item_rename())); connect(actionRemove, SIGNAL(triggered()), SLOT(item_remove())); ui.pb_sign->setEnabled(false); prompter = new MyPrompter(this); users = new CertListModel(this); ui.lv_users->setModel(users); connect(ui.lv_users->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), SLOT(users_selectionChanged(const QItemSelection &, const QItemSelection &))); roots = new CertListModel(this); ui.lv_authorities->setModel(roots); // FIXME: is this redundant? ui.lv_users->model = users; ui.lv_authorities->model = roots; cms = new QCA::CMS(this); QStringList ulist, rlist; { QSettings settings("Affinix", "CMS Signer"); ulist = settings.value("users").toStringList(); rlist = settings.value("roots").toStringList(); } QList userslist; foreach(const QString &s, ulist) { CertItem i; if(i.fromString(s)) userslist += i; } QList rootslist; foreach(const QString &s, rlist) { CertItem i; if(i.fromString(s)) rootslist += i; } users->addItems(userslist); roots->addItems(rootslist); } ~MainWin() { QStringList ulist; foreach(const CertItem &i, users->list) ulist += i.toString(); QStringList rlist; foreach(const CertItem &i, roots->list) rlist += i.toString(); QSettings settings("Affinix", "CMS Signer"); settings.setValue("users", ulist); settings.setValue("roots", rlist); delete g_icons; g_icons = 0; } private slots: void load_file() { QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), QString(), tr("X.509 Identities (*.p12 *.pfx)")); if(fileName.isEmpty()) return; setEnabled(false); keyLoader = new QCA::KeyLoader(this); connect(keyLoader, SIGNAL(finished()), SLOT(load_file_finished())); keyLoader_fileName = fileName; keyLoader->loadKeyBundleFromFile(fileName); } void load_device() { LoadStore *w = new LoadStore(this); w->setAttribute(Qt::WA_DeleteOnClose, true); w->setWindowModality(Qt::WindowModal); connect(w, SIGNAL(entrySelected(const QCA::KeyStoreEntry &)), SLOT(load_device_finished(const QCA::KeyStoreEntry &))); w->show(); } void load_root() { QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), QString(), tr("X.509 Certificates (*.pem *.crt)")); if(fileName.isEmpty()) return; QCA::Certificate cert = QCA::Certificate::fromPEMFile(fileName); if(cert.isNull()) { QMessageBox::information(this, tr("Error"), tr("Error opening certificate file.")); return; } QString name = roots->getUniqueName(cert.commonName()); // TODO: check for duplicate entries? CertItem i; i.name = name; i.chain += cert; roots->addItem(i); } void load_file_finished() { // TODO: show more descriptive reason? if(keyLoader->convertResult() != QCA::ConvertGood) { setEnabled(true); QMessageBox::information(this, tr("Error"), tr("Error opening key file.")); return; } QCA::KeyBundle kb = keyLoader->keyBundle(); delete keyLoader; keyLoader = 0; QCA::CertificateChain chain = kb.certificateChain(); QCA::Certificate cert = chain.primary(); QString name = users->getUniqueName(cert.commonName()); // TODO: check for duplicate identities? CertItem i; i.name = name; i.chain = chain; i.havePrivate = true; i.storageType = CertItem::File; i.fileName = keyLoader_fileName; i.usable = true; users->addItem(i); ui.lv_users->selectionModel()->select(users->index(users->list.count()-1), QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current); setEnabled(true); } void load_device_finished(const QCA::KeyStoreEntry &entry) { QCA::KeyBundle kb = entry.keyBundle(); QCA::CertificateChain chain = kb.certificateChain(); QCA::Certificate cert = chain.primary(); QString name = users->getUniqueName(entry.name()); // TODO: check for duplicate identities? CertItem i; i.name = name; i.chain = chain; i.havePrivate = true; i.storageType = CertItem::Entry; i.keyStoreEntry = entry; i.usable = true; users->addItem(i); ui.lv_users->selectionModel()->select(users->index(users->list.count()-1), QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current); setEnabled(true); } void mod_config() { if(!Pkcs11ConfigDlg::isSupported()) { QMessageBox::information(this, tr("Error"), tr("No provider available supporting standard PKCS#11 configuration.")); return; } Pkcs11ConfigDlg *w = new Pkcs11ConfigDlg(this); w->setAttribute(Qt::WA_DeleteOnClose, true); w->setWindowModality(Qt::WindowModal); w->show(); } void users_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { Q_UNUSED(deselected); if(!selected.indexes().isEmpty() && !ui.pb_sign->isEnabled()) ui.pb_sign->setEnabled(true); else if(selected.indexes().isEmpty() && ui.pb_sign->isEnabled()) ui.pb_sign->setEnabled(false); } /*void item_view() { if(ui.lv_identities->hasFocus()) { QItemSelection selection = ui.lv_identities->selectionModel()->selection(); if(selection.indexes().isEmpty()) return; QModelIndex index = selection.indexes().first(); identity_view(index.row()); } else // lv_known { QItemSelection selection = ui.lv_known->selectionModel()->selection(); if(selection.indexes().isEmpty()) return; QModelIndex index = selection.indexes().first(); known_view(index.row()); } }*/ void item_rename() { if(ui.lv_users->hasFocus()) { QItemSelection selection = ui.lv_users->selectionModel()->selection(); if(selection.indexes().isEmpty()) return; QModelIndex index = selection.indexes().first(); users_rename(index.row()); } else // lv_authorities { QItemSelection selection = ui.lv_authorities->selectionModel()->selection(); if(selection.indexes().isEmpty()) return; QModelIndex index = selection.indexes().first(); roots_rename(index.row()); } } void item_remove() { if(ui.lv_users->hasFocus()) { QItemSelection selection = ui.lv_users->selectionModel()->selection(); if(selection.indexes().isEmpty()) return; QModelIndex index = selection.indexes().first(); users_remove(index.row()); } else // lv_authorities { QItemSelection selection = ui.lv_authorities->selectionModel()->selection(); if(selection.indexes().isEmpty()) return; QModelIndex index = selection.indexes().first(); roots_remove(index.row()); } } /*void identity_view(int at) { printf("identity_view: %d\n", at); }*/ void users_rename(int at) { QModelIndex index = users->index(at); ui.lv_users->setFocus(); ui.lv_users->setCurrentIndex(index); ui.lv_users->selectionModel()->select(index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current); ui.lv_users->edit(index); } void users_remove(int at) { users->removeItem(at); } /*void known_view(int at) { printf("known_view: %d\n", at); }*/ void roots_rename(int at) { QModelIndex index = roots->index(at); ui.lv_authorities->setFocus(); ui.lv_authorities->setCurrentIndex(index); ui.lv_authorities->selectionModel()->select(index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current); ui.lv_authorities->edit(index); } void roots_remove(int at) { roots->removeItem(at); } void do_sign() { QItemSelection selection = ui.lv_users->selectionModel()->selection(); if(selection.indexes().isEmpty()) return; QModelIndex index = selection.indexes().first(); int at = index.row(); op = new SignOperation(ui.te_data->toPlainText().toUtf8(), &users->list[at], cms, this); connect(op, SIGNAL(finished(const QString &)), SLOT(sign_finished(const QString &))); connect(op, SIGNAL(error(const QString &)), SLOT(op_error(const QString &))); } void do_verify() { // prepare root certs QCA::CertificateCollection col; // system store col += QCA::systemStore(); // additional roots configured in application foreach(const CertItem &i, roots->list) col.addCertificate(i.chain.primary()); // consider self-signed users as roots // (it is therefore not possible with this application to // have people in your keyring that you don't trust) foreach(const CertItem &i, users->list) { QCA::Certificate cert = i.chain.primary(); if(cert.isSelfSigned()) col.addCertificate(cert); } cms->setTrustedCertificates(col); op = new VerifyOperation(ui.te_data->toPlainText().toUtf8(), ui.te_sig->toPlainText().toUtf8(), cms, this); connect(op, SIGNAL(finished()), SLOT(verify_finished())); connect(op, SIGNAL(error(const QString &)), SLOT(op_error(const QString &))); } void about() { int ver = qcaVersion(); int maj = (ver >> 16) & 0xff; int min = (ver >> 8) & 0xff; int bug = ver & 0xff; QString verstr; verstr.sprintf("%d.%d.%d", maj, min, bug); QString str; str += tr("CMS Signer version %1 by Justin Karneges").arg(VERSION) + '\n'; str += tr("A simple tool for creating and verifying digital signatures.") + '\n'; str += '\n'; str += tr("Using QCA version %1").arg(verstr) + '\n'; str += '\n'; str += tr("Icons by Jason Kim") + '\n'; QCA::ProviderList list = QCA::providers(); foreach(QCA::Provider *p, list) { QString credit = p->credit(); if(!credit.isEmpty()) { str += '\n'; str += credit; } } QMessageBox::about(this, tr("About CMS Signer"), str); } void sign_finished(const QString &sig) { ui.te_sig->setPlainText(sig); } void verify_finished() { QMessageBox::information(this, tr("Verify"), tr("Signature verified successfully.")); } void op_error(const QString &msg) { QMessageBox::information(this, tr("Error"), msg); delete op; op = 0; } }; int main(int argc, char **argv) { QCA::Initializer qcaInit; QApplication qapp(argc, argv); qapp.setApplicationName(MainWin::tr("CMS Signer")); if(!QCA::isSupported("cms")) { QMessageBox::critical(0, qapp.applicationName() + ": " + MainWin::tr("Error"), MainWin::tr("No support for CMS is available. Please install an appropriate QCA plugin, such as qca-openssl.")); return 1; } MainWin mainWin; mainWin.show(); return qapp.exec(); } #include "main.moc"