1/*
2 * Copyright (C) 2005-2007 Justin Karneges <justin@affinix.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 *
18 */
19
20#include <QtCrypto>
21
22#include <QCoreApplication>
23#include <QDebug>
24#include <QDir>
25#include <QFile>
26#include <QFileInfo>
27#include <QTextStream>
28#include <QTimer>
29
30#ifdef QT_STATICPLUGIN
31#include "import_plugins.h"
32#endif
33
34const char *const APPNAME = "qcatool";
35const char *const EXENAME = "qcatool";
36const char *const VERSION = QCA_VERSION_STR;
37
38static QStringList wrapstring(const QString &str, int width)
39{
40 QStringList out;
41 QString simp = str.simplified();
42 QString rest = simp;
43 while (true) {
44 int lastSpace = -1;
45 int n;
46 for (n = 0; n < rest.length(); ++n) {
47 if (rest[n].isSpace())
48 lastSpace = n;
49 if (n == width)
50 break;
51 }
52 if (n == rest.length()) {
53 out += rest;
54 break;
55 }
56
57 QString line;
58 if (lastSpace != -1) {
59 line = rest.mid(position: 0, n: lastSpace);
60 rest = rest.mid(position: lastSpace + 1);
61 } else {
62 line = rest.mid(position: 0, n);
63 rest = rest.mid(position: n);
64 }
65 out += line;
66 }
67 return out;
68}
69
70class StreamLogger : public QCA::AbstractLogDevice
71{
72 Q_OBJECT
73public:
74 StreamLogger(QTextStream &stream)
75 : QCA::AbstractLogDevice(QStringLiteral("Stream logger"))
76 , _stream(stream)
77 {
78 QCA::logger()->registerLogDevice(logger: this);
79 }
80
81 ~StreamLogger() override
82 {
83 QCA::logger()->unregisterLogDevice(loggerName: name());
84 }
85
86 void logTextMessage(const QString &message, enum QCA::Logger::Severity severity) override
87 {
88 _stream << now() << " " << severityName(severity) << " " << message << Qt::endl;
89 }
90
91 void logBinaryMessage(const QByteArray &blob, enum QCA::Logger::Severity severity) override
92 {
93 Q_UNUSED(blob);
94 _stream << now() << " " << severityName(severity) << " "
95 << "Binary blob not implemented yet" << Qt::endl;
96 }
97
98private:
99 inline const char *severityName(enum QCA::Logger::Severity severity)
100 {
101 if (severity <= QCA::Logger::Debug) {
102 return s_severityNames[severity];
103 } else {
104 return s_severityNames[QCA::Logger::Debug + 1];
105 }
106 }
107
108 inline QString now()
109 {
110 static QString format = QStringLiteral("yyyy-MM-dd hh:mm:ss");
111 return QDateTime::currentDateTime().toString(format);
112 }
113
114private:
115 static const char *s_severityNames[];
116 QTextStream &_stream;
117};
118
119const char *StreamLogger::s_severityNames[] = {"Q", "M", "A", "C", "E", "W", "N", "I", "D", "U"};
120
121static void output_plugin_diagnostic_text()
122{
123 QString str = QCA::pluginDiagnosticText();
124 QCA::clearPluginDiagnosticText();
125 if (str[str.length() - 1] == QLatin1Char('\n'))
126 str.truncate(pos: str.length() - 1);
127 const QStringList lines = str.split(sep: QLatin1Char('\n'), behavior: Qt::KeepEmptyParts);
128 for (int n = 0; n < lines.count(); ++n)
129 fprintf(stderr, format: "plugin: %s\n", qPrintable(lines[n]));
130}
131
132static void output_keystore_diagnostic_text()
133{
134 QString str = QCA::KeyStoreManager::diagnosticText();
135 QCA::KeyStoreManager::clearDiagnosticText();
136 if (str[str.length() - 1] == QLatin1Char('\n'))
137 str.truncate(pos: str.length() - 1);
138 const QStringList lines = str.split(sep: QLatin1Char('\n'), behavior: Qt::KeepEmptyParts);
139 for (int n = 0; n < lines.count(); ++n)
140 fprintf(stderr, format: "keystore: %s\n", qPrintable(lines[n]));
141}
142
143static void output_message_diagnostic_text(QCA::SecureMessage *msg)
144{
145 QString str = msg->diagnosticText();
146 if (str[str.length() - 1] == QLatin1Char('\n'))
147 str.truncate(pos: str.length() - 1);
148 const QStringList lines = str.split(sep: QLatin1Char('\n'), behavior: Qt::KeepEmptyParts);
149 for (int n = 0; n < lines.count(); ++n)
150 fprintf(stderr, format: "message: %s\n", qPrintable(lines[n]));
151}
152
153class AnimatedKeyGen : public QObject
154{
155 Q_OBJECT
156public:
157 static QCA::PrivateKey makeKey(QCA::PKey::Type type, int bits, QCA::DLGroupSet set)
158 {
159 AnimatedKeyGen kg;
160 kg.type = type;
161 kg.bits = bits;
162 kg.set = set;
163 QEventLoop eventLoop;
164 kg.eventLoop = &eventLoop;
165 QTimer::singleShot(interval: 0, receiver: &kg, slot: &AnimatedKeyGen::start);
166 eventLoop.exec();
167 QCA::PrivateKey key = kg.key;
168 return key;
169 }
170
171private:
172 QCA::PKey::Type type;
173 int bits;
174 QCA::DLGroupSet set;
175 QEventLoop *eventLoop;
176 QCA::KeyGenerator gen;
177 QCA::DLGroup group;
178 QCA::PrivateKey key;
179 QTimer t;
180 int x;
181
182 AnimatedKeyGen()
183 {
184 gen.setBlockingEnabled(false);
185 connect(sender: &gen, signal: &QCA::KeyGenerator::finished, context: this, slot: &AnimatedKeyGen::gen_finished);
186 connect(sender: &t, signal: &QTimer::timeout, context: this, slot: &AnimatedKeyGen::t_timeout);
187 }
188
189private Q_SLOTS:
190 void start()
191 {
192 printf(format: "Generating Key ... ");
193 fflush(stdout);
194 x = 0;
195 t.start(msec: 125);
196
197 if (type == QCA::PKey::RSA)
198 gen.createRSA(bits);
199 else
200 gen.createDLGroup(set);
201 }
202
203 void gen_finished()
204 {
205 if (type == QCA::PKey::DSA || type == QCA::PKey::DH) {
206 if (group.isNull()) {
207 group = gen.dlGroup();
208
209 if (type == QCA::PKey::DSA)
210 gen.createDSA(domain: group);
211 else
212 gen.createDH(domain: group);
213 return;
214 }
215 }
216
217 key = gen.key();
218
219 printf(format: "\b");
220 if (!key.isNull())
221 printf(format: "Done\n");
222 else
223 printf(format: "Error\n");
224
225 eventLoop->exit();
226 }
227
228 void t_timeout()
229 {
230 if (x == 0)
231 printf(format: "\b/");
232 else if (x == 1)
233 printf(format: "\b-");
234 else if (x == 2)
235 printf(format: "\b\\");
236 else if (x == 3)
237 printf(format: "\b|");
238 fflush(stdout);
239
240 ++x;
241 x %= 4;
242 }
243};
244
245class KeyStoreMonitor : public QObject
246{
247 Q_OBJECT
248public:
249 static void monitor()
250 {
251 KeyStoreMonitor monitor;
252 QEventLoop eventLoop;
253 monitor.eventLoop = &eventLoop;
254 QTimer::singleShot(interval: 0, receiver: &monitor, slot: &KeyStoreMonitor::start);
255 eventLoop.exec();
256 }
257
258private:
259 QEventLoop *eventLoop;
260 QCA::KeyStoreManager *ksm;
261 QList<QCA::KeyStore *> keyStores;
262 QCA::ConsolePrompt *prompt;
263
264private Q_SLOTS:
265 void start()
266 {
267 // user can quit the monitoring by pressing enter
268 printf(format: "Monitoring keystores, press 'q' to quit.\n");
269 prompt = new QCA::ConsolePrompt(this);
270 connect(sender: prompt, signal: &QCA::ConsolePrompt::finished, context: this, slot: &KeyStoreMonitor::prompt_finished);
271 prompt->getChar();
272
273 // kick off the subsystem
274 QCA::KeyStoreManager::start();
275
276 // setup keystore manager for monitoring
277 ksm = new QCA::KeyStoreManager(this);
278 connect(sender: ksm, signal: &QCA::KeyStoreManager::keyStoreAvailable, context: this, slot: &KeyStoreMonitor::ks_available);
279 foreach (const QString &keyStoreId, ksm->keyStores())
280 ks_available(keyStoreId);
281 }
282
283 void ks_available(const QString &keyStoreId)
284 {
285 QCA::KeyStore *ks = new QCA::KeyStore(keyStoreId, ksm);
286 connect(sender: ks, signal: &QCA::KeyStore::updated, context: this, slot: &KeyStoreMonitor::ks_updated);
287 connect(sender: ks, signal: &QCA::KeyStore::unavailable, context: this, slot: &KeyStoreMonitor::ks_unavailable);
288 keyStores += ks;
289
290 printf(format: " available: %s\n", qPrintable(ks->name()));
291 }
292
293 void ks_updated()
294 {
295 QCA::KeyStore *ks = (QCA::KeyStore *)sender();
296
297 printf(format: " updated: %s\n", qPrintable(ks->name()));
298 }
299
300 void ks_unavailable()
301 {
302 QCA::KeyStore *ks = (QCA::KeyStore *)sender();
303
304 printf(format: " unavailable: %s\n", qPrintable(ks->name()));
305 keyStores.removeAll(t: ks);
306 delete ks;
307 }
308
309 void prompt_finished()
310 {
311 QChar c = prompt->resultChar();
312 if (c == QLatin1Char('q') || c == QLatin1Char('Q')) {
313 eventLoop->exit();
314 return;
315 }
316 prompt->getChar();
317 }
318};
319
320class PassphrasePrompt : public QObject
321{
322 Q_OBJECT
323public:
324 class Item
325 {
326 public:
327 QString promptStr;
328 int id;
329 QCA::Event event;
330 };
331
332 QCA::EventHandler handler;
333 bool allowPrompt;
334 bool warned;
335 bool have_pass;
336 bool used_pass;
337 QCA::SecureArray pass;
338 QCA::ConsolePrompt *prompt;
339 int prompt_id;
340 QCA::Event prompt_event;
341 QList<Item> pending;
342 bool auto_accept;
343
344 QCA::KeyStoreManager ksm;
345 QList<QCA::KeyStore *> keyStores;
346
347 PassphrasePrompt()
348 : handler(this)
349 , ksm(this)
350 {
351 allowPrompt = true;
352 warned = false;
353 have_pass = false;
354 auto_accept = false;
355
356 prompt = nullptr;
357
358 connect(sender: &handler, signal: &QCA::EventHandler::eventReady, context: this, slot: &PassphrasePrompt::ph_eventReady);
359 handler.start();
360
361 connect(sender: &ksm, signal: &QCA::KeyStoreManager::keyStoreAvailable, context: this, slot: &PassphrasePrompt::ks_available);
362 foreach (const QString &keyStoreId, ksm.keyStores())
363 ks_available(keyStoreId);
364 }
365
366 ~PassphrasePrompt() override
367 {
368 qDeleteAll(c: keyStores);
369
370 if (prompt) {
371 handler.reject(id: prompt_id);
372 delete prompt;
373 }
374
375 while (!pending.isEmpty())
376 handler.reject(id: pending.takeFirst().id);
377 }
378
379 void setExplicitPassword(const QCA::SecureArray &_pass)
380 {
381 have_pass = true;
382 used_pass = false;
383 pass = _pass;
384 }
385
386private Q_SLOTS:
387 void ph_eventReady(int id, const QCA::Event &e)
388 {
389 if (have_pass) {
390 // only allow using an explicit passphrase once
391 if (used_pass) {
392 handler.reject(id);
393 return;
394 }
395 used_pass = true;
396 handler.submitPassword(id, password: pass);
397 return;
398 }
399
400 if (!allowPrompt) {
401 if (!have_pass && !warned) {
402 warned = true;
403 fprintf(stderr, format: "Error: no passphrase specified (use '--pass=' for none).\n");
404 }
405
406 handler.reject(id);
407 return;
408 }
409
410 if (e.type() == QCA::Event::Password) {
411 QString type = QStringLiteral("password");
412 if (e.passwordStyle() == QCA::Event::StylePassphrase)
413 type = QStringLiteral("passphrase");
414 else if (e.passwordStyle() == QCA::Event::StylePIN)
415 type = QStringLiteral("PIN");
416
417 QString str;
418 if (e.source() == QCA::Event::KeyStore) {
419 QString name;
420 QCA::KeyStoreEntry entry = e.keyStoreEntry();
421 if (!entry.isNull()) {
422 name = entry.name();
423 } else {
424 if (e.keyStoreInfo().type() == QCA::KeyStore::SmartCard)
425 name = QStringLiteral("the '") + e.keyStoreInfo().name() + QStringLiteral("' token");
426 else
427 name = e.keyStoreInfo().name();
428 }
429 str = QStringLiteral("Enter %1 for %2").arg(args&: type, args&: name);
430 } else if (!e.fileName().isEmpty())
431 str = QStringLiteral("Enter %1 for %2").arg(args&: type, args: e.fileName());
432 else
433 str = QStringLiteral("Enter %1").arg(a: type);
434
435 if (!prompt) {
436 prompt = new QCA::ConsolePrompt(this);
437 connect(sender: prompt, signal: &QCA::ConsolePrompt::finished, context: this, slot: &PassphrasePrompt::prompt_finished);
438 prompt_id = id;
439 prompt_event = e;
440 prompt->getHidden(promptStr: str);
441 } else {
442 Item i;
443 i.promptStr = str;
444 i.id = id;
445 i.event = e;
446 pending += i;
447 }
448 } else if (e.type() == QCA::Event::Token) {
449 // even though we're being prompted for a missing token,
450 // we should still check if the token is present, due to
451 // a possible race between insert and token request.
452 bool found = false;
453
454 // token-only
455 if (e.keyStoreEntry().isNull()) {
456 foreach (QCA::KeyStore *ks, keyStores) {
457 if (ks->id() == e.keyStoreInfo().id()) {
458 found = true;
459 break;
460 }
461 }
462 }
463 // token-entry
464 else {
465 QCA::KeyStoreEntry kse = e.keyStoreEntry();
466
467 QCA::KeyStore *ks = nullptr;
468 foreach (QCA::KeyStore *i, keyStores) {
469 if (i->id() == e.keyStoreInfo().id()) {
470 ks = i;
471 break;
472 }
473 }
474 if (ks) {
475 QList<QCA::KeyStoreEntry> list = ks->entryList();
476 foreach (const QCA::KeyStoreEntry &e, list) {
477 if (e.id() == kse.id() && kse.isAvailable()) {
478 found = true;
479 break;
480 }
481 }
482 }
483 }
484 if (found) {
485 // auto-accept
486 handler.tokenOkay(id);
487 return;
488 }
489
490 QCA::KeyStoreEntry entry = e.keyStoreEntry();
491 QString name;
492 if (!entry.isNull()) {
493 name = QStringLiteral("Please make ") + entry.name() + QStringLiteral(" (of ") + entry.storeName() +
494 QStringLiteral(") available");
495 } else {
496 name = QStringLiteral("Please insert the '") + e.keyStoreInfo().name() + QStringLiteral("' token");
497 }
498
499 QString str = QStringLiteral("%1 and press Enter (or 'q' to cancel) ...").arg(a: name);
500
501 if (!prompt) {
502 fprintf(stderr, format: "%s\n", qPrintable(str));
503 prompt = new QCA::ConsolePrompt(this);
504 connect(sender: prompt, signal: &QCA::ConsolePrompt::finished, context: this, slot: &PassphrasePrompt::prompt_finished);
505 prompt_id = id;
506 prompt_event = e;
507 prompt->getChar();
508 } else {
509 Item i;
510 i.promptStr = str;
511 i.id = id;
512 i.event = e;
513 pending += i;
514 }
515 } else
516 handler.reject(id);
517 }
518
519 void prompt_finished()
520 {
521 if (prompt_event.type() == QCA::Event::Password) {
522 handler.submitPassword(id: prompt_id, password: prompt->result());
523 } else {
524 if (auto_accept) {
525 auto_accept = false;
526 handler.tokenOkay(id: prompt_id);
527 } else {
528 QChar c = prompt->resultChar();
529 if (c == QLatin1Char('\r') || c == QLatin1Char('\n'))
530 handler.tokenOkay(id: prompt_id);
531 else if (c == QLatin1Char('q') || c == QLatin1Char('Q'))
532 handler.reject(id: prompt_id);
533 else {
534 // retry
535 prompt->getChar();
536 return;
537 }
538 }
539 }
540
541 if (!pending.isEmpty()) {
542 Item i = pending.takeFirst();
543 prompt_id = i.id;
544 prompt_event = i.event;
545 if (i.event.type() == QCA::Event::Password) {
546 prompt->getHidden(promptStr: i.promptStr);
547 } else // Token
548 {
549 fprintf(stderr, format: "%s\n", qPrintable(i.promptStr));
550 prompt->getChar();
551 }
552 } else {
553 delete prompt;
554 prompt = nullptr;
555 }
556 }
557
558 void ks_available(const QString &keyStoreId)
559 {
560 QCA::KeyStore *ks = new QCA::KeyStore(keyStoreId, &ksm);
561 connect(sender: ks, signal: &QCA::KeyStore::updated, context: this, slot: &PassphrasePrompt::ks_updated);
562 connect(sender: ks, signal: &QCA::KeyStore::unavailable, context: this, slot: &PassphrasePrompt::ks_unavailable);
563 keyStores += ks;
564 ks->startAsynchronousMode();
565
566 // are we currently in a token-only prompt?
567 if (prompt && prompt_event.type() == QCA::Event::Token && prompt_event.keyStoreEntry().isNull()) {
568 // was the token we're looking for just inserted?
569 if (prompt_event.keyStoreInfo().id() == keyStoreId) {
570 fprintf(stderr, format: "Token inserted! Continuing...\n");
571
572 // auto-accept
573 auto_accept = true;
574 prompt_finished();
575 }
576 }
577 }
578
579 void ks_unavailable()
580 {
581 QCA::KeyStore *ks = (QCA::KeyStore *)sender();
582 keyStores.removeAll(t: ks);
583 delete ks;
584 }
585
586 void ks_updated()
587 {
588 QCA::KeyStore *ks = (QCA::KeyStore *)sender();
589
590 // are we currently in a token-entry prompt?
591 if (prompt && prompt_event.type() == QCA::Event::Token && !prompt_event.keyStoreEntry().isNull()) {
592 QCA::KeyStoreEntry kse = prompt_event.keyStoreEntry();
593
594 // was the token of the entry we're looking for updated?
595 if (prompt_event.keyStoreInfo().id() == ks->id()) {
596 // is the entry available?
597 bool avail = false;
598 QList<QCA::KeyStoreEntry> list = ks->entryList();
599 foreach (const QCA::KeyStoreEntry &e, list) {
600 if (e.id() == kse.id()) {
601 avail = kse.isAvailable();
602 break;
603 }
604 }
605 if (avail) {
606 fprintf(stderr, format: "Entry available! Continuing...\n");
607
608 // auto-accept
609 auto_accept = true;
610 prompt_finished();
611 }
612 }
613 }
614 }
615};
616
617class PassphrasePromptThread : public QCA::SyncThread
618{
619 Q_OBJECT
620public:
621 PassphrasePrompt *pp;
622
623 PassphrasePromptThread()
624 {
625 start();
626 }
627
628 ~PassphrasePromptThread() override
629 {
630 stop();
631 }
632
633protected:
634 void atStart() override
635 {
636 pp = new PassphrasePrompt;
637 }
638
639 void atEnd() override
640 {
641 delete pp;
642 }
643};
644
645static bool promptForNewPassphrase(QCA::SecureArray *result)
646{
647 QCA::ConsolePrompt prompt;
648 prompt.getHidden(QStringLiteral("Enter new passphrase"));
649 prompt.waitForFinished();
650 QCA::SecureArray out = prompt.result();
651
652 prompt.getHidden(QStringLiteral("Confirm new passphrase"));
653 prompt.waitForFinished();
654
655 if (prompt.result() != out) {
656 fprintf(stderr, format: "Error: confirmation does not match original entry.\n");
657 return false;
658 }
659 *result = out;
660 return true;
661}
662
663static void ksm_start_and_wait()
664{
665 // activate the KeyStoreManager and block until ready
666 QCA::KeyStoreManager::start();
667 {
668 QCA::KeyStoreManager ksm;
669 ksm.waitForBusyFinished();
670 }
671}
672
673static QString line_encode(const QString &in)
674{
675 QString out;
676 for (const QChar &c : in) {
677 if (c == QLatin1Char('\\'))
678 out += QStringLiteral("\\\\");
679 else if (c == QLatin1Char('\n'))
680 out += QStringLiteral("\\n");
681 else
682 out += c;
683 }
684 return out;
685}
686
687static QString line_decode(const QString &in)
688{
689 QString out;
690 for (int n = 0; n < in.length(); ++n) {
691 if (in[n] == QLatin1Char('\\')) {
692 if (n + 1 < in.length()) {
693 if (in[n + 1] == QLatin1Char('\\'))
694 out += QLatin1Char('\\');
695 else if (in[n + 1] == QLatin1Char('n'))
696 out += QLatin1Char('\n');
697 ++n;
698 }
699 } else
700 out += in[n];
701 }
702 return out;
703}
704
705static QString make_ksentry_string(const QString &id)
706{
707 QString out;
708 out += QStringLiteral("QCATOOL_KEYSTOREENTRY_1\n");
709 out += line_encode(in: id) + QLatin1Char('\n');
710 return out;
711}
712
713/*static bool write_ksentry_file(const QString &id, const QString &fileName)
714{
715 QFile f(fileName);
716 if(!f.open(QFile::WriteOnly | QFile::Truncate))
717 return false;
718 f.write(make_ksentry_string(id).toUtf8());
719 return true;
720}*/
721
722static QString read_ksentry_file(const QString &fileName)
723{
724 QString out;
725
726 QFile f(fileName);
727 if (!f.open(flags: QFile::ReadOnly))
728 return out;
729 QTextStream ts(&f);
730 int linenum = 0;
731 while (!ts.atEnd()) {
732 QString line = ts.readLine();
733 if (linenum == 0) {
734 if (line != QLatin1String("QCATOOL_KEYSTOREENTRY_1"))
735 return out;
736 } else {
737 out = line_decode(in: line);
738 break;
739 }
740 ++linenum;
741 }
742 return out;
743}
744
745static bool is_pem_file(const QString &fileName)
746{
747 QFile f(fileName);
748 if (!f.open(flags: QFile::ReadOnly))
749 return false;
750 QTextStream ts(&f);
751 if (!ts.atEnd()) {
752 QString line = ts.readLine();
753 if (line.startsWith(s: QLatin1String("-----BEGIN")))
754 return true;
755 }
756 return false;
757}
758
759static QByteArray read_der_file(const QString &fileName)
760{
761 QFile f(fileName);
762 if (!f.open(flags: QFile::ReadOnly))
763 return QByteArray();
764 return f.readAll();
765}
766
767class InfoType
768{
769public:
770 QCA::CertificateInfoType type;
771 QString varname;
772 QString shortname;
773 QString name;
774 QString desc;
775
776 InfoType()
777 {
778 }
779
780 InfoType(const QCA::CertificateInfoType &_type,
781 const QString &_varname,
782 const QString &_shortname,
783 const QString &_name,
784 const QString &_desc)
785 : type(_type)
786 , varname(_varname)
787 , shortname(_shortname)
788 , name(_name)
789 , desc(_desc)
790 {
791 }
792};
793
794static QList<InfoType> makeInfoTypeList(bool legacyEmail = false)
795{
796 QList<InfoType> out;
797 out += InfoType(QCA::CommonName,
798 QStringLiteral("CommonName"),
799 QStringLiteral("CN"),
800 QStringLiteral("Common Name (CN)"),
801 QStringLiteral("Full name, domain, anything"));
802 out += InfoType(
803 QCA::Email, QStringLiteral("Email"), QLatin1String(""), QStringLiteral("Email Address"), QLatin1String(""));
804 if (legacyEmail)
805 out += InfoType(QCA::EmailLegacy,
806 QStringLiteral("EmailLegacy"),
807 QLatin1String(""),
808 QStringLiteral("PKCS#9 Email Address"),
809 QLatin1String(""));
810 out += InfoType(QCA::Organization,
811 QStringLiteral("Organization"),
812 QStringLiteral("O"),
813 QStringLiteral("Organization (O)"),
814 QStringLiteral("Company, group, etc"));
815 out += InfoType(QCA::OrganizationalUnit,
816 QStringLiteral("OrganizationalUnit"),
817 QStringLiteral("OU"),
818 QStringLiteral("Organizational Unit (OU)"),
819 QStringLiteral("Division/branch of organization"));
820 out += InfoType(QCA::Locality,
821 QStringLiteral("Locality"),
822 QLatin1String(""),
823 QStringLiteral("Locality (L)"),
824 QStringLiteral("City, shire, part of a state"));
825 out += InfoType(QCA::State,
826 QStringLiteral("State"),
827 QLatin1String(""),
828 QStringLiteral("State (ST)"),
829 QStringLiteral("State within the country"));
830 out += InfoType(QCA::Country,
831 QStringLiteral("Country"),
832 QStringLiteral("C"),
833 QStringLiteral("Country Code (C)"),
834 QStringLiteral("2-letter code"));
835 out += InfoType(QCA::IncorporationLocality,
836 QStringLiteral("IncorporationLocality"),
837 QLatin1String(""),
838 QStringLiteral("Incorporation Locality"),
839 QStringLiteral("For EV certificates"));
840 out += InfoType(QCA::IncorporationState,
841 QStringLiteral("IncorporationState"),
842 QLatin1String(""),
843 QStringLiteral("Incorporation State"),
844 QStringLiteral("For EV certificates"));
845 out += InfoType(QCA::IncorporationCountry,
846 QStringLiteral("IncorporationCountry"),
847 QLatin1String(""),
848 QStringLiteral("Incorporation Country"),
849 QStringLiteral("For EV certificates"));
850 out += InfoType(QCA::URI, QStringLiteral("URI"), QLatin1String(""), QStringLiteral("URI"), QLatin1String(""));
851 out += InfoType(QCA::DNS,
852 QStringLiteral("DNS"),
853 QLatin1String(""),
854 QStringLiteral("Domain Name"),
855 QStringLiteral("Domain (dnsName)"));
856 out += InfoType(QCA::IPAddress,
857 QStringLiteral("IPAddress"),
858 QLatin1String(""),
859 QStringLiteral("IP Adddress"),
860 QLatin1String(""));
861 out += InfoType(QCA::XMPP,
862 QStringLiteral("XMPP"),
863 QLatin1String(""),
864 QStringLiteral("XMPP Address (JID)"),
865 QStringLiteral("From RFC 3920 (id-on-xmppAddr)"));
866 return out;
867}
868
869class MyConstraintType
870{
871public:
872 QCA::ConstraintType type;
873 QString varname;
874 QString name;
875 QString desc;
876
877 MyConstraintType()
878 {
879 }
880
881 MyConstraintType(const QCA::ConstraintType &_type,
882 const QString &_varname,
883 const QString &_name,
884 const QString &_desc)
885 : type(_type)
886 , varname(_varname)
887 , name(_name)
888 , desc(_desc)
889 {
890 }
891};
892
893static QList<MyConstraintType> makeConstraintTypeList()
894{
895 QList<MyConstraintType> out;
896 out += MyConstraintType(QCA::DigitalSignature,
897 QStringLiteral("DigitalSignature"),
898 QStringLiteral("Digital Signature"),
899 QStringLiteral("Can be used for signing"));
900 out += MyConstraintType(QCA::NonRepudiation,
901 QStringLiteral("NonRepudiation"),
902 QStringLiteral("Non-Repudiation"),
903 QStringLiteral("Usage is legally binding"));
904 out += MyConstraintType(QCA::KeyEncipherment,
905 QStringLiteral("KeyEncipherment"),
906 QStringLiteral("Key Encipherment"),
907 QStringLiteral("Can encrypt other keys"));
908 out += MyConstraintType(QCA::DataEncipherment,
909 QStringLiteral("DataEncipherment"),
910 QStringLiteral("Data Encipherment"),
911 QStringLiteral("Can encrypt arbitrary data"));
912 out += MyConstraintType(QCA::KeyAgreement,
913 QStringLiteral("KeyAgreement"),
914 QStringLiteral("Key Agreement"),
915 QStringLiteral("Can perform key agreement (DH)"));
916 out += MyConstraintType(QCA::KeyCertificateSign,
917 QStringLiteral("KeyCertificateSign"),
918 QStringLiteral("Certificate Sign"),
919 QStringLiteral("Can sign other certificates"));
920 out += MyConstraintType(
921 QCA::CRLSign, QStringLiteral("CRLSign"), QStringLiteral("CRL Sign"), QStringLiteral("Can sign CRLs"));
922 out += MyConstraintType(QCA::EncipherOnly,
923 QStringLiteral("EncipherOnly"),
924 QStringLiteral("Encipher Only"),
925 QStringLiteral("Can be used for encrypting"));
926 out += MyConstraintType(QCA::DecipherOnly,
927 QStringLiteral("DecipherOnly"),
928 QStringLiteral("Decipher Only"),
929 QStringLiteral("Can be used for decrypting"));
930 out += MyConstraintType(QCA::ServerAuth,
931 QStringLiteral("ServerAuth"),
932 QStringLiteral("Server Authentication"),
933 QStringLiteral("TLS Server"));
934 out += MyConstraintType(QCA::ClientAuth,
935 QStringLiteral("ClientAuth"),
936 QStringLiteral("Client Authentication"),
937 QStringLiteral("TLS Client"));
938 out += MyConstraintType(
939 QCA::CodeSigning, QStringLiteral("CodeSigning"), QStringLiteral("Code Signing"), QLatin1String(""));
940 out += MyConstraintType(QCA::EmailProtection,
941 QStringLiteral("EmailProtection"),
942 QStringLiteral("Email Protection"),
943 QStringLiteral("S/MIME"));
944 out += MyConstraintType(
945 QCA::IPSecEndSystem, QStringLiteral("IPSecEndSystem"), QStringLiteral("IPSec End-System"), QLatin1String(""));
946 out += MyConstraintType(
947 QCA::IPSecTunnel, QStringLiteral("IPSecTunnel"), QStringLiteral("IPSec Tunnel"), QLatin1String(""));
948 out +=
949 MyConstraintType(QCA::IPSecUser, QStringLiteral("IPSecUser"), QStringLiteral("IPSec User"), QLatin1String(""));
950 out += MyConstraintType(
951 QCA::TimeStamping, QStringLiteral("TimeStamping"), QStringLiteral("Time Stamping"), QLatin1String(""));
952 out += MyConstraintType(
953 QCA::OCSPSigning, QStringLiteral("OCSPSigning"), QStringLiteral("OCSP Signing"), QLatin1String(""));
954 return out;
955}
956
957const char *crlEntryReasonToString(QCA::CRLEntry::Reason r)
958{
959 switch (r) {
960 case QCA::CRLEntry::Unspecified:
961 return "Unspecified";
962 case QCA::CRLEntry::KeyCompromise:
963 return "KeyCompromise";
964 case QCA::CRLEntry::CACompromise:
965 return "CACompromise";
966 case QCA::CRLEntry::AffiliationChanged:
967 return "AffiliationChanged";
968 case QCA::CRLEntry::Superseded:
969 return "Superseded";
970 case QCA::CRLEntry::CessationOfOperation:
971 return "CessationOfOperation";
972 case QCA::CRLEntry::CertificateHold:
973 return "CertificateHold";
974 case QCA::CRLEntry::RemoveFromCRL:
975 return "RemoveFromCRL";
976 case QCA::CRLEntry::PrivilegeWithdrawn:
977 return "PrivilegeWithdrawn";
978 case QCA::CRLEntry::AACompromise:
979 return "AACompromise";
980 default:
981 return "Unknown";
982 }
983}
984
985static bool validOid(const QString &in)
986{
987 for (const QChar &c : in) {
988 if (!c.isDigit() && c != QLatin1Char('.'))
989 return false;
990 }
991 return true;
992}
993
994class ValidityLength
995{
996public:
997 int years, months, days;
998};
999
1000static int vl_getnext(const QString &in, int offset = 0)
1001{
1002 if (offset >= in.length())
1003 return in.length();
1004
1005 int n = offset;
1006 bool lookForNonDigit;
1007
1008 if (in[n].isDigit())
1009 lookForNonDigit = true;
1010 else
1011 lookForNonDigit = false;
1012
1013 for (++n; n < in.length(); ++n) {
1014 if (in[n].isDigit() != lookForNonDigit)
1015 break;
1016 }
1017 return n;
1018}
1019
1020static QStringList vl_getparts(const QString &in)
1021{
1022 QStringList out;
1023 int offset = 0;
1024 while (true) {
1025 int n = vl_getnext(in, offset);
1026 if (n == offset)
1027 break;
1028 out += in.mid(position: offset, n: n - offset);
1029 offset = n;
1030 }
1031 return out;
1032}
1033
1034static bool parseValidityLength(const QString &in, ValidityLength *vl)
1035{
1036 vl->years = -1;
1037 vl->months = -1;
1038 vl->days = -1;
1039
1040 QStringList parts = vl_getparts(in);
1041 while (true) {
1042 // first part should be a number
1043 if (parts.count() < 1)
1044 break;
1045 QString str = parts.takeFirst();
1046 bool ok;
1047 int x = str.toInt(ok: &ok);
1048 if (!ok)
1049 return false;
1050
1051 // next part should be 1 letter plus any amount of space
1052 if (parts.count() < 1)
1053 return false;
1054 str = parts.takeFirst();
1055 if (!str[0].isLetter())
1056 return false;
1057 str = str.trimmed(); // remove space
1058
1059 if (str == QLatin1String("y")) {
1060 if (vl->years != -1)
1061 return false;
1062 vl->years = x;
1063 }
1064 if (str == QLatin1String("m")) {
1065 if (vl->months != -1)
1066 return false;
1067 vl->months = x;
1068 }
1069 if (str == QLatin1String("d")) {
1070 if (vl->days != -1)
1071 return false;
1072 vl->days = x;
1073 }
1074 }
1075
1076 if (vl->years == -1)
1077 vl->years = 0;
1078 if (vl->months == -1)
1079 vl->months = 0;
1080 if (vl->days == -1)
1081 vl->days = 0;
1082
1083 return true;
1084}
1085
1086static QString prompt_for(const QString &prompt)
1087{
1088 printf(format: "%s: ", prompt.toLatin1().data());
1089 fflush(stdout);
1090 QByteArray result(256, 0);
1091 if (fgets(s: (char *)result.data(), n: result.size(), stdin))
1092 return QString::fromLocal8Bit(ba: result).trimmed();
1093 else
1094 return QString();
1095}
1096
1097static QCA::CertificateOptions promptForCertAttributes(bool advanced, bool req)
1098{
1099 QCA::CertificateOptions opts;
1100
1101 if (advanced) {
1102 if (!req) {
1103 while (true) {
1104 QString str = prompt_for(
1105 QStringLiteral("Create an end user ('user') certificate or a CA ('ca') certificate? [user]"));
1106 if (str.isEmpty())
1107 str = QStringLiteral("user");
1108 if (str != QLatin1String("user") && str != QLatin1String("ca")) {
1109 printf(format: "'%s' is not a valid entry.\n", qPrintable(str));
1110 continue;
1111 }
1112
1113 if (str == QLatin1String("ca"))
1114 opts.setAsCA();
1115 break;
1116 }
1117 printf(format: "\n");
1118
1119 while (true) {
1120 QString str = prompt_for(QStringLiteral("Serial Number"));
1121 QCA::BigInteger num;
1122 if (str.isEmpty() || !num.fromString(s: str)) {
1123 printf(format: "'%s' is not a valid entry.\n", qPrintable(str));
1124 continue;
1125 }
1126
1127 opts.setSerialNumber(num);
1128 break;
1129 }
1130 printf(format: "\n");
1131 }
1132
1133 {
1134 QCA::CertificateInfoOrdered info;
1135 printf(
1136 format: "Choose the information attributes to add to the certificate. They will be\n"
1137 "added in the order they are entered.\n\n");
1138 printf(format: "Available information attributes:\n");
1139 QList<InfoType> list = makeInfoTypeList();
1140 for (int n = 0; n < list.count(); ++n) {
1141 const InfoType &i = list[n];
1142 char c = 'a' + n;
1143 printf(format: " %c) %-32s %s\n", c, qPrintable(i.name), qPrintable(i.desc));
1144 }
1145 printf(format: "\n");
1146 while (true) {
1147 int index;
1148 while (true) {
1149 QString str = prompt_for(QStringLiteral("Select an attribute to add, or enter to move on"));
1150 if (str.isEmpty()) {
1151 index = -1;
1152 break;
1153 }
1154 if (str.length() == 1) {
1155 index = str[0].toLatin1() - 'a';
1156 if (index >= 0 && index < list.count())
1157 break;
1158 }
1159 printf(format: "'%s' is not a valid entry.\n", qPrintable(str));
1160 }
1161 if (index == -1)
1162 break;
1163
1164 QString val = prompt_for(prompt: list[index].name);
1165 info += QCA::CertificateInfoPair(list[index].type, val);
1166 printf(format: "Added attribute.\n\n");
1167 }
1168 opts.setInfoOrdered(info);
1169 }
1170
1171 {
1172 QCA::Constraints constraints;
1173 printf(format: "\n");
1174 printf(format: "Choose the constraint attributes to add to the certificate.\n\n");
1175 printf(format: "Available attributes:\n");
1176 QList<MyConstraintType> list = makeConstraintTypeList();
1177 for (int n = 0; n < list.count(); ++n) {
1178 const MyConstraintType &i = list[n];
1179 char c = 'a' + n;
1180 printf(format: " %c) %-32s %s\n", c, qPrintable(i.name), qPrintable(i.desc));
1181 }
1182 printf(format: "\n");
1183 printf(format: "If no constraints are added, then the certificate may be used for any purpose.\n\n");
1184 while (true) {
1185 int index;
1186 while (true) {
1187 QString str = prompt_for(QStringLiteral("Select an attribute to add, or enter to move on"));
1188 if (str.isEmpty()) {
1189 index = -1;
1190 break;
1191 }
1192 if (str.length() == 1) {
1193 index = str[0].toLatin1() - 'a';
1194 if (index >= 0 && index < list.count())
1195 break;
1196 }
1197 printf(format: "'%s' is not a valid entry.\n\n", qPrintable(str));
1198 }
1199 if (index == -1)
1200 break;
1201
1202 if (constraints.contains(t: list[index].type)) {
1203 printf(format: "You have already added '%s'.\n\n", qPrintable(list[index].name));
1204 continue;
1205 }
1206
1207 constraints += list[index].type;
1208 printf(format: "Added attribute.\n\n");
1209 }
1210 opts.setConstraints(constraints);
1211 }
1212
1213 {
1214 QStringList policies;
1215 printf(format: "\n");
1216 printf(
1217 format: "Are there any policy OID attributes that you wish to add? Use the dotted\n"
1218 "string format.\n\n");
1219 while (true) {
1220 QString str = prompt_for(QStringLiteral("Enter a policy OID to add, or enter to move on"));
1221 if (str.isEmpty())
1222 break;
1223 if (!validOid(in: str)) {
1224 printf(format: "'%s' is not a valid entry.\n\n", qPrintable(str));
1225 continue;
1226 }
1227 if (policies.contains(str)) {
1228 printf(format: "You have already added '%s'.\n\n", qPrintable(str));
1229 continue;
1230 }
1231
1232 policies += str;
1233 printf(format: "Added attribute.\n\n");
1234 }
1235 opts.setPolicies(policies);
1236 }
1237
1238 printf(format: "\n");
1239 } else {
1240 QCA::CertificateInfo info;
1241 info.insert(key: QCA::CommonName, value: prompt_for(QStringLiteral("Common Name")));
1242 info.insert(key: QCA::Country, value: prompt_for(QStringLiteral("Country Code (2 letters)")));
1243 info.insert(key: QCA::Organization, value: prompt_for(QStringLiteral("Organization")));
1244 info.insert(key: QCA::Email, value: prompt_for(QStringLiteral("Email")));
1245 opts.setInfo(info);
1246
1247 printf(format: "\n");
1248 }
1249
1250 if (!req) {
1251 while (true) {
1252 QString str = prompt_for(QStringLiteral("How long should the certificate be valid? (e.g. '1y2m3d')"));
1253 ValidityLength vl;
1254 if (!parseValidityLength(in: str, vl: &vl)) {
1255 printf(format: "'%s' is not a valid entry.\n\n", qPrintable(str));
1256 continue;
1257 }
1258
1259 if (vl.years == 0 && vl.months == 0 && vl.days == 0) {
1260 printf(format: "The certificate must be valid for at least one day.\n\n");
1261 continue;
1262 }
1263
1264 QDateTime start = QDateTime::currentDateTimeUtc();
1265 QDateTime end = start;
1266 if (vl.years > 0)
1267 end = end.addYears(years: vl.years);
1268 if (vl.months > 0)
1269 end = end.addMonths(months: vl.months);
1270 if (vl.days > 0)
1271 end = end.addDays(days: vl.days);
1272 opts.setValidityPeriod(start, end);
1273
1274 QStringList parts;
1275 if (vl.years > 0)
1276 parts += QStringLiteral("%1 year(s)").arg(a: vl.years);
1277 if (vl.months > 0)
1278 parts += QStringLiteral("%1 month(s)").arg(a: vl.months);
1279 if (vl.days > 0)
1280 parts += QStringLiteral("%1 day(s)").arg(a: vl.days);
1281 QString out;
1282 if (parts.count() == 1)
1283 out = parts[0];
1284 else if (parts.count() == 2)
1285 out = parts[0] + QStringLiteral(" and ") + parts[1];
1286 else if (parts.count() == 3)
1287 out = parts[0] + QStringLiteral(", ") + parts[1] + QStringLiteral(", and ") + parts[2];
1288 printf(format: "Certificate will be valid for %s.\n", qPrintable(out));
1289 break;
1290 }
1291 printf(format: "\n");
1292 }
1293
1294 return opts;
1295}
1296
1297// qsettings seems to give us a string type for both bool and int (and
1298// possibly others, but those are the only two we care about here).
1299// in order to figure out what is actually a bool or an int, we need
1300// to examine the string. so for the functions below, we convert
1301// the variant to a string, and then inspect it to see if it looks
1302// like a bool or an int.
1303
1304static bool string_is_bool(const QString &in)
1305{
1306 QString lc = in.toLower();
1307 if (lc == QLatin1String("true") || lc == QLatin1String("false"))
1308 return true;
1309 return false;
1310}
1311
1312static bool string_is_int(const QString &in)
1313{
1314 bool ok;
1315 in.toInt(ok: &ok);
1316 return ok;
1317}
1318
1319static bool variant_is_bool(const QVariant &in)
1320{
1321 if (in.canConvert<QString>() && string_is_bool(in: in.toString()))
1322 return true;
1323 return false;
1324}
1325
1326static bool variant_is_int(const QVariant &in)
1327{
1328 if (in.canConvert<QString>() && string_is_int(in: in.toString()))
1329 return true;
1330 return false;
1331}
1332
1333static QString prompt_for_string(const QString &prompt, const QString &def = QString())
1334{
1335 printf(format: "%s", prompt.toLatin1().data());
1336 fflush(stdout);
1337 QByteArray result(256, 0);
1338 if (!fgets(s: (char *)result.data(), n: result.size(), stdin))
1339 return QString();
1340 if (result[result.length() - 1] == '\n')
1341 result.truncate(pos: result.length() - 1);
1342 // empty input -> use default
1343 if (result.isEmpty())
1344 return def;
1345 // trimmed input could result in an empty value, but in that case
1346 // it is treated as if the user wishes to submit an empty value.
1347 return QString::fromLocal8Bit(ba: result).trimmed();
1348}
1349
1350static int prompt_for_int(const QString &prompt, int def = 0)
1351{
1352 while (true) {
1353 QString str = prompt_for_string(prompt);
1354 if (str.isEmpty())
1355 return def;
1356 bool ok;
1357 int x = str.toInt(ok: &ok);
1358 if (ok)
1359 return x;
1360 printf(format: "'%s' is not a valid entry.\n\n", qPrintable(str));
1361 }
1362}
1363
1364static bool partial_compare_nocase(const QString &in, const QString &target, int min = 1)
1365{
1366 if (in.length() >= min && in.length() <= target.length() && target.mid(position: 0, n: in.length()).toLower() == in.toLower())
1367 return true;
1368 return false;
1369}
1370
1371static bool prompt_for_bool(const QString &prompt, bool def = false)
1372{
1373 while (true) {
1374 QString str = prompt_for_string(prompt);
1375 if (str.isEmpty())
1376 return def;
1377 if (partial_compare_nocase(in: str, QStringLiteral("true")))
1378 return true;
1379 else if (partial_compare_nocase(in: str, QStringLiteral("false")))
1380 return false;
1381 printf(format: "'%s' is not a valid entry.\n\n", qPrintable(str));
1382 }
1383}
1384
1385static bool prompt_for_yesno(const QString &prompt, bool def = false)
1386{
1387 while (true) {
1388 QString str = prompt_for_string(prompt);
1389 if (str.isEmpty())
1390 return def;
1391 if (partial_compare_nocase(in: str, QStringLiteral("yes")))
1392 return true;
1393 else if (partial_compare_nocase(in: str, QStringLiteral("no")))
1394 return false;
1395 printf(format: "'%s' is not a valid entry.\n\n", qPrintable(str));
1396 }
1397}
1398
1399static QString prompt_for_slotevent_method(const QString &prompt, const QString &def = QString())
1400{
1401 while (true) {
1402 QString str = prompt_for_string(prompt);
1403 if (str.isEmpty())
1404 return def;
1405 if (partial_compare_nocase(in: str, QStringLiteral("auto")))
1406 return QStringLiteral("auto");
1407 else if (partial_compare_nocase(in: str, QStringLiteral("trigger")))
1408 return QStringLiteral("trigger");
1409 else if (partial_compare_nocase(in: str, QStringLiteral("poll")))
1410 return QStringLiteral("poll");
1411 printf(format: "'%s' is not a valid entry.\n\n", qPrintable(str));
1412 }
1413}
1414
1415static QVariantMap provider_config_edit_generic(const QVariantMap &in)
1416{
1417 QVariantMap config = in;
1418 QMutableMapIterator<QString, QVariant> it(config);
1419 while (it.hasNext()) {
1420 it.next();
1421 QString var = it.key();
1422 if (var == QLatin1String("formtype"))
1423 continue;
1424 QVariant val = it.value();
1425
1426 // fields must be bool, int, or string
1427 QVariant newval;
1428 QString prompt = QStringLiteral("%1: [%2] ").arg(args&: var, args: val.toString());
1429 if (variant_is_bool(in: val))
1430 newval = prompt_for_bool(QStringLiteral("bool ") + prompt, def: val.toBool());
1431 else if (variant_is_int(in: val))
1432 newval = prompt_for_int(QStringLiteral("int ") + prompt, def: val.toInt());
1433 else if (val.canConvert<QString>())
1434 newval = prompt_for_string(QStringLiteral("string ") + prompt, def: val.toString());
1435 else
1436 continue; // skip bogus fields
1437
1438 it.setValue(newval);
1439 }
1440
1441 return config;
1442}
1443
1444class Pkcs11ProviderConfig
1445{
1446public:
1447 bool allow_protected_authentication;
1448 bool cert_private;
1449 bool enabled;
1450 QString library;
1451 QString name;
1452 int private_mask;
1453 QString slotevent_method;
1454 int slotevent_timeout;
1455
1456 Pkcs11ProviderConfig()
1457 : allow_protected_authentication(true)
1458 , cert_private(false)
1459 , enabled(false)
1460 , private_mask(0)
1461 , slotevent_method(QStringLiteral("auto"))
1462 , slotevent_timeout(0)
1463 {
1464 }
1465
1466 QVariantMap toVariantMap() const
1467 {
1468 QVariantMap out;
1469 out[QStringLiteral("allow_protected_authentication")] = allow_protected_authentication;
1470 out[QStringLiteral("cert_private")] = cert_private;
1471 out[QStringLiteral("enabled")] = enabled;
1472 out[QStringLiteral("library")] = library;
1473 out[QStringLiteral("name")] = name;
1474 out[QStringLiteral("private_mask")] = private_mask;
1475 out[QStringLiteral("slotevent_method")] = slotevent_method;
1476 out[QStringLiteral("slotevent_timeout")] = slotevent_timeout;
1477 return out;
1478 }
1479
1480 bool fromVariantMap(const QVariantMap &in)
1481 {
1482 allow_protected_authentication = in[QStringLiteral("allow_protected_authentication")].toBool();
1483 cert_private = in[QStringLiteral("cert_private")].toBool();
1484 enabled = in[QStringLiteral("enabled")].toBool();
1485 library = in[QStringLiteral("library")].toString();
1486 name = in[QStringLiteral("name")].toString();
1487 private_mask = in[QStringLiteral("private_mask")].toInt();
1488 slotevent_method = in[QStringLiteral("slotevent_method")].toString();
1489 slotevent_timeout = in[QStringLiteral("slotevent_timeout")].toInt();
1490 return true;
1491 }
1492};
1493
1494class Pkcs11Config
1495{
1496public:
1497 bool allow_load_rootca;
1498 bool allow_protected_authentication;
1499 int log_level;
1500 int pin_cache;
1501 QList<Pkcs11ProviderConfig> providers;
1502
1503 QVariantMap orig_config;
1504
1505 Pkcs11Config()
1506 : allow_load_rootca(false)
1507 , allow_protected_authentication(true)
1508 , log_level(0)
1509 , pin_cache(-1)
1510 {
1511 }
1512
1513 QVariantMap toVariantMap() const
1514 {
1515 QVariantMap out = orig_config;
1516
1517 // form type
1518 out[QStringLiteral("formtype")] = QLatin1String("http://affinix.com/qca/forms/qca-pkcs11#1.0");
1519
1520 // base settings
1521 out[QStringLiteral("allow_load_rootca")] = allow_load_rootca;
1522 out[QStringLiteral("allow_protected_authentication")] = allow_protected_authentication;
1523 out[QStringLiteral("log_level")] = log_level;
1524 out[QStringLiteral("pin_cache")] = pin_cache;
1525
1526 // provider settings (always write at least 10 providers)
1527 for (int n = 0; n < 10 || n < providers.count(); ++n) {
1528 QString prefix = QString::asprintf(format: "provider_%02d_", n);
1529
1530 Pkcs11ProviderConfig provider;
1531 if (n < providers.count())
1532 provider = providers[n];
1533
1534 QVariantMap subconfig = provider.toVariantMap();
1535 QMapIterator<QString, QVariant> it(subconfig);
1536 while (it.hasNext()) {
1537 it.next();
1538 out.insert(key: prefix + it.key(), value: it.value());
1539 }
1540 }
1541
1542 return out;
1543 }
1544
1545 bool fromVariantMap(const QVariantMap &in)
1546 {
1547 if (in[QStringLiteral("formtype")] != QLatin1String("http://affinix.com/qca/forms/qca-pkcs11#1.0"))
1548 return false;
1549
1550 allow_load_rootca = in[QStringLiteral("allow_load_rootca")].toBool();
1551 allow_protected_authentication = in[QStringLiteral("allow_protected_authentication")].toBool();
1552 log_level = in[QStringLiteral("log_level")].toInt();
1553 pin_cache = in[QStringLiteral("pin_cache")].toInt();
1554
1555 for (int n = 0;; ++n) {
1556 QString prefix = QString::asprintf(format: "provider_%02d_", n);
1557
1558 // collect all key/values with this prefix into a
1559 // a separate container, leaving out the prefix
1560 // from the keys.
1561 QVariantMap subconfig;
1562 QMapIterator<QString, QVariant> it(in);
1563 while (it.hasNext()) {
1564 it.next();
1565 if (it.key().startsWith(s: prefix))
1566 subconfig.insert(key: it.key().mid(position: prefix.length()), value: it.value());
1567 }
1568
1569 // if there are no config items with this prefix, we're done
1570 if (subconfig.isEmpty())
1571 break;
1572
1573 Pkcs11ProviderConfig provider;
1574 if (!provider.fromVariantMap(in: subconfig))
1575 return false;
1576
1577 // skip unnamed entries
1578 if (provider.name.isEmpty())
1579 continue;
1580
1581 // skip duplicate entries
1582 bool have_name_already = false;
1583 foreach (const Pkcs11ProviderConfig &i, providers) {
1584 if (i.name == provider.name) {
1585 have_name_already = true;
1586 break;
1587 }
1588 }
1589 if (have_name_already)
1590 continue;
1591
1592 providers += provider;
1593 }
1594
1595 orig_config = in;
1596 return true;
1597 }
1598};
1599
1600static QVariantMap provider_config_edit_pkcs11(const QVariantMap &in)
1601{
1602 Pkcs11Config config;
1603 if (!config.fromVariantMap(in)) {
1604 fprintf(stderr, format: "Error: unable to parse PKCS#11 provider configuration.\n");
1605 return QVariantMap();
1606 }
1607
1608 while (true) {
1609 printf(format: "\n");
1610 printf(format: "Global settings:\n");
1611 printf(format: " Allow loading of root CAs: %s\n", config.allow_load_rootca ? "Yes" : "No");
1612 printf(format: " Allow protected authentication: %s\n", config.allow_protected_authentication ? "Yes" : "No");
1613 QString str;
1614 if (config.pin_cache == -1)
1615 str = QStringLiteral("No limit");
1616 else
1617 str = QStringLiteral("%1 seconds").arg(a: config.pin_cache);
1618 printf(format: " Maximum PIN cache time: %s\n", qPrintable(str));
1619 printf(format: " Log level: %d\n", config.log_level);
1620 printf(format: "\n");
1621 printf(format: "PKCS#11 modules:\n");
1622 if (!config.providers.isEmpty()) {
1623 foreach (const Pkcs11ProviderConfig &provider, config.providers)
1624 printf(format: " %s\n", qPrintable(provider.name));
1625 } else
1626 printf(format: " (None)\n");
1627 printf(format: "\n");
1628 printf(format: "Actions:\n");
1629 printf(format: " a) Edit global settings\n");
1630 printf(format: " b) Add PKCS#11 module\n");
1631 printf(format: " c) Edit PKCS#11 module\n");
1632 printf(format: " d) Remove PKCS#11 module\n");
1633 printf(format: "\n");
1634
1635 int index;
1636 while (true) {
1637 QString str = prompt_for(QStringLiteral("Select an action, or enter to quit"));
1638 if (str.isEmpty()) {
1639 index = -1;
1640 break;
1641 }
1642 if (str.length() == 1) {
1643 index = str[0].toLatin1() - 'a';
1644 if (index >= 0 && index < 4)
1645 break;
1646 }
1647 printf(format: "'%s' is not a valid entry.\n\n", qPrintable(str));
1648 }
1649 if (index == -1)
1650 break;
1651
1652 if (index == 0) {
1653 printf(format: "\n");
1654
1655 QString prompt;
1656 prompt = QStringLiteral("Allow loading of root CAs: [%1] ")
1657 .arg(a: config.allow_load_rootca ? QStringLiteral("Yes") : QStringLiteral("No"));
1658 config.allow_load_rootca = prompt_for_yesno(prompt, def: config.allow_load_rootca);
1659 prompt = QStringLiteral("Allow protected authentication: [%1] ")
1660 .arg(a: config.allow_protected_authentication ? QStringLiteral("Yes") : QStringLiteral("No"));
1661 config.allow_protected_authentication = prompt_for_yesno(prompt, def: config.allow_protected_authentication);
1662 prompt = QStringLiteral("Maximum PIN cache time in seconds (-1 for no limit): [%1] ").arg(a: config.pin_cache);
1663 config.pin_cache = prompt_for_int(prompt, def: config.pin_cache);
1664 prompt = QStringLiteral("Log level: [%1] ").arg(a: config.log_level);
1665 config.log_level = prompt_for_int(prompt, def: config.log_level);
1666 } else // 1, 2, 3
1667 {
1668 int at = -1;
1669
1670 // for edit/remove, need to select provider
1671 if (index == 2 || index == 3) {
1672 printf(format: "\nWhich PKCS#11 module?\n");
1673 for (int n = 0; n < config.providers.count(); ++n) {
1674 const Pkcs11ProviderConfig &provider = config.providers[n];
1675 char c = 'a' + n;
1676 printf(format: " %c) %s\n", c, qPrintable(provider.name));
1677 }
1678 printf(format: "\n");
1679
1680 int index;
1681 while (true) {
1682 QString str = prompt_for(QStringLiteral("Select a module, or enter to go back"));
1683 if (str.isEmpty()) {
1684 index = -1;
1685 break;
1686 }
1687 if (str.length() == 1) {
1688 index = str[0].toLatin1() - 'a';
1689 if (index >= 0 && index < config.providers.count())
1690 break;
1691 }
1692 printf(format: "'%s' is not a valid entry.\n", qPrintable(str));
1693 }
1694
1695 // exit?
1696 if (index == -1)
1697 continue;
1698
1699 at = index;
1700 }
1701
1702 // edit the entry
1703 if (index == 1 || index == 2) {
1704 Pkcs11ProviderConfig provider;
1705 if (index == 2) // edit
1706 provider = config.providers[at];
1707 provider.enabled = true;
1708 printf(format: "\n");
1709
1710 QString prompt;
1711
1712 // prompt for unique name
1713 while (true) {
1714 if (index == 1)
1715 prompt = QStringLiteral("Unique friendly name: ");
1716 else
1717 prompt = QStringLiteral("Unique friendly name: [%1] ").arg(a: provider.name);
1718 provider.name = prompt_for_string(prompt, def: provider.name);
1719
1720 if (provider.name.isEmpty()) {
1721 printf(format: "The friendly name cannot be blank.\n\n");
1722 continue;
1723 }
1724
1725 bool have_name_already = false;
1726 for (int n = 0; n < config.providers.count(); ++n) {
1727 const Pkcs11ProviderConfig &i = config.providers[n];
1728
1729 // skip checking against the entry we are editing
1730 if (at != -1 && n == at)
1731 continue;
1732
1733 if (i.name == provider.name) {
1734 have_name_already = true;
1735 break;
1736 }
1737 }
1738 if (have_name_already) {
1739 printf(format: "This name is already used by another module.\n\n");
1740 continue;
1741 }
1742
1743 break;
1744 }
1745
1746 // prompt for library file
1747 QString last;
1748 while (true) {
1749 if (index == 1)
1750 prompt = QStringLiteral("Library filename: ");
1751 else
1752 prompt = QStringLiteral("Library filename: [%1] ").arg(a: provider.library);
1753 provider.library = prompt_for_string(prompt, def: provider.library);
1754
1755 if (provider.library.isEmpty()) {
1756 printf(format: "The library filename cannot be blank.\n\n");
1757 continue;
1758 }
1759
1760 if (last != provider.library && !QFile::exists(fileName: provider.library)) {
1761 last = provider.library;
1762 printf(format: "'%s' does not exist.\nPress enter again if you really want this.\n\n",
1763 qPrintable(provider.library));
1764 continue;
1765 }
1766
1767 break;
1768 }
1769
1770 prompt =
1771 QStringLiteral("Allow protected authentication: [%1] ")
1772 .arg(a: provider.allow_protected_authentication ? QStringLiteral("Yes") : QStringLiteral("No"));
1773 provider.allow_protected_authentication =
1774 prompt_for_yesno(prompt, def: provider.allow_protected_authentication);
1775 prompt = QStringLiteral("Provider stores certificates as private objects: [%1] ")
1776 .arg(a: provider.cert_private ? QStringLiteral("Yes") : QStringLiteral("No"));
1777 provider.cert_private = prompt_for_yesno(prompt, def: provider.cert_private);
1778 printf(format: "\n");
1779 printf(format: "Provider private key mask:\n");
1780 printf(format: " 0 Determine automatically.\n");
1781 printf(format: " 1 Use sign.\n");
1782 printf(format: " 2 Use sign recover.\n");
1783 printf(format: " 4 Use decrypt.\n");
1784 printf(format: " 8 Use unwrap.\n");
1785 prompt = QStringLiteral("Mask value: [%1] ").arg(a: provider.private_mask);
1786 provider.private_mask = prompt_for_int(prompt, def: provider.private_mask);
1787 printf(format: "\n");
1788 printf(format: "Slot event method:\n");
1789 printf(format: " auto Determine automatically.\n");
1790 printf(format: " trigger Use trigger.\n");
1791 printf(format: " poll Use poll.\n");
1792 prompt = QStringLiteral("Method value: [%1] ").arg(a: provider.slotevent_method);
1793 provider.slotevent_method = prompt_for_slotevent_method(prompt, def: provider.slotevent_method);
1794 if (provider.slotevent_method == QLatin1String("poll")) {
1795 prompt =
1796 QStringLiteral("Poll timeout (0 for no preference): [%1] ").arg(a: provider.slotevent_timeout);
1797 provider.slotevent_timeout = prompt_for_int(prompt, def: provider.slotevent_timeout);
1798 } else
1799 provider.slotevent_timeout = 0;
1800
1801 if (index == 1)
1802 config.providers += provider;
1803 else // 2
1804 config.providers[at] = provider;
1805 }
1806 // remove the entry
1807 else // 3
1808 {
1809 config.providers.removeAt(i: at);
1810 }
1811 }
1812 }
1813
1814 return config.toVariantMap();
1815}
1816
1817static QVariantMap provider_config_edit(const QVariantMap &in)
1818{
1819 // see if we have a configurator for a known form type
1820 if (in[QStringLiteral("formtype")] == QLatin1String("http://affinix.com/qca/forms/qca-pkcs11#1.0"))
1821 return provider_config_edit_pkcs11(in);
1822
1823 // otherwise, use the generic configurator
1824 return provider_config_edit_generic(in);
1825}
1826
1827static QString get_fingerprint(const QCA::Certificate &cert, const QString &hashType)
1828{
1829 QString hex = QCA::Hash(hashType).hashToString(array: cert.toDER());
1830 QString out;
1831 for (int n = 0; n < hex.size(); ++n) {
1832 if (n != 0 && n % 2 == 0)
1833 out += QLatin1Char(':');
1834 out += hex[n];
1835 }
1836 return out;
1837}
1838
1839static QString kstype_to_string(QCA::KeyStore::Type _type)
1840{
1841 QString type;
1842 switch (_type) {
1843 case QCA::KeyStore::System:
1844 type = QStringLiteral("Sys ");
1845 break;
1846 case QCA::KeyStore::User:
1847 type = QStringLiteral("User");
1848 break;
1849 case QCA::KeyStore::Application:
1850 type = QStringLiteral("App ");
1851 break;
1852 case QCA::KeyStore::SmartCard:
1853 type = QStringLiteral("Card");
1854 break;
1855 case QCA::KeyStore::PGPKeyring:
1856 type = QStringLiteral("PGP ");
1857 break;
1858 default:
1859 type = QStringLiteral("XXXX");
1860 break;
1861 }
1862 return type;
1863}
1864
1865static QString ksentrytype_to_string(QCA::KeyStoreEntry::Type _type)
1866{
1867 QString type;
1868 switch (_type) {
1869 case QCA::KeyStoreEntry::TypeKeyBundle:
1870 type = QStringLiteral("Key ");
1871 break;
1872 case QCA::KeyStoreEntry::TypeCertificate:
1873 type = QStringLiteral("Cert");
1874 break;
1875 case QCA::KeyStoreEntry::TypeCRL:
1876 type = QStringLiteral("CRL ");
1877 break;
1878 case QCA::KeyStoreEntry::TypePGPSecretKey:
1879 type = QStringLiteral("PSec");
1880 break;
1881 case QCA::KeyStoreEntry::TypePGPPublicKey:
1882 type = QStringLiteral("PPub");
1883 break;
1884 default:
1885 type = QStringLiteral("XXXX");
1886 break;
1887 }
1888 return type;
1889}
1890
1891static void try_print_info(const char *name, const QStringList &values)
1892{
1893 if (!values.isEmpty()) {
1894 QString value = values.join(QStringLiteral(", "));
1895 printf(format: " %s: %s\n", name, value.toUtf8().data());
1896 }
1897}
1898
1899static void print_info(const char *title, const QCA::CertificateInfo &info)
1900{
1901 QList<InfoType> list = makeInfoTypeList();
1902 printf(format: "%s\n", title);
1903 foreach (const InfoType &t, list)
1904 try_print_info(qPrintable(t.name), values: info.values(key: t.type));
1905}
1906
1907static void print_info_ordered(const char *title, const QCA::CertificateInfoOrdered &info)
1908{
1909 QList<InfoType> list = makeInfoTypeList(legacyEmail: true);
1910 printf(format: "%s\n", title);
1911 foreach (const QCA::CertificateInfoPair &pair, info) {
1912 QCA::CertificateInfoType type = pair.type();
1913 QString name;
1914 int at = -1;
1915 for (int n = 0; n < list.count(); ++n) {
1916 if (list[n].type == type) {
1917 at = n;
1918 break;
1919 }
1920 }
1921
1922 // known type?
1923 if (at != -1) {
1924 name = list[at].name;
1925 } else {
1926 if (pair.type().section() == QCA::CertificateInfoType::DN)
1927 name = QStringLiteral("DN:") + pair.type().id();
1928 else
1929 name = QStringLiteral("AN:") + pair.type().id();
1930 }
1931
1932 printf(format: " %s: %s\n", qPrintable(name), pair.value().toUtf8().data());
1933 }
1934}
1935
1936static QString constraint_to_string(const QCA::ConstraintType &t)
1937{
1938 QList<MyConstraintType> list = makeConstraintTypeList();
1939 for (int n = 0; n < list.count(); ++n) {
1940 if (list[n].type == t)
1941 return list[n].name;
1942 }
1943 return t.id();
1944}
1945
1946static QString sigalgo_to_string(QCA::SignatureAlgorithm algo)
1947{
1948 QString str;
1949 switch (algo) {
1950 case QCA::EMSA1_SHA1:
1951 str = QStringLiteral("EMSA1(SHA1)");
1952 break;
1953 case QCA::EMSA3_SHA1:
1954 str = QStringLiteral("EMSA3(SHA1)");
1955 break;
1956 case QCA::EMSA3_MD5:
1957 str = QStringLiteral("EMSA3(MD5)");
1958 break;
1959 case QCA::EMSA3_MD2:
1960 str = QStringLiteral("EMSA3(MD2)");
1961 break;
1962 case QCA::EMSA3_RIPEMD160:
1963 str = QStringLiteral("EMSA3(RIPEMD160)");
1964 break;
1965 case QCA::EMSA3_Raw:
1966 str = QStringLiteral("EMSA3(raw)");
1967 break;
1968 default:
1969 str = QStringLiteral("Unknown");
1970 break;
1971 }
1972 return str;
1973}
1974
1975static void print_cert(const QCA::Certificate &cert, bool ordered = false)
1976{
1977 printf(format: "Serial Number: %s\n", qPrintable(cert.serialNumber().toString()));
1978
1979 if (ordered) {
1980 print_info_ordered(title: "Subject", info: cert.subjectInfoOrdered());
1981 print_info_ordered(title: "Issuer", info: cert.issuerInfoOrdered());
1982 } else {
1983 print_info(title: "Subject", info: cert.subjectInfo());
1984 print_info(title: "Issuer", info: cert.issuerInfo());
1985 }
1986
1987 printf(format: "Validity\n");
1988 printf(format: " Not before: %s\n", qPrintable(cert.notValidBefore().toString()));
1989 printf(format: " Not after: %s\n", qPrintable(cert.notValidAfter().toString()));
1990
1991 printf(format: "Constraints\n");
1992 QCA::Constraints constraints = cert.constraints();
1993 int n;
1994 if (!constraints.isEmpty()) {
1995 for (n = 0; n < constraints.count(); ++n)
1996 printf(format: " %s\n", qPrintable(constraint_to_string(constraints[n])));
1997 } else
1998 printf(format: " No constraints\n");
1999
2000 printf(format: "Policies\n");
2001 QStringList policies = cert.policies();
2002 if (!policies.isEmpty()) {
2003 for (n = 0; n < policies.count(); ++n)
2004 printf(format: " %s\n", qPrintable(policies[n]));
2005 } else
2006 printf(format: " No policies\n");
2007
2008 QByteArray id;
2009 printf(format: "Issuer Key ID: ");
2010 id = cert.issuerKeyId();
2011 if (!id.isEmpty())
2012 printf(format: "%s\n", qPrintable(QCA::arrayToHex(id)));
2013 else
2014 printf(format: "None\n");
2015
2016 printf(format: "Subject Key ID: ");
2017 id = cert.subjectKeyId();
2018 if (!id.isEmpty())
2019 printf(format: "%s\n", qPrintable(QCA::arrayToHex(id)));
2020 else
2021 printf(format: "None\n");
2022
2023 printf(format: "CA: %s\n", cert.isCA() ? "Yes" : "No");
2024 printf(format: "Signature Algorithm: %s\n", qPrintable(sigalgo_to_string(cert.signatureAlgorithm())));
2025
2026 QCA::PublicKey key = cert.subjectPublicKey();
2027 printf(format: "Public Key:\n%s", key.toPEM().toLatin1().data());
2028
2029 printf(format: "SHA1 Fingerprint: %s\n", qPrintable(get_fingerprint(cert, QStringLiteral("sha1"))));
2030 printf(format: "MD5 Fingerprint: %s\n", qPrintable(get_fingerprint(cert, QStringLiteral("md5"))));
2031}
2032
2033static void print_certreq(const QCA::CertificateRequest &cert, bool ordered = false)
2034{
2035 if (ordered)
2036 print_info_ordered(title: "Subject", info: cert.subjectInfoOrdered());
2037 else
2038 print_info(title: "Subject", info: cert.subjectInfo());
2039
2040 printf(format: "Constraints\n");
2041 QCA::Constraints constraints = cert.constraints();
2042 int n;
2043 if (!constraints.isEmpty()) {
2044 for (n = 0; n < constraints.count(); ++n)
2045 printf(format: " %s\n", qPrintable(constraint_to_string(constraints[n])));
2046 } else
2047 printf(format: " No constraints\n");
2048
2049 printf(format: "Policies\n");
2050 QStringList policies = cert.policies();
2051 if (!policies.isEmpty()) {
2052 for (n = 0; n < policies.count(); ++n)
2053 printf(format: " %s\n", qPrintable(policies[n]));
2054 } else
2055 printf(format: " No policies\n");
2056
2057 printf(format: "CA: %s\n", cert.isCA() ? "Yes" : "No");
2058 printf(format: "Signature Algorithm: %s\n", qPrintable(sigalgo_to_string(cert.signatureAlgorithm())));
2059
2060 QCA::PublicKey key = cert.subjectPublicKey();
2061 printf(format: "Public Key:\n%s", key.toPEM().toLatin1().data());
2062}
2063
2064static void print_crl(const QCA::CRL &crl, bool ordered = false)
2065{
2066 if (ordered)
2067 print_info_ordered(title: "Issuer", info: crl.issuerInfoOrdered());
2068 else
2069 print_info(title: "Issuer", info: crl.issuerInfo());
2070
2071 int num = crl.number();
2072 if (num != -1)
2073 printf(format: "Number: %d\n", num);
2074
2075 printf(format: "Validity\n");
2076 printf(format: " This update: %s\n", qPrintable(crl.thisUpdate().toString()));
2077 printf(format: " Next update: %s\n", qPrintable(crl.nextUpdate().toString()));
2078
2079 QByteArray id;
2080 printf(format: "Issuer Key ID: ");
2081 id = crl.issuerKeyId();
2082 if (!id.isEmpty())
2083 printf(format: "%s\n", qPrintable(QCA::arrayToHex(id)));
2084 else
2085 printf(format: "None\n");
2086
2087 printf(format: "Signature Algorithm: %s\n", qPrintable(sigalgo_to_string(crl.signatureAlgorithm())));
2088
2089 QList<QCA::CRLEntry> revokedList = crl.revoked();
2090 foreach (const QCA::CRLEntry &entry, revokedList) {
2091 printf(format: " %s: %s, %s\n",
2092 qPrintable(entry.serialNumber().toString()),
2093 crlEntryReasonToString(r: entry.reason()),
2094 qPrintable(entry.time().toString()));
2095 }
2096}
2097
2098static QString format_pgp_fingerprint(const QString &in)
2099{
2100 QString out;
2101 bool first = true;
2102 for (int n = 0; n + 3 < in.length(); n += 4) {
2103 if (!first)
2104 out += QLatin1Char(' ');
2105 else
2106 first = false;
2107 out += in.mid(position: n, n: 4).toUpper();
2108 }
2109 return out;
2110}
2111
2112static void print_pgp(const QCA::PGPKey &key)
2113{
2114 printf(format: "Key ID: %s\n", qPrintable(key.keyId()));
2115 printf(format: "User IDs:\n");
2116 foreach (const QString &s, key.userIds())
2117 printf(format: " %s\n", qPrintable(s));
2118 printf(format: "Validity\n");
2119 printf(format: " Not before: %s\n", qPrintable(key.creationDate().toString()));
2120 if (!key.expirationDate().isNull())
2121 printf(format: " Not after: %s\n", qPrintable(key.expirationDate().toString()));
2122 else
2123 printf(format: " Not after: (no expiration)\n");
2124 printf(format: "In Keyring: %s\n", key.inKeyring() ? "Yes" : "No");
2125 printf(format: "Secret Key: %s\n", key.isSecret() ? "Yes" : "No");
2126 printf(format: "Trusted: %s\n", key.isTrusted() ? "Yes" : "No");
2127 printf(format: "Fingerprint: %s\n", qPrintable(format_pgp_fingerprint(key.fingerprint())));
2128}
2129
2130static QString validityToString(QCA::Validity v)
2131{
2132 QString s;
2133 switch (v) {
2134 case QCA::ValidityGood:
2135 s = QStringLiteral("Validated");
2136 break;
2137 case QCA::ErrorRejected:
2138 s = QStringLiteral("Root CA is marked to reject the specified purpose");
2139 break;
2140 case QCA::ErrorUntrusted:
2141 s = QStringLiteral("Certificate not trusted for the required purpose");
2142 break;
2143 case QCA::ErrorSignatureFailed:
2144 s = QStringLiteral("Invalid signature");
2145 break;
2146 case QCA::ErrorInvalidCA:
2147 s = QStringLiteral("Invalid CA certificate");
2148 break;
2149 case QCA::ErrorInvalidPurpose:
2150 s = QStringLiteral("Invalid certificate purpose");
2151 break;
2152 case QCA::ErrorSelfSigned:
2153 s = QStringLiteral("Certificate is self-signed");
2154 break;
2155 case QCA::ErrorRevoked:
2156 s = QStringLiteral("Certificate has been revoked");
2157 break;
2158 case QCA::ErrorPathLengthExceeded:
2159 s = QStringLiteral("Maximum certificate chain length exceeded");
2160 break;
2161 case QCA::ErrorExpired:
2162 s = QStringLiteral("Certificate has expired");
2163 break;
2164 case QCA::ErrorExpiredCA:
2165 s = QStringLiteral("CA has expired");
2166 break;
2167 case QCA::ErrorValidityUnknown:
2168 default:
2169 s = QStringLiteral("General certificate validation error");
2170 break;
2171 }
2172 return s;
2173}
2174
2175static QString smIdentityResultToString(QCA::SecureMessageSignature::IdentityResult r)
2176{
2177 QString str;
2178 switch (r) {
2179 case QCA::SecureMessageSignature::Valid:
2180 str = QStringLiteral("Valid");
2181 break;
2182 case QCA::SecureMessageSignature::InvalidSignature:
2183 str = QStringLiteral("InvalidSignature");
2184 break;
2185 case QCA::SecureMessageSignature::InvalidKey:
2186 str = QStringLiteral("InvalidKey");
2187 break;
2188 case QCA::SecureMessageSignature::NoKey:
2189 str = QStringLiteral("NoKey");
2190 break;
2191 default:
2192 str = QStringLiteral("Unknown");
2193 }
2194 return str;
2195}
2196
2197static QString smErrorToString(QCA::SecureMessage::Error e)
2198{
2199 QMap<QCA::SecureMessage::Error, QString> map;
2200 map[QCA::SecureMessage::ErrorPassphrase] = QStringLiteral("ErrorPassphrase");
2201 map[QCA::SecureMessage::ErrorFormat] = QStringLiteral("ErrorFormat");
2202 map[QCA::SecureMessage::ErrorSignerExpired] = QStringLiteral("ErrorSignerExpired");
2203 map[QCA::SecureMessage::ErrorSignerInvalid] = QStringLiteral("ErrorSignerInvalid");
2204 map[QCA::SecureMessage::ErrorEncryptExpired] = QStringLiteral("ErrorEncryptExpired");
2205 map[QCA::SecureMessage::ErrorEncryptUntrusted] = QStringLiteral("ErrorEncryptUntrusted");
2206 map[QCA::SecureMessage::ErrorEncryptInvalid] = QStringLiteral("ErrorEncryptInvalid");
2207 map[QCA::SecureMessage::ErrorNeedCard] = QStringLiteral("ErrorNeedCard");
2208 map[QCA::SecureMessage::ErrorCertKeyMismatch] = QStringLiteral("ErrorCertKeyMismatch");
2209 map[QCA::SecureMessage::ErrorUnknown] = QStringLiteral("ErrorUnknown");
2210 return map[e];
2211}
2212
2213static void smDisplaySignatures(const QList<QCA::SecureMessageSignature> &signers)
2214{
2215 foreach (const QCA::SecureMessageSignature &signer, signers) {
2216 QCA::SecureMessageSignature::IdentityResult r = signer.identityResult();
2217 fprintf(stderr, format: "IdentityResult: %s\n", qPrintable(smIdentityResultToString(r)));
2218
2219 QCA::SecureMessageKey key = signer.key();
2220 if (!key.isNull()) {
2221 if (key.type() == QCA::SecureMessageKey::PGP) {
2222 QCA::PGPKey pub = key.pgpPublicKey();
2223 fprintf(stderr, format: "From: %s (%s)\n", qPrintable(pub.primaryUserId()), qPrintable(pub.keyId()));
2224 } else {
2225 QCA::Certificate cert = key.x509CertificateChain().primary();
2226 QString emailStr;
2227 QCA::CertificateInfo info = cert.subjectInfo();
2228 if (info.contains(key: QCA::Email))
2229 emailStr = QStringLiteral(" (%1)").arg(a: info.value(key: QCA::Email));
2230 fprintf(stderr, format: "From: %s%s\n", qPrintable(cert.commonName()), qPrintable(emailStr));
2231 }
2232 }
2233 }
2234}
2235
2236static const char *mime_signpart =
2237 "Content-Type: text/plain; charset=UTF-8\r\n"
2238 "Content-Transfer-Encoding: 8bit\r\n"
2239 "\r\n"
2240 "%1";
2241
2242static const char *mime_signed =
2243 "Content-Type: multipart/signed;\r\n"
2244 " micalg=%1;\r\n"
2245 " boundary=QCATOOL-0001;\r\n"
2246 " protocol=\"application/pkcs7-signature\"\r\n"
2247 "\r\n"
2248 "\r\n"
2249 "--QCATOOL-0001\r\n"
2250 "%2\r\n"
2251 "--QCATOOL-0001\r\n"
2252 "Content-Transfer-Encoding: base64\r\n"
2253 "Content-Type: application/pkcs7-signature;\r\n"
2254 " name=smime.p7s\r\n"
2255 "Content-Disposition: attachment;\r\n"
2256 " filename=smime.p7s\r\n"
2257 "\r\n"
2258 "%3\r\n"
2259 "\r\n"
2260 "--QCATOOL-0001--\r\n";
2261
2262static const char *mime_enveloped =
2263 "Mime-Version: 1.0\r\n"
2264 "Content-Transfer-Encoding: base64\r\n"
2265 "Content-Type: application/pkcs7-mime;\r\n"
2266 " name=smime.p7m;\r\n"
2267 " smime-type=enveloped-data\r\n"
2268 "Content-Disposition: attachment;\r\n"
2269 " filename=smime.p7m\r\n"
2270 "\r\n"
2271 "%1\r\n";
2272
2273static QString add_cr(const QString &in)
2274{
2275 QString out = in;
2276 int at = 0;
2277 while (true) {
2278 at = out.indexOf(c: QLatin1Char('\n'), from: at);
2279 if (at == -1)
2280 break;
2281 if (at - 1 >= 0 && out[at - 1] != QLatin1Char('\r')) {
2282 out.insert(i: at, c: QLatin1Char('\r'));
2283 ++at;
2284 }
2285 ++at;
2286 }
2287 return out;
2288}
2289
2290static QString rem_cr(const QString &in)
2291{
2292 QString out = in;
2293 out.replace(before: QLatin1String("\r\n"), after: QLatin1String("\n"));
2294 return out;
2295}
2296
2297static int indexOf_newline(const QString &in, int offset = 0)
2298{
2299 for (int n = offset; n < in.length(); ++n) {
2300 if (n + 1 < in.length() && in[n] == QLatin1Char('\r') && in[n + 1] == QLatin1Char('\n'))
2301 return n;
2302 if (in[n] == QLatin1Char('\n'))
2303 return n;
2304 }
2305 return -1;
2306}
2307
2308static int indexOf_doublenewline(const QString &in, int offset = 0)
2309{
2310 int at = -1;
2311 while (true) {
2312 int n = indexOf_newline(in, offset);
2313 if (n == -1)
2314 return -1;
2315
2316 if (at != -1) {
2317 if (n == offset)
2318 break;
2319 }
2320
2321 at = n;
2322 if (in[n] == QLatin1Char('\n'))
2323 offset = n + 1;
2324 else
2325 offset = n + 2;
2326 }
2327 return at;
2328}
2329
2330// this is so gross
2331static int newline_len(const QString &in, int offset = 0)
2332{
2333 if (in[offset] == QLatin1Char('\r'))
2334 return 2;
2335 else
2336 return 1;
2337}
2338
2339// all of this mime stuff is a total hack
2340static QString open_mime_envelope(const QString &in)
2341{
2342 int n = indexOf_doublenewline(in);
2343 if (n == -1)
2344 return QString();
2345 return in.mid(position: n + (newline_len(in, offset: n) * 2)); // good lord
2346}
2347
2348static bool open_mime_data_sig(const QString &in, QString *data, QString *sig)
2349{
2350 int n = in.indexOf(s: QLatin1String("boundary="));
2351 if (n == -1)
2352 return false;
2353 n += 9;
2354 int i = indexOf_newline(in, offset: n);
2355 if (i == -1)
2356 return false;
2357 QString boundary;
2358 QString bregion = in.mid(position: n, n: i - n);
2359 n = bregion.indexOf(c: QLatin1Char(';'));
2360 if (n != -1)
2361 boundary = bregion.mid(position: 0, n);
2362 else
2363 boundary = bregion;
2364
2365 if (boundary[0] == QLatin1Char('\"'))
2366 boundary.remove(i: 0, len: 1);
2367 if (boundary[boundary.length() - 1] == QLatin1Char('\"'))
2368 boundary.remove(i: boundary.length() - 1, len: 1);
2369 // printf("boundary: [%s]\n", qPrintable(boundary));
2370 QString boundary_end = QStringLiteral("--") + boundary;
2371 boundary = QStringLiteral("--") + boundary;
2372
2373 QString work = open_mime_envelope(in);
2374 // printf("work: [%s]\n", qPrintable(work));
2375
2376 n = work.indexOf(s: boundary);
2377 if (n == -1)
2378 return false;
2379 n += boundary.length();
2380 i = indexOf_newline(in: work, offset: n);
2381 if (i == -1)
2382 return false;
2383 n += newline_len(in: work, offset: i);
2384 int data_start = n;
2385
2386 n = work.indexOf(s: boundary, from: data_start);
2387 if (n == -1)
2388 return false;
2389 int data_end = n;
2390
2391 n = data_end + boundary.length();
2392 i = indexOf_newline(in: work, offset: n);
2393 if (i == -1)
2394 return false;
2395 n += newline_len(in: work, offset: i);
2396 int next = n;
2397
2398 QString tmp_data = work.mid(position: data_start, n: data_end - data_start);
2399 n = work.indexOf(s: boundary_end, from: next);
2400 if (n == -1)
2401 return false;
2402 QString tmp_sig = work.mid(position: next, n: n - next);
2403
2404 // nuke some newlines
2405 if (tmp_data.right(n: 2) == QLatin1String("\r\n"))
2406 tmp_data.truncate(pos: tmp_data.length() - 2);
2407 else if (tmp_data.right(n: 1) == QLatin1String("\n"))
2408 tmp_data.truncate(pos: tmp_data.length() - 1);
2409 if (tmp_sig.right(n: 2) == QLatin1String("\r\n"))
2410 tmp_sig.truncate(pos: tmp_sig.length() - 2);
2411 else if (tmp_sig.right(n: 1) == QLatin1String("\n"))
2412 tmp_sig.truncate(pos: tmp_sig.length() - 1);
2413
2414 tmp_sig = open_mime_envelope(in: tmp_sig);
2415
2416 *data = tmp_data;
2417 *sig = tmp_sig;
2418 return true;
2419}
2420
2421static QString idHash(const QString &id)
2422{
2423 // hash the id and take the rightmost 4 hex characters
2424 return QCA::Hash(QStringLiteral("md5")).hashToString(array: id.toUtf8()).right(n: 4);
2425}
2426
2427// first = ids, second = names
2428static QPair<QStringList, QStringList> getKeyStoreStrings(const QStringList &list, QCA::KeyStoreManager *ksm)
2429{
2430 QPair<QStringList, QStringList> out;
2431 for (int n = 0; n < list.count(); ++n) {
2432 QCA::KeyStore ks(list[n], ksm);
2433 out.first.append(t: idHash(id: ks.id()));
2434 out.second.append(t: ks.name());
2435 }
2436 return out;
2437}
2438
2439static QPair<QStringList, QStringList> getKeyStoreEntryStrings(const QList<QCA::KeyStoreEntry> &list)
2440{
2441 QPair<QStringList, QStringList> out;
2442 for (int n = 0; n < list.count(); ++n) {
2443 out.first.append(t: idHash(id: list[n].id()));
2444 out.second.append(t: list[n].name());
2445 }
2446 return out;
2447}
2448
2449static QList<int> getPartialMatches(const QStringList &list, const QString &str)
2450{
2451 QList<int> out;
2452 for (int n = 0; n < list.count(); ++n) {
2453 if (list[n].contains(s: str, cs: Qt::CaseInsensitive))
2454 out += n;
2455 }
2456 return out;
2457}
2458
2459static int findByString(const QPair<QStringList, QStringList> &in, const QString &str)
2460{
2461 // exact id match
2462 int n = in.first.indexOf(str);
2463 if (n != -1)
2464 return n;
2465
2466 // partial id match
2467 QList<int> ret = getPartialMatches(list: in.first, str);
2468 if (!ret.isEmpty())
2469 return ret.first();
2470
2471 // partial name match
2472 ret = getPartialMatches(list: in.second, str);
2473 if (!ret.isEmpty())
2474 return ret.first();
2475
2476 return -1;
2477}
2478
2479static QString getKeyStore(const QString &name)
2480{
2481 QCA::KeyStoreManager ksm;
2482 QStringList storeList = ksm.keyStores();
2483 int n = findByString(in: getKeyStoreStrings(list: storeList, ksm: &ksm), str: name);
2484 if (n != -1)
2485 return storeList[n];
2486 return QString();
2487}
2488
2489static QCA::KeyStoreEntry getKeyStoreEntry(QCA::KeyStore *store, const QString &name)
2490{
2491 QList<QCA::KeyStoreEntry> list = store->entryList();
2492 int n = findByString(in: getKeyStoreEntryStrings(list), str: name);
2493 if (n != -1)
2494 return list[n];
2495 return QCA::KeyStoreEntry();
2496}
2497
2498// here are a bunch of get_Foo functions for the various types
2499
2500// E - generic entry
2501// K - private key
2502// C - cert
2503// X - keybundle
2504// P - pgp public key
2505// S - pgp secret key
2506
2507// in all cases but K, the store:obj notation can be used. if there
2508// is no colon present, then we treat the input as a filename. we
2509// try the file as an exported passive entry id, and if the type
2510// is C or X, we'll fall back to regular files if necessary.
2511
2512static QCA::KeyStoreEntry get_E(const QString &name, bool nopassiveerror = false)
2513{
2514 QCA::KeyStoreEntry entry;
2515
2516 QCA::KeyStoreManager::start();
2517
2518 int n = name.indexOf(c: QLatin1Char(':'));
2519 if (n != -1) {
2520 ksm_start_and_wait();
2521
2522 // store:obj lookup
2523 QString storeName = name.mid(position: 0, n);
2524 QString objectName = name.mid(position: n + 1);
2525
2526 QCA::KeyStoreManager ksm;
2527 QCA::KeyStore store(getKeyStore(name: storeName), &ksm);
2528 if (!store.isValid()) {
2529 fprintf(stderr, format: "Error: no such store [%s].\n", qPrintable(storeName));
2530 return entry;
2531 }
2532
2533 entry = getKeyStoreEntry(store: &store, name: objectName);
2534 if (entry.isNull()) {
2535 fprintf(stderr, format: "Error: no such object [%s].\n", qPrintable(objectName));
2536 return entry;
2537 }
2538 } else {
2539 // exported id
2540 QString serialized = read_ksentry_file(fileName: name);
2541 entry = QCA::KeyStoreEntry(serialized);
2542 if (entry.isNull()) {
2543 if (!nopassiveerror)
2544 fprintf(stderr, format: "Error: invalid/unknown entry [%s].\n", qPrintable(name));
2545 return entry;
2546 }
2547 }
2548
2549 return entry;
2550}
2551
2552static QCA::PrivateKey get_K(const QString &name)
2553{
2554 QCA::PrivateKey key;
2555
2556 int n = name.indexOf(c: QLatin1Char(':'));
2557 if (n != -1) {
2558 fprintf(stderr, format: "Error: cannot use store:obj notation for raw private keys.\n");
2559 return key;
2560 }
2561
2562 if (is_pem_file(fileName: name))
2563 key = QCA::PrivateKey::fromPEMFile(fileName: name);
2564 else
2565 key = QCA::PrivateKey::fromDER(a: read_der_file(fileName: name));
2566 if (key.isNull()) {
2567 fprintf(stderr, format: "Error: unable to read/process private key file.\n");
2568 return key;
2569 }
2570
2571 return key;
2572}
2573
2574static QCA::Certificate get_C(const QString &name)
2575{
2576 QCA::KeyStoreEntry entry = get_E(name, nopassiveerror: true);
2577 if (!entry.isNull()) {
2578 if (entry.type() != QCA::KeyStoreEntry::TypeCertificate) {
2579 fprintf(stderr, format: "Error: entry is not a certificate.\n");
2580 return QCA::Certificate();
2581 }
2582 return entry.certificate();
2583 }
2584
2585 if (!QCA::isSupported(features: "cert")) {
2586 fprintf(stderr, format: "Error: need 'cert' feature.\n");
2587 return QCA::Certificate();
2588 }
2589
2590 // try file
2591 QCA::Certificate cert;
2592 if (is_pem_file(fileName: name))
2593 cert = QCA::Certificate::fromPEMFile(fileName: name);
2594 else
2595 cert = QCA::Certificate::fromDER(a: read_der_file(fileName: name));
2596 if (cert.isNull()) {
2597 fprintf(stderr, format: "Error: unable to read/process certificate file.\n");
2598 return cert;
2599 }
2600
2601 return cert;
2602}
2603
2604static QCA::KeyBundle get_X(const QString &name)
2605{
2606 QCA::KeyStoreEntry entry = get_E(name, nopassiveerror: true);
2607 if (!entry.isNull()) {
2608 if (entry.type() != QCA::KeyStoreEntry::TypeKeyBundle) {
2609 fprintf(stderr, format: "Error: entry is not a keybundle.\n");
2610 return QCA::KeyBundle();
2611 }
2612 return entry.keyBundle();
2613 }
2614
2615 if (!QCA::isSupported(features: "pkcs12")) {
2616 fprintf(stderr, format: "Error: need 'pkcs12' feature.\n");
2617 return QCA::KeyBundle();
2618 }
2619
2620 // try file
2621 QCA::KeyBundle key = QCA::KeyBundle::fromFile(fileName: name);
2622 if (key.isNull()) {
2623 fprintf(stderr, format: "Error: unable to read/process keybundle file.\n");
2624 return key;
2625 }
2626
2627 return key;
2628}
2629
2630static QCA::PGPKey get_P(const QString &name)
2631{
2632 QCA::KeyStoreEntry entry = get_E(name, nopassiveerror: true);
2633 if (!entry.isNull()) {
2634 if (entry.type() != QCA::KeyStoreEntry::TypePGPPublicKey &&
2635 entry.type() != QCA::KeyStoreEntry::TypePGPSecretKey) {
2636 fprintf(stderr, format: "Error: entry is not a pgp public key.\n");
2637 return QCA::PGPKey();
2638 }
2639 return entry.pgpPublicKey();
2640 }
2641
2642 // try file
2643 QCA::PGPKey key = QCA::PGPKey::fromFile(fileName: name);
2644 if (key.isNull()) {
2645 fprintf(stderr, format: "Error: unable to read/process pgp key file.\n");
2646 return key;
2647 }
2648
2649 return key;
2650}
2651
2652static QPair<QCA::PGPKey, QCA::PGPKey> get_S(const QString &name, bool noerror = false)
2653{
2654 QPair<QCA::PGPKey, QCA::PGPKey> key;
2655 QCA::KeyStoreEntry entry = get_E(name, nopassiveerror: true);
2656 if (!entry.isNull()) {
2657 if (entry.type() != QCA::KeyStoreEntry::TypePGPSecretKey) {
2658 if (!noerror)
2659 fprintf(stderr, format: "Error: entry is not a pgp secret key.\n");
2660 return key;
2661 }
2662
2663 key.first = entry.pgpSecretKey();
2664 key.second = entry.pgpPublicKey();
2665 return key;
2666 }
2667 return key;
2668}
2669
2670static void usage()
2671{
2672 printf(format: "%s: simple qca utility\n", APPNAME);
2673 printf(format: "usage: %s (options) [command]\n", EXENAME);
2674 printf(format: " options: --pass=x, --newpass=x, --nonroots=x, --roots=x, --nosys,\n");
2675 printf(format: " --noprompt, --ordered, --debug, --log-file=x, --log-level=n,\n");
2676 printf(format: " --nobundle\n");
2677 printf(format: "\n");
2678 printf(format: " help|--help|-h This help text\n");
2679 printf(format: " version|--version|-v Print version information\n");
2680 printf(format: " plugins List available plugins\n");
2681 printf(format: " config [command]\n");
2682 printf(format: " save [provider] Save default provider config\n");
2683 printf(format: " edit [provider] Edit provider config\n");
2684 printf(format: " key [command]\n");
2685 printf(format: " make rsa|dsa [bits] Create a key pair\n");
2686 printf(format: " changepass [K] Add/change/remove passphrase of a key\n");
2687 printf(format: " cert [command]\n");
2688 printf(format: " makereq [K] Create certificate request (CSR)\n");
2689 printf(format: " makeself [K] Create self-signed certificate\n");
2690 printf(format: " makereqadv [K] Advanced version of 'makereq'\n");
2691 printf(format: " makeselfadv [K] Advanced version of 'makeself'\n");
2692 printf(format: " validate [C] Validate certificate\n");
2693 printf(format: " keybundle [command]\n");
2694 printf(format: " make [K] [C] Create a keybundle\n");
2695 printf(format: " extract [X] Extract certificate(s) and key\n");
2696 printf(format: " changepass [X] Change passphrase of a keybundle\n");
2697 printf(format: " keystore [command]\n");
2698 printf(format: " list-stores List all available keystores\n");
2699 printf(format: " list [storeName] List content of a keystore\n");
2700 printf(format: " monitor Monitor for keystore availability\n");
2701 printf(format: " export [E] Export a keystore entry's content\n");
2702 printf(format: " exportref [E] Export a keystore entry reference\n");
2703 printf(format: " addkb [storeName] [cert.p12] Add a keybundle into a keystore\n");
2704 printf(format: " addpgp [storeName] [key.asc] Add a PGP key into a keystore\n");
2705 printf(format: " remove [E] Remove an object from a keystore\n");
2706 printf(format: " show [command]\n");
2707 printf(format: " cert [C] Examine a certificate\n");
2708 printf(format: " req [req.pem] Examine a certificate request (CSR)\n");
2709 printf(format: " crl [crl.pem] Examine a certificate revocation list\n");
2710 printf(format: " kb [X] Examine a keybundle\n");
2711 printf(format: " pgp [P|S] Examine a PGP key\n");
2712 printf(format: " message [command]\n");
2713 printf(format: " sign pgp|pgpdetach|smime [X|S] Sign a message\n");
2714 printf(format: " encrypt pgp|smime [C|P] Encrypt a message\n");
2715 printf(format: " signencrypt [S] [P] PGP sign & encrypt a message\n");
2716 printf(format: " verify pgp|smime Verify a message\n");
2717 printf(format: " decrypt pgp|smime ((X) ...) Decrypt a message (S/MIME needs X)\n");
2718 printf(format: " exportcerts Export certs from S/MIME message\n");
2719 printf(format: "\n");
2720 printf(format: "Object types: K = private key, C = certificate, X = key bundle,\n");
2721 printf(format: " P = PGP public key, S = PGP secret key, E = generic entry\n");
2722 printf(format: "\n");
2723 printf(format: "An object must be either a filename or a keystore reference (\"store:obj\").\n");
2724 printf(format: "\n");
2725 printf(format: "Log level is from 0 (quiet) to 8 (debug)\n");
2726 printf(format: "\n");
2727}
2728
2729int main(int argc, char **argv)
2730{
2731 QCA::Initializer qcaInit;
2732 QCoreApplication app(argc, argv);
2733 QFile logFile;
2734 QTextStream logStream(stderr);
2735 StreamLogger streamLogger(logStream);
2736
2737 QStringList args;
2738 for (int n = 1; n < argc; ++n)
2739 args.append(t: QString::fromLocal8Bit(ba: argv[n]));
2740
2741 if (args.count() < 1) {
2742 usage();
2743 return 1;
2744 }
2745
2746 bool have_pass = false;
2747 bool have_newpass = false;
2748 QCA::SecureArray pass, newpass;
2749 bool allowprompt = true;
2750 bool ordered = false;
2751 bool debug = false;
2752 bool nosys = false;
2753 bool nobundle = false;
2754 QString rootsFile, nonRootsFile;
2755
2756 for (int n = 0; n < args.count(); ++n) {
2757 QString s = args[n];
2758 if (!s.startsWith(s: QLatin1String("--")))
2759 continue;
2760 QString var;
2761 QString val;
2762 int x = s.indexOf(c: QLatin1Char('='));
2763 if (x != -1) {
2764 var = s.mid(position: 2, n: x - 2);
2765 val = s.mid(position: x + 1);
2766 } else {
2767 var = s.mid(position: 2);
2768 }
2769
2770 bool known = true;
2771
2772 if (var == QLatin1String("pass")) {
2773 have_pass = true;
2774 pass = val.toUtf8();
2775 } else if (var == QLatin1String("newpass")) {
2776 have_newpass = true;
2777 newpass = val.toUtf8();
2778 } else if (var == QLatin1String("log-file")) {
2779 logFile.setFileName(val);
2780 logFile.open(flags: QIODevice::Append | QIODevice::Text | QIODevice::Unbuffered);
2781 logStream.setDevice(&logFile);
2782 } else if (var == QLatin1String("log-level")) {
2783 QCA::logger()->setLevel((QCA::Logger::Severity)val.toInt());
2784 } else if (var == QLatin1String("noprompt"))
2785 allowprompt = false;
2786 else if (var == QLatin1String("ordered"))
2787 ordered = true;
2788 else if (var == QLatin1String("debug"))
2789 debug = true;
2790 else if (var == QLatin1String("roots"))
2791 rootsFile = val;
2792 else if (var == QLatin1String("nonroots"))
2793 nonRootsFile = val;
2794 else if (var == QLatin1String("nosys"))
2795 nosys = true;
2796 else if (var == QLatin1String("nobundle"))
2797 nobundle = true;
2798 else
2799 known = false;
2800
2801 if (known) {
2802 args.removeAt(i: n);
2803 --n; // adjust position
2804 }
2805 }
2806
2807 // help
2808 if (args.isEmpty() || args[0] == QLatin1String("help") || args[0] == QLatin1String("--help") ||
2809 args[0] == QLatin1String("-h")) {
2810 usage();
2811 return 0;
2812 }
2813
2814 // version
2815 if (args[0] == QLatin1String("version") || args[0] == QLatin1String("--version") ||
2816 args[0] == QLatin1String("-v")) {
2817 int ver = qcaVersion();
2818 int maj = (ver >> 16) & 0xff;
2819 int min = (ver >> 8) & 0xff;
2820 int bug = ver & 0xff;
2821 printf(format: "%s version %s by Justin Karneges\n", APPNAME, VERSION);
2822 printf(format: "Using QCA version %d.%d.%d\n", maj, min, bug);
2823 return 0;
2824 }
2825
2826 // show plugins
2827 if (args[0] == QLatin1String("plugins")) {
2828 QStringList paths = QCA::pluginPaths();
2829 if (!paths.isEmpty()) {
2830 for (int n = 0; n < paths.count(); ++n) {
2831 printf(format: " %s\n", qPrintable(QDir::toNativeSeparators(paths[n])));
2832 }
2833 } else
2834 printf(format: " (none)\n");
2835
2836 QCA::ProviderList list = QCA::providers();
2837
2838 if (debug)
2839 output_plugin_diagnostic_text();
2840
2841 printf(format: "Available Providers:\n");
2842 if (!list.isEmpty()) {
2843 for (int n = 0; n < list.count(); ++n) {
2844 printf(format: " %s\n", qPrintable(list[n]->name()));
2845 QString credit = list[n]->credit();
2846 if (!credit.isEmpty()) {
2847 QStringList lines = wrapstring(str: credit, width: 74);
2848 foreach (const QString &s, lines)
2849 printf(format: " %s\n", qPrintable(s));
2850 }
2851 if (debug) {
2852 QStringList capabilities = list[n]->features();
2853 foreach (const QString &capability, capabilities) {
2854 printf(format: " *%s", qPrintable(capability));
2855 if (!QCA::isSupported(qPrintable(capability), provider: list[n]->name())) {
2856 printf(format: "(NOT supported) - bug");
2857 }
2858 printf(format: "\n");
2859 }
2860 }
2861 }
2862 } else
2863 printf(format: " (none)\n");
2864
2865 QCA::unloadAllPlugins();
2866
2867 if (debug)
2868 output_plugin_diagnostic_text();
2869
2870 return 0;
2871 }
2872
2873 // config stuff
2874 if (args[0] == QLatin1String("config")) {
2875 if (args.count() < 2) {
2876 usage();
2877 return 1;
2878 }
2879
2880 if (args[1] == QLatin1String("save")) {
2881 if (args.count() < 3) {
2882 usage();
2883 return 1;
2884 }
2885
2886 QString name = args[2];
2887 QCA::Provider *p = QCA::findProvider(name);
2888 if (!p) {
2889 fprintf(stderr, format: "Error: no such provider '%s'.\n", qPrintable(name));
2890 return 1;
2891 }
2892
2893 QVariantMap map1 = p->defaultConfig();
2894 if (map1.isEmpty()) {
2895 fprintf(stderr, format: "Error: provider does not support configuration.\n");
2896 return 1;
2897 }
2898
2899 // set and save
2900 QCA::setProviderConfig(name, config: map1);
2901 QCA::saveProviderConfig(name);
2902 printf(format: "Done.\n");
2903 return 0;
2904 } else if (args[1] == QLatin1String("edit")) {
2905 if (args.count() < 3) {
2906 usage();
2907 return 1;
2908 }
2909
2910 QString name = args[2];
2911 if (!QCA::findProvider(name)) {
2912 fprintf(stderr, format: "Error: no such provider '%s'.\n", qPrintable(name));
2913 return 1;
2914 }
2915
2916 QVariantMap map1 = QCA::getProviderConfig(name);
2917 if (map1.isEmpty()) {
2918 fprintf(stderr, format: "Error: provider does not support configuration.\n");
2919 return 1;
2920 }
2921
2922 printf(format: "Editing configuration for %s ...\n", qPrintable(name));
2923 printf(format: "Note: to clear a string entry, type whitespace and press enter.\n");
2924
2925 map1 = provider_config_edit(in: map1);
2926 if (map1.isEmpty())
2927 return 1;
2928
2929 // set and save
2930 QCA::setProviderConfig(name, config: map1);
2931 QCA::saveProviderConfig(name);
2932 printf(format: "Done.\n");
2933 return 0;
2934 } else {
2935 usage();
2936 return 1;
2937 }
2938 }
2939
2940 // enable console passphrase prompt
2941 PassphrasePromptThread passphrasePrompt;
2942 if (!allowprompt)
2943 passphrasePrompt.pp->allowPrompt = false;
2944 if (have_pass)
2945 passphrasePrompt.pp->setExplicitPassword(pass);
2946
2947 if (args[0] == QLatin1String("key")) {
2948 if (args.count() < 2) {
2949 usage();
2950 return 1;
2951 }
2952
2953 if (args[1] == QLatin1String("make")) {
2954 if (args.count() < 4) {
2955 usage();
2956 return 1;
2957 }
2958
2959 bool genrsa;
2960 int bits;
2961
2962 if (args[2] == QLatin1String("rsa")) {
2963 if (!QCA::isSupported(features: "rsa")) {
2964 fprintf(stderr, format: "Error: need 'rsa' feature.\n");
2965 return 1;
2966 }
2967
2968 genrsa = true;
2969 bits = args[3].toInt();
2970 if (bits < 512) {
2971 fprintf(stderr, format: "Error: RSA bits must be at least 512.\n");
2972 return 1;
2973 }
2974 } else if (args[2] == QLatin1String("dsa")) {
2975 if (!QCA::isSupported(features: "dsa")) {
2976 fprintf(stderr, format: "Error: need 'dsa' feature.\n");
2977 return 1;
2978 }
2979
2980 if (!QCA::isSupported(features: "dlgroup")) {
2981 fprintf(stderr, format: "Error: need 'dlgroup' feature.\n");
2982 return 1;
2983 }
2984
2985 genrsa = false;
2986 bits = args[3].toInt();
2987 if (bits != 512 && bits != 768 && bits != 1024) {
2988 fprintf(stderr, format: "Error: DSA bits must be 512, 768, or 1024.\n");
2989 return 1;
2990 }
2991 } else {
2992 usage();
2993 return 1;
2994 }
2995
2996 if (!allowprompt && !have_newpass) {
2997 fprintf(stderr, format: "Error: no passphrase specified (use '--newpass=' for none).\n");
2998 return 1;
2999 }
3000
3001 QCA::PrivateKey priv;
3002 QString pubFileName, privFileName;
3003
3004 if (genrsa) {
3005 // note: third arg is bogus, doesn't apply to RSA
3006 priv = AnimatedKeyGen::makeKey(type: QCA::PKey::RSA, bits, set: QCA::DSA_512);
3007 pubFileName = QStringLiteral("rsapub.pem");
3008 privFileName = QStringLiteral("rsapriv.pem");
3009 } else // dsa
3010 {
3011 QCA::DLGroupSet set;
3012 if (bits == 512)
3013 set = QCA::DSA_512;
3014 else if (bits == 768)
3015 set = QCA::DSA_768;
3016 else // 1024
3017 set = QCA::DSA_1024;
3018
3019 // note: second arg is bogus, doesn't apply to DSA
3020 priv = AnimatedKeyGen::makeKey(type: QCA::PKey::DSA, bits: 0, set);
3021 pubFileName = QStringLiteral("dsapub.pem");
3022 privFileName = QStringLiteral("dsapriv.pem");
3023 }
3024
3025 if (priv.isNull()) {
3026 fprintf(stderr, format: "Error: unable to generate key.\n");
3027 return 1;
3028 }
3029
3030 QCA::PublicKey pub = priv.toPublicKey();
3031
3032 // prompt for new passphrase if necessary
3033 if (!have_newpass) {
3034 while (!promptForNewPassphrase(result: &newpass)) { }
3035 have_newpass = true;
3036 }
3037
3038 if (pub.toPEMFile(fileName: pubFileName))
3039 printf(format: "Public key saved to %s\n", qPrintable(pubFileName));
3040 else {
3041 fprintf(stderr, format: "Error: can't encode/write %s\n", qPrintable(pubFileName));
3042 return 1;
3043 }
3044
3045 bool ok;
3046 if (!newpass.isEmpty())
3047 ok = priv.toPEMFile(fileName: privFileName, passphrase: newpass);
3048 else
3049 ok = priv.toPEMFile(fileName: privFileName);
3050 if (ok)
3051 printf(format: "Private key saved to %s\n", qPrintable(privFileName));
3052 else {
3053 fprintf(stderr, format: "Error: can't encode/write %s\n", qPrintable(privFileName));
3054 return 1;
3055 }
3056 } else if (args[1] == QLatin1String("changepass")) {
3057 if (args.count() < 3) {
3058 usage();
3059 return 1;
3060 }
3061
3062 QCA::PrivateKey priv = get_K(name: args[2]);
3063 if (priv.isNull())
3064 return 1;
3065
3066 if (!allowprompt && !have_newpass) {
3067 fprintf(stderr, format: "Error: no passphrase specified (use '--newpass=' for none).\n");
3068 return 1;
3069 }
3070
3071 // prompt for new passphrase if necessary
3072 if (!have_newpass) {
3073 while (!promptForNewPassphrase(result: &newpass)) { }
3074 have_newpass = true;
3075 }
3076
3077 QString out;
3078 if (!newpass.isEmpty())
3079 out = priv.toPEM(passphrase: newpass);
3080 else
3081 out = priv.toPEM();
3082 if (!out.isEmpty())
3083 printf(format: "%s", qPrintable(out));
3084 else {
3085 fprintf(stderr, format: "Error: can't encode key.\n");
3086 return 1;
3087 }
3088 } else {
3089 usage();
3090 return 1;
3091 }
3092 } else if (args[0] == QLatin1String("cert")) {
3093 if (args.count() < 2) {
3094 usage();
3095 return 1;
3096 }
3097
3098 if (args[1] == QLatin1String("makereq") || args[1] == QLatin1String("makereqadv")) {
3099 if (args.count() < 3) {
3100 usage();
3101 return 1;
3102 }
3103
3104 if (!QCA::isSupported(features: "csr")) {
3105 fprintf(stderr, format: "Error: need 'csr' feature.\n");
3106 return 1;
3107 }
3108
3109 QCA::PrivateKey priv = get_K(name: args[2]);
3110 if (priv.isNull())
3111 return 1;
3112
3113 printf(format: "\n");
3114
3115 bool advanced = (args[1] == QLatin1String("makereqadv")) ? true : false;
3116
3117 QCA::CertificateOptions opts = promptForCertAttributes(advanced, req: true);
3118 QCA::CertificateRequest req(opts, priv);
3119
3120 QString reqname = QStringLiteral("certreq.pem");
3121 if (req.toPEMFile(fileName: reqname))
3122 printf(format: "Certificate request saved to %s\n", qPrintable(reqname));
3123 else {
3124 fprintf(stderr, format: "Error: can't encode/write %s\n", qPrintable(reqname));
3125 return 1;
3126 }
3127 } else if (args[1] == QLatin1String("makeself") || args[1] == QLatin1String("makeselfadv")) {
3128 if (args.count() < 3) {
3129 usage();
3130 return 1;
3131 }
3132
3133 if (!QCA::isSupported(features: "cert")) {
3134 fprintf(stderr, format: "Error: need 'cert' feature.\n");
3135 return 1;
3136 }
3137
3138 QCA::PrivateKey priv = get_K(name: args[2]);
3139 if (priv.isNull())
3140 return 1;
3141
3142 printf(format: "\n");
3143
3144 bool advanced = (args[1] == QLatin1String("makeselfadv")) ? true : false;
3145
3146 QCA::CertificateOptions opts = promptForCertAttributes(advanced, req: false);
3147 QCA::Certificate cert(opts, priv);
3148
3149 QString certname = QStringLiteral("cert.pem");
3150 if (cert.toPEMFile(fileName: certname))
3151 printf(format: "Certificate saved to %s\n", qPrintable(certname));
3152 else {
3153 fprintf(stderr, format: "Error: can't encode/write %s\n", qPrintable(certname));
3154 return 1;
3155 }
3156 } else if (args[1] == QLatin1String("validate")) {
3157 if (args.count() < 3) {
3158 usage();
3159 return 1;
3160 }
3161
3162 QCA::Certificate target = get_C(name: args[2]);
3163 if (target.isNull())
3164 return 1;
3165
3166 // get roots
3167 QCA::CertificateCollection roots;
3168 if (!nosys)
3169 roots += QCA::systemStore();
3170 if (!rootsFile.isEmpty())
3171 roots += QCA::CertificateCollection::fromFlatTextFile(fileName: rootsFile);
3172
3173 // get nonroots
3174 QCA::CertificateCollection nonroots;
3175 if (!nonRootsFile.isEmpty())
3176 nonroots = QCA::CertificateCollection::fromFlatTextFile(fileName: nonRootsFile);
3177
3178 QCA::Validity v = target.validate(trusted: roots, untrusted: nonroots);
3179 if (v == QCA::ValidityGood)
3180 printf(format: "Certificate is valid\n");
3181 else {
3182 printf(format: "Certificate is NOT valid: %s\n", qPrintable(validityToString(v)));
3183 return 1;
3184 }
3185 } else {
3186 usage();
3187 return 1;
3188 }
3189 } else if (args[0] == QLatin1String("keybundle")) {
3190 if (args.count() < 2) {
3191 usage();
3192 return 1;
3193 }
3194
3195 if (args[1] == QLatin1String("make")) {
3196 if (args.count() < 4) {
3197 usage();
3198 return 1;
3199 }
3200
3201 if (!QCA::isSupported(features: "pkcs12")) {
3202 fprintf(stderr, format: "Error: need 'pkcs12' feature.\n");
3203 return 1;
3204 }
3205
3206 QCA::PrivateKey priv = get_K(name: args[2]);
3207 if (priv.isNull())
3208 return 1;
3209
3210 QCA::Certificate cert = get_C(name: args[3]);
3211 if (cert.isNull())
3212 return 1;
3213
3214 // get roots
3215 QCA::CertificateCollection roots;
3216 if (!nosys)
3217 roots += QCA::systemStore();
3218 if (!rootsFile.isEmpty())
3219 roots += QCA::CertificateCollection::fromFlatTextFile(fileName: rootsFile);
3220
3221 // get nonroots
3222 QCA::CertificateCollection nonroots;
3223 if (!nonRootsFile.isEmpty())
3224 nonroots = QCA::CertificateCollection::fromFlatTextFile(fileName: nonRootsFile);
3225
3226 QList<QCA::Certificate> issuer_pool = roots.certificates() + nonroots.certificates();
3227
3228 QCA::CertificateChain chain;
3229 chain += cert;
3230 chain = chain.complete(issuers: issuer_pool);
3231
3232 QCA::KeyBundle key;
3233 key.setName(chain.primary().commonName());
3234 key.setCertificateChainAndKey(c: chain, key: priv);
3235
3236 if (!allowprompt && !have_newpass) {
3237 fprintf(stderr, format: "Error: no passphrase specified (use '--newpass=' for none).\n");
3238 return 1;
3239 }
3240
3241 // prompt for new passphrase if necessary
3242 if (!have_newpass) {
3243 while (!promptForNewPassphrase(result: &newpass)) { }
3244 have_newpass = true;
3245 }
3246
3247 if (newpass.isEmpty()) {
3248 fprintf(stderr, format: "Error: keybundles cannot have empty passphrases.\n");
3249 return 1;
3250 }
3251
3252 QString newFileName = QStringLiteral("cert.p12");
3253
3254 if (key.toFile(fileName: newFileName, passphrase: newpass))
3255 printf(format: "Keybundle saved to %s\n", qPrintable(newFileName));
3256 else {
3257 fprintf(stderr, format: "Error: can't encode keybundle.\n");
3258 return 1;
3259 }
3260 } else if (args[1] == QLatin1String("extract")) {
3261 if (args.count() < 3) {
3262 usage();
3263 return 1;
3264 }
3265
3266 QCA::KeyBundle key = get_X(name: args[2]);
3267 if (key.isNull())
3268 return 1;
3269
3270 QCA::PrivateKey priv = key.privateKey();
3271 bool export_priv = priv.canExport();
3272
3273 if (export_priv) {
3274 fprintf(stderr, format: "You will need to create a passphrase for the extracted private key.\n");
3275
3276 if (!allowprompt && !have_newpass) {
3277 fprintf(stderr, format: "Error: no passphrase specified (use '--newpass=' for none).\n");
3278 return 1;
3279 }
3280
3281 // prompt for new passphrase if necessary
3282 if (!have_newpass) {
3283 while (!promptForNewPassphrase(result: &newpass)) { }
3284 have_newpass = true;
3285 }
3286 }
3287
3288 printf(format: "Certs: (first is primary)\n");
3289 QCA::CertificateChain chain = key.certificateChain();
3290 for (int n = 0; n < chain.count(); ++n)
3291 printf(format: "%s", qPrintable(chain[n].toPEM()));
3292 printf(format: "Private Key:\n");
3293 if (export_priv) {
3294 QString out;
3295 if (!newpass.isEmpty())
3296 out = priv.toPEM(passphrase: newpass);
3297 else
3298 out = priv.toPEM();
3299 printf(format: "%s", qPrintable(out));
3300 } else {
3301 printf(format: "(Key is not exportable)\n");
3302 }
3303 } else if (args[1] == QLatin1String("changepass")) {
3304 if (args.count() < 3) {
3305 usage();
3306 return 1;
3307 }
3308
3309 QCA::KeyBundle key = get_X(name: args[2]);
3310 if (key.isNull())
3311 return 1;
3312
3313 if (!key.privateKey().canExport()) {
3314 fprintf(stderr, format: "Error: private key not exportable.\n");
3315 return 1;
3316 }
3317
3318 if (!allowprompt && !have_newpass) {
3319 fprintf(stderr, format: "Error: no passphrase specified (use '--newpass=' for none).\n");
3320 return 1;
3321 }
3322
3323 // prompt for new passphrase if necessary
3324 if (!have_newpass) {
3325 while (!promptForNewPassphrase(result: &newpass)) { }
3326 have_newpass = true;
3327 }
3328
3329 if (newpass.isEmpty()) {
3330 fprintf(stderr, format: "Error: keybundles cannot have empty passphrases.\n");
3331 return 1;
3332 }
3333
3334 QFileInfo fi(args[2]);
3335 QString newFileName = fi.baseName() + QStringLiteral("_new.p12");
3336
3337 if (key.toFile(fileName: newFileName, passphrase: newpass))
3338 printf(format: "Keybundle saved to %s\n", qPrintable(newFileName));
3339 else {
3340 fprintf(stderr, format: "Error: can't encode keybundle.\n");
3341 return 1;
3342 }
3343 } else {
3344 usage();
3345 return 1;
3346 }
3347 } else if (args[0] == QLatin1String("keystore")) {
3348 if (args.count() < 2) {
3349 usage();
3350 return 1;
3351 }
3352
3353 if (args[1] == QLatin1String("list-stores")) {
3354 ksm_start_and_wait();
3355
3356 QCA::KeyStoreManager ksm;
3357 QStringList storeList = ksm.keyStores();
3358
3359 for (int n = 0; n < storeList.count(); ++n) {
3360 QCA::KeyStore ks(storeList[n], &ksm);
3361 QString type = kstype_to_string(type: ks.type());
3362 printf(format: "%s %s [%s]\n", qPrintable(type), qPrintable(idHash(ks.id())), qPrintable(ks.name()));
3363 }
3364
3365 if (debug)
3366 output_keystore_diagnostic_text();
3367 } else if (args[1] == QLatin1String("list")) {
3368 if (args.count() < 3) {
3369 usage();
3370 return 1;
3371 }
3372
3373 ksm_start_and_wait();
3374
3375 QCA::KeyStoreManager ksm;
3376 QCA::KeyStore store(getKeyStore(name: args[2]), &ksm);
3377 if (!store.isValid()) {
3378 if (debug)
3379 output_keystore_diagnostic_text();
3380
3381 fprintf(stderr, format: "Error: no such store\n");
3382 return 1;
3383 }
3384
3385 QList<QCA::KeyStoreEntry> list = store.entryList();
3386 for (int n = 0; n < list.count(); ++n) {
3387 QCA::KeyStoreEntry i = list[n];
3388 QString type = ksentrytype_to_string(type: i.type());
3389 printf(format: "%s %s [%s]\n", qPrintable(type), qPrintable(idHash(i.id())), qPrintable(i.name()));
3390 }
3391
3392 if (debug)
3393 output_keystore_diagnostic_text();
3394 } else if (args[1] == QLatin1String("monitor")) {
3395 KeyStoreMonitor::monitor();
3396
3397 if (debug)
3398 output_keystore_diagnostic_text();
3399 } else if (args[1] == QLatin1String("export")) {
3400 if (args.count() < 3) {
3401 usage();
3402 return 1;
3403 }
3404
3405 QCA::KeyStoreEntry entry = get_E(name: args[2]);
3406 if (entry.isNull())
3407 return 1;
3408
3409 if (entry.type() == QCA::KeyStoreEntry::TypeCertificate)
3410 printf(format: "%s", qPrintable(entry.certificate().toPEM()));
3411 else if (entry.type() == QCA::KeyStoreEntry::TypeCRL)
3412 printf(format: "%s", qPrintable(entry.crl().toPEM()));
3413 else if (entry.type() == QCA::KeyStoreEntry::TypePGPPublicKey ||
3414 entry.type() == QCA::KeyStoreEntry::TypePGPSecretKey)
3415 printf(format: "%s", qPrintable(entry.pgpPublicKey().toString()));
3416 else if (entry.type() == QCA::KeyStoreEntry::TypeKeyBundle) {
3417 fprintf(stderr, format: "Error: use 'keybundle extract' command instead.\n");
3418 return 1;
3419 } else {
3420 fprintf(stderr, format: "Error: cannot export type '%d'.\n", entry.type());
3421 return 1;
3422 }
3423 } else if (args[1] == QLatin1String("exportref")) {
3424 if (args.count() < 3) {
3425 usage();
3426 return 1;
3427 }
3428
3429 QCA::KeyStoreEntry entry = get_E(name: args[2]);
3430 if (entry.isNull())
3431 return 1;
3432 printf(format: "%s", make_ksentry_string(id: entry.toString()).toUtf8().data());
3433 } else if (args[1] == QLatin1String("addkb")) {
3434 if (args.count() < 4) {
3435 usage();
3436 return 1;
3437 }
3438
3439 ksm_start_and_wait();
3440
3441 QCA::KeyStoreManager ksm;
3442 QCA::KeyStore store(getKeyStore(name: args[2]), &ksm);
3443 if (!store.isValid()) {
3444 fprintf(stderr, format: "Error: no such store\n");
3445 return 1;
3446 }
3447
3448 QCA::KeyBundle key = get_X(name: args[3]);
3449 if (key.isNull())
3450 return 1;
3451
3452 if (!store.writeEntry(kb: key).isEmpty())
3453 printf(format: "Entry written.\n");
3454 else {
3455 fprintf(stderr, format: "Error: unable to write entry.\n");
3456 return 1;
3457 }
3458 } else if (args[1] == QLatin1String("addpgp")) {
3459 if (args.count() < 4) {
3460 usage();
3461 return 1;
3462 }
3463
3464 if (!QCA::isSupported(features: "openpgp")) {
3465 fprintf(stderr, format: "Error: need 'openpgp' feature.\n");
3466 return 1;
3467 }
3468
3469 ksm_start_and_wait();
3470
3471 QCA::KeyStoreManager ksm;
3472 QCA::KeyStore store(getKeyStore(name: args[2]), &ksm);
3473 if (!store.isValid()) {
3474 fprintf(stderr, format: "Error: no such store\n");
3475 return 1;
3476 }
3477
3478 QCA::PGPKey pub = QCA::PGPKey::fromFile(fileName: args[3]);
3479 if (pub.isNull())
3480 return 1;
3481
3482 if (!store.writeEntry(key: pub).isEmpty())
3483 printf(format: "Entry written.\n");
3484 else {
3485 fprintf(stderr, format: "Error: unable to write entry.\n");
3486 return 1;
3487 }
3488 } else if (args[1] == QLatin1String("remove")) {
3489 if (args.count() < 3) {
3490 usage();
3491 return 1;
3492 }
3493
3494 QCA::KeyStoreEntry entry = get_E(name: args[2]);
3495 if (entry.isNull())
3496 return 1;
3497
3498 QCA::KeyStoreManager ksm;
3499 QCA::KeyStore store(entry.storeId(), &ksm);
3500 if (!store.isValid()) {
3501 fprintf(stderr, format: "Error: no such store\n");
3502 return 1;
3503 }
3504
3505 if (store.removeEntry(id: entry.id()))
3506 printf(format: "Entry removed.\n");
3507 else {
3508 fprintf(stderr, format: "Error: unable to remove entry.\n");
3509 return 1;
3510 }
3511 } else {
3512 usage();
3513 return 1;
3514 }
3515 } else if (args[0] == QLatin1String("show")) {
3516 if (args.count() < 2) {
3517 usage();
3518 return 1;
3519 }
3520
3521 if (args[1] == QLatin1String("cert")) {
3522 if (args.count() < 3) {
3523 usage();
3524 return 1;
3525 }
3526
3527 QCA::Certificate cert = get_C(name: args[2]);
3528 if (cert.isNull())
3529 return 1;
3530
3531 print_cert(cert, ordered);
3532 } else if (args[1] == QLatin1String("req")) {
3533 if (args.count() < 3) {
3534 usage();
3535 return 1;
3536 }
3537
3538 if (!QCA::isSupported(features: "csr")) {
3539 fprintf(stderr, format: "Error: need 'csr' feature.\n");
3540 return 1;
3541 }
3542
3543 QCA::CertificateRequest req(args[2]);
3544 if (req.isNull()) {
3545 fprintf(stderr, format: "Error: can't read/process certificate request file.\n");
3546 return 1;
3547 }
3548
3549 print_certreq(cert: req, ordered);
3550 } else if (args[1] == QLatin1String("crl")) {
3551 if (args.count() < 3) {
3552 usage();
3553 return 1;
3554 }
3555
3556 if (!QCA::isSupported(features: "crl")) {
3557 fprintf(stderr, format: "Error: need 'crl' feature.\n");
3558 return 1;
3559 }
3560
3561 QCA::CRL crl;
3562 if (is_pem_file(fileName: args[2]))
3563 crl = QCA::CRL::fromPEMFile(fileName: args[2]);
3564 else
3565 crl = QCA::CRL::fromDER(a: read_der_file(fileName: args[2]));
3566 if (crl.isNull()) {
3567 fprintf(stderr, format: "Error: unable to read/process CRL file.\n");
3568 return 1;
3569 }
3570
3571 print_crl(crl, ordered);
3572 } else if (args[1] == QLatin1String("kb")) {
3573 if (args.count() < 3) {
3574 usage();
3575 return 1;
3576 }
3577
3578 QCA::KeyBundle key = get_X(name: args[2]);
3579 if (key.isNull())
3580 return 1;
3581
3582 printf(format: "Keybundle contains %d certificates. Displaying primary:\n", int(key.certificateChain().count()));
3583 print_cert(cert: key.certificateChain().primary(), ordered);
3584 } else if (args[1] == QLatin1String("pgp")) {
3585 if (args.count() < 3) {
3586 usage();
3587 return 1;
3588 }
3589
3590 // try for secret key, then try public key
3591 QCA::PGPKey key = get_S(name: args[2], noerror: true).first;
3592 if (key.isNull()) {
3593 key = get_P(name: args[2]);
3594 if (key.isNull())
3595 return 1;
3596 }
3597
3598 print_pgp(key);
3599 } else {
3600 usage();
3601 return 1;
3602 }
3603 } else if (args[0] == QLatin1String("message")) {
3604 if (args.count() < 2) {
3605 usage();
3606 return 1;
3607 }
3608
3609 if (args[1] == QLatin1String("sign")) {
3610 if (args.count() < 4) {
3611 usage();
3612 return 1;
3613 }
3614
3615 QCA::SecureMessageSystem *sms;
3616 QCA::SecureMessageKey skey;
3617 QCA::SecureMessage::SignMode mode;
3618 bool pgp = false;
3619
3620 if (args[2] == QLatin1String("pgp")) {
3621 if (!QCA::isSupported(features: "openpgp")) {
3622 fprintf(stderr, format: "Error: need 'openpgp' feature.\n");
3623 return 1;
3624 }
3625
3626 QPair<QCA::PGPKey, QCA::PGPKey> key = get_S(name: args[3]);
3627 if (key.first.isNull())
3628 return 1;
3629
3630 sms = new QCA::OpenPGP;
3631 skey.setPGPSecretKey(key.first);
3632 mode = QCA::SecureMessage::Clearsign;
3633 pgp = true;
3634 } else if (args[2] == QLatin1String("pgpdetach")) {
3635 if (!QCA::isSupported(features: "openpgp")) {
3636 fprintf(stderr, format: "Error: need 'openpgp' feature.\n");
3637 return 1;
3638 }
3639
3640 QPair<QCA::PGPKey, QCA::PGPKey> key = get_S(name: args[3]);
3641 if (key.first.isNull())
3642 return 1;
3643
3644 sms = new QCA::OpenPGP;
3645 skey.setPGPSecretKey(key.first);
3646 mode = QCA::SecureMessage::Detached;
3647 pgp = true;
3648 } else if (args[2] == QLatin1String("smime")) {
3649 if (!QCA::isSupported(features: "cms")) {
3650 fprintf(stderr, format: "Error: need 'cms' feature.\n");
3651 return 1;
3652 }
3653
3654 QCA::KeyBundle key = get_X(name: args[3]);
3655 if (key.isNull())
3656 return 1;
3657
3658 // get nonroots
3659 QCA::CertificateCollection nonroots;
3660 if (!nonRootsFile.isEmpty())
3661 nonroots = QCA::CertificateCollection::fromFlatTextFile(fileName: nonRootsFile);
3662
3663 QList<QCA::Certificate> issuer_pool = nonroots.certificates();
3664
3665 QCA::CertificateChain chain = key.certificateChain();
3666 chain = chain.complete(issuers: issuer_pool);
3667
3668 sms = new QCA::CMS;
3669 skey.setX509CertificateChain(chain);
3670 skey.setX509PrivateKey(key.privateKey());
3671 mode = QCA::SecureMessage::Detached;
3672 } else {
3673 usage();
3674 return 1;
3675 }
3676
3677 // read input data from stdin all at once
3678 QByteArray plain;
3679 while (!feof(stdin)) {
3680 QByteArray block(1024, 0);
3681 int n = fread(ptr: block.data(), size: 1, n: 1024, stdin);
3682 if (n < 0)
3683 break;
3684 block.resize(size: n);
3685 plain += block;
3686 }
3687
3688 // smime envelope
3689 if (!pgp) {
3690 QString text = add_cr(in: QString::fromUtf8(ba: plain));
3691 plain = QString::fromLatin1(ba: mime_signpart).arg(a: text).toUtf8();
3692 }
3693
3694 QCA::SecureMessage *msg = new QCA::SecureMessage(sms);
3695 msg->setSigner(skey);
3696 // pgp should always be ascii
3697 if (pgp)
3698 msg->setFormat(QCA::SecureMessage::Ascii);
3699 msg->setBundleSignerEnabled(!nobundle);
3700 msg->startSign(m: mode);
3701 msg->update(in: plain);
3702 msg->end();
3703 msg->waitForFinished(msecs: -1);
3704
3705 if (debug) {
3706 output_keystore_diagnostic_text();
3707 output_message_diagnostic_text(msg);
3708 }
3709
3710 if (!msg->success()) {
3711 QString errstr = smErrorToString(e: msg->errorCode());
3712 delete msg;
3713 delete sms;
3714
3715 fprintf(stderr, format: "Error: unable to sign: %s\n", qPrintable(errstr));
3716 return 1;
3717 }
3718
3719 QString hashName = msg->hashName();
3720
3721 QByteArray output;
3722 if (mode == QCA::SecureMessage::Detached)
3723 output = msg->signature();
3724 else
3725 output = msg->read();
3726
3727 delete msg;
3728 delete sms;
3729
3730 // smime envelope
3731 if (!pgp) {
3732 QCA::Base64 enc;
3733 enc.setLineBreaksEnabled(true);
3734 enc.setLineBreaksColumn(76);
3735 QString sigtext = add_cr(in: enc.arrayToString(a: output));
3736 QString str = QString::fromLatin1(ba: mime_signed).arg(args&: hashName, args: QString::fromUtf8(ba: plain), args&: sigtext);
3737 output = str.toUtf8();
3738 }
3739
3740 printf(format: "%s", output.data());
3741 } else if (args[1] == QLatin1String("encrypt")) {
3742 if (args.count() < 4) {
3743 usage();
3744 return 1;
3745 }
3746
3747 QCA::SecureMessageSystem *sms;
3748 QCA::SecureMessageKey skey;
3749 bool pgp = false;
3750
3751 if (args[2] == QLatin1String("pgp")) {
3752 if (!QCA::isSupported(features: "openpgp")) {
3753 fprintf(stderr, format: "Error: need 'openpgp' feature.\n");
3754 return 1;
3755 }
3756
3757 QCA::PGPKey key = get_P(name: args[3]);
3758 if (key.isNull())
3759 return 1;
3760
3761 sms = new QCA::OpenPGP;
3762 skey.setPGPPublicKey(key);
3763 pgp = true;
3764 } else if (args[2] == QLatin1String("smime")) {
3765 if (!QCA::isSupported(features: "cms")) {
3766 fprintf(stderr, format: "Error: need 'cms' feature.\n");
3767 return 1;
3768 }
3769
3770 QCA::Certificate cert = get_C(name: args[3]);
3771 if (cert.isNull())
3772 return 1;
3773
3774 sms = new QCA::CMS;
3775 skey.setX509CertificateChain(cert);
3776 } else {
3777 usage();
3778 return 1;
3779 }
3780
3781 // read input data from stdin all at once
3782 QByteArray plain;
3783 while (!feof(stdin)) {
3784 QByteArray block(1024, 0);
3785 int n = fread(ptr: block.data(), size: 1, n: 1024, stdin);
3786 if (n < 0)
3787 break;
3788 block.resize(size: n);
3789 plain += block;
3790 }
3791
3792 QCA::SecureMessage *msg = new QCA::SecureMessage(sms);
3793 msg->setRecipient(skey);
3794 // pgp should always be ascii
3795 if (pgp)
3796 msg->setFormat(QCA::SecureMessage::Ascii);
3797 msg->startEncrypt();
3798 msg->update(in: plain);
3799 msg->end();
3800 msg->waitForFinished(msecs: -1);
3801
3802 if (debug) {
3803 output_keystore_diagnostic_text();
3804 output_message_diagnostic_text(msg);
3805 }
3806
3807 if (!msg->success()) {
3808 QString errstr = smErrorToString(e: msg->errorCode());
3809 delete msg;
3810 delete sms;
3811 fprintf(stderr, format: "Error: unable to encrypt: %s\n", qPrintable(errstr));
3812 return 1;
3813 }
3814
3815 QByteArray output = msg->read();
3816 delete msg;
3817 delete sms;
3818
3819 // smime envelope
3820 if (!pgp) {
3821 QCA::Base64 enc;
3822 enc.setLineBreaksEnabled(true);
3823 enc.setLineBreaksColumn(76);
3824 QString enctext = add_cr(in: enc.arrayToString(a: output));
3825 QString str = QString::fromLatin1(ba: mime_enveloped).arg(a: enctext);
3826 output = str.toUtf8();
3827 }
3828
3829 printf(format: "%s", output.data());
3830 } else if (args[1] == QLatin1String("signencrypt")) {
3831 if (args.count() < 4) {
3832 usage();
3833 return 1;
3834 }
3835
3836 if (!QCA::isSupported(features: "openpgp")) {
3837 fprintf(stderr, format: "Error: need 'openpgp' feature.\n");
3838 return 1;
3839 }
3840
3841 QCA::SecureMessageSystem *sms;
3842 QCA::SecureMessageKey skey;
3843 QCA::SecureMessageKey rkey;
3844
3845 {
3846 QPair<QCA::PGPKey, QCA::PGPKey> sec = get_S(name: args[2]);
3847 if (sec.first.isNull())
3848 return 1;
3849
3850 QCA::PGPKey pub = get_P(name: args[3]);
3851 if (pub.isNull())
3852 return 1;
3853
3854 sms = new QCA::OpenPGP;
3855 skey.setPGPSecretKey(sec.first);
3856 rkey.setPGPPublicKey(pub);
3857 }
3858
3859 // read input data from stdin all at once
3860 QByteArray plain;
3861 while (!feof(stdin)) {
3862 QByteArray block(1024, 0);
3863 int n = fread(ptr: block.data(), size: 1, n: 1024, stdin);
3864 if (n < 0)
3865 break;
3866 block.resize(size: n);
3867 plain += block;
3868 }
3869
3870 QCA::SecureMessage *msg = new QCA::SecureMessage(sms);
3871 if (!msg->canSignAndEncrypt()) {
3872 delete msg;
3873 delete sms;
3874 fprintf(stderr, format: "Error: cannot perform integrated sign and encrypt.\n");
3875 return 1;
3876 }
3877
3878 msg->setSigner(skey);
3879 msg->setRecipient(rkey);
3880 msg->setFormat(QCA::SecureMessage::Ascii);
3881 msg->startSignAndEncrypt();
3882 msg->update(in: plain);
3883 msg->end();
3884 msg->waitForFinished(msecs: -1);
3885
3886 if (debug) {
3887 output_keystore_diagnostic_text();
3888 output_message_diagnostic_text(msg);
3889 }
3890
3891 if (!msg->success()) {
3892 QString errstr = smErrorToString(e: msg->errorCode());
3893 delete msg;
3894 delete sms;
3895 fprintf(stderr, format: "Error: unable to sign and encrypt: %s\n", qPrintable(errstr));
3896 return 1;
3897 }
3898
3899 QByteArray output = msg->read();
3900 delete msg;
3901 delete sms;
3902
3903 printf(format: "%s", output.data());
3904 } else if (args[1] == QLatin1String("verify")) {
3905 if (args.count() < 3) {
3906 usage();
3907 return 1;
3908 }
3909
3910 QCA::SecureMessageSystem *sms;
3911 bool pgp = false;
3912
3913 if (args[2] == QLatin1String("pgp")) {
3914 if (!QCA::isSupported(features: "openpgp")) {
3915 fprintf(stderr, format: "Error: need 'openpgp' feature.\n");
3916 return 1;
3917 }
3918
3919 sms = new QCA::OpenPGP;
3920 pgp = true;
3921 } else if (args[2] == QLatin1String("smime")) {
3922 if (!QCA::isSupported(features: "cms")) {
3923 fprintf(stderr, format: "Error: need 'cms' feature.\n");
3924 return 1;
3925 }
3926
3927 // get roots
3928 QCA::CertificateCollection roots;
3929 if (!nosys)
3930 roots += QCA::systemStore();
3931 if (!rootsFile.isEmpty())
3932 roots += QCA::CertificateCollection::fromFlatTextFile(fileName: rootsFile);
3933
3934 // get intermediates and possible signers, in case
3935 // the message does not have them.
3936 QCA::CertificateCollection nonroots;
3937 if (!nonRootsFile.isEmpty())
3938 nonroots += QCA::CertificateCollection::fromFlatTextFile(fileName: nonRootsFile);
3939
3940 sms = new QCA::CMS;
3941 ((QCA::CMS *)sms)->setTrustedCertificates(roots);
3942 ((QCA::CMS *)sms)->setUntrustedCertificates(nonroots);
3943 } else {
3944 usage();
3945 return 1;
3946 }
3947
3948 QByteArray data, sig;
3949 QString smime_text;
3950 {
3951 // read input data from stdin all at once
3952 QByteArray plain;
3953 while (!feof(stdin)) {
3954 QByteArray block(1024, 0);
3955 int n = fread(ptr: block.data(), size: 1, n: 1024, stdin);
3956 if (n < 0)
3957 break;
3958 block.resize(size: n);
3959 plain += block;
3960 }
3961
3962 if (pgp) {
3963 // pgp can be either a detached signature followed
3964 // by data, or an integrated message.
3965
3966 // detached signature?
3967 if (plain.startsWith(bv: "-----BEGIN PGP SIGNATURE-----")) {
3968 QByteArray footer = "-----END PGP SIGNATURE-----\n";
3969 int n = plain.indexOf(bv: footer);
3970 if (n == -1) {
3971 delete sms;
3972 fprintf(stderr, format: "Error: pgp signature header, but no footer.\n");
3973 return 1;
3974 }
3975
3976 n += footer.length();
3977 sig = plain.mid(index: 0, len: n);
3978 data = plain.mid(index: n);
3979 } else {
3980 data = plain;
3981 }
3982 } else {
3983 // smime envelope
3984 QString in = QString::fromUtf8(ba: plain);
3985 in = add_cr(in); // change the line endings?!
3986 QString str, sigtext;
3987 if (!open_mime_data_sig(in, data: &str, sig: &sigtext)) {
3988 fprintf(stderr, format: "Error: can't parse message file.\n");
3989 return 1;
3990 }
3991
3992 data = str.toUtf8();
3993 smime_text = str;
3994
3995 QCA::Base64 dec;
3996 dec.setLineBreaksEnabled(true);
3997 sig = dec.stringToArray(s: rem_cr(in: sigtext)).toByteArray();
3998 }
3999 }
4000
4001 QCA::SecureMessage *msg = new QCA::SecureMessage(sms);
4002 if (pgp)
4003 msg->setFormat(QCA::SecureMessage::Ascii);
4004 msg->startVerify(detachedSig: sig);
4005 msg->update(in: data);
4006 msg->end();
4007 msg->waitForFinished(msecs: -1);
4008
4009 if (debug) {
4010 output_keystore_diagnostic_text();
4011 output_message_diagnostic_text(msg);
4012 }
4013
4014 if (!msg->success()) {
4015 QString errstr = smErrorToString(e: msg->errorCode());
4016 delete msg;
4017 delete sms;
4018 fprintf(stderr, format: "Error: verify failed: %s\n", qPrintable(errstr));
4019 return 1;
4020 }
4021
4022 QByteArray output;
4023 if (pgp && sig.isEmpty())
4024 output = msg->read();
4025
4026 QList<QCA::SecureMessageSignature> signers = msg->signers();
4027 delete msg;
4028 delete sms;
4029
4030 // for pgp clearsign, pgp signed (non-detached), and smime,
4031 // the signed content was inside of the message. we need
4032 // to print that content now
4033 if (pgp) {
4034 printf(format: "%s", output.data());
4035 } else {
4036 QString str = open_mime_envelope(in: smime_text);
4037 printf(format: "%s", str.toUtf8().data());
4038 }
4039
4040 smDisplaySignatures(signers);
4041
4042 bool allgood = true;
4043 foreach (const QCA::SecureMessageSignature &signer, signers) {
4044 if (signer.identityResult() != QCA::SecureMessageSignature::Valid) {
4045 allgood = false;
4046 break;
4047 }
4048 }
4049
4050 if (!allgood)
4051 return 1;
4052 } else if (args[1] == QLatin1String("decrypt")) {
4053 if (args.count() < 3) {
4054 usage();
4055 return 1;
4056 }
4057
4058 QCA::SecureMessageSystem *sms;
4059 bool pgp = false;
4060
4061 if (args[2] == QLatin1String("pgp")) {
4062 if (!QCA::isSupported(features: "openpgp")) {
4063 fprintf(stderr, format: "Error: need 'openpgp' feature.\n");
4064 return 1;
4065 }
4066
4067 ksm_start_and_wait();
4068
4069 sms = new QCA::OpenPGP;
4070 pgp = true;
4071 } else if (args[2] == QLatin1String("smime")) {
4072 if (args.count() < 4) {
4073 usage();
4074 return 1;
4075 }
4076
4077 if (!QCA::isSupported(features: "cms")) {
4078 fprintf(stderr, format: "Error: need 'cms' feature.\n");
4079 return 1;
4080 }
4081
4082 // user can provide many possible decrypt keys
4083 QList<QCA::KeyBundle> keys;
4084 for (int n = 3; n < args.count(); ++n) {
4085 QCA::KeyBundle key = get_X(name: args[n]);
4086 if (key.isNull())
4087 return 1;
4088 keys += key;
4089 }
4090
4091 sms = new QCA::CMS;
4092
4093 QList<QCA::SecureMessageKey> skeys;
4094 foreach (const QCA::KeyBundle &key, keys) {
4095 QCA::SecureMessageKey skey;
4096 skey.setX509CertificateChain(key.certificateChain());
4097 skey.setX509PrivateKey(key.privateKey());
4098 skeys += skey;
4099 }
4100
4101 ((QCA::CMS *)sms)->setPrivateKeys(skeys);
4102 } else {
4103 usage();
4104 return 1;
4105 }
4106
4107 // read input data from stdin all at once
4108 QByteArray plain;
4109 while (!feof(stdin)) {
4110 QByteArray block(1024, 0);
4111 int n = fread(ptr: block.data(), size: 1, n: 1024, stdin);
4112 if (n < 0)
4113 break;
4114 block.resize(size: n);
4115 plain += block;
4116 }
4117
4118 // smime envelope
4119 if (!pgp) {
4120 QString in = QString::fromUtf8(ba: plain);
4121 QString str = open_mime_envelope(in);
4122 if (str.isEmpty()) {
4123 delete sms;
4124 fprintf(stderr, format: "Error: can't parse message file.\n");
4125 return 1;
4126 }
4127
4128 QCA::Base64 dec;
4129 dec.setLineBreaksEnabled(true);
4130 plain = dec.stringToArray(s: rem_cr(in: str)).toByteArray();
4131 }
4132
4133 QCA::SecureMessage *msg = new QCA::SecureMessage(sms);
4134 if (pgp)
4135 msg->setFormat(QCA::SecureMessage::Ascii);
4136 msg->startDecrypt();
4137 msg->update(in: plain);
4138 msg->end();
4139 msg->waitForFinished(msecs: -1);
4140
4141 if (debug) {
4142 output_keystore_diagnostic_text();
4143 output_message_diagnostic_text(msg);
4144 }
4145
4146 if (!msg->success()) {
4147 QString errstr = smErrorToString(e: msg->errorCode());
4148 delete msg;
4149 delete sms;
4150 fprintf(stderr, format: "Error: decrypt failed: %s\n", qPrintable(errstr));
4151 return 1;
4152 }
4153
4154 QByteArray output = msg->read();
4155
4156 QList<QCA::SecureMessageSignature> signers;
4157 bool wasSigned = false;
4158 if (msg->wasSigned()) {
4159 signers = msg->signers();
4160 wasSigned = true;
4161 }
4162 delete msg;
4163 delete sms;
4164
4165 printf(format: "%s", output.data());
4166
4167 if (wasSigned) {
4168 fprintf(stderr, format: "Message was also signed:\n");
4169
4170 smDisplaySignatures(signers);
4171
4172 bool allgood = true;
4173 foreach (const QCA::SecureMessageSignature &signer, signers) {
4174 if (signer.identityResult() != QCA::SecureMessageSignature::Valid) {
4175 allgood = false;
4176 break;
4177 }
4178 }
4179
4180 if (!allgood)
4181 return 1;
4182 }
4183 } else if (args[1] == QLatin1String("exportcerts")) {
4184 if (!QCA::isSupported(features: "cms")) {
4185 fprintf(stderr, format: "Error: need 'cms' feature.\n");
4186 return 1;
4187 }
4188
4189 QCA::SecureMessageSystem *sms = new QCA::CMS;
4190
4191 QByteArray data, sig;
4192 QString smime_text;
4193 {
4194 // read input data from stdin all at once
4195 QByteArray plain;
4196 while (!feof(stdin)) {
4197 QByteArray block(1024, 0);
4198 int n = fread(ptr: block.data(), size: 1, n: 1024, stdin);
4199 if (n < 0)
4200 break;
4201 block.resize(size: n);
4202 plain += block;
4203 }
4204
4205 // smime envelope
4206 QString in = QString::fromUtf8(ba: plain);
4207 QString str, sigtext;
4208 if (!open_mime_data_sig(in, data: &str, sig: &sigtext)) {
4209 delete sms;
4210 fprintf(stderr, format: "Error: can't parse message file.\n");
4211 return 1;
4212 }
4213
4214 data = str.toUtf8();
4215 smime_text = str;
4216
4217 QCA::Base64 dec;
4218 dec.setLineBreaksEnabled(true);
4219 sig = dec.stringToArray(s: rem_cr(in: sigtext)).toByteArray();
4220 }
4221
4222 QCA::SecureMessage *msg = new QCA::SecureMessage(sms);
4223 msg->startVerify(detachedSig: sig);
4224 msg->update(in: data);
4225 msg->end();
4226 msg->waitForFinished(msecs: -1);
4227
4228 if (debug)
4229 output_message_diagnostic_text(msg);
4230
4231 if (!msg->success()) {
4232 QString errstr = smErrorToString(e: msg->errorCode());
4233 delete msg;
4234 delete sms;
4235 fprintf(stderr, format: "Error: export failed: %s\n", qPrintable(errstr));
4236 return 1;
4237 }
4238
4239 QList<QCA::SecureMessageSignature> signers = msg->signers();
4240 delete msg;
4241 delete sms;
4242
4243 // print out all certs of all signers
4244 foreach (const QCA::SecureMessageSignature &signer, signers) {
4245 QCA::SecureMessageKey key = signer.key();
4246 if (!key.isNull()) {
4247 foreach (const QCA::Certificate &c, key.x509CertificateChain())
4248 printf(format: "%s", qPrintable(c.toPEM()));
4249 }
4250 }
4251 } else {
4252 usage();
4253 return 1;
4254 }
4255 } else {
4256 usage();
4257 return 1;
4258 }
4259
4260 return 0;
4261}
4262
4263#include "main.moc"
4264

source code of qca/tools/qcatool/main.cpp