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 | |
34 | const char *const APPNAME = "qcatool" ; |
35 | const char *const EXENAME = "qcatool" ; |
36 | const char *const VERSION = QCA_VERSION_STR; |
37 | |
38 | static 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 | |
70 | class StreamLogger : public QCA::AbstractLogDevice |
71 | { |
72 | Q_OBJECT |
73 | public: |
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 | |
98 | private: |
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 | |
114 | private: |
115 | static const char *s_severityNames[]; |
116 | QTextStream &_stream; |
117 | }; |
118 | |
119 | const char *StreamLogger::s_severityNames[] = {"Q" , "M" , "A" , "C" , "E" , "W" , "N" , "I" , "D" , "U" }; |
120 | |
121 | static 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 | |
132 | static 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 | |
143 | static 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 | |
153 | class AnimatedKeyGen : public QObject |
154 | { |
155 | Q_OBJECT |
156 | public: |
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 | |
171 | private: |
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 | |
189 | private 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 | |
245 | class KeyStoreMonitor : public QObject |
246 | { |
247 | Q_OBJECT |
248 | public: |
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 | |
258 | private: |
259 | QEventLoop *eventLoop; |
260 | QCA::KeyStoreManager *ksm; |
261 | QList<QCA::KeyStore *> keyStores; |
262 | QCA::ConsolePrompt *prompt; |
263 | |
264 | private 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 | |
320 | class PassphrasePrompt : public QObject |
321 | { |
322 | Q_OBJECT |
323 | public: |
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 | |
386 | private 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 | |
617 | class PassphrasePromptThread : public QCA::SyncThread |
618 | { |
619 | Q_OBJECT |
620 | public: |
621 | PassphrasePrompt *pp; |
622 | |
623 | PassphrasePromptThread() |
624 | { |
625 | start(); |
626 | } |
627 | |
628 | ~PassphrasePromptThread() override |
629 | { |
630 | stop(); |
631 | } |
632 | |
633 | protected: |
634 | void atStart() override |
635 | { |
636 | pp = new PassphrasePrompt; |
637 | } |
638 | |
639 | void atEnd() override |
640 | { |
641 | delete pp; |
642 | } |
643 | }; |
644 | |
645 | static 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 | |
663 | static 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 | |
673 | static 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 | |
687 | static 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 | |
705 | static 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 | |
722 | static 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 | |
745 | static 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 | |
759 | static 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 | |
767 | class InfoType |
768 | { |
769 | public: |
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 | |
794 | static 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 | |
869 | class MyConstraintType |
870 | { |
871 | public: |
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 | |
893 | static 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 | |
957 | const 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 | |
985 | static 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 | |
994 | class ValidityLength |
995 | { |
996 | public: |
997 | int years, months, days; |
998 | }; |
999 | |
1000 | static 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 | |
1020 | static 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 | |
1034 | static 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 | |
1086 | static 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 | |
1097 | static 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 | |
1304 | static 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 | |
1312 | static bool string_is_int(const QString &in) |
1313 | { |
1314 | bool ok; |
1315 | in.toInt(ok: &ok); |
1316 | return ok; |
1317 | } |
1318 | |
1319 | static 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 | |
1326 | static 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 | |
1333 | static 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 | |
1350 | static 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 | |
1364 | static 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 | |
1371 | static 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 | |
1385 | static 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 | |
1399 | static 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 | |
1415 | static 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 | |
1444 | class Pkcs11ProviderConfig |
1445 | { |
1446 | public: |
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 | |
1494 | class Pkcs11Config |
1495 | { |
1496 | public: |
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 | |
1600 | static 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 | |
1817 | static 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 | |
1827 | static 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 | |
1839 | static 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 | |
1865 | static 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 | |
1891 | static 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 | |
1899 | static 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 | |
1907 | static 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 | |
1936 | static 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 | |
1946 | static 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 | |
1975 | static 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 | |
2033 | static 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 | |
2064 | static 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 | |
2098 | static 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 | |
2112 | static 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 | |
2130 | static 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 | |
2175 | static 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 | |
2197 | static 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 | |
2213 | static 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 | |
2236 | static 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 | |
2242 | static 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 | |
2262 | static 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 | |
2273 | static 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 | |
2290 | static 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 | |
2297 | static 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 | |
2308 | static 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 |
2331 | static 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 |
2340 | static 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 | |
2348 | static 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 | |
2421 | static 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 |
2428 | static 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 | |
2439 | static 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 | |
2449 | static 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 | |
2459 | static 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 | |
2479 | static 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 | |
2489 | static 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 | |
2512 | static 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 | |
2552 | static 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 | |
2574 | static 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 | |
2604 | static 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 | |
2630 | static 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 | |
2652 | static 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 | |
2670 | static 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 | |
2729 | int 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 = "-----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 | |