1 | /* |
2 | * Copyright (C) 2007 Alon Bar-Lev <alon.barlev@gmail.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 WITHANY 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 <QFile> |
21 | #include <QHash> |
22 | #include <QtCrypto> |
23 | #include <QtPlugin> |
24 | |
25 | using namespace QCA; |
26 | |
27 | // qPrintable is ASCII only!!! |
28 | #define myPrintable(s) (s).toUtf8().constData() |
29 | |
30 | namespace softstoreQCAPlugin { |
31 | |
32 | class softstoreKeyStoreListContext; |
33 | static softstoreKeyStoreListContext *s_keyStoreList = nullptr; |
34 | |
35 | enum KeyType |
36 | { |
37 | keyTypeInvalid, |
38 | keyTypePKCS12, |
39 | keyTypePKCS8Inline, |
40 | keyTypePKCS8FilePEM, |
41 | keyTypePKCS8FileDER |
42 | }; |
43 | |
44 | enum PublicType |
45 | { |
46 | publicTypeInvalid, |
47 | publicTypeX509Chain |
48 | }; |
49 | |
50 | struct SoftStoreEntry |
51 | { |
52 | QString name; |
53 | CertificateChain chain; |
54 | KeyType keyReferenceType; |
55 | QString keyReference; |
56 | bool noPassphrase; |
57 | int unlockTimeout; |
58 | }; |
59 | |
60 | class softstorePKeyBase : public PKeyBase |
61 | { |
62 | Q_OBJECT |
63 | |
64 | private: |
65 | bool _has_privateKeyRole; |
66 | SoftStoreEntry _entry; |
67 | QString _serialized; |
68 | PrivateKey _privkey; |
69 | PrivateKey _privkeySign; |
70 | PublicKey _pubkey; |
71 | QDateTime dueTime; |
72 | |
73 | public: |
74 | static inline QString typeToString(PKey::Type t) |
75 | { |
76 | switch (t) { |
77 | case PKey::RSA: |
78 | return QStringLiteral("rsa" ); |
79 | case PKey::DSA: |
80 | return QStringLiteral("dsa" ); |
81 | case PKey::DH: |
82 | return QStringLiteral("dh" ); |
83 | default: |
84 | return QLatin1String("" ); |
85 | } |
86 | } |
87 | |
88 | softstorePKeyBase(const SoftStoreEntry &entry, const QString &serialized, Provider *p) |
89 | : PKeyBase(p, QStringLiteral("rsa" ) /*typeToString (entry.chain.primary ().subjectPublicKey ().type ())*/) |
90 | { |
91 | QCA_logTextMessage(QStringLiteral("softstorePKeyBase::softstorePKeyBase1 - entry" ), Logger::Debug); |
92 | |
93 | _has_privateKeyRole = true; |
94 | _entry = entry; |
95 | _serialized = serialized; |
96 | _pubkey = _entry.chain.primary().subjectPublicKey(); |
97 | |
98 | QCA_logTextMessage(QStringLiteral("softstorePKeyBase::softstorePKeyBase1 - return" ), Logger::Debug); |
99 | } |
100 | |
101 | softstorePKeyBase(const softstorePKeyBase &from) |
102 | : PKeyBase(from.provider(), QStringLiteral("rsa" ) /*typeToString (from._pubkey.type ())*/) |
103 | { |
104 | QCA_logTextMessage(QStringLiteral("softstorePKeyBase::softstorePKeyBaseC - entry" ), Logger::Debug); |
105 | |
106 | _has_privateKeyRole = from._has_privateKeyRole; |
107 | _entry = from._entry; |
108 | _serialized = from._serialized; |
109 | _pubkey = from._pubkey; |
110 | _privkey = from._privkey; |
111 | |
112 | QCA_logTextMessage(QStringLiteral("softstorePKeyBase::softstorePKeyBaseC - return" ), Logger::Debug); |
113 | } |
114 | |
115 | ~softstorePKeyBase() override |
116 | { |
117 | QCA_logTextMessage(QStringLiteral("softstorePKeyBase::~softstorePKeyBase - entry" ), Logger::Debug); |
118 | |
119 | QCA_logTextMessage(QStringLiteral("softstorePKeyBase::~softstorePKeyBase - return" ), Logger::Debug); |
120 | } |
121 | |
122 | Provider::Context *clone() const override |
123 | { |
124 | return new softstorePKeyBase(*this); |
125 | } |
126 | |
127 | public: |
128 | bool isNull() const override |
129 | { |
130 | return _pubkey.isNull(); |
131 | } |
132 | |
133 | PKey::Type type() const override |
134 | { |
135 | return _pubkey.type(); |
136 | } |
137 | |
138 | bool isPrivate() const override |
139 | { |
140 | return _has_privateKeyRole; |
141 | } |
142 | |
143 | bool canExport() const override |
144 | { |
145 | return !_has_privateKeyRole; |
146 | } |
147 | |
148 | void convertToPublic() override |
149 | { |
150 | QCA_logTextMessage(QStringLiteral("softstorePKeyBase::convertToPublic - entry" ), Logger::Debug); |
151 | |
152 | if (_has_privateKeyRole) { |
153 | _has_privateKeyRole = false; |
154 | } |
155 | |
156 | QCA_logTextMessage(QStringLiteral("softstorePKeyBase::convertToPublic - return" ), Logger::Debug); |
157 | } |
158 | |
159 | int bits() const override |
160 | { |
161 | return _pubkey.bitSize(); |
162 | } |
163 | |
164 | int maximumEncryptSize(EncryptionAlgorithm alg) const override |
165 | { |
166 | return _pubkey.maximumEncryptSize(alg); |
167 | } |
168 | |
169 | SecureArray encrypt(const SecureArray &in, EncryptionAlgorithm alg) override |
170 | { |
171 | return _pubkey.encrypt(a: in, alg); |
172 | } |
173 | |
174 | bool decrypt(const SecureArray &in, SecureArray *out, EncryptionAlgorithm alg) override |
175 | { |
176 | if (_ensureAccess()) { |
177 | return _privkey.decrypt(in, out, alg); |
178 | } else { |
179 | return false; |
180 | } |
181 | } |
182 | |
183 | void startSign(SignatureAlgorithm alg, SignatureFormat format) override |
184 | { |
185 | if (_ensureAccess()) { |
186 | /* |
187 | * We must use one object thought |
188 | * signing, so it won't expire by |
189 | * it-self or during passphrase. |
190 | */ |
191 | _privkeySign = _privkey; |
192 | _privkeySign.startSign(alg, format); |
193 | } |
194 | } |
195 | |
196 | void startVerify(SignatureAlgorithm alg, SignatureFormat sf) override |
197 | { |
198 | _pubkey.startVerify(alg, format: sf); |
199 | } |
200 | |
201 | void update(const MemoryRegion &in) override |
202 | { |
203 | if (_has_privateKeyRole) { |
204 | _privkeySign.update(a: in); |
205 | } else { |
206 | _pubkey.update(a: in); |
207 | } |
208 | } |
209 | |
210 | QByteArray endSign() override |
211 | { |
212 | const QByteArray r = _privkeySign.signature(); |
213 | _privkeySign = PrivateKey(); |
214 | return r; |
215 | } |
216 | |
217 | virtual bool validSignature(const QByteArray &sig) |
218 | { |
219 | return _pubkey.validSignature(sig); |
220 | } |
221 | |
222 | virtual void createPrivate(int bits, int exp, bool block) |
223 | { |
224 | Q_UNUSED(bits); |
225 | Q_UNUSED(exp); |
226 | Q_UNUSED(block); |
227 | } |
228 | |
229 | virtual void createPrivate(const BigInteger &n, |
230 | const BigInteger &e, |
231 | const BigInteger &p, |
232 | const BigInteger &q, |
233 | const BigInteger &d) |
234 | { |
235 | Q_UNUSED(n); |
236 | Q_UNUSED(e); |
237 | Q_UNUSED(p); |
238 | Q_UNUSED(q); |
239 | Q_UNUSED(d); |
240 | } |
241 | |
242 | virtual void createPublic(const BigInteger &n, const BigInteger &e) |
243 | { |
244 | Q_UNUSED(n); |
245 | Q_UNUSED(e); |
246 | } |
247 | |
248 | public: |
249 | PublicKey _publicKey() const |
250 | { |
251 | return _pubkey; |
252 | } |
253 | |
254 | bool _ensureAccess() |
255 | { |
256 | bool ret = false; |
257 | |
258 | QCA_logTextMessage(QStringLiteral("softstorePKeyBase::_ensureAccess - entry" ), Logger::Debug); |
259 | |
260 | if (_entry.unlockTimeout != -1) { |
261 | if (dueTime >= QDateTime::currentDateTime()) { |
262 | QCA_logTextMessage(QStringLiteral("softstorePKeyBase::_ensureAccess - dueTime reached, clearing" ), |
263 | Logger::Debug); |
264 | _privkey = PrivateKey(); |
265 | } |
266 | } |
267 | |
268 | if (!_privkey.isNull()) { |
269 | ret = true; |
270 | } else { |
271 | KeyStoreEntry entry; |
272 | KeyStoreEntryContext *context = nullptr; |
273 | QString storeId, storeName; |
274 | ConvertResult cresult; |
275 | |
276 | QCA_logTextMessage(QStringLiteral("softstorePKeyBase::_ensureAccess - no current key, creating" ), |
277 | Logger::Debug); |
278 | |
279 | // too lazy to create scope |
280 | context = reinterpret_cast<KeyStoreListContext *>(s_keyStoreList)->entryPassive(serialized: _serialized); |
281 | if (context != nullptr) { |
282 | storeId = context->storeId(); |
283 | storeName = context->storeName(); |
284 | entry.change(c: context); |
285 | } |
286 | |
287 | while (!ret) { |
288 | SecureArray passphrase; |
289 | |
290 | switch (_entry.keyReferenceType) { |
291 | case keyTypeInvalid: |
292 | case keyTypePKCS8Inline: |
293 | break; |
294 | case keyTypePKCS12: |
295 | case keyTypePKCS8FilePEM: |
296 | case keyTypePKCS8FileDER: |
297 | { |
298 | QFile file(_entry.keyReference); |
299 | while (!file.open(flags: QIODevice::ReadOnly)) { |
300 | TokenAsker asker; |
301 | asker.ask(keyStoreInfo: KeyStoreInfo(KeyStore::SmartCard, storeId, storeName), keyStoreEntry: entry, ptr: context); |
302 | asker.waitForResponse(); |
303 | if (!asker.accepted()) { |
304 | goto cleanup1; |
305 | } |
306 | } |
307 | } break; |
308 | } |
309 | |
310 | if (!_entry.noPassphrase) { |
311 | PasswordAsker asker; |
312 | asker.ask(pstyle: Event::StylePassphrase, keyStoreInfo: KeyStoreInfo(KeyStore::User, storeId, storeName), keyStoreEntry: entry, ptr: context); |
313 | asker.waitForResponse(); |
314 | passphrase = asker.password(); |
315 | if (!asker.accepted()) { |
316 | goto cleanup1; |
317 | } |
318 | } |
319 | |
320 | switch (_entry.keyReferenceType) { |
321 | case keyTypeInvalid: |
322 | break; |
323 | case keyTypePKCS12: |
324 | { |
325 | KeyBundle bundle = KeyBundle::fromFile(fileName: _entry.keyReference, passphrase, result: &cresult); |
326 | if (cresult == ConvertGood) { |
327 | _privkey = bundle.privateKey(); |
328 | ret = true; |
329 | } |
330 | } break; |
331 | case keyTypePKCS8Inline: |
332 | { |
333 | PrivateKey k = |
334 | PrivateKey::fromDER(a: Base64().stringToArray(s: _entry.keyReference), passphrase, result: &cresult); |
335 | if (cresult == ConvertGood) { |
336 | _privkey = k; |
337 | ret = true; |
338 | } |
339 | } break; |
340 | case keyTypePKCS8FilePEM: |
341 | { |
342 | PrivateKey k = PrivateKey::fromPEMFile(fileName: _entry.keyReference, passphrase, result: &cresult); |
343 | if (cresult == ConvertGood) { |
344 | _privkey = k; |
345 | ret = true; |
346 | } |
347 | } break; |
348 | case keyTypePKCS8FileDER: |
349 | { |
350 | QFile file(_entry.keyReference); |
351 | if (file.open(flags: QIODevice::ReadOnly)) { |
352 | const QByteArray contents = file.readAll(); |
353 | |
354 | PrivateKey k = PrivateKey::fromDER(a: contents, passphrase, result: &cresult); |
355 | if (cresult == ConvertGood) { |
356 | _privkey = k; |
357 | ret = true; |
358 | } |
359 | } |
360 | } break; |
361 | } |
362 | } |
363 | |
364 | if (_entry.unlockTimeout != -1) { |
365 | dueTime = QDateTime::currentDateTime().addSecs(secs: _entry.unlockTimeout); |
366 | } |
367 | |
368 | cleanup1:; |
369 | } |
370 | |
371 | QCA_logTextMessage(QString::asprintf("softstorePKeyBase::_ensureAccess - return ret=%d" , ret ? 1 : 0), |
372 | Logger::Debug); |
373 | |
374 | return ret; |
375 | } |
376 | }; |
377 | |
378 | class softstorePKeyContext : public PKeyContext |
379 | { |
380 | Q_OBJECT |
381 | |
382 | private: |
383 | PKeyBase *_k; |
384 | |
385 | public: |
386 | softstorePKeyContext(Provider *p) |
387 | : PKeyContext(p) |
388 | { |
389 | _k = nullptr; |
390 | } |
391 | |
392 | ~softstorePKeyContext() override |
393 | { |
394 | delete _k; |
395 | _k = nullptr; |
396 | } |
397 | |
398 | Provider::Context *clone() const override |
399 | { |
400 | softstorePKeyContext *c = new softstorePKeyContext(*this); |
401 | c->_k = (PKeyBase *)_k->clone(); |
402 | return c; |
403 | } |
404 | |
405 | public: |
406 | QList<PKey::Type> supportedTypes() const override |
407 | { |
408 | QList<PKey::Type> list; |
409 | list += static_cast<softstorePKeyBase *>(_k)->_publicKey().type(); |
410 | return list; |
411 | } |
412 | |
413 | QList<PKey::Type> supportedIOTypes() const override |
414 | { |
415 | QList<PKey::Type> list; |
416 | list += static_cast<softstorePKeyBase *>(_k)->_publicKey().type(); |
417 | return list; |
418 | } |
419 | |
420 | QList<PBEAlgorithm> supportedPBEAlgorithms() const override |
421 | { |
422 | QList<PBEAlgorithm> list; |
423 | return list; |
424 | } |
425 | |
426 | PKeyBase *key() override |
427 | { |
428 | return _k; |
429 | } |
430 | |
431 | const PKeyBase *key() const override |
432 | { |
433 | return _k; |
434 | } |
435 | |
436 | void setKey(PKeyBase *key) override |
437 | { |
438 | delete _k; |
439 | _k = key; |
440 | } |
441 | |
442 | bool importKey(const PKeyBase *key) override |
443 | { |
444 | Q_UNUSED(key); |
445 | return false; |
446 | } |
447 | |
448 | static int passphrase_cb(char *buf, int size, int rwflag, void *u) |
449 | { |
450 | Q_UNUSED(buf); |
451 | Q_UNUSED(size); |
452 | Q_UNUSED(rwflag); |
453 | Q_UNUSED(u); |
454 | return 0; |
455 | } |
456 | |
457 | QByteArray publicToDER() const override |
458 | { |
459 | return static_cast<softstorePKeyBase *>(_k)->_publicKey().toDER(); |
460 | } |
461 | |
462 | QString publicToPEM() const override |
463 | { |
464 | return static_cast<softstorePKeyBase *>(_k)->_publicKey().toPEM(); |
465 | } |
466 | |
467 | ConvertResult publicFromDER(const QByteArray &in) override |
468 | { |
469 | Q_UNUSED(in); |
470 | return ErrorDecode; |
471 | } |
472 | |
473 | ConvertResult publicFromPEM(const QString &s) override |
474 | { |
475 | Q_UNUSED(s); |
476 | return ErrorDecode; |
477 | } |
478 | |
479 | SecureArray privateToDER(const SecureArray &passphrase, PBEAlgorithm pbe) const override |
480 | { |
481 | Q_UNUSED(passphrase); |
482 | Q_UNUSED(pbe); |
483 | return SecureArray(); |
484 | } |
485 | |
486 | QString privateToPEM(const SecureArray &passphrase, PBEAlgorithm pbe) const override |
487 | { |
488 | Q_UNUSED(passphrase); |
489 | Q_UNUSED(pbe); |
490 | return QString(); |
491 | } |
492 | |
493 | ConvertResult privateFromDER(const SecureArray &in, const SecureArray &passphrase) override |
494 | { |
495 | Q_UNUSED(in); |
496 | Q_UNUSED(passphrase); |
497 | return ErrorDecode; |
498 | } |
499 | |
500 | ConvertResult privateFromPEM(const QString &s, const SecureArray &passphrase) override |
501 | { |
502 | Q_UNUSED(s); |
503 | Q_UNUSED(passphrase); |
504 | return ErrorDecode; |
505 | } |
506 | }; |
507 | |
508 | class softstoreKeyStoreEntryContext : public KeyStoreEntryContext |
509 | { |
510 | Q_OBJECT |
511 | private: |
512 | KeyStoreEntry::Type _item_type; |
513 | KeyBundle _key; |
514 | SoftStoreEntry _entry; |
515 | QString _serialized; |
516 | |
517 | public: |
518 | softstoreKeyStoreEntryContext(const KeyBundle &key, |
519 | const SoftStoreEntry &entry, |
520 | const QString &serialized, |
521 | Provider *p) |
522 | : KeyStoreEntryContext(p) |
523 | { |
524 | _item_type = KeyStoreEntry::TypeKeyBundle; |
525 | _key = key; |
526 | _entry = entry; |
527 | _serialized = serialized; |
528 | } |
529 | |
530 | softstoreKeyStoreEntryContext(const softstoreKeyStoreEntryContext &from) |
531 | : KeyStoreEntryContext(from) |
532 | { |
533 | _item_type = from._item_type; |
534 | _key = from._key; |
535 | _entry = from._entry; |
536 | _serialized = from._serialized; |
537 | } |
538 | |
539 | Provider::Context *clone() const override |
540 | { |
541 | return new softstoreKeyStoreEntryContext(*this); |
542 | } |
543 | |
544 | public: |
545 | KeyStoreEntry::Type type() const override |
546 | { |
547 | return KeyStoreEntry::TypeKeyBundle; |
548 | } |
549 | |
550 | QString name() const override |
551 | { |
552 | return _entry.name; |
553 | } |
554 | |
555 | QString id() const override |
556 | { |
557 | return _entry.name; |
558 | } |
559 | |
560 | KeyBundle keyBundle() const override |
561 | { |
562 | return _key; |
563 | } |
564 | |
565 | Certificate certificate() const override |
566 | { |
567 | return _entry.chain.primary(); |
568 | } |
569 | |
570 | QString storeId() const override |
571 | { |
572 | return QString::asprintf(format: "%s/%s" , "qca-softstore" , myPrintable(_entry.name)); |
573 | } |
574 | |
575 | QString storeName() const override |
576 | { |
577 | return _entry.name; |
578 | } |
579 | |
580 | bool ensureAccess() override |
581 | { |
582 | return static_cast<softstorePKeyBase *>(static_cast<PKeyContext *>(_key.privateKey().context())->key()) |
583 | ->_ensureAccess(); |
584 | } |
585 | |
586 | QString serialize() const override |
587 | { |
588 | return _serialized; |
589 | } |
590 | }; |
591 | |
592 | class softstoreKeyStoreListContext : public KeyStoreListContext |
593 | { |
594 | Q_OBJECT |
595 | |
596 | private: |
597 | int _last_id; |
598 | QList<SoftStoreEntry> _entries; |
599 | |
600 | public: |
601 | softstoreKeyStoreListContext(Provider *p) |
602 | : KeyStoreListContext(p) |
603 | { |
604 | QCA_logTextMessage( |
605 | QString::asprintf("softstoreKeyStoreListContext::softstoreKeyStoreListContext - entry Provider=%p" , |
606 | (void *)p), |
607 | Logger::Debug); |
608 | |
609 | _last_id = 0; |
610 | |
611 | QCA_logTextMessage(QStringLiteral("softstoreKeyStoreListContext::softstoreKeyStoreListContext - return" ), |
612 | Logger::Debug); |
613 | } |
614 | |
615 | ~softstoreKeyStoreListContext() override |
616 | { |
617 | QCA_logTextMessage(QStringLiteral("softstoreKeyStoreListContext::~softstoreKeyStoreListContext - entry" ), |
618 | Logger::Debug); |
619 | |
620 | s_keyStoreList = nullptr; |
621 | |
622 | QCA_logTextMessage(QStringLiteral("softstoreKeyStoreListContext::~softstoreKeyStoreListContext - return" ), |
623 | Logger::Debug); |
624 | } |
625 | |
626 | Provider::Context *clone() const override |
627 | { |
628 | QCA_logTextMessage(QStringLiteral("softstoreKeyStoreListContext::clone - entry/return" ), Logger::Debug); |
629 | return nullptr; |
630 | } |
631 | |
632 | public: |
633 | void start() override |
634 | { |
635 | QCA_logTextMessage(QStringLiteral("softstoreKeyStoreListContext::start - entry" ), Logger::Debug); |
636 | |
637 | QMetaObject::invokeMethod(obj: this, member: "doReady" , c: Qt::QueuedConnection); |
638 | |
639 | QCA_logTextMessage(QStringLiteral("softstoreKeyStoreListContext::start - return" ), Logger::Debug); |
640 | } |
641 | |
642 | void setUpdatesEnabled(bool enabled) override |
643 | { |
644 | QCA_logTextMessage( |
645 | QString::asprintf("softstoreKeyStoreListContext::setUpdatesEnabled - entry/return enabled=%d" , |
646 | enabled ? 1 : 0), |
647 | Logger::Debug); |
648 | } |
649 | |
650 | KeyStoreEntryContext *entry(int id, const QString &entryId) override |
651 | { |
652 | QCA_logTextMessage(QString::asprintf("softstoreKeyStoreListContext::entry - entry/return id=%d entryId='%s'" , |
653 | id, |
654 | myPrintable(entryId)), |
655 | Logger::Debug); |
656 | |
657 | Q_UNUSED(id); |
658 | Q_UNUSED(entryId); |
659 | return nullptr; |
660 | } |
661 | |
662 | KeyStoreEntryContext *entryPassive(const QString &serialized) override |
663 | { |
664 | KeyStoreEntryContext *entry = nullptr; |
665 | |
666 | QCA_logTextMessage(QString::asprintf("softstoreKeyStoreListContext::entryPassive - entry serialized='%s'" , |
667 | myPrintable(serialized)), |
668 | Logger::Debug); |
669 | |
670 | if (serialized.startsWith(s: QLatin1String("qca-softstore/" ))) { |
671 | SoftStoreEntry sentry; |
672 | |
673 | if (_deserializeSoftStoreEntry(serialized, entry&: sentry)) { |
674 | entry = _keyStoreEntryBySoftStoreEntry(sentry); |
675 | } |
676 | } |
677 | |
678 | QCA_logTextMessage( |
679 | QString::asprintf("softstoreKeyStoreListContext::entryPassive - return entry=%p" , (void *)entry), |
680 | Logger::Debug); |
681 | |
682 | return entry; |
683 | } |
684 | |
685 | KeyStore::Type type(int id) const override |
686 | { |
687 | Q_UNUSED(id); |
688 | |
689 | QCA_logTextMessage(QString::asprintf("softstoreKeyStoreListContext::type - entry/return id=%d" , id), |
690 | Logger::Debug); |
691 | |
692 | return KeyStore::User; |
693 | } |
694 | |
695 | QString storeId(int id) const override |
696 | { |
697 | QString ret; |
698 | |
699 | QCA_logTextMessage(QString::asprintf("softstoreKeyStoreListContext::storeId - entry id=%d" , id), Logger::Debug); |
700 | |
701 | ret = QStringLiteral("qca-softstore" ); |
702 | |
703 | QCA_logTextMessage(QString::asprintf("softstoreKeyStoreListContext::storeId - return ret=%s" , myPrintable(ret)), |
704 | Logger::Debug); |
705 | |
706 | return ret; |
707 | } |
708 | |
709 | QString name(int id) const override |
710 | { |
711 | QString ret; |
712 | |
713 | QCA_logTextMessage(QString::asprintf("softstoreKeyStoreListContext::name - entry id=%d" , id), Logger::Debug); |
714 | |
715 | ret = QStringLiteral("User Software Store" ); |
716 | |
717 | QCA_logTextMessage(QString::asprintf("softstoreKeyStoreListContext::name - return ret=%s" , myPrintable(ret)), |
718 | Logger::Debug); |
719 | |
720 | return ret; |
721 | } |
722 | |
723 | QList<KeyStoreEntry::Type> entryTypes(int id) const override |
724 | { |
725 | Q_UNUSED(id); |
726 | |
727 | QCA_logTextMessage(QString::asprintf("softstoreKeyStoreListContext::entryTypes - entry/return id=%d" , id), |
728 | Logger::Debug); |
729 | |
730 | QList<KeyStoreEntry::Type> list; |
731 | list += KeyStoreEntry::TypeKeyBundle; |
732 | list += KeyStoreEntry::TypeCertificate; |
733 | return list; |
734 | } |
735 | |
736 | QList<int> keyStores() override |
737 | { |
738 | QList<int> list; |
739 | |
740 | QCA_logTextMessage(QStringLiteral("softstoreKeyStoreListContext::keyStores - entry" ), Logger::Debug); |
741 | |
742 | list += _last_id; |
743 | |
744 | QCA_logTextMessage( |
745 | QString::asprintf("softstoreKeyStoreListContext::keyStores - return out.size()=%d" , int(list.size())), |
746 | Logger::Debug); |
747 | |
748 | return list; |
749 | } |
750 | |
751 | QList<KeyStoreEntryContext *> entryList(int id) override |
752 | { |
753 | QList<KeyStoreEntryContext *> list; |
754 | |
755 | QCA_logTextMessage(QString::asprintf("softstoreKeyStoreListContext::entryList - entry id=%d" , id), |
756 | Logger::Debug); |
757 | |
758 | foreach (const SoftStoreEntry &e, _entries) { |
759 | list += _keyStoreEntryBySoftStoreEntry(sentry: e); |
760 | } |
761 | |
762 | QCA_logTextMessage( |
763 | QString::asprintf("softstoreKeyStoreListContext::entryList - return out.size()=%d" , int(list.size())), |
764 | Logger::Debug); |
765 | |
766 | return list; |
767 | } |
768 | |
769 | void _emit_diagnosticText(const QString &t) |
770 | { |
771 | QCA_logTextMessage( |
772 | QString::asprintf("softstoreKeyStoreListContext::_emit_diagnosticText - entry t='%s'" , myPrintable(t)), |
773 | Logger::Debug); |
774 | |
775 | QCA_logTextMessage(t, Logger::Warning); |
776 | |
777 | emit diagnosticText(str: t); |
778 | |
779 | QCA_logTextMessage(QStringLiteral("softstoreKeyStoreListContext::_emit_diagnosticText - return" ), |
780 | Logger::Debug); |
781 | } |
782 | |
783 | private Q_SLOTS: |
784 | void doReady() |
785 | { |
786 | QCA_logTextMessage(QStringLiteral("softstoreKeyStoreListContext::doReady - entry" ), Logger::Debug); |
787 | |
788 | emit busyEnd(); |
789 | |
790 | QCA_logTextMessage(QStringLiteral("softstoreKeyStoreListContext::doReady - return" ), Logger::Debug); |
791 | } |
792 | |
793 | void doUpdated() |
794 | { |
795 | QCA_logTextMessage(QStringLiteral("softstoreKeyStoreListContext::doUpdated - entry" ), Logger::Debug); |
796 | |
797 | emit updated(); |
798 | |
799 | QCA_logTextMessage(QStringLiteral("softstoreKeyStoreListContext::doUpdated - return" ), Logger::Debug); |
800 | } |
801 | |
802 | public: |
803 | void _updateConfig(const QVariantMap &config, const int maxEntries) |
804 | { |
805 | QCA_logTextMessage(QStringLiteral("softstoreKeyStoreListContext::_updateConfig - entry" ), Logger::Debug); |
806 | |
807 | QMap<QString, KeyType> keyTypeMap; |
808 | keyTypeMap[QStringLiteral("pkcs12" )] = keyTypePKCS12; |
809 | keyTypeMap[QStringLiteral("pkcs8" )] = keyTypePKCS8Inline; |
810 | keyTypeMap[QStringLiteral("pkcs8-file-pem" )] = keyTypePKCS8FilePEM; |
811 | keyTypeMap[QStringLiteral("pkcs8-file-der" )] = keyTypePKCS8FileDER; |
812 | |
813 | QMap<QString, PublicType> publicTypeMap; |
814 | publicTypeMap[QStringLiteral("x509chain" )] = publicTypeX509Chain; |
815 | |
816 | _last_id++; |
817 | _entries.clear(); |
818 | |
819 | for (int i = 0; i < maxEntries; i++) { |
820 | if (config[QString::asprintf(format: "entry_%02d_enabled" , i)].toBool()) { |
821 | ConvertResult cresult; |
822 | SoftStoreEntry entry; |
823 | PublicType publicType = publicTypeInvalid; |
824 | |
825 | entry.name = config[QString::asprintf(format: "entry_%02d_name" , i)].toString(); |
826 | const QString stringReferenceType = config[QString::asprintf(format: "entry_%02d_private_type" , i)].toString(); |
827 | const QString stringPublicType = config[QString::asprintf(format: "entry_%02d_public_type" , i)].toString(); |
828 | entry.noPassphrase = config[QString::asprintf(format: "entry_%02d_no_passphrase" , i)].toBool(); |
829 | entry.unlockTimeout = config[QString::asprintf(format: "entry_%02d_unlock_timeout" , i)].toInt(); |
830 | |
831 | if (publicTypeMap.contains(key: stringPublicType)) { |
832 | publicType = publicTypeMap[stringPublicType]; |
833 | } else { |
834 | _emit_diagnosticText(t: QString::asprintf(format: "Software Store: Bad public key type of '%s' entry.\n" , |
835 | myPrintable(entry.name))); |
836 | goto cleanup1; |
837 | } |
838 | |
839 | if (keyTypeMap.contains(key: stringReferenceType)) { |
840 | entry.keyReferenceType = keyTypeMap[stringReferenceType]; |
841 | } else { |
842 | _emit_diagnosticText(t: QString::asprintf(format: "Software Store: Bad private key type of '%s' entry.\n" , |
843 | myPrintable(entry.name))); |
844 | goto cleanup1; |
845 | } |
846 | |
847 | entry.keyReference = config[QString::asprintf(format: "entry_%02d_private" , i)].toString(); |
848 | |
849 | switch (publicType) { |
850 | case publicTypeInvalid: |
851 | goto cleanup1; |
852 | break; |
853 | case publicTypeX509Chain: |
854 | const QStringList base64certs = |
855 | config[QString::asprintf(format: "entry_%02d_public" , i)].toString().split(QStringLiteral("!" )); |
856 | |
857 | foreach (const QString &s, base64certs) { |
858 | entry.chain += Certificate::fromDER(a: Base64().stringToArray(s).toByteArray(), result: &cresult); |
859 | } |
860 | |
861 | if (cresult != ConvertGood) { |
862 | _emit_diagnosticText(t: QString::asprintf( |
863 | format: "Software Store: Cannot load certificate of '%s' entry.\n" , myPrintable(entry.name))); |
864 | goto cleanup1; |
865 | } |
866 | break; |
867 | } |
868 | |
869 | _entries += entry; |
870 | |
871 | cleanup1:; // nothing to do for this entry. |
872 | } |
873 | } |
874 | |
875 | QMetaObject::invokeMethod(obj: s_keyStoreList, member: "doUpdated" , c: Qt::QueuedConnection); |
876 | |
877 | QCA_logTextMessage(QStringLiteral("softstoreKeyStoreListContext::_updateConfig - return" ), Logger::Debug); |
878 | } |
879 | |
880 | private: |
881 | QString _serializeSoftStoreEntry(const SoftStoreEntry &entry) const |
882 | { |
883 | QString serialized; |
884 | |
885 | QCA_logTextMessage(QString::asprintf("softstoreKeyStoreListContext::_serializeSoftStoreEntry - entry name=%s" , |
886 | myPrintable(entry.name)), |
887 | Logger::Debug); |
888 | |
889 | serialized = QString::asprintf(format: "qca-softstore/0/%s/%d/%s/%d/%d/x509chain/" , |
890 | myPrintable(_escapeString(entry.name)), |
891 | entry.keyReferenceType, |
892 | myPrintable(_escapeString(entry.keyReference)), |
893 | entry.noPassphrase ? 1 : 0, |
894 | entry.unlockTimeout); |
895 | |
896 | QStringList list; |
897 | foreach (const Certificate &i, entry.chain) { |
898 | list += _escapeString(from: Base64().arrayToString(a: i.toDER())); |
899 | } |
900 | |
901 | serialized.append(s: list.join(QStringLiteral("/" ))); |
902 | |
903 | QCA_logTextMessage( |
904 | QString::asprintf("softstoreKeyStoreListContext::_serializeSoftStoreEntry - return serialized='%s'" , |
905 | myPrintable(serialized)), |
906 | Logger::Debug); |
907 | |
908 | return serialized; |
909 | } |
910 | |
911 | bool _deserializeSoftStoreEntry(const QString &serialized, SoftStoreEntry &entry) const |
912 | { |
913 | bool ret = false; |
914 | |
915 | QCA_logTextMessage( |
916 | QString::asprintf("softstoreKeyStoreListContext::_deserializeSoftStoreEntry - entry from='%s'" , |
917 | myPrintable(serialized)), |
918 | Logger::Debug); |
919 | |
920 | entry = SoftStoreEntry(); |
921 | |
922 | const QStringList list = serialized.split(QStringLiteral("/" )); |
923 | int n = 0; |
924 | |
925 | if (list.size() < 8) { |
926 | goto cleanup; |
927 | } |
928 | |
929 | if (list[n++] != QLatin1String("qca-softstore" )) { |
930 | goto cleanup; |
931 | } |
932 | |
933 | if (list[n++].toInt() != 0) { |
934 | goto cleanup; |
935 | } |
936 | |
937 | entry.name = _unescapeString(from: list[n++]); |
938 | entry.keyReferenceType = (KeyType)list[n++].toInt(); |
939 | entry.keyReference = _unescapeString(from: list[n++]); |
940 | entry.noPassphrase = list[n++].toInt() != 0; |
941 | entry.unlockTimeout = list[n++].toInt(); |
942 | n++; // skip public key for now. |
943 | |
944 | while (n < list.size()) { |
945 | Certificate cert = Certificate::fromDER(a: Base64().stringToArray(s: _unescapeString(from: list[n++])).toByteArray()); |
946 | if (cert.isNull()) { |
947 | goto cleanup; |
948 | } |
949 | entry.chain += cert; |
950 | } |
951 | |
952 | ret = true; |
953 | |
954 | cleanup: |
955 | |
956 | QCA_logTextMessage( |
957 | QString::asprintf( |
958 | "softstoreKeyStoreListContext::_deserializeSoftStoreEntry - return ret=%d chain.size()=%d" , |
959 | ret ? 1 : 0, |
960 | int(entry.chain.size())), |
961 | Logger::Debug); |
962 | |
963 | return ret; |
964 | } |
965 | |
966 | softstoreKeyStoreEntryContext *_keyStoreEntryBySoftStoreEntry(const SoftStoreEntry &sentry) const |
967 | { |
968 | softstoreKeyStoreEntryContext *entry = nullptr; |
969 | |
970 | QCA_logTextMessage( |
971 | QString::asprintf("softstoreKeyStoreListContext::_keyStoreEntryBySoftStoreEntry - entry name=%s" , |
972 | myPrintable(sentry.name)), |
973 | Logger::Debug); |
974 | |
975 | QString serialized = _serializeSoftStoreEntry(entry: sentry); |
976 | |
977 | softstorePKeyBase *pkey = new softstorePKeyBase(sentry, serialized, provider()); |
978 | |
979 | softstorePKeyContext *pkc = new softstorePKeyContext(provider()); |
980 | pkc->setKey(pkey); |
981 | PrivateKey privkey; |
982 | privkey.change(c: pkc); |
983 | KeyBundle key; |
984 | key.setCertificateChainAndKey(c: sentry.chain, key: privkey); |
985 | |
986 | entry = new softstoreKeyStoreEntryContext(key, sentry, serialized, provider()); |
987 | |
988 | QCA_logTextMessage( |
989 | QString::asprintf("softstoreKeyStoreListContext::_keyStoreEntryBySoftStoreEntry - return entry=%p" , |
990 | (void *)entry), |
991 | Logger::Debug); |
992 | |
993 | return entry; |
994 | } |
995 | |
996 | QString _escapeString(const QString &from) const |
997 | { |
998 | QString to; |
999 | |
1000 | foreach (const QChar &c, from) { |
1001 | if (c == QLatin1Char('/') || c == QLatin1Char('\\')) { |
1002 | to += QString::asprintf(format: "\\x%04x" , c.unicode()); |
1003 | } else { |
1004 | to += c; |
1005 | } |
1006 | } |
1007 | |
1008 | return to; |
1009 | } |
1010 | |
1011 | QString _unescapeString(const QString &from) const |
1012 | { |
1013 | QString to; |
1014 | |
1015 | for (int i = 0; i < from.size(); i++) { |
1016 | QChar c = from[i]; |
1017 | |
1018 | if (c == QLatin1Char('\\')) { |
1019 | #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 2) |
1020 | to += QChar((ushort)QStringView(from).mid(pos: i + 2, n: 4).toInt(ok: nullptr, base: 16)); |
1021 | #else |
1022 | to += QChar((ushort)from.midRef(i + 2, 4).toInt(nullptr, 16)); |
1023 | #endif |
1024 | i += 5; |
1025 | } else { |
1026 | to += c; |
1027 | } |
1028 | } |
1029 | |
1030 | return to; |
1031 | } |
1032 | }; |
1033 | |
1034 | } |
1035 | |
1036 | using namespace softstoreQCAPlugin; |
1037 | |
1038 | class softstoreProvider : public Provider |
1039 | { |
1040 | private: |
1041 | static const int _CONFIG_MAX_ENTRIES; |
1042 | |
1043 | QVariantMap _config; |
1044 | |
1045 | public: |
1046 | softstoreProvider() |
1047 | { |
1048 | } |
1049 | |
1050 | ~softstoreProvider() override |
1051 | { |
1052 | } |
1053 | |
1054 | public: |
1055 | int qcaVersion() const override |
1056 | { |
1057 | return QCA_VERSION; |
1058 | } |
1059 | |
1060 | void init() override |
1061 | { |
1062 | } |
1063 | |
1064 | QString name() const override |
1065 | { |
1066 | return QStringLiteral("qca-softstore" ); |
1067 | } |
1068 | |
1069 | QStringList features() const override |
1070 | { |
1071 | QCA_logTextMessage(QStringLiteral("softstoreProvider::features - entry/return" ), Logger::Debug); |
1072 | |
1073 | QStringList list; |
1074 | list += QStringLiteral("pkey" ); |
1075 | list += QStringLiteral("keystorelist" ); |
1076 | return list; |
1077 | } |
1078 | |
1079 | Context *createContext(const QString &type) override |
1080 | { |
1081 | Provider::Context *context = nullptr; |
1082 | |
1083 | QCA_logTextMessage(QString::asprintf("softstoreProvider::createContext - entry type='%s'" , myPrintable(type)), |
1084 | Logger::Debug); |
1085 | |
1086 | if (type == QLatin1String("keystorelist" )) { |
1087 | if (s_keyStoreList == nullptr) { |
1088 | s_keyStoreList = new softstoreKeyStoreListContext(this); |
1089 | s_keyStoreList->_updateConfig(config: _config, maxEntries: _CONFIG_MAX_ENTRIES); |
1090 | } |
1091 | context = s_keyStoreList; |
1092 | } |
1093 | |
1094 | QCA_logTextMessage(QString::asprintf("softstoreProvider::createContext - return context=%p" , (void *)context), |
1095 | Logger::Debug); |
1096 | |
1097 | return context; |
1098 | } |
1099 | |
1100 | QVariantMap defaultConfig() const override |
1101 | { |
1102 | QVariantMap mytemplate; |
1103 | |
1104 | QCA_logTextMessage(QStringLiteral("softstoreProvider::defaultConfig - entry/return" ), Logger::Debug); |
1105 | |
1106 | mytemplate[QStringLiteral("formtype" )] = QStringLiteral("http://affinix.com/qca/forms/qca-softstore#1.0" ); |
1107 | for (int i = 0; i < _CONFIG_MAX_ENTRIES; i++) { |
1108 | mytemplate[QString::asprintf(format: "entry_%02d_enabled" , i)] = false; |
1109 | mytemplate[QString::asprintf(format: "entry_%02d_name" , i)] = QLatin1String("" ); |
1110 | mytemplate[QString::asprintf(format: "entry_%02d_public_type" , i)] = QLatin1String("" ); |
1111 | mytemplate[QString::asprintf(format: "entry_%02d_private_type" , i)] = QLatin1String("" ); |
1112 | mytemplate[QString::asprintf(format: "entry_%02d_public" , i)] = QLatin1String("" ); |
1113 | mytemplate[QString::asprintf(format: "entry_%02d_private" , i)] = QLatin1String("" ); |
1114 | mytemplate[QString::asprintf(format: "entry_%02d_unlock_timeout" , i)] = -1; |
1115 | mytemplate[QString::asprintf(format: "entry_%02d_no_passphrase" , i)] = false; |
1116 | } |
1117 | |
1118 | return mytemplate; |
1119 | } |
1120 | |
1121 | void configChanged(const QVariantMap &config) override |
1122 | { |
1123 | QCA_logTextMessage(QStringLiteral("softstoreProvider::configChanged - entry" ), Logger::Debug); |
1124 | |
1125 | _config = config; |
1126 | |
1127 | if (s_keyStoreList != nullptr) { |
1128 | s_keyStoreList->_updateConfig(config: _config, maxEntries: _CONFIG_MAX_ENTRIES); |
1129 | } |
1130 | |
1131 | QCA_logTextMessage(QStringLiteral("softstoreProvider::configChanged - return" ), Logger::Debug); |
1132 | } |
1133 | }; |
1134 | |
1135 | const int softstoreProvider::_CONFIG_MAX_ENTRIES = 50; |
1136 | |
1137 | class softstorePlugin : public QObject, public QCAPlugin |
1138 | { |
1139 | Q_OBJECT |
1140 | Q_PLUGIN_METADATA(IID "com.affinix.qca.Plugin/1.0" ) |
1141 | Q_INTERFACES(QCAPlugin) |
1142 | |
1143 | public: |
1144 | Provider *createProvider() override |
1145 | { |
1146 | return new softstoreProvider; |
1147 | } |
1148 | }; |
1149 | |
1150 | #include "qca-softstore.moc" |
1151 | |