QCA

certitem.cpp
1 /*
2  Copyright (C) 2007 Justin Karneges <[email protected]>
3 
4  Permission is hereby granted, free of charge, to any person obtaining a copy
5  of this software and associated documentation files (the "Software"), to deal
6  in the Software without restriction, including without limitation the rights
7  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  copies of the Software, and to permit persons to whom the Software is
9  furnished to do so, subject to the following conditions:
10 
11  The above copyright notice and this permission notice shall be included in
12  all copies or substantial portions of the Software.
13 
14  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 */
21 
22 #include "certitem.h"
23 
24 #include "prompter.h"
25 #include <QMessageBox>
26 #include <QtCore>
27 #include <QtCrypto>
28 #include <QtGui>
29 
31 
32 //----------------------------------------------------------------------------
33 // MyPrompter
34 //----------------------------------------------------------------------------
35 class MyPrompter : public Prompter
36 {
37  Q_OBJECT
38 private:
41 
42 public:
43  MyPrompter(QObject *parent = 0)
44  : Prompter(parent)
45  {
46  }
47 
48  void fileSuccess(const QString &fileName)
49  {
50  if (maybe.contains(fileName)) {
51  known[fileName] = maybe[fileName];
52  maybe.remove(fileName);
53  }
54  }
55 
56  void fileFailed(const QString &fileName)
57  {
58  maybe.remove(fileName);
59  known.remove(fileName);
60  }
61 
62 protected:
63  virtual QCA::SecureArray knownPassword(const QCA::Event &event)
64  {
65  if (event.source() == QCA::Event::Data && !event.fileName().isEmpty())
66  return known.value(event.fileName());
67  else
68  return QCA::SecureArray();
69  }
70 
71  virtual void userSubmitted(const QCA::SecureArray &password, const QCA::Event &event)
72  {
73  if (event.source() == QCA::Event::Data && !event.fileName().isEmpty())
74  maybe[event.fileName()] = password;
75  }
76 };
77 
78 //----------------------------------------------------------------------------
79 // CertItem
80 //----------------------------------------------------------------------------
81 static QString escape(const QString &in)
82 {
83  QString out;
84  for (int n = 0; n < in.length(); ++n) {
85  if (in[n] == '\\')
86  out += "\\\\";
87  else if (in[n] == ':')
88  out += "\\c";
89  else if (in[n] == '\n')
90  out += "\\n";
91  else
92  out += in[n];
93  }
94  return out;
95 }
96 
97 static QString unescape(const QString &in)
98 {
99  QString out;
100  for (int n = 0; n < in.length(); ++n) {
101  if (in[n] == '\\') {
102  if (n + 1 < in.length()) {
103  ++n;
104  if (in[n] == '\\')
105  out += '\\';
106  else if (in[n] == 'c')
107  out += ':';
108  else if (in[n] == 'n')
109  out += '\n';
110  }
111  } else
112  out += in[n];
113  }
114  return out;
115 }
116 
117 class CertItem::Private : public QSharedData
118 {
119 public:
120  QString name;
121  QCA::CertificateChain chain;
122  bool havePrivate;
123  StorageType storageType;
124  bool usable;
125 
126  QString fileName;
127  QCA::KeyStoreEntry keyStoreEntry;
128  QString keyStoreEntryString;
129 
130  Private()
131  : havePrivate(false)
132  , storageType(File)
133  , usable(false)
134  {
135  }
136 
137  QString toString() const
138  {
139  QStringList parts;
140 
141  parts += name;
142  parts += QString::number(chain.count());
143  foreach (const QCA::Certificate &cert, chain)
144  parts += QCA::Base64().arrayToString(cert.toDER());
145 
146  if (havePrivate) {
147  if (storageType == File) {
148  parts += "privateFile";
149  parts += fileName;
150  } else // KeyStoreEntry
151  {
152  parts += "privateEntry";
153  if (!keyStoreEntry.isNull())
154  parts += keyStoreEntry.toString();
155  else
156  parts += keyStoreEntryString;
157  }
158  }
159 
160  for (int n = 0; n < parts.count(); ++n)
161  parts[n] = escape(parts[n]);
162  return parts.join(":");
163  }
164 
165  bool fromString(const QString &in)
166  {
167  const QStringList parts = in.split(':');
168  for (int n = 0; n < parts.count(); ++n)
169  parts[n] = unescape(parts[n]);
170 
171  if (parts.count() < 3)
172  return false;
173 
174  name = parts[0];
175  int chainCount = parts[1].toInt();
176  if (chainCount < 1 || chainCount > parts.count() - 2)
177  return false;
178  chain.clear();
179  for (int n = 0; n < chainCount; ++n) {
180  QCA::Certificate cert = QCA::Certificate::fromDER(QCA::Base64().stringToArray(parts[n + 2]).toByteArray());
181  if (cert.isNull())
182  return false;
183  chain += cert;
184  }
185  int at = chain.count() + 2;
186 
187  if (at < parts.count()) {
188  havePrivate = true;
189  usable = false;
190 
191  if (parts[at] == "privateFile") {
192  storageType = File;
193  fileName = parts[at + 1];
194  if (QFile::exists(fileName))
195  usable = true;
196  } else if (parts[at] == "privateEntry") {
197  storageType = KeyStore;
198  keyStoreEntryString = parts[at + 1];
199  keyStoreEntry = QCA::KeyStoreEntry(keyStoreEntryString);
200  if (!keyStoreEntry.isNull())
201  usable = true;
202  } else
203  return false;
204  }
205 
206  return true;
207  }
208 };
209 
210 CertItem::CertItem()
211 {
212 }
213 
214 CertItem::CertItem(const CertItem &from)
215  : d(from.d)
216 {
217 }
218 
219 CertItem::~CertItem()
220 {
221 }
222 
223 CertItem &CertItem::operator=(const CertItem &from)
224 {
225  d = from.d;
226  return *this;
227 }
228 
229 QString CertItem::name() const
230 {
231  return d->name;
232 }
233 
234 QCA::CertificateChain CertItem::certificateChain() const
235 {
236  return d->chain;
237 }
238 
239 bool CertItem::havePrivate() const
240 {
241  return d->havePrivate;
242 }
243 
244 CertItem::StorageType CertItem::storageType() const
245 {
246  return d->storageType;
247 }
248 
249 bool CertItem::isUsable() const
250 {
251  return d->usable;
252 }
253 
254 //----------------------------------------------------------------------------
255 // CertItemStore
256 //----------------------------------------------------------------------------
257 static MyPrompter *g_prompter = 0;
258 static int g_prompter_refs = 0;
259 
260 class CertItemStorePrivate : public QObject
261 {
262  Q_OBJECT
263 public:
264  CertItemStore * q;
265  MyPrompter * prompter;
267  QList<int> idList;
268  CertItemIconset iconset;
269  int next_id;
270  int next_req_id;
271 
272  class LoaderItem
273  {
274  public:
275  int req_id;
276  QCA::KeyLoader *keyLoader;
277  QString fileName;
278  };
279 
280  QList<LoaderItem> loaders;
281 
282  CertItemStorePrivate(CertItemStore *_q)
283  : QObject(_q)
284  , q(_q)
285  , next_id(0)
286  , next_req_id(0)
287  {
288  if (!g_prompter) {
289  g_prompter = new MyPrompter;
290  g_prompter_refs = 1;
291  } else
292  ++g_prompter_refs;
293 
294  prompter = g_prompter;
295  }
296 
297  ~CertItemStorePrivate()
298  {
299  foreach (const LoaderItem &i, loaders)
300  delete i.keyLoader;
301 
302  --g_prompter_refs;
303  if (g_prompter_refs == 0) {
304  delete g_prompter;
305  g_prompter = 0;
306  }
307  }
308 
309  QString getUniqueName(const QString &name)
310  {
311  int num = 1;
312  while (1) {
313  QString tryname;
314  if (num == 1)
315  tryname = name;
316  else
317  tryname = name + QString(" (%1)").arg(num);
318 
319  bool found = false;
320  foreach (const CertItem &i, list) {
321  if (i.name() == tryname) {
322  found = true;
323  break;
324  }
325  }
326  if (!found)
327  return tryname;
328 
329  ++num;
330  }
331  }
332 
333  static QString convertErrorToString(QCA::ConvertResult r)
334  {
335  QString str;
336  switch (r) {
337  case QCA::ConvertGood:
338  break;
340  str = tr("Incorrect passphrase.");
341  case QCA::ErrorFile:
342  str = tr("Unable to open or read file.");
343  case QCA::ErrorDecode:
344  default:
345  str = tr("Unable to decode format.");
346  }
347  return str;
348  }
349 
350 public Q_SLOTS:
351  void loader_finished()
352  {
353  QCA::KeyLoader *keyLoader = (QCA::KeyLoader *)sender();
354  int at = -1;
355  for (int n = 0; n < loaders.count(); ++n) {
356  if (loaders[n].keyLoader == keyLoader) {
357  at = n;
358  break;
359  }
360  }
361  Q_ASSERT(at != -1);
362 
363  int req_id = loaders[at].req_id;
364  QString fileName = loaders[at].fileName;
365  loaders.removeAt(at);
366 
367  QCA::ConvertResult r = keyLoader->convertResult();
368  if (r != QCA::ConvertGood) {
369  delete keyLoader;
370  prompter->fileFailed(fileName);
372  0,
373  tr("Error"),
374  tr("Error importing certificate and private key.\nReason: %1").arg(convertErrorToString(r)));
375  emit q->addFailed(req_id);
376  return;
377  }
378 
379  prompter->fileSuccess(fileName);
380 
381  QCA::KeyBundle kb = keyLoader->keyBundle();
382  delete keyLoader;
383 
385  QCA::Certificate cert = chain.primary();
386 
387  QString name = getUniqueName(cert.commonName());
388 
389  CertItem i;
390  i.d = new CertItem::Private;
391  i.d->name = name;
392  i.d->chain = chain;
393  i.d->havePrivate = true;
394  i.d->storageType = CertItem::File;
395  i.d->usable = true;
396  i.d->fileName = fileName;
397 
398  int id = next_id++;
399 
400  q->beginInsertRows(QModelIndex(), list.size(), list.size());
401  list += i;
402  idList += id;
403  q->endInsertRows();
404 
405  emit q->addSuccess(req_id, id);
406  }
407 };
408 
409 CertItemStore::CertItemStore(QObject *parent)
410  : QAbstractListModel(parent)
411 {
412  d = new CertItemStorePrivate(this);
413 }
414 
415 CertItemStore::~CertItemStore()
416 {
417  delete d;
418 }
419 
420 int CertItemStore::idFromRow(int row) const
421 {
422  return d->idList[row];
423 }
424 
425 int CertItemStore::rowFromId(int id) const
426 {
427  for (int n = 0; n < d->idList.count(); ++n) {
428  if (d->idList[n] == id)
429  return n;
430  }
431  return -1;
432 }
433 
434 CertItem CertItemStore::itemFromId(int id) const
435 {
436  return d->list[rowFromId(id)];
437 }
438 
439 CertItem CertItemStore::itemFromRow(int row) const
440 {
441  return d->list[row];
442 }
443 
444 QList<CertItem> CertItemStore::items() const
445 {
446  return d->list;
447 }
448 
449 QStringList CertItemStore::save() const
450 {
451  QStringList out;
452  foreach (const CertItem &i, d->list)
453  out += i.d->toString();
454  return out;
455 }
456 
457 bool CertItemStore::load(const QStringList &in)
458 {
459  QList<CertItem> addList;
460  QList<int> addIdList;
461  foreach (const QString &s, in) {
462  CertItem i;
463  i.d = new CertItem::Private;
464  if (i.d->fromString(s)) {
465  addList += i;
466  addIdList += d->next_id++;
467  }
468  }
469 
470  if (addList.isEmpty())
471  return true;
472 
473  beginInsertRows(QModelIndex(), d->list.size(), d->list.size() + addList.count() - 1);
474  d->list += addList;
475  d->idList += addIdList;
476  endInsertRows();
477 
478  return true;
479 }
480 
481 int CertItemStore::addFromFile(const QString &fileName)
482 {
483  CertItemStorePrivate::LoaderItem i;
484  i.req_id = d->next_req_id++;
485  i.keyLoader = new QCA::KeyLoader(d);
486  i.fileName = fileName;
487  connect(i.keyLoader, SIGNAL(finished()), d, SLOT(loader_finished()));
488  d->loaders += i;
489  i.keyLoader->loadKeyBundleFromFile(fileName);
490  return i.req_id;
491 }
492 
493 int CertItemStore::addFromKeyStore(const QCA::KeyStoreEntry &entry)
494 {
495  QCA::KeyBundle kb = entry.keyBundle();
496 
498  QCA::Certificate cert = chain.primary();
499 
500  QString name = d->getUniqueName(entry.name());
501 
502  CertItem i;
503  i.d = new CertItem::Private;
504  i.d->name = name;
505  i.d->chain = chain;
506  i.d->havePrivate = true;
507  i.d->storageType = CertItem::KeyStore;
508  i.d->usable = true;
509  i.d->keyStoreEntry = entry;
510 
511  int id = d->next_id++;
512 
513  beginInsertRows(QModelIndex(), d->list.size(), d->list.size());
514  d->list += i;
515  d->idList += id;
516  endInsertRows();
517 
518  int req_id = d->next_req_id++;
519  QMetaObject::invokeMethod(this, "addSuccess", Qt::QueuedConnection, Q_ARG(int, req_id), Q_ARG(int, id));
520  return req_id;
521 }
522 
523 int CertItemStore::addUser(const QCA::CertificateChain &chain)
524 {
525  QCA::Certificate cert = chain.primary();
526 
527  QString name = d->getUniqueName(cert.commonName());
528 
529  CertItem i;
530  i.d = new CertItem::Private;
531  i.d->name = name;
532  i.d->chain = chain;
533 
534  int id = d->next_id++;
535 
536  beginInsertRows(QModelIndex(), d->list.size(), d->list.size());
537  d->list += i;
538  d->idList += id;
539  endInsertRows();
540 
541  int req_id = d->next_req_id++;
542  QMetaObject::invokeMethod(this, "addSuccess", Qt::QueuedConnection, Q_ARG(int, req_id), Q_ARG(int, id));
543  return req_id;
544 }
545 
546 void CertItemStore::updateChain(int id, const QCA::CertificateChain &chain)
547 {
548  int at = rowFromId(id);
549  d->list[at].d->chain = chain;
550 }
551 
552 void CertItemStore::removeItem(int id)
553 {
554  int at = rowFromId(id);
555 
556  beginRemoveRows(QModelIndex(), at, at);
557  d->list.removeAt(at);
558  d->idList.removeAt(at);
559  endRemoveRows();
560 }
561 
562 void CertItemStore::setIcon(IconType type, const QPixmap &icon)
563 {
564  d->iconset[type] = icon;
565 }
566 
567 int CertItemStore::rowCount(const QModelIndex &parent) const
568 {
569  Q_UNUSED(parent);
570  return d->list.count();
571 }
572 
573 QVariant CertItemStore::data(const QModelIndex &index, int role) const
574 {
575  if (!index.isValid())
576  return QVariant();
577 
578  int at = index.row();
579  QList<CertItem> &list = d->list;
580 
581  if (at >= list.count())
582  return QVariant();
583 
584  if (role == Qt::DisplayRole) {
585  QString str = list[at].name();
586  if (list[at].havePrivate() && !list[at].isUsable())
587  str += QString(" ") + tr("(not usable)");
588  return str;
589  } else if (role == Qt::EditRole)
590  return list[at].name();
591  else if (role == Qt::DecorationRole) {
592  if (list[at].havePrivate())
593  return d->iconset[CertItemStore::IconKeyBundle];
594  else
595  return d->iconset[CertItemStore::IconCert];
596  } else
597  return QVariant();
598 }
599 
600 Qt::ItemFlags CertItemStore::flags(const QModelIndex &index) const
601 {
602  if (!index.isValid())
603  return Qt::ItemIsEnabled;
604 
606 }
607 
608 bool CertItemStore::setData(const QModelIndex &index, const QVariant &value, int role)
609 {
610  if (index.isValid() && role == Qt::EditRole) {
611  QString str = value.toString();
612  d->list[index.row()].d->name = str;
613  emit dataChanged(index, index);
614  return true;
615  }
616  return false;
617 }
618 
619 //----------------------------------------------------------------------------
620 // CertItemPrivateLoader
621 //----------------------------------------------------------------------------
622 class CertItemPrivateLoaderPrivate : public QObject
623 {
624  Q_OBJECT
625 public:
626  CertItemPrivateLoader *q;
627  CertItemStore * store;
628  QCA::KeyLoader * loader;
629  QString fileName;
630  QCA::PrivateKey key;
631 
632  CertItemPrivateLoaderPrivate(CertItemPrivateLoader *_q)
633  : QObject(_q)
634  , q(_q)
635  {
636  }
637 
638 public Q_SLOTS:
639  void loader_finished()
640  {
641  QCA::ConvertResult r = loader->convertResult();
642  if (r != QCA::ConvertGood) {
643  delete loader;
644  loader = 0;
645  store->d->prompter->fileFailed(fileName);
647  0,
648  tr("Error"),
649  tr("Error accessing private key.\nReason: %1").arg(CertItemStorePrivate::convertErrorToString(r)));
650  emit q->finished();
651  return;
652  }
653 
654  store->d->prompter->fileSuccess(fileName);
655 
656  key = loader->keyBundle().privateKey();
657  delete loader;
658  loader = 0;
659  emit q->finished();
660  }
661 };
662 
663 CertItemPrivateLoader::CertItemPrivateLoader(CertItemStore *store, QObject *parent)
664  : QObject(parent)
665 {
666  d = new CertItemPrivateLoaderPrivate(this);
667  d->store = store;
668 }
669 
670 CertItemPrivateLoader::~CertItemPrivateLoader()
671 {
672  delete d;
673 }
674 
675 void CertItemPrivateLoader::start(int id)
676 {
677  CertItem i = d->store->itemFromId(id);
678 
679  if (i.storageType() == CertItem::KeyStore) {
680  d->key = i.d->keyStoreEntry.keyBundle().privateKey();
682  return;
683  }
684 
685  d->key = QCA::PrivateKey();
686  d->fileName = i.d->fileName;
687  d->loader = new QCA::KeyLoader(d);
688  connect(d->loader, SIGNAL(finished()), d, SLOT(loader_finished()));
689  d->loader->loadKeyBundleFromFile(d->fileName);
690 }
691 
692 QCA::PrivateKey CertItemPrivateLoader::privateKey() const
693 {
694  return d->key;
695 }
696 
697 #include "certitem.moc"
ConvertResult
Return value from a format conversion.
An asynchronous event.
Definition: qca_core.h:1390
bool isNull() const
Test if the certificate is empty (null)
void clear()
QByteArray toDER() const
Export the Certificate into a DER format.
const Certificate & primary() const
Return the primary (end-user) Certificate.
Definition: qca_cert.h:1249
QString name(const QVariant &location)
Generic private key.
bool contains(const Key &key) const const
QString escape(const QString &plain)
QString arrayToString(const MemoryRegion &a)
Process an array in the "forward" direction, returning a QString.
General failure in the decode stage.
Certificate chain and private key pair.
Definition: qca_cert.h:2175
void removeAt(int i)
bool isNull() const
Test if this key is empty (null)
QString name() const
The name associated with the key stored in this object.
KeyBundle keyBundle() const
The key bundle that has been loaded.
QString join(const QString &separator) const const
bool exists() const const
Base64 encoding / decoding
QString tr(const char *sourceText, const char *disambiguation, int n)
QMessageBox::StandardButton information(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
int size() const const
Conversion succeeded, results should be valid.
bool isValid() const const
QString number(int n, int base)
int count(const T &value) const const
Q_OBJECTQ_OBJECT
Failure because of incorrect passphrase.
bool isEmpty() const const
DisplayRole
int row() const const
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString fileName() const
Name or other identifier for the file or byte array associated with this event.
File or bytearray generated the event.
Definition: qca_core.h:1419
Asynchronous private key loader.
Definition: qca_cert.h:2612
Failure because of incorrect file.
Single entry in a KeyStore.
Definition: qca_keystore.h:140
CertificateChain certificateChain() const
The public certificate part of this bundle.
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
Secure array of bytes.
Definition: qca_tools.h:316
QString toString() const
Serialize into a string for use as a passive entry.
char * toString(const T &value)
QString commonName() const
The common name of the subject of the certificate.
static Certificate fromDER(const QByteArray &a, ConvertResult *result=nullptr, const QString &provider=QString())
Import the certificate from DER.
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
A chain of related Certificates.
Definition: qca_cert.h:1225
Source source() const
the Source of this event
int length() const const
ConvertResult convertResult() const
The result of the loading process.
KeyBundle keyBundle() const
If a KeyBundle is stored in this object, return that bundle.
Public Key (X.509) certificate.
Definition: qca_cert.h:856
void finished()
Signal that is emitted when the load process has completed.
Q_SLOTSQ_SLOTS
QueuedConnection
QObject * parent() const const
QString toString() const const
PrivateKey privateKey() const
The private key part of this bundle.
KIOFILEWIDGETS_EXPORT QStringList list(const QString &fileClass)
const T value(const Key &key, const T &defaultValue) const const
int remove(const Key &key)
typedef ItemFlags
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Sep 25 2021 23:05:35 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.