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
14QT_BEGIN_NAMESPACE
15
16namespace QTlsPrivate {
17
18void 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> headers;
28 const auto pem = pemFromDer(der, headers);
29
30 decodePem(type, algorithm, pem, passPhrase, deepClear);
31}
32
33void 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
96QByteArray TlsKeyOpenSSL::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const
97{
98 QByteArray header = pemHeader();
99 QByteArray footer = pemFooter();
100
101 QByteArray der(pem);
102
103 int headerIndex = der.indexOf(bv: header);
104 int footerIndex = 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
158void 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
202Qt::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
229int 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
255QByteArray 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
349void 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
363bool 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
411QByteArray 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
479QByteArray 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
485QByteArray 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
491TlsKeyOpenSSL *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
541QT_END_NAMESPACE
542

source code of qtbase/src/plugins/tls/openssl/qtlskey_openssl.cpp