1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qsslsocket_openssl_symbols_p.h" |
5 | #include "qtlsbackend_openssl_p.h" |
6 | #include "qtlskey_openssl_p.h" |
7 | |
8 | #include <QtNetwork/private/qsslkey_p.h> |
9 | |
10 | #include <QtNetwork/qsslsocket.h> |
11 | |
12 | #include <QtCore/qscopeguard.h> |
13 | |
14 | QT_BEGIN_NAMESPACE |
15 | |
16 | namespace QTlsPrivate { |
17 | |
18 | void TlsKeyOpenSSL::decodeDer(QSsl::KeyType type, QSsl::KeyAlgorithm algorithm, const QByteArray &der, |
19 | const QByteArray &passPhrase, bool deepClear) |
20 | { |
21 | if (der.isEmpty()) |
22 | return; |
23 | |
24 | keyType = type; |
25 | keyAlgorithm = algorithm; |
26 | |
27 | QMap<QByteArray, QByteArray> ; |
28 | const auto pem = pemFromDer(der, headers); |
29 | |
30 | decodePem(type, algorithm, pem, passPhrase, deepClear); |
31 | } |
32 | |
33 | void TlsKeyOpenSSL::decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem, |
34 | const QByteArray &passPhrase, bool deepClear) |
35 | { |
36 | if (pem.isEmpty()) |
37 | return; |
38 | |
39 | keyType = type; |
40 | keyAlgorithm = algorithm; |
41 | |
42 | clear(deep: deepClear); |
43 | |
44 | BIO *bio = q_BIO_new_mem_buf(a: const_cast<char *>(pem.data()), b: pem.size()); |
45 | if (!bio) |
46 | return; |
47 | |
48 | const auto bioRaii = qScopeGuard(f: [bio]{q_BIO_free(a: bio);}); |
49 | |
50 | void *phrase = const_cast<char *>(passPhrase.data()); |
51 | |
52 | #ifdef OPENSSL_NO_DEPRECATED_3_0 |
53 | if (type == QSsl::PublicKey) |
54 | genericKey = q_PEM_read_bio_PUBKEY(bio, nullptr, nullptr, phrase); |
55 | else |
56 | genericKey = q_PEM_read_bio_PrivateKey(bio, nullptr, nullptr, phrase); |
57 | keyIsNull = !genericKey; |
58 | if (keyIsNull) |
59 | QTlsBackendOpenSSL::logAndClearErrorQueue(); |
60 | #else |
61 | |
62 | if (algorithm == QSsl::Rsa) { |
63 | RSA *result = (type == QSsl::PublicKey) |
64 | ? q_PEM_read_bio_RSA_PUBKEY(a: bio, b: &rsa, c: nullptr, d: phrase) |
65 | : q_PEM_read_bio_RSAPrivateKey(a: bio, b: &rsa, c: nullptr, d: phrase); |
66 | if (rsa && rsa == result) |
67 | keyIsNull = false; |
68 | } else if (algorithm == QSsl::Dsa) { |
69 | DSA *result = (type == QSsl::PublicKey) |
70 | ? q_PEM_read_bio_DSA_PUBKEY(a: bio, b: &dsa, c: nullptr, d: phrase) |
71 | : q_PEM_read_bio_DSAPrivateKey(a: bio, b: &dsa, c: nullptr, d: phrase); |
72 | if (dsa && dsa == result) |
73 | keyIsNull = false; |
74 | } else if (algorithm == QSsl::Dh) { |
75 | EVP_PKEY *result = (type == QSsl::PublicKey) |
76 | ? q_PEM_read_bio_PUBKEY(a: bio, b: nullptr, c: nullptr, d: phrase) |
77 | : q_PEM_read_bio_PrivateKey(a: bio, b: nullptr, c: nullptr, d: phrase); |
78 | if (result) |
79 | dh = q_EVP_PKEY_get1_DH(a: result); |
80 | if (dh) |
81 | keyIsNull = false; |
82 | q_EVP_PKEY_free(a: result); |
83 | #ifndef OPENSSL_NO_EC |
84 | } else if (algorithm == QSsl::Ec) { |
85 | EC_KEY *result = (type == QSsl::PublicKey) |
86 | ? q_PEM_read_bio_EC_PUBKEY(a: bio, b: &ec, c: nullptr, d: phrase) |
87 | : q_PEM_read_bio_ECPrivateKey(a: bio, b: &ec, c: nullptr, d: phrase); |
88 | if (ec && ec == result) |
89 | keyIsNull = false; |
90 | #endif // OPENSSL_NO_EC |
91 | } |
92 | |
93 | #endif // OPENSSL_NO_DEPRECATED_3_0 |
94 | } |
95 | |
96 | QByteArray TlsKeyOpenSSL::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *) const |
97 | { |
98 | QByteArray = pemHeader(); |
99 | QByteArray = pemFooter(); |
100 | |
101 | QByteArray der(pem); |
102 | |
103 | int = der.indexOf(bv: header); |
104 | int = der.indexOf(bv: footer, from: headerIndex + header.size()); |
105 | if (type() != QSsl::PublicKey) { |
106 | if (headerIndex == -1 || footerIndex == -1) { |
107 | header = pkcs8Header(encrypted: true); |
108 | footer = pkcs8Footer(encrypted: true); |
109 | headerIndex = der.indexOf(bv: header); |
110 | footerIndex = der.indexOf(bv: footer, from: headerIndex + header.size()); |
111 | } |
112 | if (headerIndex == -1 || footerIndex == -1) { |
113 | header = pkcs8Header(encrypted: false); |
114 | footer = pkcs8Footer(encrypted: false); |
115 | headerIndex = der.indexOf(bv: header); |
116 | footerIndex = der.indexOf(bv: footer, from: headerIndex + header.size()); |
117 | } |
118 | } |
119 | if (headerIndex == -1 || footerIndex == -1) |
120 | return QByteArray(); |
121 | |
122 | der = der.mid(index: headerIndex + header.size(), len: footerIndex - (headerIndex + header.size())); |
123 | |
124 | if (der.contains(bv: "Proc-Type:" )) { |
125 | // taken from QHttpNetworkReplyPrivate::parseHeader |
126 | int i = 0; |
127 | while (i < der.size()) { |
128 | int j = der.indexOf(c: ':', from: i); // field-name |
129 | if (j == -1) |
130 | break; |
131 | const QByteArray field = der.mid(index: i, len: j - i).trimmed(); |
132 | j++; |
133 | // any number of LWS is allowed before and after the value |
134 | QByteArray value; |
135 | do { |
136 | i = der.indexOf(c: '\n', from: j); |
137 | if (i == -1) |
138 | break; |
139 | if (!value.isEmpty()) |
140 | value += ' '; |
141 | // check if we have CRLF or only LF |
142 | bool hasCR = (i && der[i-1] == '\r'); |
143 | int length = i -(hasCR ? 1: 0) - j; |
144 | value += der.mid(index: j, len: length).trimmed(); |
145 | j = ++i; |
146 | } while (i < der.size() && (der.at(i) == ' ' || der.at(i) == '\t')); |
147 | if (i == -1) |
148 | break; // something is wrong |
149 | |
150 | headers->insert(key: field, value); |
151 | } |
152 | der = der.mid(index: i); |
153 | } |
154 | |
155 | return QByteArray::fromBase64(base64: der); // ignores newlines |
156 | } |
157 | |
158 | void TlsKeyOpenSSL::clear(bool deep) |
159 | { |
160 | keyIsNull = true; |
161 | |
162 | #ifndef OPENSSL_NO_DEPRECATED_3_0 |
163 | if (algorithm() == QSsl::Rsa && rsa) { |
164 | if (deep) |
165 | q_RSA_free(a: rsa); |
166 | rsa = nullptr; |
167 | } |
168 | if (algorithm() == QSsl::Dsa && dsa) { |
169 | if (deep) |
170 | q_DSA_free(a: dsa); |
171 | dsa = nullptr; |
172 | } |
173 | if (algorithm() == QSsl::Dh && dh) { |
174 | if (deep) |
175 | q_DH_free(dh: dh); |
176 | dh = nullptr; |
177 | } |
178 | #ifndef OPENSSL_NO_EC |
179 | if (algorithm() == QSsl::Ec && ec) { |
180 | if (deep) |
181 | q_EC_KEY_free(ecdh: ec); |
182 | ec = nullptr; |
183 | } |
184 | #endif |
185 | #endif // OPENSSL_NO_DEPRECATED_3_0 |
186 | |
187 | if (algorithm() == QSsl::Opaque && opaque) { |
188 | if (deep) |
189 | q_EVP_PKEY_free(a: opaque); |
190 | opaque = nullptr; |
191 | } |
192 | |
193 | if (genericKey) { |
194 | // None of the above cleared it. genericKey is either |
195 | // initialised by PEM read operation, or from X509, and |
196 | // we are the owners and not sharing. So we free it. |
197 | q_EVP_PKEY_free(a: genericKey); |
198 | genericKey = nullptr; |
199 | } |
200 | } |
201 | |
202 | Qt::HANDLE TlsKeyOpenSSL::handle() const |
203 | { |
204 | if (keyAlgorithm == QSsl::Opaque) |
205 | return Qt::HANDLE(opaque); |
206 | |
207 | #ifndef OPENSSL_NO_DEPRECATED_3_0 |
208 | switch (keyAlgorithm) { |
209 | case QSsl::Rsa: |
210 | return Qt::HANDLE(rsa); |
211 | case QSsl::Dsa: |
212 | return Qt::HANDLE(dsa); |
213 | case QSsl::Dh: |
214 | return Qt::HANDLE(dh); |
215 | #ifndef OPENSSL_NO_EC |
216 | case QSsl::Ec: |
217 | return Qt::HANDLE(ec); |
218 | #endif |
219 | default: |
220 | return Qt::HANDLE(nullptr); |
221 | } |
222 | #else |
223 | qCWarning(lcTlsBackend, |
224 | "This version of OpenSSL disabled direct manipulation with RSA/DSA/DH/EC_KEY structures, consider using QSsl::Opaque instead." ); |
225 | return Qt::HANDLE(nullptr); |
226 | #endif |
227 | } |
228 | |
229 | int TlsKeyOpenSSL::length() const |
230 | { |
231 | if (isNull() || algorithm() == QSsl::Opaque) |
232 | return -1; |
233 | |
234 | #ifndef OPENSSL_NO_DEPRECATED_3_0 |
235 | switch (algorithm()) { |
236 | case QSsl::Rsa: |
237 | return q_RSA_bits(a: rsa); |
238 | case QSsl::Dsa: |
239 | return q_DSA_bits(a: dsa); |
240 | case QSsl::Dh: |
241 | return q_DH_bits(dh: dh); |
242 | #ifndef OPENSSL_NO_EC |
243 | case QSsl::Ec: |
244 | return q_EC_GROUP_get_degree(g: q_EC_KEY_get0_group(k: ec)); |
245 | #endif |
246 | default: |
247 | return -1; |
248 | } |
249 | #else // OPENSSL_NO_DEPRECATED_3_0 |
250 | Q_ASSERT(genericKey); |
251 | return q_EVP_PKEY_get_bits(genericKey); |
252 | #endif // OPENSSL_NO_DEPRECATED_3_0 |
253 | } |
254 | |
255 | QByteArray TlsKeyOpenSSL::toPem(const QByteArray &passPhrase) const |
256 | { |
257 | if (!QSslSocket::supportsSsl() || isNull() || algorithm() == QSsl::Opaque) |
258 | return {}; |
259 | |
260 | const EVP_CIPHER *cipher = nullptr; |
261 | if (type() == QSsl::PrivateKey && !passPhrase.isEmpty()) { |
262 | #ifndef OPENSSL_NO_DES |
263 | cipher = q_EVP_des_ede3_cbc(); |
264 | #else |
265 | return {}; |
266 | #endif |
267 | } |
268 | |
269 | BIO *bio = q_BIO_new(a: q_BIO_s_mem()); |
270 | if (!bio) |
271 | return {}; |
272 | |
273 | const auto bioRaii = qScopeGuard(f: [bio]{q_BIO_free(a: bio);}); |
274 | |
275 | #ifndef OPENSSL_NO_DEPRECATED_3_0 |
276 | |
277 | #define write_pubkey(alg, key) q_PEM_write_bio_##alg##_PUBKEY(bio, key) |
278 | #define write_privatekey(alg, key) \ |
279 | q_PEM_write_bio_##alg##PrivateKey(bio, key, cipher, (uchar *)passPhrase.data(), \ |
280 | passPhrase.size(), nullptr, nullptr) |
281 | |
282 | #else |
283 | |
284 | #define write_pubkey(alg, key) q_PEM_write_bio_PUBKEY(bio, genericKey) |
285 | #define write_privatekey(alg, key) \ |
286 | q_PEM_write_bio_PrivateKey_traditional(bio, genericKey, cipher, (uchar *)passPhrase.data(), passPhrase.size(), nullptr, nullptr) |
287 | |
288 | #endif // OPENSSL_NO_DEPRECATED_3_0 |
289 | |
290 | bool fail = false; |
291 | if (algorithm() == QSsl::Rsa) { |
292 | if (type() == QSsl::PublicKey) { |
293 | if (!write_pubkey(RSA, rsa)) |
294 | fail = true; |
295 | } else if (!write_privatekey(RSA, rsa)) { |
296 | fail = true; |
297 | } |
298 | } else if (algorithm() == QSsl::Dsa) { |
299 | if (type() == QSsl::PublicKey) { |
300 | if (!write_pubkey(DSA, dsa)) |
301 | fail = true; |
302 | } else if (!write_privatekey(DSA, dsa)) { |
303 | fail = true; |
304 | } |
305 | } else if (algorithm() == QSsl::Dh) { |
306 | #ifdef OPENSSL_NO_DEPRECATED_3_0 |
307 | EVP_PKEY *result = genericKey; |
308 | #else |
309 | EVP_PKEY *result = q_EVP_PKEY_new(); |
310 | const auto guard = qScopeGuard(f: [result]{if (result) q_EVP_PKEY_free(a: result);}); |
311 | if (!result || !q_EVP_PKEY_set1_DH(a: result, b: dh)) { |
312 | fail = true; |
313 | } else |
314 | #endif |
315 | if (type() == QSsl::PublicKey) { |
316 | if (!q_PEM_write_bio_PUBKEY(a: bio, b: result)) |
317 | fail = true; |
318 | } else if (!q_PEM_write_bio_PrivateKey(a: bio, b: result, c: cipher, d: (uchar *)passPhrase.data(), |
319 | e: passPhrase.size(), f: nullptr, g: nullptr)) { |
320 | fail = true; |
321 | } |
322 | #ifndef OPENSSL_NO_EC |
323 | } else if (algorithm() == QSsl::Ec) { |
324 | if (type() == QSsl::PublicKey) { |
325 | if (!write_pubkey(EC, ec)) |
326 | fail = true; |
327 | } else { |
328 | if (!write_privatekey(EC, ec)) |
329 | fail = true; |
330 | } |
331 | #endif |
332 | } else { |
333 | fail = true; |
334 | } |
335 | |
336 | QByteArray pem; |
337 | if (!fail) { |
338 | char *data = nullptr; |
339 | const long size = q_BIO_get_mem_data(bio, &data); |
340 | if (size > 0 && data) |
341 | pem = QByteArray(data, size); |
342 | } else { |
343 | QTlsBackendOpenSSL::logAndClearErrorQueue(); |
344 | } |
345 | |
346 | return pem; |
347 | } |
348 | |
349 | void TlsKeyOpenSSL::fromHandle(Qt::HANDLE handle, QSsl::KeyType expectedType) |
350 | { |
351 | EVP_PKEY *evpKey = reinterpret_cast<EVP_PKEY *>(handle); |
352 | if (!evpKey || !fromEVP_PKEY(pkey: evpKey)) { |
353 | opaque = evpKey; |
354 | keyAlgorithm = QSsl::Opaque; |
355 | } else { |
356 | q_EVP_PKEY_free(a: evpKey); |
357 | } |
358 | |
359 | keyType = expectedType; |
360 | keyIsNull = !opaque; |
361 | } |
362 | |
363 | bool TlsKeyOpenSSL::fromEVP_PKEY(EVP_PKEY *pkey) |
364 | { |
365 | if (!pkey) |
366 | return false; |
367 | |
368 | #ifndef OPENSSL_NO_DEPRECATED_3_0 |
369 | #define get_key(key, alg) key = q_EVP_PKEY_get1_##alg(pkey) |
370 | #else |
371 | #define get_key(key, alg) q_EVP_PKEY_up_ref(pkey); genericKey = pkey; |
372 | #endif |
373 | |
374 | switch (q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey))) { |
375 | case EVP_PKEY_RSA: |
376 | keyIsNull = false; |
377 | keyAlgorithm = QSsl::Rsa; |
378 | keyType = QSsl::PrivateKey; |
379 | get_key(rsa, RSA); |
380 | return true; |
381 | case EVP_PKEY_DSA: |
382 | keyIsNull = false; |
383 | keyAlgorithm = QSsl::Dsa; |
384 | keyType = QSsl::PrivateKey; |
385 | get_key(dsa, DSA); |
386 | return true; |
387 | case EVP_PKEY_DH: |
388 | keyIsNull = false; |
389 | keyAlgorithm = QSsl::Dh; |
390 | keyType = QSsl::PrivateKey; |
391 | get_key(dh, DH); |
392 | return true; |
393 | #ifndef OPENSSL_NO_EC |
394 | case EVP_PKEY_EC: |
395 | keyIsNull = false; |
396 | keyAlgorithm = QSsl::Ec; |
397 | keyType = QSsl::PrivateKey; |
398 | get_key(ec, EC_KEY); |
399 | return true; |
400 | #endif |
401 | default:; |
402 | // Unknown key type. This could be handled as opaque, but then |
403 | // we'd eventually leak memory since we wouldn't be able to free |
404 | // the underlying EVP_PKEY structure. For now, we won't support |
405 | // this. |
406 | } |
407 | |
408 | return false; |
409 | } |
410 | |
411 | QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, |
412 | const QByteArray &key, const QByteArray &iv, bool enc) |
413 | { |
414 | const EVP_CIPHER *type = nullptr; |
415 | int i = 0, len = 0; |
416 | |
417 | switch (cipher) { |
418 | case Cipher::DesCbc: |
419 | #ifndef OPENSSL_NO_DES |
420 | type = q_EVP_des_cbc(); |
421 | #endif |
422 | break; |
423 | case Cipher::DesEde3Cbc: |
424 | #ifndef OPENSSL_NO_DES |
425 | type = q_EVP_des_ede3_cbc(); |
426 | #endif |
427 | break; |
428 | case Cipher::Rc2Cbc: |
429 | #ifndef OPENSSL_NO_RC2 |
430 | type = q_EVP_rc2_cbc(); |
431 | #endif |
432 | break; |
433 | case Cipher::Aes128Cbc: |
434 | type = q_EVP_aes_128_cbc(); |
435 | break; |
436 | case Cipher::Aes192Cbc: |
437 | type = q_EVP_aes_192_cbc(); |
438 | break; |
439 | case Cipher::Aes256Cbc: |
440 | type = q_EVP_aes_256_cbc(); |
441 | break; |
442 | } |
443 | |
444 | if (type == nullptr) |
445 | return {}; |
446 | |
447 | QByteArray output; |
448 | output.resize(size: data.size() + EVP_MAX_BLOCK_LENGTH); |
449 | |
450 | EVP_CIPHER_CTX *ctx = q_EVP_CIPHER_CTX_new(); |
451 | q_EVP_CIPHER_CTX_reset(c: ctx); |
452 | if (q_EVP_CipherInit(ctx, type, key: nullptr, iv: nullptr, enc) != 1) { |
453 | q_EVP_CIPHER_CTX_free(a: ctx); |
454 | QTlsBackendOpenSSL::logAndClearErrorQueue(); |
455 | return {}; |
456 | } |
457 | |
458 | q_EVP_CIPHER_CTX_set_key_length(x: ctx, keylen: key.size()); |
459 | if (cipher == Cipher::Rc2Cbc) |
460 | q_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, arg: 8 * key.size(), ptr: nullptr); |
461 | |
462 | q_EVP_CipherInit_ex(ctx, cipher: nullptr, impl: nullptr, |
463 | key: reinterpret_cast<const unsigned char *>(key.constData()), |
464 | iv: reinterpret_cast<const unsigned char *>(iv.constData()), |
465 | enc); |
466 | q_EVP_CipherUpdate(ctx, |
467 | out: reinterpret_cast<unsigned char *>(output.data()), outl: &len, |
468 | in: reinterpret_cast<const unsigned char *>(data.constData()), inl: data.size()); |
469 | q_EVP_CipherFinal(ctx, |
470 | out: reinterpret_cast<unsigned char *>(output.data()) + len, outl: &i); |
471 | len += i; |
472 | |
473 | q_EVP_CIPHER_CTX_reset(c: ctx); |
474 | q_EVP_CIPHER_CTX_free(a: ctx); |
475 | |
476 | return output.left(len); |
477 | } |
478 | |
479 | QByteArray TlsKeyOpenSSL::decrypt(Cipher cipher, const QByteArray &data, |
480 | const QByteArray &key, const QByteArray &iv) const |
481 | { |
482 | return doCrypt(cipher, data, key, iv, enc: false); |
483 | } |
484 | |
485 | QByteArray TlsKeyOpenSSL::encrypt(Cipher cipher, const QByteArray &data, |
486 | const QByteArray &key, const QByteArray &iv) const |
487 | { |
488 | return doCrypt(cipher, data, key, iv, enc: true); |
489 | } |
490 | |
491 | TlsKeyOpenSSL *TlsKeyOpenSSL::publicKeyFromX509(X509 *x) |
492 | { |
493 | TlsKeyOpenSSL *tlsKey = new TlsKeyOpenSSL; |
494 | std::unique_ptr<TlsKeyOpenSSL> keyRaii(tlsKey); |
495 | |
496 | tlsKey->keyType = QSsl::PublicKey; |
497 | |
498 | #ifndef OPENSSL_NO_DEPRECATED_3_0 |
499 | |
500 | #define get_pubkey(keyName, alg) tlsKey->keyName = q_EVP_PKEY_get1_##alg(pkey) |
501 | |
502 | #else |
503 | |
504 | #define get_pubkey(a, b) tlsKey->genericKey = pkey |
505 | |
506 | #endif |
507 | |
508 | EVP_PKEY *pkey = q_X509_get_pubkey(a: x); |
509 | Q_ASSERT(pkey); |
510 | const int keyType = q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey)); |
511 | |
512 | if (keyType == EVP_PKEY_RSA) { |
513 | get_pubkey(rsa, RSA); |
514 | tlsKey->keyAlgorithm = QSsl::Rsa; |
515 | tlsKey->keyIsNull = false; |
516 | } else if (keyType == EVP_PKEY_DSA) { |
517 | get_pubkey(dsa, DSA); |
518 | tlsKey->keyAlgorithm = QSsl::Dsa; |
519 | tlsKey->keyIsNull = false; |
520 | #ifndef OPENSSL_NO_EC |
521 | } else if (keyType == EVP_PKEY_EC) { |
522 | get_pubkey(ec, EC_KEY); |
523 | tlsKey->keyAlgorithm = QSsl::Ec; |
524 | tlsKey->keyIsNull = false; |
525 | #endif |
526 | } else if (keyType == EVP_PKEY_DH) { |
527 | // DH unsupported (key is null) |
528 | } else { |
529 | // error? (key is null) |
530 | } |
531 | |
532 | #ifndef OPENSSL_NO_DEPRECATED_3_0 |
533 | q_EVP_PKEY_free(a: pkey); |
534 | #endif |
535 | |
536 | return keyRaii.release(); |
537 | } |
538 | |
539 | } // namespace QTlsPrivate |
540 | |
541 | QT_END_NAMESPACE |
542 | |