Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qtlskey_generic.cpp
Go to the documentation of this file.
1// Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
2// Copyright (C) 2021 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qtlskey_generic_p.h"
6#include "qasn1element_p.h"
7
8#include <QtNetwork/private/qsslkey_p.h>
9
10#include <QtNetwork/qpassworddigestor.h>
11
12#include <QtCore/QMessageAuthenticationCode>
13#include <QtCore/qcryptographichash.h>
14#include <QtCore/qrandom.h>
15
16#include <QtCore/qdatastream.h>
17#include <QtCore/qbytearray.h>
18#include <QtCore/qdebug.h>
19#include <QtCore/qmap.h>
20
21#include <cstring>
22
24
25// The code here is essentially what we had in qsslkey_qt.cpp before, with
26// minimal changes/restructure.
27
28namespace QTlsPrivate {
29
30// OIDs of named curves allowed in TLS as per RFCs 4492 and 7027,
31// see also https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
32namespace {
33
34const quint8 bits_table[256] = {
35 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,
36 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
37 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
38 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
39 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
40 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
41 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
42 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
43 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
44 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
45 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
46 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
47 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
48 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
49 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
50 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
51};
52
53using OidLengthMap = QMap<QByteArray, int>;
54
55OidLengthMap createOidMap()
56{
57 OidLengthMap oids;
58 oids.insert(oids.cend(), QByteArrayLiteral("1.2.840.10045.3.1.1"), 192); // secp192r1 a.k.a prime192v1
59 oids.insert(oids.cend(), QByteArrayLiteral("1.2.840.10045.3.1.7"), 256); // secp256r1 a.k.a prime256v1
60 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.1"), 193); // sect193r2
61 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.10"), 256); // secp256k1
62 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.16"), 283); // sect283k1
63 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.17"), 283); // sect283r1
64 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.26"), 233); // sect233k1
65 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.27"), 233); // sect233r1
66 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.3"), 239); // sect239k1
67 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.30"), 160); // secp160r2
68 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.31"), 192); // secp192k1
69 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.32"), 224); // secp224k1
70 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.33"), 224); // secp224r1
71 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.34"), 384); // secp384r1
72 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.35"), 521); // secp521r1
73 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.36"), 409); // sect409k1
74 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.37"), 409); // sect409r1
75 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.38"), 571); // sect571k1
76 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.39"), 571); // sect571r1
77 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.8"), 160); // secp160r1
78 oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.9"), 160); // secp160k1
79 oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.11"), 384); // brainpoolP384r1
80 oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.13"), 512); // brainpoolP512r1
81 oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.7"), 256); // brainpoolP256r1
82 return oids;
83}
84
85} // Unnamed namespace.
86
87Q_GLOBAL_STATIC_WITH_ARGS(OidLengthMap, oidLengthMap, (createOidMap()))
88
89namespace {
90
91// Maps OIDs to the encryption cipher they specify
92const QMap<QByteArray, QSslKeyPrivate::Cipher> oidCipherMap {
93 {DES_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::DesCbc},
94 {DES_EDE3_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::DesEde3Cbc},
95 // {PKCS5_MD2_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc}, // No MD2
96 {PKCS5_MD5_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc},
97 {PKCS5_SHA1_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc},
98 // {PKCS5_MD2_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc}, // No MD2
99 {PKCS5_MD5_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc},
100 {PKCS5_SHA1_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc},
101 {RC2_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Rc2Cbc}
102 // {RC5_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Rc5Cbc}, // No RC5
103 // {AES128_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes128}, // no AES
104 // {AES192_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes192},
105 // {AES256_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes256}
106};
107
108struct EncryptionData
109{
110 EncryptionData() = default;
111
112 EncryptionData(QSslKeyPrivate::Cipher cipher, QByteArray key, QByteArray iv)
113 : initialized(true), cipher(cipher), key(key), iv(iv)
114 {
115 }
116 bool initialized = false;
119 QByteArray iv;
120};
121
122EncryptionData readPbes2(const QList<QAsn1Element> &element, const QByteArray &passPhrase)
123{
124 // RFC 8018: https://tools.ietf.org/html/rfc8018#section-6.2
125 /*** Scheme: ***
126 * Sequence (scheme-specific info..)
127 * Sequence (key derivation info)
128 * Object Identifier (Key derivation algorithm (e.g. PBKDF2))
129 * Sequence (salt)
130 * CHOICE (this entry can be either of the types it contains)
131 * Octet string (actual salt)
132 * Object identifier (Anything using this is deferred to a later version of PKCS #5)
133 * Integer (iteration count)
134 * Sequence (encryption algorithm info)
135 * Object identifier (identifier for the algorithm)
136 * Algorithm dependent, is covered in the switch further down
137 */
138
139 static const QMap<QByteArray, QCryptographicHash::Algorithm> pbes2OidHashFunctionMap {
140 // PBES2/PBKDF2
148 };
149
150 // Values from their respective sections here: https://tools.ietf.org/html/rfc8018#appendix-B.2
151 static const QMap<QSslKeyPrivate::Cipher, int> cipherKeyLengthMap {
152 {QSslKeyPrivate::Cipher::DesCbc, 8},
153 {QSslKeyPrivate::Cipher::DesEde3Cbc, 24},
154 // @note: variable key-length (https://tools.ietf.org/html/rfc8018#appendix-B.2.3)
155 {QSslKeyPrivate::Cipher::Rc2Cbc, 4}
156 // @todo: AES(, rc5?)
157 };
158
159 const QList<QAsn1Element> keyDerivationContainer = element[0].toList();
160 if (keyDerivationContainer.size() != 2
161 || keyDerivationContainer[0].type() != QAsn1Element::ObjectIdentifierType
162 || keyDerivationContainer[1].type() != QAsn1Element::SequenceType) {
163 return {};
164 }
165
166 const QByteArray keyDerivationAlgorithm = keyDerivationContainer[0].toObjectId();
167 const auto keyDerivationParams = keyDerivationContainer[1].toList();
168
169 const auto encryptionAlgorithmContainer = element[1].toList();
170 if (encryptionAlgorithmContainer.size() != 2
171 || encryptionAlgorithmContainer[0].type() != QAsn1Element::ObjectIdentifierType) {
172 return {};
173 }
174
175 auto iterator = oidCipherMap.constFind(encryptionAlgorithmContainer[0].toObjectId());
176 if (iterator == oidCipherMap.cend()) {
177 qWarning()
178 << "QSslKey: Unsupported encryption cipher OID:" << encryptionAlgorithmContainer[0].toObjectId()
179 << "\nFile a bug report to Qt (include the line above).";
180 return {};
181 }
182
183 QSslKeyPrivate::Cipher cipher = *iterator;
185 QByteArray iv;
186 switch (cipher) {
187 case QSslKeyPrivate::Cipher::DesCbc:
188 case QSslKeyPrivate::Cipher::DesEde3Cbc:
189 // https://tools.ietf.org/html/rfc8018#appendix-B.2.1 (DES-CBC-PAD)
190 // https://tools.ietf.org/html/rfc8018#appendix-B.2.2 (DES-EDE3-CBC-PAD)
191 // @todo https://tools.ietf.org/html/rfc8018#appendix-B.2.5 (AES-CBC-PAD)
192 /*** Scheme: ***
193 * Octet string (IV)
194 */
195 if (encryptionAlgorithmContainer[1].type() != QAsn1Element::OctetStringType)
196 return {};
197
198 // @note: All AES identifiers should be able to use this branch!!
199 iv = encryptionAlgorithmContainer[1].value();
200
201 if (iv.size() != 8) // @note: AES needs 16 bytes
202 return {};
203 break;
204 case QSslKeyPrivate::Cipher::Rc2Cbc: {
205 // https://tools.ietf.org/html/rfc8018#appendix-B.2.3
206 /*** Scheme: ***
207 * Sequence (rc2 parameters)
208 * Integer (rc2 parameter version)
209 * Octet string (IV)
210 */
211 if (encryptionAlgorithmContainer[1].type() != QAsn1Element::SequenceType)
212 return {};
213 const auto rc2ParametersContainer = encryptionAlgorithmContainer[1].toList();
214 if ((rc2ParametersContainer.size() != 1 && rc2ParametersContainer.size() != 2)
215 || rc2ParametersContainer.back().type() != QAsn1Element::OctetStringType) {
216 return {};
217 }
218 iv = rc2ParametersContainer.back().value();
219 if (iv.size() != 8)
220 return {};
221 break;
222 } // @todo(?): case (RC5 , AES)
223 case QSslKeyPrivate::Cipher::Aes128Cbc:
224 case QSslKeyPrivate::Cipher::Aes192Cbc:
225 case QSslKeyPrivate::Cipher::Aes256Cbc:
226 Q_UNREACHABLE();
227 }
228
229 if (Q_LIKELY(keyDerivationAlgorithm == PKCS5_PBKDF2_ENCRYPTION_OID)) {
230 // Definition: https://tools.ietf.org/html/rfc8018#appendix-A.2
231 QByteArray salt;
232 if (keyDerivationParams[0].type() == QAsn1Element::OctetStringType) {
233 salt = keyDerivationParams[0].value();
234 } else if (keyDerivationParams[0].type() == QAsn1Element::ObjectIdentifierType) {
236 /* See paragraph from https://tools.ietf.org/html/rfc8018#appendix-A.2
237 which ends with: "such facilities are deferred to a future version of PKCS #5"
238 */
239 return {};
240 } else {
241 return {};
242 }
243
244 // Iterations needed to derive the key
245 int iterationCount = keyDerivationParams[1].toInteger();
246 // Optional integer
247 int keyLength = -1;
248 int vectorPos = 2;
249 if (keyDerivationParams.size() > vectorPos
250 && keyDerivationParams[vectorPos].type() == QAsn1Element::IntegerType) {
251 keyLength = keyDerivationParams[vectorPos].toInteger(nullptr);
252 ++vectorPos;
253 } else {
254 keyLength = cipherKeyLengthMap[cipher];
255 }
256
257 // Optional algorithm identifier (default: HMAC-SHA-1)
259 if (keyDerivationParams.size() > vectorPos
260 && keyDerivationParams[vectorPos].type() == QAsn1Element::SequenceType) {
261 const auto hashAlgorithmContainer = keyDerivationParams[vectorPos].toList();
262 hashAlgorithm = pbes2OidHashFunctionMap[hashAlgorithmContainer.front().toObjectId()];
263 Q_ASSERT(hashAlgorithmContainer[1].type() == QAsn1Element::NullType);
264 ++vectorPos;
265 }
266 Q_ASSERT(keyDerivationParams.size() == vectorPos);
267
268 key = QPasswordDigestor::deriveKeyPbkdf2(hashAlgorithm, passPhrase, salt, iterationCount, keyLength);
269 } else {
270 qWarning()
271 << "QSslKey: Unsupported key derivation algorithm OID:" << keyDerivationAlgorithm
272 << "\nFile a bugreport to Qt (include the line above).";
273 return {};
274 }
275 return {cipher, key, iv};
276}
277
278// Maps OIDs to the hash function it specifies
279const QMap<QByteArray, QCryptographicHash::Algorithm> pbes1OidHashFunctionMap {
280#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
281 // PKCS5
282 //{PKCS5_MD2_DES_CBC_OID, QCryptographicHash::Md2}, No MD2
283 //{PKCS5_MD2_RC2_CBC_OID, QCryptographicHash::Md2},
286#endif
289 // PKCS12 (unimplemented)
290 // {PKCS12_SHA1_RC4_128_OID, QCryptographicHash::Sha1}, // No RC4
291 // {PKCS12_SHA1_RC4_40_OID, QCryptographicHash::Sha1},
292 // @todo: lacking support. @note: there might be code to do this inside qsslsocket_mac...
293 // further note that more work may be required for the 3DES variations listed to be available.
294 // {PKCS12_SHA1_3KEY_3DES_CBC_OID, QCryptographicHash::Sha1},
295 // {PKCS12_SHA1_2KEY_3DES_CBC_OID, QCryptographicHash::Sha1},
296 // {PKCS12_SHA1_RC2_128_CBC_OID, QCryptographicHash::Sha1},
297 // {PKCS12_SHA1_RC2_40_CBC_OID, QCryptographicHash::Sha1}
298};
299
300EncryptionData readPbes1(const QList<QAsn1Element> &element, const QByteArray &encryptionScheme,
301 const QByteArray &passPhrase)
302{
303 // RFC 8018: https://tools.ietf.org/html/rfc8018#section-6.1
304 // Steps refer to this section: https://tools.ietf.org/html/rfc8018#section-6.1.2
305 /*** Scheme: ***
306 * Sequence (PBE Parameter)
307 * Octet string (salt)
308 * Integer (iteration counter)
309 */
310 // Step 1
311 if (element.size() != 2
312 || element[0].type() != QAsn1Element::ElementType::OctetStringType
313 || element[1].type() != QAsn1Element::ElementType::IntegerType) {
314 return {};
315 }
316 QByteArray salt = element[0].value();
317 if (salt.size() != 8)
318 return {};
319
320 int iterationCount = element[1].toInteger();
321 if (iterationCount < 0)
322 return {};
323
324 // Step 2
325 auto iterator = pbes1OidHashFunctionMap.constFind(encryptionScheme);
326 if (iterator == pbes1OidHashFunctionMap.cend()) {
327 // Qt was compiled with ONLY_SHA1 (or it's MD2)
328 return {};
329 }
330 QCryptographicHash::Algorithm hashAlgorithm = *iterator;
331 QByteArray key = QPasswordDigestor::deriveKeyPbkdf1(hashAlgorithm, passPhrase, salt, iterationCount, 16);
332 if (key.size() != 16)
333 return {};
334
335 // Step 3
336 QByteArray iv = key.right(8); // last 8 bytes are used as IV
337 key.truncate(8); // first 8 bytes are used for the key
338
339 QSslKeyPrivate::Cipher cipher = oidCipherMap[encryptionScheme];
340 // Steps 4-6 are done after returning
341 return {cipher, key, iv};
342}
343
344int curveBits(const QByteArray &oid)
345{
346 const int length = oidLengthMap->value(oid);
347 return length ? length : -1;
348}
349
350int numberOfBits(const QByteArray &modulus)
351{
352 int bits = modulus.size() * 8;
353 for (int i = 0; i < modulus.size(); ++i) {
354 quint8 b = modulus[i];
355 bits -= 8;
356 if (b != 0) {
357 bits += bits_table[b];
358 break;
359 }
360 }
361 return bits;
362}
363
364QByteArray deriveAesKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase,
365 const QByteArray &iv)
366{
367 // This is somewhat simplified and shortened version of what OpenSSL does.
368 // See, for example, EVP_BytesToKey for the "algorithm" itself and elsewhere
369 // in their code for what they pass as arguments to EVP_BytesToKey when
370 // deriving encryption keys (when reading/writing pems files with encrypted
371 // keys).
372
373 Q_ASSERT(iv.size() >= 8);
374
376
377 QByteArray data(passPhrase);
378 data.append(iv.data(), 8); // AKA PKCS5_SALT_LEN in OpenSSL.
379
380 hash.addData(data);
381
382 if (cipher == Cipher::Aes128Cbc)
383 return hash.result();
384
385 QByteArray key(hash.result());
386 hash.reset();
387 hash.addData(key);
388 hash.addData(data);
389
390 if (cipher == Cipher::Aes192Cbc)
391 return key.append(hash.resultView().first(8));
392
393 return key.append(hash.resultView());
394}
395
396QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase,
397 const QByteArray &iv)
398{
401 hash.addData(passPhrase);
402 hash.addData(iv);
403 switch (cipher) {
404 case Cipher::DesCbc:
405 key = hash.result().left(8);
406 break;
408 key = hash.result();
409 hash.reset();
410 hash.addData(key);
411 hash.addData(passPhrase);
412 hash.addData(iv);
413 key += hash.result().left(8);
414 break;
415 case Cipher::Rc2Cbc:
416 key = hash.result();
417 break;
421 return deriveAesKey(cipher, passPhrase, iv);
422 }
423 return key;
424}
425
426int extractPkcs8KeyLength(const QList<QAsn1Element> &items, TlsKey *that)
427{
428 Q_ASSERT(items.size() == 3);
429 Q_ASSERT(that);
430
431 int keyLength = -1;
432
433 auto getName = [](QSsl::KeyAlgorithm algorithm) {
434 switch (algorithm){
435 case QSsl::Rsa: return "RSA";
436 case QSsl::Dsa: return "DSA";
437 case QSsl::Dh: return "DH";
438 case QSsl::Ec: return "EC";
439 case QSsl::Opaque: return "Opaque";
440 }
441 Q_UNREACHABLE();
442 };
443
444 const auto pkcs8Info = items[1].toList();
445 if (pkcs8Info.size() != 2 || pkcs8Info[0].type() != QAsn1Element::ObjectIdentifierType)
446 return -1;
447 const QByteArray value = pkcs8Info[0].toObjectId();
448 if (value == RSA_ENCRYPTION_OID) {
449 if (Q_UNLIKELY(that->algorithm() != QSsl::Rsa)) {
450 // We could change the 'algorithm' of QSslKey here and continue loading, but
451 // this is not supported in the openssl back-end, so we'll fail here and give
452 // the user some feedback.
453 qWarning() << "QSslKey: Found RSA key when asked to use" << getName(that->algorithm())
454 << "\nLoading will fail.";
455 return -1;
456 }
457 // Luckily it contains the 'normal' RSA-key format inside, so we can just recurse
458 // and read the key's info.
459 that->decodeDer(that->type(), that->algorithm(), items[2].value(), {}, true);
460 // The real info has been filled out in the call above, so return as if it was invalid
461 // to avoid overwriting the data.
462 return -1;
463 } else if (value == EC_ENCRYPTION_OID) {
464 if (Q_UNLIKELY(that->algorithm() != QSsl::Ec)) {
465 // As above for RSA.
466 qWarning() << "QSslKey: Found EC key when asked to use" << getName(that->algorithm())
467 << "\nLoading will fail.";
468 return -1;
469 }
470 // I don't know where this is documented, but the elliptic-curve identifier has been
471 // moved into the "pkcs#8 wrapper", which is what we're interested in.
472 if (pkcs8Info[1].type() != QAsn1Element::ObjectIdentifierType)
473 return -1;
474 keyLength = curveBits(pkcs8Info[1].toObjectId());
475 } else if (value == DSA_ENCRYPTION_OID) {
476 if (Q_UNLIKELY(that->algorithm() != QSsl::Dsa)) {
477 // As above for RSA.
478 qWarning() << "QSslKey: Found DSA when asked to use" << getName(that->algorithm())
479 << "\nLoading will fail.";
480 return -1;
481 }
482 // DSA's structure is documented here:
483 // https://www.cryptsoft.com/pkcs11doc/STANDARD/v201-95.pdf in section 11.9.
484 if (pkcs8Info[1].type() != QAsn1Element::SequenceType)
485 return -1;
486 const auto dsaInfo = pkcs8Info[1].toList();
487 if (dsaInfo.size() != 3 || dsaInfo[0].type() != QAsn1Element::IntegerType)
488 return -1;
489 keyLength = numberOfBits(dsaInfo[0].value());
490 } else if (value == DH_ENCRYPTION_OID) {
491 if (Q_UNLIKELY(that->algorithm() != QSsl::Dh)) {
492 // As above for RSA.
493 qWarning() << "QSslKey: Found DH when asked to use" << getName(that->algorithm())
494 << "\nLoading will fail.";
495 return -1;
496 }
497 // DH's structure is documented here:
498 // https://www.cryptsoft.com/pkcs11doc/STANDARD/v201-95.pdf in section 11.9.
499 if (pkcs8Info[1].type() != QAsn1Element::SequenceType)
500 return -1;
501 const auto dhInfo = pkcs8Info[1].toList();
502 if (dhInfo.size() < 2 || dhInfo.size() > 3 || dhInfo[0].type() != QAsn1Element::IntegerType)
503 return -1;
504 keyLength = numberOfBits(dhInfo[0].value());
505 } else {
506 // in case of unexpected formats:
507 qWarning() << "QSslKey: Unsupported PKCS#8 key algorithm:" << value
508 << "\nFile a bugreport to Qt (include the line above).";
509 return -1;
510 }
511
512 return keyLength;
513}
514
515} // Unnamed namespace
516
518 const QByteArray &passPhrase, bool deepClear)
519{
520 keyType = type;
522
523 clear(deepClear);
524
525 if (der.isEmpty())
526 return;
527 // decryptPkcs8 decrypts if necessary or returns 'der' unaltered
528 QByteArray decryptedDer = decryptPkcs8(der, passPhrase);
529
530 QAsn1Element elem;
531 if (!elem.read(decryptedDer) || elem.type() != QAsn1Element::SequenceType)
532 return;
533
534 if (type == QSsl::PublicKey) {
535 // key info
536 QDataStream keyStream(elem.value());
537 if (!elem.read(keyStream) || elem.type() != QAsn1Element::SequenceType)
538 return;
539 const auto infoItems = elem.toList();
540 if (infoItems.size() < 2 || infoItems[0].type() != QAsn1Element::ObjectIdentifierType)
541 return;
542 if (algorithm == QSsl::Rsa) {
543 if (infoItems[0].toObjectId() != RSA_ENCRYPTION_OID)
544 return;
545 // key data
546 if (!elem.read(keyStream) || elem.type() != QAsn1Element::BitStringType || elem.value().isEmpty())
547 return;
548 if (!elem.read(elem.value().mid(1)) || elem.type() != QAsn1Element::SequenceType)
549 return;
550 if (!elem.read(elem.value()) || elem.type() != QAsn1Element::IntegerType)
551 return;
552 keyLength = numberOfBits(elem.value());
553 } else if (algorithm == QSsl::Dsa) {
554 if (infoItems[0].toObjectId() != DSA_ENCRYPTION_OID)
555 return;
556 if (infoItems[1].type() != QAsn1Element::SequenceType)
557 return;
558 // key params
559 const auto params = infoItems[1].toList();
560 if (params.isEmpty() || params[0].type() != QAsn1Element::IntegerType)
561 return;
562 keyLength = numberOfBits(params[0].value());
563 } else if (algorithm == QSsl::Dh) {
564 if (infoItems[0].toObjectId() != DH_ENCRYPTION_OID)
565 return;
566 if (infoItems[1].type() != QAsn1Element::SequenceType)
567 return;
568 // key params
569 const auto params = infoItems[1].toList();
570 if (params.isEmpty() || params[0].type() != QAsn1Element::IntegerType)
571 return;
572 keyLength = numberOfBits(params[0].value());
573 } else if (algorithm == QSsl::Ec) {
574 if (infoItems[0].toObjectId() != EC_ENCRYPTION_OID)
575 return;
576 if (infoItems[1].type() != QAsn1Element::ObjectIdentifierType)
577 return;
578 keyLength = curveBits(infoItems[1].toObjectId());
579 }
580
581 } else {
582 const auto items = elem.toList();
583 if (items.isEmpty())
584 return;
585
586 // version
588 return;
589 const QByteArray versionHex = items[0].value().toHex();
590
591 if (items.size() == 3 && items[1].type() == QAsn1Element::SequenceType
592 && items[2].type() == QAsn1Element::OctetStringType) {
593 if (versionHex != "00" && versionHex != "01")
594 return;
595 int pkcs8KeyLength = extractPkcs8KeyLength(items, this);
596 if (pkcs8KeyLength == -1)
597 return;
598 pkcs8 = true;
599 keyLength = pkcs8KeyLength;
600 } else if (algorithm == QSsl::Rsa) {
601 if (versionHex != "00")
602 return;
603 if (items.size() != 9 || items[1].type() != QAsn1Element::IntegerType)
604 return;
605 keyLength = numberOfBits(items[1].value());
606 } else if (algorithm == QSsl::Dsa) {
607 if (versionHex != "00")
608 return;
609 if (items.size() != 6 || items[1].type() != QAsn1Element::IntegerType)
610 return;
611 keyLength = numberOfBits(items[1].value());
612 } else if (algorithm == QSsl::Dh) {
613 if (versionHex != "00")
614 return;
615 if (items.size() < 5 || items.size() > 6 || items[1].type() != QAsn1Element::IntegerType)
616 return;
617 keyLength = numberOfBits(items[1].value());
618 } else if (algorithm == QSsl::Ec) {
619 if (versionHex != "01")
620 return;
621 if (items.size() != 4
623 || items[2].type() != QAsn1Element::Context0Type
624 || items[3].type() != QAsn1Element::Context1Type)
625 return;
626 QAsn1Element oidElem;
627 if (!oidElem.read(items[2].value())
628 || oidElem.type() != QAsn1Element::ObjectIdentifierType)
629 return;
630 keyLength = curveBits(oidElem.toObjectId());
631 }
632 }
633
634 derData = decryptedDer;
635 keyIsNull = false;
636}
637
639 const QByteArray &passPhrase, bool deepClear)
640{
641 keyType = type;
643
644 QMap<QByteArray, QByteArray> headers;
645 QByteArray data = derFromPem(pem, &headers);
646
647 if (headers.value("Proc-Type") == "4,ENCRYPTED") {
648 const QList<QByteArray> dekInfo = headers.value("DEK-Info").split(',');
649 if (dekInfo.size() != 2) {
650 clear(deepClear);
651 return;
652 }
653
655 if (dekInfo.first() == "DES-CBC") {
656 cipher = Cipher::DesCbc;
657 } else if (dekInfo.first() == "DES-EDE3-CBC") {
658 cipher = Cipher::DesEde3Cbc;
659 } else if (dekInfo.first() == "RC2-CBC") {
660 cipher = Cipher::Rc2Cbc;
661 } else if (dekInfo.first() == "AES-128-CBC") {
662 cipher = Cipher::Aes128Cbc;
663 } else if (dekInfo.first() == "AES-192-CBC") {
664 cipher = Cipher::Aes192Cbc;
665 } else if (dekInfo.first() == "AES-256-CBC") {
666 cipher = Cipher::Aes256Cbc;
667 } else {
668 clear(deepClear);
669 return;
670 }
671
672 const QByteArray iv = QByteArray::fromHex(dekInfo.last());
673 const QByteArray key = deriveKey(cipher, passPhrase, iv);
674 data = decrypt(cipher, data, key, iv);
675 }
676
677 decodeDer(keyType, keyAlgorithm, data, passPhrase, deepClear);
678}
679
681{
683 QMap<QByteArray, QByteArray> headers;
684
685 if (type() == QSsl::PrivateKey && !passPhrase.isEmpty()) {
686 // ### use a cryptographically secure random number generator
687 quint64 random = QRandomGenerator::system()->generate64();
688 QByteArray iv = QByteArray::fromRawData(reinterpret_cast<const char *>(&random), sizeof(random));
689
690 auto cipher = Cipher::DesEde3Cbc;
691 const QByteArray key = deriveKey(cipher, passPhrase, iv);
692 data = encrypt(cipher, derData, key, iv);
693
694 headers.insert("Proc-Type", "4,ENCRYPTED");
695 headers.insert("DEK-Info", "DES-EDE3-CBC," + iv.toHex());
696 } else {
697 data = derData;
698 }
699
700 return pemFromDer(data, headers);
701}
702
703QByteArray TlsKeyGeneric::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const
704{
705 if (derData.size())
706 return derData;
707
709 QByteArray footer = pemFooter();
710
711 QByteArray der(pem);
712
713 int headerIndex = der.indexOf(header);
714 int footerIndex = der.indexOf(footer, headerIndex + header.length());
715 if (type() != QSsl::PublicKey) {
716 if (headerIndex == -1 || footerIndex == -1) {
717 header = pkcs8Header(true);
718 footer = pkcs8Footer(true);
719 headerIndex = der.indexOf(header);
720 footerIndex = der.indexOf(footer, headerIndex + header.length());
721 }
722 if (headerIndex == -1 || footerIndex == -1) {
723 header = pkcs8Header(false);
724 footer = pkcs8Footer(false);
725 headerIndex = der.indexOf(header);
726 footerIndex = der.indexOf(footer, headerIndex + header.length());
727 }
728 }
729 if (headerIndex == -1 || footerIndex == -1)
730 return QByteArray();
731
732 der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size()));
733
734 if (der.contains("Proc-Type:")) {
735 // taken from QHttpNetworkReplyPrivate::parseHeader
736 int i = 0;
737 while (i < der.length()) {
738 int j = der.indexOf(':', i); // field-name
739 if (j == -1)
740 break;
741 const QByteArray field = der.mid(i, j - i).trimmed();
742 j++;
743 // any number of LWS is allowed before and after the value
745 do {
746 i = der.indexOf('\n', j);
747 if (i == -1)
748 break;
749 if (!value.isEmpty())
750 value += ' ';
751 // check if we have CRLF or only LF
752 bool hasCR = (i && der[i-1] == '\r');
753 int length = i -(hasCR ? 1: 0) - j;
754 value += der.mid(j, length).trimmed();
755 j = ++i;
756 } while (i < der.length() && (der.at(i) == ' ' || der.at(i) == '\t'));
757 if (i == -1)
758 break; // something is wrong
759
760 headers->insert(field, value);
761 }
762 der = der.mid(i);
763 }
764
765 return QByteArray::fromBase64(der); // ignores newlines
766}
767
769{
770 opaque = handle;
771 keyType = expectedType;
772}
773
774void TlsKeyGeneric::clear(bool deep)
775{
776 keyIsNull = true;
777 if (deep)
778 std::memset(derData.data(), 0, derData.size());
779 derData.clear();
780 keyLength = -1;
781}
782
783QByteArray TlsKeyGeneric::decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase)
784{
785 // RFC 5958: https://tools.ietf.org/html/rfc5958
786 /*** Scheme: ***
787 * Sequence
788 * Sequence
789 * Object Identifier (encryption scheme (currently PBES2, PBES1, @todo PKCS12))
790 * Sequence (scheme parameters)
791 * Octet String (the encrypted data)
792 */
793 QAsn1Element elem;
794 if (!elem.read(encrypted) || elem.type() != QAsn1Element::SequenceType)
795 return encrypted;
796
797 const auto items = elem.toList();
798 if (items.size() != 2
799 || items[0].type() != QAsn1Element::SequenceType
800 || items[1].type() != QAsn1Element::OctetStringType) {
801 return encrypted;
802 }
803
804 const auto encryptionSchemeContainer = items[0].toList();
805
806 if (encryptionSchemeContainer.size() != 2
807 || encryptionSchemeContainer[0].type() != QAsn1Element::ObjectIdentifierType
808 || encryptionSchemeContainer[1].type() != QAsn1Element::SequenceType) {
809 return encrypted;
810 }
811
812 const QByteArray encryptionScheme = encryptionSchemeContainer[0].toObjectId();
813 const auto schemeParameterContainer = encryptionSchemeContainer[1].toList();
814
815 if (schemeParameterContainer.size() != 2
816 && schemeParameterContainer[0].type() != QAsn1Element::SequenceType
817 && schemeParameterContainer[1].type() != QAsn1Element::SequenceType) {
818 return encrypted;
819 }
820
821 EncryptionData data;
822 if (encryptionScheme == PKCS5_PBES2_ENCRYPTION_OID) {
823 data = readPbes2(schemeParameterContainer, passPhrase);
824 } else if (pbes1OidHashFunctionMap.contains(encryptionScheme)) {
825 data = readPbes1(schemeParameterContainer, encryptionScheme, passPhrase);
826 } else if (encryptionScheme.startsWith(PKCS12_OID)) {
827 Q_UNIMPLEMENTED(); // this isn't some 'unknown', I know these aren't implemented
828 return encrypted;
829 } else {
830 qWarning()
831 << "QSslKey: Unsupported encryption scheme OID:" << encryptionScheme
832 << "\nFile a bugreport to Qt (include the line above).";
833 return encrypted;
834 }
835
836 if (!data.initialized) {
837 // something went wrong, return
838 return encrypted;
839 }
840
841 QByteArray decryptedKey = decrypt(data.cipher, items[1].value(), data.key, data.iv);
842 // The data is still wrapped in a octet string, so let's unwrap it
843 QAsn1Element decryptedKeyElement(QAsn1Element::ElementType::OctetStringType, decryptedKey);
844 return decryptedKeyElement.value();
845}
846
847} // namespace QTlsPrivate
848
\inmodule QtCore
Definition qbytearray.h:57
QByteArray trimmed() const &
Definition qbytearray.h:262
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
static QByteArray fromHex(const QByteArray &hexEncoded)
Returns a decoded copy of the hex encoded array hexEncoded.
QByteArray right(qsizetype n) const &
Definition qbytearray.h:181
void truncate(qsizetype pos)
Truncates the byte array at index position pos.
static QByteArray fromBase64(const QByteArray &base64, Base64Options options=Base64Encoding)
void clear()
Clears the contents of the byte array and makes it null.
char back() const
Definition qbytearray.h:136
QByteArray mid(qsizetype index, qsizetype len=-1) const &
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
Definition qbytearray.h:409
\inmodule QtCore\reentrant
Definition qdatastream.h:46
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
QList< T > toList() const noexcept
Definition qlist.h:723
T value(qsizetype i) const
Definition qlist.h:664
static Q_DECL_CONST_FUNCTION QRandomGenerator * system()
\threadsafe
Definition qrandom.h:270
KeyType type() const override
QByteArray pemFromDer(const QByteArray &der, const QMap< QByteArray, QByteArray > &headers) const override
KeyAlgorithm algorithm() const override
static QByteArray pkcs8Footer(bool encrypted)
static QByteArray pkcs8Header(bool encrypted)
QByteArray derFromPem(const QByteArray &pem, QMap< QByteArray, QByteArray > *headers) const override
QByteArray toPem(const QByteArray &passPhrase) const override
void decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem, const QByteArray &passPhrase, bool deepClear) override
void fromHandle(Qt::HANDLE opaque, KeyType expectedType) override
Qt::HANDLE handle() const override
void clear(bool deep) override
void decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der, const QByteArray &passPhrase, bool deepClear) override
TlsKey is an abstract class, that allows a TLS plugin to provide an underlying implementation for the...
virtual QByteArray encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv) const =0
QByteArray pemHeader() const
QByteArray pemFooter() const
virtual QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &passPhrase, const QByteArray &iv) const =0
QHash< int, QWidget * > hash
[35multi]
b clear()
Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf1(QCryptographicHash::Algorithm algorithm, const QByteArray &data, const QByteArray &salt, int iterations, quint64 dkLen)
Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf2(QCryptographicHash::Algorithm algorithm, const QByteArray &data, const QByteArray &salt, int iterations, quint64 dkLen)
KeyType
Describes the two types of keys QSslKey supports.
Definition qssl.h:22
@ PublicKey
Definition qssl.h:24
@ PrivateKey
Definition qssl.h:23
KeyAlgorithm
Describes the different key algorithms supported by QSslKey.
Definition qssl.h:34
@ Rsa
Definition qssl.h:36
@ Ec
Definition qssl.h:38
@ Opaque
Definition qssl.h:35
@ Dsa
Definition qssl.h:37
@ Dh
Definition qssl.h:39
Combined button and popup list for selecting options.
Namespace containing onternal types that TLS backends implement.
void * HANDLE
static OidNameMap createOidMap()
#define HMAC_WITH_SHA224
#define HMAC_WITH_SHA512
#define PKCS5_MD5_DES_CBC_OID
#define EC_ENCRYPTION_OID
#define PKCS12_OID
#define RC2_CBC_ENCRYPTION_OID
#define PKCS5_PBES2_ENCRYPTION_OID
#define HMAC_WITH_SHA512_256
#define DSA_ENCRYPTION_OID
#define RSA_ENCRYPTION_OID
#define PKCS5_PBKDF2_ENCRYPTION_OID
#define DH_ENCRYPTION_OID
#define PKCS5_SHA1_RC2_CBC_OID
#define HMAC_WITH_SHA256
#define DES_CBC_ENCRYPTION_OID
#define DES_EDE3_CBC_ENCRYPTION_OID
#define HMAC_WITH_SHA384
#define PKCS5_MD5_RC2_CBC_OID
#define HMAC_WITH_SHA512_224
#define HMAC_WITH_SHA1
#define PKCS5_SHA1_DES_CBC_OID
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
#define Q_UNLIKELY(x)
#define Q_LIKELY(x)
static QString header(const QString &name)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS)
#define qWarning
Definition qlogging.h:166
GLboolean GLboolean GLboolean b
GLuint64 GLenum void * handle
GLuint64 key
GLenum GLuint GLenum GLsizei length
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum type
void ** params
GLenum GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const void * bits
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_UNIMPLEMENTED()
unsigned long long quint64
Definition qtypes.h:61
unsigned char quint8
Definition qtypes.h:46
QList< QTreeWidgetItem * > items