|
|
|
|
@ -87,6 +87,12 @@
|
|
|
|
|
#ifndef SP_PROT_TLS1_3
|
|
|
|
|
#define SP_PROT_TLS1_3 (SP_PROT_TLS1_3_CLIENT | SP_PROT_TLS1_3_SERVER)
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef BCRYPT_ECDH_ALGORITHM
|
|
|
|
|
#define BCRYPT_ECDH_ALGORITHM L"ECDH"
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef BCRYPT_ECDSA_ALGORITHM
|
|
|
|
|
#define BCRYPT_ECDSA_ALGORITHM L"ECDSA"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
@future!:
|
|
|
|
|
@ -108,10 +114,6 @@
|
|
|
|
|
- Check if SEC_I_INCOMPLETE_CREDENTIALS is still returned for both "missing certificate" and
|
|
|
|
|
"missing PSK" when calling InitializeSecurityContext in "performHandshake".
|
|
|
|
|
|
|
|
|
|
Medium priority:
|
|
|
|
|
- Setting cipher-suites (or ALG_ID)
|
|
|
|
|
- People have survived without it in WinRT
|
|
|
|
|
|
|
|
|
|
Low priority:
|
|
|
|
|
- Possibly make RAII wrappers for SecBuffer (which I commonly create QScopeGuards for)
|
|
|
|
|
|
|
|
|
|
@ -127,39 +129,341 @@ Q_LOGGING_CATEGORY(lcTlsBackendSchannel, "qt.tlsbackend.schannel");
|
|
|
|
|
QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key,
|
|
|
|
|
const QString &passPhrase);
|
|
|
|
|
|
|
|
|
|
namespace QTlsPrivate {
|
|
|
|
|
|
|
|
|
|
QList<QSslCipher> defaultCiphers()
|
|
|
|
|
{
|
|
|
|
|
// Previously the code was in QSslSocketBackendPrivate.
|
|
|
|
|
QList<QSslCipher> ciphers;
|
|
|
|
|
const QString protocolStrings[] = { QStringLiteral("TLSv1"), QStringLiteral("TLSv1.1"),
|
|
|
|
|
QStringLiteral("TLSv1.2"), QStringLiteral("TLSv1.3") };
|
|
|
|
|
QT_WARNING_PUSH
|
|
|
|
|
QT_WARNING_DISABLE_DEPRECATED
|
|
|
|
|
const QSsl::SslProtocol protocols[] = { QSsl::TlsV1_0, QSsl::TlsV1_1,
|
|
|
|
|
QSsl::TlsV1_2, QSsl::TlsV1_3 };
|
|
|
|
|
QT_WARNING_POP
|
|
|
|
|
const int size = ARRAYSIZE(protocols);
|
|
|
|
|
static_assert(size == ARRAYSIZE(protocolStrings));
|
|
|
|
|
ciphers.reserve(size);
|
|
|
|
|
for (int i = 0; i < size; ++i) {
|
|
|
|
|
const QSslCipher cipher = QTlsBackend::createCipher(QStringLiteral("Schannel"),
|
|
|
|
|
protocols[i], protocolStrings[i]);
|
|
|
|
|
|
|
|
|
|
ciphers.append(cipher);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ciphers;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace QTlsPrivate
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
bool supportsTls13();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace QTlsPrivate {
|
|
|
|
|
|
|
|
|
|
QList<QSslCipher> defaultCiphers();
|
|
|
|
|
|
|
|
|
|
struct SchannelCipherInfo {
|
|
|
|
|
const char *openSslCipherSuite;
|
|
|
|
|
const char *schannelCipherSuite;
|
|
|
|
|
const char *keyExchangeMethod;
|
|
|
|
|
const char *authenticationMethod;
|
|
|
|
|
const char *encryptionMethod;
|
|
|
|
|
int encryptionBits;
|
|
|
|
|
const char *hashMethod;
|
|
|
|
|
QList<QSsl::SslProtocol> protocols;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// The list of supported ciphers according to
|
|
|
|
|
// https://learn.microsoft.com/en-us/windows/win32/secauthn/tls-cipher-suites-in-windows-server-2022
|
|
|
|
|
QT_WARNING_PUSH
|
|
|
|
|
QT_WARNING_DISABLE_DEPRECATED
|
|
|
|
|
std::array<SchannelCipherInfo, 44> schannelCipherInfo = {{
|
|
|
|
|
{"TLS_AES_256_GCM_SHA384", "TLS_AES_256_GCM_SHA384", "", "", "AES", 256, "SHA384", {QSsl::TlsV1_3}},
|
|
|
|
|
{"TLS_AES_128_GCM_SHA256", "TLS_AES_128_GCM_SHA256", "", "", "AES", 128, "SHA256", {QSsl::TlsV1_3}},
|
|
|
|
|
{"ECDHE-ECDSA-AES256-GCM-SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "ECDH", "ECDSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
|
|
|
|
|
{"ECDHE-ECDSA-AES128-GCM-SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "ECDH", "ECDSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
|
|
|
|
|
{"ECDHE-RSA-AES256-GCM-SHA384", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "ECDH", "RSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
|
|
|
|
|
{"ECDHE-RSA-AES128-GCM-SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "ECDH", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
|
|
|
|
|
{"DHE-RSA-AES256-GCM-SHA384", "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "DH", "RSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
|
|
|
|
|
{"DHE-RSA-AES128-GCM-SHA256", "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "DH", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
|
|
|
|
|
{"ECDHE-ECDSA-AES256-SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "ECDH", "ECDSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
|
|
|
|
|
{"ECDHE-ECDSA-AES128-SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "ECDH", "ECDSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
|
|
|
|
|
{"ECDHE-RSA-AES256-SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "ECDH", "RSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
|
|
|
|
|
{"ECDHE-RSA-AES128-SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "ECDH", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
|
|
|
|
|
{"ECDHE-ECDSA-AES256-SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "ECDH", "ECDSA", "AES", 256, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
{"ECDHE-ECDSA-AES128-SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "ECDH", "ECDSA", "AES", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
{"ECDHE-RSA-AES256-SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "ECDH", "RSA", "AES", 256, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
{"ECDHE-RSA-AES128-SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "ECDH", "RSA", "AES", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
{"AES256-GCM-SHA384", "TLS_RSA_WITH_AES_256_GCM_SHA384", "RSA", "RSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
|
|
|
|
|
{"AES128-GCM-SHA256", "TLS_RSA_WITH_AES_128_GCM_SHA256", "RSA", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
|
|
|
|
|
{"AES256-SHA256", "TLS_RSA_WITH_AES_256_CBC_SHA256", "RSA", "RSA", "AES", 256, "SHA256", {QSsl::TlsV1_2}},
|
|
|
|
|
{"AES128-SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA256", "RSA", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
|
|
|
|
|
{"AES256-SHA", "TLS_RSA_WITH_AES_256_CBC_SHA", "RSA", "RSA", "AES", 256, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
{"AES128-SHA", "TLS_RSA_WITH_AES_128_CBC_SHA", "RSA", "RSA", "AES", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
{"DES-CBC3-SHA", "TLS_RSA_WITH_3DES_EDE_CBC_SHA", "RSA", "RSA", "3DES", 168, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
{"NULL-SHA256", "TLS_RSA_WITH_NULL_SHA256", "RSA", "RSA", "", 0, "SHA256", {QSsl::TlsV1_2}},
|
|
|
|
|
{"NULL-SHA", "TLS_RSA_WITH_NULL_SHA", "RSA", "RSA", "", 0, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
|
|
|
|
|
// the following cipher suites are not enabled by default in schannel provider
|
|
|
|
|
{"TLS_CHACHA20_POLY1305_SHA256", "TLS_CHACHA20_POLY1305_SHA256", "", "", "CHACHA20_POLY1305", 0, "", {QSsl::TlsV1_3}},
|
|
|
|
|
{"DHE-RSA-AES256-SHA", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", "DH", "RSA", "AES", 256, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
{"DHE-RSA-AES128-SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "DH", "RSA", "AES", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
{"DHE-DSS-AES256-SHA256", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", "DH", "DSA", "AES", 256, "SHA256", {QSsl::TlsV1_2}},
|
|
|
|
|
{"DHE-DSS-AES128-SHA256", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", "DH", "DSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
|
|
|
|
|
{"DHE-DSS-AES256-SHA", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", "DH", "DSA", "AES", 256, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
{"DHE-DSS-AES128-SHA", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", "DH", "DSA", "AES", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
{"EDH-DSS-DES-CBC3-SHA", "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", "DH", "DSA", "3DES", 168, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
{"RC4-SHA", "TLS_RSA_WITH_RC4_128_SHA", "RSA", "RSA", "RC4", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
{"RC4-MD5", "TLS_RSA_WITH_RC4_128_MD5", "RSA", "RSA", "RC4", 128, "MD5", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
{"DES-CBC-SHA", "TLS_RSA_WITH_DES_CBC_SHA", "RSA", "RSA", "DES", 56, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
{"EDH-DSS-DES-CBC-SHA", "TLS_DHE_DSS_WITH_DES_CBC_SHA", "DH", "DSA", "DES", 56, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
{"NULL-MD5", "TLS_RSA_WITH_NULL_MD5", "RSA", "RSA", "", 0, "MD5", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
|
|
|
|
|
|
|
|
|
|
// PSK cipher suites
|
|
|
|
|
{"PSK-AES256-GCM-SHA384", "TLS_PSK_WITH_AES_256_GCM_SHA384", "PSK", "", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
|
|
|
|
|
{"PSK-AES128-GCM-SHA256", "TLS_PSK_WITH_AES_128_GCM_SHA256", "PSK", "", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
|
|
|
|
|
{"PSK-AES256-CBC-SHA384", "TLS_PSK_WITH_AES_256_CBC_SHA384", "PSK", "", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
|
|
|
|
|
{"PSK-AES128-CBC-SHA256", "TLS_PSK_WITH_AES_128_CBC_SHA256", "PSK", "", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
|
|
|
|
|
{"PSK-NULL-SHA384", "TLS_PSK_WITH_NULL_SHA384", "PSK", "", "", 0, "SHA384", {QSsl::TlsV1_2}},
|
|
|
|
|
{"PSK-NULL-SHA256", "TLS_PSK_WITH_NULL_SHA256", "PSK", "", "", 0, "SHA256", {QSsl::TlsV1_2}},
|
|
|
|
|
}};
|
|
|
|
|
QT_WARNING_POP
|
|
|
|
|
|
|
|
|
|
const SchannelCipherInfo *cipherInfoByOpenSslName(const QString &name)
|
|
|
|
|
{
|
|
|
|
|
for (const auto &cipherInfo : schannelCipherInfo) {
|
|
|
|
|
if (name == QLatin1StringView(cipherInfo.openSslCipherSuite))
|
|
|
|
|
return &cipherInfo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UNICODE_STRING cbcChainingMode = {
|
|
|
|
|
sizeof(BCRYPT_CHAIN_MODE_CBC) - 2,
|
|
|
|
|
sizeof(BCRYPT_CHAIN_MODE_CBC),
|
|
|
|
|
const_cast<PWSTR>(BCRYPT_CHAIN_MODE_CBC)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
UNICODE_STRING gcmChainingMode = {
|
|
|
|
|
sizeof(BCRYPT_CHAIN_MODE_GCM) - 2,
|
|
|
|
|
sizeof(BCRYPT_CHAIN_MODE_GCM),
|
|
|
|
|
const_cast<PWSTR>(BCRYPT_CHAIN_MODE_GCM)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Determines which algorithms are not used by the requested ciphers to build
|
|
|
|
|
up a black list that can be passed to SCH_CREDENTIALS.
|
|
|
|
|
*/
|
|
|
|
|
QList<CRYPTO_SETTINGS> cryptoSettingsForCiphers(const QList<QSslCipher> &ciphers)
|
|
|
|
|
{
|
|
|
|
|
static const QList<QSslCipher> defaultCipherList = defaultCiphers();
|
|
|
|
|
|
|
|
|
|
if (defaultCipherList == ciphers) {
|
|
|
|
|
// the ciphers have not been restricted for this session, so no black listing needed
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<const SchannelCipherInfo*> cipherInfo;
|
|
|
|
|
|
|
|
|
|
for (const auto &cipher : ciphers) {
|
|
|
|
|
if (cipher.isNull())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
const auto *info = cipherInfoByOpenSslName(cipher.name());
|
|
|
|
|
if (!cipherInfo.contains(info))
|
|
|
|
|
cipherInfo.append(info);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<CRYPTO_SETTINGS> cryptoSettings;
|
|
|
|
|
|
|
|
|
|
const auto assignUnicodeString = [](UNICODE_STRING &unicodeString, const wchar_t *characters) {
|
|
|
|
|
unicodeString.Length = static_cast<USHORT>(wcslen(characters) * sizeof(WCHAR));
|
|
|
|
|
unicodeString.MaximumLength = unicodeString.Length + sizeof(UNICODE_NULL);
|
|
|
|
|
unicodeString.Buffer = const_cast<wchar_t*>(characters);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// black list of key exchange algorithms
|
|
|
|
|
const auto allKeyExchangeAlgorithms = {BCRYPT_RSA_ALGORITHM,
|
|
|
|
|
BCRYPT_ECDH_ALGORITHM,
|
|
|
|
|
BCRYPT_DH_ALGORITHM};
|
|
|
|
|
|
|
|
|
|
for (const auto &algorithm : allKeyExchangeAlgorithms) {
|
|
|
|
|
const auto method = QStringView(algorithm);
|
|
|
|
|
|
|
|
|
|
const auto usesMethod = [method](const SchannelCipherInfo *info) {
|
|
|
|
|
return QLatin1StringView(info->keyExchangeMethod) == method;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const bool exclude = std::none_of(cipherInfo.cbegin(), cipherInfo.cend(), usesMethod);
|
|
|
|
|
|
|
|
|
|
if (exclude) {
|
|
|
|
|
CRYPTO_SETTINGS settings = {};
|
|
|
|
|
settings.eAlgorithmUsage = TlsParametersCngAlgUsageKeyExchange;
|
|
|
|
|
assignUnicodeString(settings.strCngAlgId, algorithm);
|
|
|
|
|
cryptoSettings.append(settings);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// black list of authentication algorithms
|
|
|
|
|
const auto allAuthenticationAlgorithms = {BCRYPT_RSA_ALGORITHM,
|
|
|
|
|
BCRYPT_DSA_ALGORITHM,
|
|
|
|
|
BCRYPT_ECDSA_ALGORITHM,
|
|
|
|
|
BCRYPT_DH_ALGORITHM};
|
|
|
|
|
|
|
|
|
|
for (const auto &algorithm : allAuthenticationAlgorithms) {
|
|
|
|
|
const auto method = QStringView(algorithm);
|
|
|
|
|
|
|
|
|
|
const auto usesMethod = [method](const SchannelCipherInfo *info) {
|
|
|
|
|
return QLatin1StringView(info->authenticationMethod) == method;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const bool exclude = std::none_of(cipherInfo.begin(), cipherInfo.end(), usesMethod);
|
|
|
|
|
|
|
|
|
|
if (exclude) {
|
|
|
|
|
CRYPTO_SETTINGS settings = {};
|
|
|
|
|
settings.eAlgorithmUsage = TlsParametersCngAlgUsageSignature;
|
|
|
|
|
assignUnicodeString(settings.strCngAlgId, algorithm);
|
|
|
|
|
cryptoSettings.append(settings);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// black list of encryption algorithms
|
|
|
|
|
const auto allEncryptionAlgorithms = {BCRYPT_AES_ALGORITHM,
|
|
|
|
|
BCRYPT_RC4_ALGORITHM,
|
|
|
|
|
BCRYPT_DES_ALGORITHM,
|
|
|
|
|
BCRYPT_3DES_ALGORITHM};
|
|
|
|
|
|
|
|
|
|
for (const auto &algorithm : allEncryptionAlgorithms) {
|
|
|
|
|
const auto method = QStringView(algorithm);
|
|
|
|
|
|
|
|
|
|
if (method == QLatin1StringView("AES")) {
|
|
|
|
|
bool uses128Bit = false;
|
|
|
|
|
bool uses256Bit = false;
|
|
|
|
|
bool usesGcm = false;
|
|
|
|
|
bool usesCbc = false;
|
|
|
|
|
for (const auto *info : cipherInfo) {
|
|
|
|
|
if (QLatin1StringView(info->encryptionMethod) == method) {
|
|
|
|
|
uses128Bit = uses128Bit || (info->encryptionBits == 128);
|
|
|
|
|
uses256Bit = uses256Bit || (info->encryptionBits == 256);
|
|
|
|
|
usesGcm = usesGcm ||
|
|
|
|
|
QLatin1StringView(info->schannelCipherSuite).contains("_GCM_"_L1);
|
|
|
|
|
usesCbc = usesCbc ||
|
|
|
|
|
QLatin1StringView(info->schannelCipherSuite).contains("_CBC_"_L1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CRYPTO_SETTINGS settings = {};
|
|
|
|
|
settings.eAlgorithmUsage = TlsParametersCngAlgUsageCipher;
|
|
|
|
|
assignUnicodeString(settings.strCngAlgId, algorithm);
|
|
|
|
|
|
|
|
|
|
if (usesGcm && !usesCbc) {
|
|
|
|
|
settings.cChainingModes = 1;
|
|
|
|
|
settings.rgstrChainingModes = &cbcChainingMode;
|
|
|
|
|
} else if (!usesGcm && usesCbc) {
|
|
|
|
|
settings.cChainingModes = 1;
|
|
|
|
|
settings.rgstrChainingModes = &gcmChainingMode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!uses128Bit && uses256Bit) {
|
|
|
|
|
settings.dwMinBitLength = 256;
|
|
|
|
|
cryptoSettings.append(settings);
|
|
|
|
|
} else if (uses128Bit && !uses256Bit) {
|
|
|
|
|
settings.dwMaxBitLength = 128;
|
|
|
|
|
cryptoSettings.append(settings);
|
|
|
|
|
} else if (!uses128Bit && !uses256Bit) {
|
|
|
|
|
cryptoSettings.append(settings);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
const auto usesMethod = [method](const SchannelCipherInfo *info) {
|
|
|
|
|
return QLatin1StringView(info->encryptionMethod) == method;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const bool exclude = std::none_of(cipherInfo.begin(), cipherInfo.end(), usesMethod);
|
|
|
|
|
|
|
|
|
|
if (exclude) {
|
|
|
|
|
CRYPTO_SETTINGS settings = {};
|
|
|
|
|
settings.eAlgorithmUsage = TlsParametersCngAlgUsageCipher;
|
|
|
|
|
assignUnicodeString(settings.strCngAlgId, algorithm);
|
|
|
|
|
cryptoSettings.append(settings);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// black list of hash algorithms
|
|
|
|
|
const auto allHashAlgorithms = {BCRYPT_MD5_ALGORITHM,
|
|
|
|
|
BCRYPT_SHA1_ALGORITHM,
|
|
|
|
|
BCRYPT_SHA256_ALGORITHM,
|
|
|
|
|
BCRYPT_SHA384_ALGORITHM};
|
|
|
|
|
|
|
|
|
|
for (const auto &algorithm : allHashAlgorithms) {
|
|
|
|
|
const auto method = QStringView(algorithm);
|
|
|
|
|
|
|
|
|
|
const auto usesMethod = [method](const SchannelCipherInfo *info) {
|
|
|
|
|
return QLatin1StringView(info->hashMethod) == method;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const bool exclude = std::none_of(cipherInfo.begin(), cipherInfo.end(), usesMethod);
|
|
|
|
|
|
|
|
|
|
if (exclude) {
|
|
|
|
|
CRYPTO_SETTINGS settings = {};
|
|
|
|
|
settings.eAlgorithmUsage = TlsParametersCngAlgUsageDigest;
|
|
|
|
|
assignUnicodeString(settings.strCngAlgId, algorithm);
|
|
|
|
|
cryptoSettings.append(settings);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return cryptoSettings;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<QSslCipher> ciphersByName(QStringView schannelSuiteName)
|
|
|
|
|
{
|
|
|
|
|
QList<QSslCipher> ciphers;
|
|
|
|
|
|
|
|
|
|
for (const auto &cipher : schannelCipherInfo) {
|
|
|
|
|
if (QLatin1StringView(cipher.schannelCipherSuite) == schannelSuiteName) {
|
|
|
|
|
for (const auto &protocol : cipher.protocols) {
|
|
|
|
|
QT_WARNING_PUSH
|
|
|
|
|
QT_WARNING_DISABLE_DEPRECATED
|
|
|
|
|
const QString protocolName = (
|
|
|
|
|
protocol == QSsl::TlsV1_0 ? QStringLiteral("TLSv1.0") :
|
|
|
|
|
protocol == QSsl::TlsV1_1 ? QStringLiteral("TLSv1.1") :
|
|
|
|
|
protocol == QSsl::TlsV1_2 ? QStringLiteral("TLSv1.2") :
|
|
|
|
|
protocol == QSsl::TlsV1_3 ? QStringLiteral("TLSv1.3") :
|
|
|
|
|
QString());
|
|
|
|
|
QT_WARNING_POP
|
|
|
|
|
|
|
|
|
|
ciphers.append(QTlsBackend::createCiphersuite(QLatin1StringView(cipher.openSslCipherSuite),
|
|
|
|
|
QLatin1StringView(cipher.keyExchangeMethod),
|
|
|
|
|
QLatin1StringView(cipher.encryptionMethod),
|
|
|
|
|
QLatin1StringView(cipher.authenticationMethod),
|
|
|
|
|
cipher.encryptionBits,
|
|
|
|
|
protocol, protocolName));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ciphers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<QSslCipher> defaultCiphers()
|
|
|
|
|
{
|
|
|
|
|
ULONG contextFunctionsCount = {};
|
|
|
|
|
PCRYPT_CONTEXT_FUNCTIONS contextFunctions = {};
|
|
|
|
|
|
|
|
|
|
const auto status = BCryptEnumContextFunctions(CRYPT_LOCAL, L"SSL", NCRYPT_SCHANNEL_INTERFACE,
|
|
|
|
|
&contextFunctionsCount, &contextFunctions);
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
qCWarning(lcTlsBackendSchannel, "Failed to enumerate ciphers");
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool supportsV13 = supportsTls13();
|
|
|
|
|
|
|
|
|
|
QList<QSslCipher> ciphers;
|
|
|
|
|
|
|
|
|
|
for (ULONG index = 0; index < contextFunctions->cFunctions; ++index) {
|
|
|
|
|
const auto suiteName = QStringView(contextFunctions->rgpszFunctions[index]);
|
|
|
|
|
|
|
|
|
|
const QList<QSslCipher> allCiphers = ciphersByName(suiteName);
|
|
|
|
|
|
|
|
|
|
for (const auto &cipher : allCiphers) {
|
|
|
|
|
if (!supportsV13 && (cipher.protocol() == QSsl::TlsV1_3))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
ciphers.append(cipher);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BCryptFreeBuffer(contextFunctions);
|
|
|
|
|
|
|
|
|
|
return ciphers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool containsTls13Cipher(const QList<QSslCipher> &ciphers)
|
|
|
|
|
{
|
|
|
|
|
return std::any_of(ciphers.cbegin(), ciphers.cend(),
|
|
|
|
|
[](const QSslCipher &cipher) { return cipher.protocol() == QSsl::TlsV1_3; });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace QTlsPrivate
|
|
|
|
|
|
|
|
|
|
bool QSchannelBackend::s_loadedCiphersAndCerts = false;
|
|
|
|
|
Q_GLOBAL_STATIC(QRecursiveMutex, qt_schannel_mutex)
|
|
|
|
|
|
|
|
|
|
@ -458,10 +762,10 @@ QT_WARNING_POP
|
|
|
|
|
// In the new API that descended down upon us we are not asked which protocols we want
|
|
|
|
|
// but rather which protocols we don't want. So now we have this function to disable
|
|
|
|
|
// anything that is not enabled.
|
|
|
|
|
DWORD toSchannelProtocolNegated(QSsl::SslProtocol protocol)
|
|
|
|
|
DWORD negatedSchannelProtocols(DWORD wantedProtocols)
|
|
|
|
|
{
|
|
|
|
|
DWORD protocols = SP_PROT_ALL; // all protocols
|
|
|
|
|
protocols &= ~toSchannelProtocol(protocol); // minus the one(s) we want
|
|
|
|
|
protocols &= ~wantedProtocols; // minus the one(s) we want
|
|
|
|
|
return protocols;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -778,7 +1082,7 @@ bool TlsCryptographSchannel::acquireCredentialsHandle()
|
|
|
|
|
Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
|
|
|
|
|
|
|
|
|
|
const bool isClient = d->tlsMode() == QSslSocket::SslClientMode;
|
|
|
|
|
const DWORD protocols = toSchannelProtocol(configuration.protocol());
|
|
|
|
|
DWORD protocols = toSchannelProtocol(configuration.protocol());
|
|
|
|
|
if (protocols == DWORD(-1)) {
|
|
|
|
|
setErrorAndEmit(d, QAbstractSocket::SslInvalidUserDataError,
|
|
|
|
|
QSslSocket::tr("Invalid protocol chosen"));
|
|
|
|
|
@ -833,12 +1137,18 @@ bool TlsCryptographSchannel::acquireCredentialsHandle()
|
|
|
|
|
Q_ASSERT(localCertContext);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QList<QSslCipher> ciphers = configuration.ciphers();
|
|
|
|
|
if (!containsTls13Cipher(ciphers))
|
|
|
|
|
protocols &= ~SP_PROT_TLS1_3;
|
|
|
|
|
|
|
|
|
|
QList<CRYPTO_SETTINGS> cryptoSettings = cryptoSettingsForCiphers(ciphers);
|
|
|
|
|
|
|
|
|
|
TLS_PARAMETERS tlsParameters = {
|
|
|
|
|
0,
|
|
|
|
|
nullptr,
|
|
|
|
|
toSchannelProtocolNegated(configuration.protocol()), // what protocols to disable
|
|
|
|
|
0,
|
|
|
|
|
nullptr,
|
|
|
|
|
negatedSchannelProtocols(protocols), // what protocols to disable
|
|
|
|
|
static_cast<DWORD>(cryptoSettings.size()),
|
|
|
|
|
(cryptoSettings.isEmpty() ? nullptr : cryptoSettings.data()),
|
|
|
|
|
0
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@ -1240,6 +1550,11 @@ bool TlsCryptographSchannel::verifyHandshake()
|
|
|
|
|
CHECK_STATUS(status);
|
|
|
|
|
|
|
|
|
|
// Get session cipher info
|
|
|
|
|
status = QueryContextAttributes(&contextHandle,
|
|
|
|
|
SECPKG_ATTR_CIPHER_INFO,
|
|
|
|
|
&cipherInfo);
|
|
|
|
|
CHECK_STATUS(status);
|
|
|
|
|
|
|
|
|
|
status = QueryContextAttributes(&contextHandle,
|
|
|
|
|
SECPKG_ATTR_CONNECTION_INFO,
|
|
|
|
|
&connectionInfo);
|
|
|
|
|
@ -1393,6 +1708,7 @@ void TlsCryptographSchannel::reset()
|
|
|
|
|
deallocateContext();
|
|
|
|
|
freeCredentialsHandle(); // in case we already had one (@future: session resumption requires re-use)
|
|
|
|
|
|
|
|
|
|
cipherInfo = {};
|
|
|
|
|
connectionInfo = {};
|
|
|
|
|
streamSizes = {};
|
|
|
|
|
|
|
|
|
|
@ -1767,8 +2083,17 @@ QSslCipher TlsCryptographSchannel::sessionCipher() const
|
|
|
|
|
Q_ASSERT(q);
|
|
|
|
|
|
|
|
|
|
if (!q->isEncrypted())
|
|
|
|
|
return QSslCipher();
|
|
|
|
|
return QSslCipher(QStringLiteral("Schannel"), sessionProtocol());
|
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
const auto sessionProtocol = toQtSslProtocol(connectionInfo.dwProtocol);
|
|
|
|
|
|
|
|
|
|
const auto ciphers = ciphersByName(QStringView(cipherInfo.szCipherSuite));
|
|
|
|
|
for (const auto& cipher : ciphers) {
|
|
|
|
|
if (cipher.protocol() == sessionProtocol)
|
|
|
|
|
return cipher;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QSsl::SslProtocol TlsCryptographSchannel::sessionProtocol() const
|
|
|
|
|
|