Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtlskey_schannel.cpp
Go to the documentation of this file.
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// Qt-Security score:critical reason:cryptography
4
5#include <QtNetwork/private/qssl_p.h>
6
9
10#include "../shared/qwincrypt_p.h"
11
12#include <QtNetwork/private/qtlsbackend_p.h>
13#include <QtNetwork/private/qsslkey_p.h>
14
15#include <QtNetwork/qsslkey.h>
16
17#include <QtCore/qscopeguard.h>
18#include <QtCore/qbytearray.h>
19#include <QtCore/qvarlengtharray.h>
20
22
23namespace {
24const wchar_t *getName(QSslKeyPrivate::Cipher cipher)
25{
26 switch (cipher) {
27 case QTlsPrivate::Cipher::DesCbc:
28 return BCRYPT_DES_ALGORITHM;
29 case QTlsPrivate::Cipher::DesEde3Cbc:
30 return BCRYPT_3DES_ALGORITHM;
31 case QTlsPrivate::Cipher::Rc2Cbc:
32 return BCRYPT_RC2_ALGORITHM;
33 case QTlsPrivate::Cipher::Aes128Cbc:
34 case QTlsPrivate::Cipher::Aes192Cbc:
35 case QTlsPrivate::Cipher::Aes256Cbc:
36 return BCRYPT_AES_ALGORITHM;
37 }
38 Q_UNREACHABLE();
39}
40
41BCRYPT_ALG_HANDLE getHandle(QSslKeyPrivate::Cipher cipher)
42{
43 BCRYPT_ALG_HANDLE handle;
44 NTSTATUS status = BCryptOpenAlgorithmProvider(
45 &handle, // phAlgorithm
46 getName(cipher), // pszAlgId
47 nullptr, // pszImplementation
48 0 // dwFlags
49 );
50 if (status < 0) {
51 qCWarning(lcTlsBackendSchannel, "Failed to open algorithm handle (%ld)!", status);
52 return nullptr;
53 }
54
55 return handle;
56}
57
58BCRYPT_KEY_HANDLE generateSymmetricKey(BCRYPT_ALG_HANDLE handle,
59 const QByteArray &key)
60{
61 BCRYPT_KEY_HANDLE keyHandle;
62 NTSTATUS status = BCryptGenerateSymmetricKey(
63 handle, // hAlgorithm
64 &keyHandle, // phKey
65 nullptr, // pbKeyObject (can ignore)
66 0, // cbKeyObject (also ignoring)
67 reinterpret_cast<unsigned char *>(const_cast<char *>(key.data())), // pbSecret
68 ULONG(key.length()), // cbSecret
69 0 // dwFlags
70 );
71 if (status < 0) {
72 qCWarning(lcTlsBackendSchannel, "Failed to generate symmetric key (%ld)!", status);
73 return nullptr;
74 }
75
76 status = BCryptSetProperty(
77 keyHandle, // hObject
78 BCRYPT_CHAINING_MODE, // pszProperty
79 reinterpret_cast<UCHAR *>(const_cast<wchar_t *>(BCRYPT_CHAIN_MODE_CBC)), // pbInput
80 ARRAYSIZE(BCRYPT_CHAIN_MODE_CBC), // cbInput
81 0 // dwFlags
82 );
83 if (status < 0) {
84 BCryptDestroyKey(keyHandle);
85 qCWarning(lcTlsBackendSchannel, "Failed to change the symmetric key's chaining mode (%ld)!",
86 status);
87 return nullptr;
88 }
89 return keyHandle;
90}
91
92QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, const QByteArray &key,
93 const QByteArray &iv, bool encrypt)
94{
95 BCRYPT_ALG_HANDLE handle = getHandle(cipher);
96 if (!handle)
97 return {};
98 auto handleDealloc = qScopeGuard([&handle]() {
99 BCryptCloseAlgorithmProvider(handle, 0);
100 });
101
102 BCRYPT_KEY_HANDLE keyHandle = generateSymmetricKey(handle, key);
103 if (!keyHandle)
104 return {};
105 auto keyHandleDealloc = qScopeGuard([&keyHandle]() {
106 BCryptDestroyKey(keyHandle);
107 });
108
109 QByteArray ivCopy = iv; // This gets modified, so we take a copy
110
111 ULONG sizeNeeded = 0;
112 QVarLengthArray<unsigned char> output;
113 auto cryptFunction = encrypt ? BCryptEncrypt : BCryptDecrypt;
114 for (int i = 0; i < 2; i++) {
115 output.resize(int(sizeNeeded));
116 auto input = reinterpret_cast<unsigned char *>(const_cast<char *>(data.data()));
117 // Need to call it twice because the first iteration lets us know the size needed.
118 NTSTATUS status = cryptFunction(
119 keyHandle, // hKey
120 input, // pbInput
121 ULONG(data.length()), // cbInput
122 nullptr, // pPaddingInfo
123 reinterpret_cast<unsigned char *>(ivCopy.data()), // pbIV
124 ULONG(ivCopy.length()), // cbIV
125 sizeNeeded ? output.data() : nullptr, // pbOutput
126 ULONG(output.length()), // cbOutput
127 &sizeNeeded, // pcbResult
128 BCRYPT_BLOCK_PADDING // dwFlags
129 );
130 if (status < 0) {
131 qCWarning(lcTlsBackendSchannel, "%s failed (%ld)!", encrypt ? "Encrypt" : "Decrypt",
132 status);
133 return {};
134 }
135 }
136
137 return QByteArray(reinterpret_cast<const char *>(output.constData()), int(sizeNeeded));
138}
139} // anonymous namespace
140
141namespace QTlsPrivate {
142
143QByteArray TlsKeySchannel::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
144 const QByteArray &iv) const
145{
146 return doCrypt(cipher, data, key, iv, false);
147}
148
149QByteArray TlsKeySchannel::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
150 const QByteArray &iv) const
151{
152 return doCrypt(cipher, data, key, iv, true);
153}
154
155} // namespace QTlsPrivate
156
157QT_END_NAMESPACE
QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv) const override
QByteArray encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv) const override
QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv, bool encrypt)
const wchar_t * getName(QSslKeyPrivate::Cipher cipher)
BCRYPT_ALG_HANDLE getHandle(QSslKeyPrivate::Cipher cipher)
BCRYPT_KEY_HANDLE generateSymmetricKey(BCRYPT_ALG_HANDLE handle, const QByteArray &key)
Namespace containing onternal types that TLS backends implement.