diff options
Diffstat (limited to 'src/plugins/tls/schannel/qtlskey_schannel.cpp')
-rw-r--r-- | src/plugins/tls/schannel/qtlskey_schannel.cpp | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/src/plugins/tls/schannel/qtlskey_schannel.cpp b/src/plugins/tls/schannel/qtlskey_schannel.cpp new file mode 100644 index 0000000000..eb0a2371ab --- /dev/null +++ b/src/plugins/tls/schannel/qtlskey_schannel.cpp @@ -0,0 +1,157 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QtNetwork/private/qssl_p.h> + +#include "qtlsbackend_schannel_p.h" +#include "qtlskey_schannel_p.h" + +#include "../shared/qwincrypt_p.h" + +#include <QtNetwork/private/qtlsbackend_p.h> +#include <QtNetwork/private/qsslkey_p.h> + +#include <QtNetwork/qsslkey.h> + +#include <QtCore/qscopeguard.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qvarlengtharray.h> + +QT_BEGIN_NAMESPACE + +namespace { +const wchar_t *getName(QSslKeyPrivate::Cipher cipher) +{ + switch (cipher) { + case QTlsPrivate::Cipher::DesCbc: + return BCRYPT_DES_ALGORITHM; + case QTlsPrivate::Cipher::DesEde3Cbc: + return BCRYPT_3DES_ALGORITHM; + case QTlsPrivate::Cipher::Rc2Cbc: + return BCRYPT_RC2_ALGORITHM; + case QTlsPrivate::Cipher::Aes128Cbc: + case QTlsPrivate::Cipher::Aes192Cbc: + case QTlsPrivate::Cipher::Aes256Cbc: + return BCRYPT_AES_ALGORITHM; + } + Q_UNREACHABLE(); +} + +BCRYPT_ALG_HANDLE getHandle(QSslKeyPrivate::Cipher cipher) +{ + BCRYPT_ALG_HANDLE handle; + NTSTATUS status = BCryptOpenAlgorithmProvider( + &handle, // phAlgorithm + getName(cipher), // pszAlgId + nullptr, // pszImplementation + 0 // dwFlags + ); + if (status < 0) { + qCWarning(lcTlsBackendSchannel, "Failed to open algorithm handle (%ld)!", status); + return nullptr; + } + + return handle; +} + +BCRYPT_KEY_HANDLE generateSymmetricKey(BCRYPT_ALG_HANDLE handle, + const QByteArray &key) +{ + BCRYPT_KEY_HANDLE keyHandle; + NTSTATUS status = BCryptGenerateSymmetricKey( + handle, // hAlgorithm + &keyHandle, // phKey + nullptr, // pbKeyObject (can ignore) + 0, // cbKeyObject (also ignoring) + reinterpret_cast<unsigned char *>(const_cast<char *>(key.data())), // pbSecret + ULONG(key.length()), // cbSecret + 0 // dwFlags + ); + if (status < 0) { + qCWarning(lcTlsBackendSchannel, "Failed to generate symmetric key (%ld)!", status); + return nullptr; + } + + status = BCryptSetProperty( + keyHandle, // hObject + BCRYPT_CHAINING_MODE, // pszProperty + reinterpret_cast<UCHAR *>(const_cast<wchar_t *>(BCRYPT_CHAIN_MODE_CBC)), // pbInput + ARRAYSIZE(BCRYPT_CHAIN_MODE_CBC), // cbInput + 0 // dwFlags + ); + if (status < 0) { + BCryptDestroyKey(keyHandle); + qCWarning(lcTlsBackendSchannel, "Failed to change the symmetric key's chaining mode (%ld)!", + status); + return nullptr; + } + return keyHandle; +} + +QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, const QByteArray &key, + const QByteArray &iv, bool encrypt) +{ + BCRYPT_ALG_HANDLE handle = getHandle(cipher); + if (!handle) + return {}; + auto handleDealloc = qScopeGuard([&handle]() { + BCryptCloseAlgorithmProvider(handle, 0); + }); + + BCRYPT_KEY_HANDLE keyHandle = generateSymmetricKey(handle, key); + if (!keyHandle) + return {}; + auto keyHandleDealloc = qScopeGuard([&keyHandle]() { + BCryptDestroyKey(keyHandle); + }); + + QByteArray ivCopy = iv; // This gets modified, so we take a copy + + ULONG sizeNeeded = 0; + QVarLengthArray<unsigned char> output; + auto cryptFunction = encrypt ? BCryptEncrypt : BCryptDecrypt; + for (int i = 0; i < 2; i++) { + output.resize(int(sizeNeeded)); + auto input = reinterpret_cast<unsigned char *>(const_cast<char *>(data.data())); + // Need to call it twice because the first iteration lets us know the size needed. + NTSTATUS status = cryptFunction( + keyHandle, // hKey + input, // pbInput + ULONG(data.length()), // cbInput + nullptr, // pPaddingInfo + reinterpret_cast<unsigned char *>(ivCopy.data()), // pbIV + ULONG(ivCopy.length()), // cbIV + sizeNeeded ? output.data() : nullptr, // pbOutput + ULONG(output.length()), // cbOutput + &sizeNeeded, // pcbResult + BCRYPT_BLOCK_PADDING // dwFlags + ); + if (status < 0) { + qCWarning(lcTlsBackendSchannel, "%s failed (%ld)!", encrypt ? "Encrypt" : "Decrypt", + status); + return {}; + } + } + + return QByteArray(reinterpret_cast<const char *>(output.constData()), int(sizeNeeded)); +} +} // anonymous namespace + +namespace QTlsPrivate { + +QByteArray TlsKeySchannel::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, + const QByteArray &iv) const +{ + return doCrypt(cipher, data, key, iv, false); +} + +QByteArray TlsKeySchannel::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, + const QByteArray &iv) const +{ + return doCrypt(cipher, data, key, iv, true); +} + +} // namespace QTlsPrivate + +QT_END_NAMESPACE + |