4
0
mirror of https://github.com/QuasarApp/qca.git synced 2025-04-29 13:04:31 +00:00

1290 lines
28 KiB
C++
Raw Normal View History

#include <QtCore>
#include <QtGui>
#include <QtCrypto>
#include "ui_mainwin.h"
#include "mylistview.h"
#include "ui_loadstore.h"
#include "ui_modconfig.h"
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
out += in[n];
}
return out;
}
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
out += in[n];
}
return out;
}
class IdentityItem
{
public:
enum Type { File, Entry };
Type type;
QString name;
QString fileName;
QCA::KeyStoreEntry entry;
QCA::SecureArray password; // for runtime of File type only
bool usable;
QString toString() const
{
QStringList parts;
if(type == File)
{
parts += "file";
parts += name;
parts += fileName;
}
else // Entry
{
parts += "entry";
parts += name;
parts += entry.toString();
}
for(int n = 0; n < parts.count(); ++n)
parts[n] = escape(parts[n]);
return parts.join(":");
}
bool 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;
usable = false;
if(parts[0] == "file")
{
type = File;
name = parts[1];
fileName = parts[2];
if(QFile::exists(fileName))
usable = true;
}
else if(parts[0] == "entry")
{
type = Entry;
name = parts[1];
entry = QCA::KeyStoreEntry(parts[2]);
if(!entry.isNull())
usable = true;
}
else
return false;
return true;
}
};
class IdentityListModel : public QAbstractListModel
{
Q_OBJECT
//private:
public:
QList<IdentityItem> list;
public:
IdentityListModel(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::DecorationRole)
{
const IdentityItem &i = list[index.row()];
if(i.type == IdentityItem::File)
return QPixmap(":/gfx/key.png");
else // Entry
return QPixmap(":/gfx/key.png");
}
else
return QVariant();
}
void addItem(const IdentityItem &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 IdentityItem &i, list)
{
if(i.name == tryname)
{
found = true;
break;
}
}
if(!found)
return tryname;
++num;
}
}
};
class KnownItem
{
public:
QString name;
QCA::Certificate cert;
QString toString() const
{
QStringList parts;
parts += name;
parts += QCA::Base64().arrayToString(cert.toDER());
for(int n = 0; n < parts.count(); ++n)
parts[n] = escape(parts[n]);
return parts.join(":");
}
bool fromString(const QString &in)
{
QStringList parts = in.split(':');
for(int n = 0; n < parts.count(); ++n)
parts[n] = unescape(parts[n]);
if(parts.count() < 2)
return false;
name = parts[0];
cert = QCA::Certificate::fromDER(QCA::Base64().stringToArray(parts[1]));
if(cert.isNull())
return false;
return true;
}
};
class KnownListModel : public QAbstractListModel
{
Q_OBJECT
//private:
public:
QList<KnownItem> list;
public:
KnownListModel(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::DecorationRole)
{
return QPixmap(":/gfx/key.png");
}
else
return QVariant();
}
void addItem(const KnownItem &i)
{
beginInsertRows(QModelIndex(), list.size(), list.size());
list += i;
endInsertRows();
}
void removeItem(int at)
{
beginRemoveRows(QModelIndex(), at, at);
list.removeAt(at);
endRemoveRows();
}
};
class Operation : public QObject
{
Q_OBJECT
public:
Operation(QObject *parent = 0) :
QObject(parent)
{
}
signals:
void error(const QString &str);
};
class SignOperation : public Operation
{
Q_OBJECT
private:
QByteArray in;
IdentityItem *item;
QCA::CMS *cms;
QCA::KeyLoader *loader;
QCA::KeyBundle key;
QCA::SecureMessage *msg;
public:
SignOperation(const QByteArray &_in, IdentityItem *_item, QCA::CMS *_cms, QObject *parent = 0) :
Operation(parent),
in(_in),
item(_item),
cms(_cms),
loader(0),
msg(0)
{
if(item->type == IdentityItem::File)
{
loader = new QCA::KeyLoader(this);
connect(loader, SIGNAL(finished()), SLOT(loaded()));
loader->loadKeyBundleFromFile(item->fileName);
}
else // Entry
{
key = item->entry.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())
{
delete msg;
msg = 0;
emit error(tr("Error during sign operation."));
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())
{
delete msg;
msg = 0;
emit error(tr("Error during verify operation."));
return;
}
QCA::SecureMessageSignature signer = msg->signer();
QCA::SecureMessageSignature::IdentityResult r = signer.identityResult();
delete msg;
msg = 0;
if(r != QCA::SecureMessageSignature::Valid)
{
emit error(tr("Verification failed! [%1]").arg((int)r));
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;
}
class KeyStoreModel : public QStandardItemModel
{
Q_OBJECT
public:
QCA::KeyStoreManager ksm;
QList<QCA::KeyStore*> stores;
QList<QStandardItem*> storeItems;
QList< QList<QCA::KeyStoreEntry> > storeEntries;
QList< QList<QStandardItem*> > 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);
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<QCA::KeyStoreEntry>();
storeEntryItems += QList<QStandardItem*>();
appendRow(item);
}
void ks_updated()
{
QCA::KeyStore *ks = (QCA::KeyStore *)sender();
int at = stores.indexOf(ks);
QList<QCA::KeyStoreEntry> entries = ks->entryList();
// 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();
foreach(const QCA::KeyStoreEntry &entry, entries)
{
QStandardItem *item = new QStandardItem(entryTypeToString(entry.type()) + " - " + entry.name());
storeEntryItems[at] += item;
storeItems[at]->appendRow(item);
}
}
void ks_unavailable()
{
QCA::KeyStore *ks = (QCA::KeyStore *)sender();
Q_UNUSED(ks);
// TODO
}
};
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);
}
};
// support for the 'http://affinix.com/qca/forms/qca-pkcs11#1.0' provider form
class ModItem
{
public:
QString name; // must be unique among all modules
QString library; // dll
bool enabled;
bool allow_protected_auth;
bool cert_private;
int private_mask;
QString slotevent_method;
int slotevent_timeout;
ModItem() :
enabled(false),
allow_protected_auth(true),
cert_private(false),
private_mask(0),
slotevent_method("auto"),
slotevent_timeout(0)
{
}
};
class ModListModel : public QAbstractListModel
{
Q_OBJECT
//private:
public:
QList<ModItem> list;
public:
ModListModel(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::DecorationRole)
{
return QPixmap(":/gfx/key.png");
}*/
else
return QVariant();
}
void addItem(const ModItem &i)
{
beginInsertRows(QModelIndex(), list.size(), list.size());
list += i;
endInsertRows();
}
void removeItem(int at)
{
beginRemoveRows(QModelIndex(), at, at);
list.removeAt(at);
endRemoveRows();
}
void updateItem(int at)
{
QModelIndex i = index(at);
emit dataChanged(i, i);
}
};
class ModConfig : public QDialog
{
Q_OBJECT
private:
Ui_ModConfig ui;
ModListModel *model;
QString providerName;
QVariantMap config;
public:
ModConfig(const QString &_providerName, const QVariantMap &_config, QWidget *parent = 0) :
QDialog(parent),
providerName(_providerName),
config(_config)
{
ui.setupUi(this);
model = new ModListModel(this);
ui.lv_modules->setModel(model);
connect(ui.lv_modules->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), SLOT(modules_selectionChanged(const QItemSelection &, const QItemSelection &)));
#if defined(Q_OS_WIN)
ui.lb_file->setText("Module File (.dll):");
#elif defined(Q_OS_MAC)
ui.lb_file->setText("Module File (.dylib):");
#else
ui.lb_file->setText("Module File (.so):");
#endif
connect(ui.pb_add, SIGNAL(clicked()), SLOT(mod_add()));
connect(ui.pb_remove, SIGNAL(clicked()), SLOT(mod_remove()));
connect(ui.pb_browse, SIGNAL(clicked()), SLOT(mod_browse()));
connect(ui.le_name, SIGNAL(textEdited(const QString &)), SLOT(name_edited(const QString &)));
connect(ui.le_library, SIGNAL(textEdited(const QString &)), SLOT(library_edited(const QString &)));
ui.pb_remove->setEnabled(false);
ui.gb_details->setEnabled(false);
// TODO: config integrity check
// FIXME: this reorders the provider config. we may not want that
for(int n = 0; n < 10; ++n)
{
ModItem i;
QString prefix = QString().sprintf("provider_%02d_", n);
i.name = config[prefix + "name"].toString();
if(i.name.isEmpty())
continue;
i.library = config[prefix + "library"].toString();
i.enabled = config[prefix + "enabled"].toBool();
i.allow_protected_auth = config[prefix + "allow_protected_authentication"].toBool();
i.cert_private = config[prefix + "cert_private"].toBool();
i.private_mask = config[prefix + "private_mask"].toInt();
i.slotevent_method = config[prefix + "slotevent_method"].toString();
i.slotevent_timeout = config[prefix + "slotevent_method"].toInt();
model->addItem(i);
}
if(!model->list.isEmpty())
ui.lv_modules->selectionModel()->select(model->index(0), QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
}
protected slots:
virtual void accept()
{
for(int n = 0; n < 10 || n < model->list.count(); ++n)
{
ModItem i;
if(n < model->list.count())
i = model->list[n];
else
i = ModItem(); // default for padded items
QString prefix = QString().sprintf("provider_%02d_", n);
config[prefix + "name"] = i.name;
config[prefix + "library"] = i.library;
config[prefix + "enabled"] = i.enabled;
config[prefix + "allow_protected_authentication"] = i.allow_protected_auth;
config[prefix + "cert_private"] = i.cert_private;
config[prefix + "private_mask"] = i.private_mask;
config[prefix + "slotevent_method"] = i.slotevent_method;
config[prefix + "slotevent_method"] = i.slotevent_timeout;
}
QCA::setProviderConfig(providerName, config);
QCA::saveProviderConfig(providerName);
QDialog::accept();
}
private slots:
void modules_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
Q_UNUSED(deselected);
if(!selected.indexes().isEmpty())
{
if(!ui.pb_remove->isEnabled())
{
ui.pb_remove->setEnabled(true);
ui.gb_details->setEnabled(true);
}
QModelIndex index = selected.indexes().first();
// TODO: ensure plaintext only
ui.le_name->setText(model->list[index.row()].name);
ui.le_library->setText(model->list[index.row()].library);
}
else if(selected.indexes().isEmpty() && ui.pb_remove->isEnabled())
{
ui.le_name->setText("");
ui.le_library->setText("");
ui.pb_remove->setEnabled(false);
ui.gb_details->setEnabled(false);
}
}
void name_edited(const QString &text)
{
QItemSelection selection = ui.lv_modules->selectionModel()->selection();
if(selection.indexes().isEmpty())
return;
QModelIndex index = selection.indexes().first();
int at = index.row();
model->list[at].name = text;
model->updateItem(at);
}
void library_edited(const QString &text)
{
QItemSelection selection = ui.lv_modules->selectionModel()->selection();
if(selection.indexes().isEmpty())
return;
QModelIndex index = selection.indexes().first();
int at = index.row();
model->list[at].library = text;
}
void mod_add()
{
ModItem i;
i.name = "New Module";
i.enabled = true;
model->addItem(i);
ui.lv_modules->selectionModel()->select(model->index(model->list.count()-1), QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
ui.le_name->setFocus();
ui.le_name->selectAll();
}
void mod_remove()
{
QItemSelection selection = ui.lv_modules->selectionModel()->selection();
if(selection.indexes().isEmpty())
return;
QModelIndex index = selection.indexes().first();
model->removeItem(index.row());
}
void mod_browse()
{
QString spec;
// FIXME: is this too restrictive?
#if defined(Q_OS_WIN)
spec = "(*.dll)";
#elif defined(Q_OS_MAC)
spec = "(*.dylib)";
#else
spec = "(*.so)";
#endif
QString fileName = QFileDialog::getOpenFileName(this, tr("Select PKCS#11 Module"), QString(), tr("PKCS#11 Modules") + ' ' + spec);
if(fileName.isEmpty())
return;
ui.le_library->setText(fileName);
library_edited(fileName);
}
};
class MainWin : public QMainWindow
{
Q_OBJECT
private:
Ui_MainWin ui;
QCA::EventHandler *eventHandler;
QCA::SecureArray lastPassword;
QCA::KeyLoader *keyLoader;
QString keyLoader_fileName;
IdentityListModel *model;
KnownListModel *known;
QCA::CMS *cms;
Operation *op;
public:
MainWin(QWidget *parent = 0) :
QMainWindow(parent),
keyLoader(0),
op(0)
{
ui.setupUi(this);
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.actionLoad_Identity_From_File, SIGNAL(triggered()), SLOT(load_file()));
connect(ui.actionLoad_Identity_From_Storage_Device, SIGNAL(triggered()), SLOT(load_device()));
connect(ui.actionConfigure_PKCS_11_Modules, SIGNAL(triggered()), SLOT(mod_config()));
connect(ui.actionQuit, SIGNAL(triggered()), SLOT(close()));
connect(ui.actionAbout_CMS_Signer, 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);
eventHandler = new QCA::EventHandler(this);
connect(eventHandler, SIGNAL(eventReady(int, const QCA::Event &)), SLOT(eh_eventReady(int, const QCA::Event &)));
eventHandler->start();
model = new IdentityListModel(this);
ui.lv_identities->setModel(model);
connect(ui.lv_identities->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), SLOT(identities_selectionChanged(const QItemSelection &, const QItemSelection &)));
known = new KnownListModel(this);
ui.lv_known->setModel(known);
ui.lv_identities->model = model;
ui.lv_known->model = known;
cms = new QCA::CMS(this);
}
private slots:
void eh_eventReady(int id, const QCA::Event &event)
{
QString promptType;
if(event.passwordStyle() == QCA::Event::StylePassphrase)
promptType = tr("Passphrase");
else if(event.passwordStyle() == QCA::Event::StylePIN)
promptType = tr("PIN");
else // Password
promptType = tr("Password");
QString promptStr = promptType + ": ";
bool ok;
QString pass = QInputDialog::getText(this, tr("CMS Signer"), promptStr, QLineEdit::Password, QString(), &ok);
if(!ok)
{
eventHandler->reject(id);
return;
}
QCA::SecureArray password = pass.toUtf8();
// cache file passwords
if(event.source() == QCA::Event::Data)
lastPassword = password;
eventHandler->submitPassword(id, password);
}
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_file_finished()
{
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::Certificate cert = kb.certificateChain().primary();
QString name = model->getUniqueName(cert.commonName());
// TODO: check for duplicate identities?
IdentityItem i;
i.type = IdentityItem::File;
i.name = name;
i.fileName = keyLoader_fileName;
i.password = lastPassword;
i.usable = true;
lastPassword.clear();
model->addItem(i);
ui.lv_identities->selectionModel()->select(model->index(model->list.count()-1), QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
// TODO: give unique names to knowns? check for dups also?
KnownItem ki;
ki.name = i.name;
ki.cert = cert;
known->addItem(ki);
setEnabled(true);
}
void load_device_finished(const QCA::KeyStoreEntry &entry)
{
QCA::KeyBundle kb = entry.keyBundle();
QCA::Certificate cert = kb.certificateChain().primary();
QString name = model->getUniqueName(entry.name());
IdentityItem i;
i.type = IdentityItem::Entry;
i.name = name;
i.entry = entry;
i.usable = true;
model->addItem(i);
ui.lv_identities->selectionModel()->select(model->index(model->list.count()-1), QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
KnownItem ki;
ki.name = i.name;
ki.cert = cert;
known->addItem(ki);
setEnabled(true);
}
void mod_config()
{
QCA::ProviderList providers = QCA::providers();
providers += QCA::defaultProvider();
QCA::Provider *provider = 0;
QVariantMap config;
foreach(QCA::Provider *p, providers)
{
config = QCA::getProviderConfig(p->name());
if(!config.isEmpty() && config["formtype"] == "http://affinix.com/qca/forms/qca-pkcs11#1.0")
{
provider = p;
break;
}
}
if(!provider)
{
QMessageBox::information(this, tr("Error"), tr("No provider available supporting standard PKCS#11 configuration."));
return;
}
ModConfig *w = new ModConfig(provider->name(), config, this);
w->setAttribute(Qt::WA_DeleteOnClose, true);
w->setWindowModality(Qt::WindowModal);
w->show();
}
void identities_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_identities->hasFocus())
{
QItemSelection selection = ui.lv_identities->selectionModel()->selection();
if(selection.indexes().isEmpty())
return;
QModelIndex index = selection.indexes().first();
identity_rename(index.row());
}
else // lv_known
{
QItemSelection selection = ui.lv_known->selectionModel()->selection();
if(selection.indexes().isEmpty())
return;
QModelIndex index = selection.indexes().first();
known_rename(index.row());
}
}
void item_remove()
{
if(ui.lv_identities->hasFocus())
{
QItemSelection selection = ui.lv_identities->selectionModel()->selection();
if(selection.indexes().isEmpty())
return;
QModelIndex index = selection.indexes().first();
identity_remove(index.row());
}
else // lv_known
{
QItemSelection selection = ui.lv_known->selectionModel()->selection();
if(selection.indexes().isEmpty())
return;
QModelIndex index = selection.indexes().first();
known_remove(index.row());
}
}
void identity_view(int at)
{
printf("identity_view: %d\n", at);
}
void identity_rename(int at)
{
printf("identity_rename: %d\n", at);
}
void identity_remove(int at)
{
model->removeItem(at);
}
void known_view(int at)
{
printf("known_view: %d\n", at);
}
void known_rename(int at)
{
printf("known_rename: %d\n", at);
}
void known_remove(int at)
{
known->removeItem(at);
}
void do_sign()
{
op = new SignOperation(ui.te_data->toPlainText().toUtf8(), &model->list[0], 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()
{
// get known
QCA::CertificateCollection col;
foreach(const KnownItem &i, known->list)
col.addCertificate(i.cert);
col += QCA::systemStore();
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()
{
QMessageBox::about(this, tr("About CMS Signer"), tr("CMS Signer v0.1\nA simple tool for creating and verifying digital signatures."));
}
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);
if(!QCA::isSupported("cms"))
{
QMessageBox::critical(0, MainWin::tr("CMS Signer: 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"