diff options
Diffstat (limited to 'src/network/ssl/qsslkey_qt.cpp')
-rw-r--r-- | src/network/ssl/qsslkey_qt.cpp | 785 |
1 files changed, 0 insertions, 785 deletions
diff --git a/src/network/ssl/qsslkey_qt.cpp b/src/network/ssl/qsslkey_qt.cpp deleted file mode 100644 index 3b8fada8fc..0000000000 --- a/src/network/ssl/qsslkey_qt.cpp +++ /dev/null @@ -1,785 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsslkey.h" -#include "qsslkey_p.h" -#include "qasn1element_p.h" - -#include <QtCore/qdatastream.h> -#include <QtCore/qcryptographichash.h> -#include <QtCore/QMessageAuthenticationCode> -#include <QtCore/qrandom.h> - -#include <QtNetwork/qpassworddigestor.h> - -#include <cstring> - -QT_USE_NAMESPACE - -static const quint8 bits_table[256] = { - 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, -}; - -// OIDs of named curves allowed in TLS as per RFCs 4492 and 7027, -// see also https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8 - -typedef QMap<QByteArray, int> OidLengthMap; -static OidLengthMap createOidMap() -{ - OidLengthMap oids; - oids.insert(oids.cend(), QByteArrayLiteral("1.2.840.10045.3.1.1"), 192); // secp192r1 a.k.a prime192v1 - oids.insert(oids.cend(), QByteArrayLiteral("1.2.840.10045.3.1.7"), 256); // secp256r1 a.k.a prime256v1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.1"), 193); // sect193r2 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.10"), 256); // secp256k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.16"), 283); // sect283k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.17"), 283); // sect283r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.26"), 233); // sect233k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.27"), 233); // sect233r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.3"), 239); // sect239k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.30"), 160); // secp160r2 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.31"), 192); // secp192k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.32"), 224); // secp224k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.33"), 224); // secp224r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.34"), 384); // secp384r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.35"), 521); // secp521r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.36"), 409); // sect409k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.37"), 409); // sect409r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.38"), 571); // sect571k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.39"), 571); // sect571r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.8"), 160); // secp160r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.9"), 160); // secp160k1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.11"), 384); // brainpoolP384r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.13"), 512); // brainpoolP512r1 - oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.7"), 256); // brainpoolP256r1 - return oids; -} -Q_GLOBAL_STATIC_WITH_ARGS(OidLengthMap, oidLengthMap, (createOidMap())) - -static int curveBits(const QByteArray &oid) -{ - const int length = oidLengthMap->value(oid); - return length ? length : -1; -} - -static int numberOfBits(const QByteArray &modulus) -{ - int bits = modulus.size() * 8; - for (int i = 0; i < modulus.size(); ++i) { - quint8 b = modulus[i]; - bits -= 8; - if (b != 0) { - bits += bits_table[b]; - break; - } - } - return bits; -} - -static QByteArray deriveAesKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase, const QByteArray &iv) -{ - // This is somewhat simplified and shortened version of what OpenSSL does. - // See, for example, EVP_BytesToKey for the "algorithm" itself and elsewhere - // in their code for what they pass as arguments to EVP_BytesToKey when - // deriving encryption keys (when reading/writing pems files with encrypted - // keys). - - Q_ASSERT(iv.size() >= 8); - - QCryptographicHash hash(QCryptographicHash::Md5); - - QByteArray data(passPhrase); - data.append(iv.data(), 8); // AKA PKCS5_SALT_LEN in OpenSSL. - - hash.addData(data); - - if (cipher == QSslKeyPrivate::Aes128Cbc) - return hash.result(); - - QByteArray key(hash.result()); - hash.reset(); - hash.addData(key); - hash.addData(data); - - if (cipher == QSslKeyPrivate::Aes192Cbc) - return key.append(hash.result().constData(), 8); - - return key.append(hash.result()); -} - -static QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase, const QByteArray &iv) -{ - QByteArray key; - QCryptographicHash hash(QCryptographicHash::Md5); - hash.addData(passPhrase); - hash.addData(iv); - switch (cipher) { - case QSslKeyPrivate::DesCbc: - key = hash.result().left(8); - break; - case QSslKeyPrivate::DesEde3Cbc: - key = hash.result(); - hash.reset(); - hash.addData(key); - hash.addData(passPhrase); - hash.addData(iv); - key += hash.result().left(8); - break; - case QSslKeyPrivate::Rc2Cbc: - key = hash.result(); - break; - case QSslKeyPrivate::Aes128Cbc: - case QSslKeyPrivate::Aes192Cbc: - case QSslKeyPrivate::Aes256Cbc: - return deriveAesKey(cipher, passPhrase, iv); - } - return key; -} - -void QSslKeyPrivate::clear(bool deep) -{ - isNull = true; - if (deep) - std::memset(derData.data(), 0, derData.size()); - derData.clear(); - keyLength = -1; -} - -static int extractPkcs8KeyLength(const QList<QAsn1Element> &items, QSslKeyPrivate *that) -{ - Q_ASSERT(items.size() == 3); - int keyLength; - - auto getName = [](QSsl::KeyAlgorithm algorithm) { - switch (algorithm){ - case QSsl::Rsa: return "RSA"; - case QSsl::Dsa: return "DSA"; - case QSsl::Dh: return "DH"; - case QSsl::Ec: return "EC"; - case QSsl::Opaque: return "Opaque"; - } - Q_UNREACHABLE(); - }; - - const auto pkcs8Info = items[1].toList(); - if (pkcs8Info.size() != 2 || pkcs8Info[0].type() != QAsn1Element::ObjectIdentifierType) - return -1; - const QByteArray value = pkcs8Info[0].toObjectId(); - if (value == RSA_ENCRYPTION_OID) { - if (Q_UNLIKELY(that->algorithm != QSsl::Rsa)) { - // We could change the 'algorithm' of QSslKey here and continue loading, but - // this is not supported in the openssl back-end, so we'll fail here and give - // the user some feedback. - qWarning() << "QSslKey: Found RSA key when asked to use" << getName(that->algorithm) - << "\nLoading will fail."; - return -1; - } - // Luckily it contains the 'normal' RSA-key format inside, so we can just recurse - // and read the key's info. - that->decodeDer(items[2].value()); - // The real info has been filled out in the call above, so return as if it was invalid - // to avoid overwriting the data. - return -1; - } else if (value == EC_ENCRYPTION_OID) { - if (Q_UNLIKELY(that->algorithm != QSsl::Ec)) { - // As above for RSA. - qWarning() << "QSslKey: Found EC key when asked to use" << getName(that->algorithm) - << "\nLoading will fail."; - return -1; - } - // I don't know where this is documented, but the elliptic-curve identifier has been - // moved into the "pkcs#8 wrapper", which is what we're interested in. - if (pkcs8Info[1].type() != QAsn1Element::ObjectIdentifierType) - return -1; - keyLength = curveBits(pkcs8Info[1].toObjectId()); - } else if (value == DSA_ENCRYPTION_OID) { - if (Q_UNLIKELY(that->algorithm != QSsl::Dsa)) { - // As above for RSA. - qWarning() << "QSslKey: Found DSA when asked to use" << getName(that->algorithm) - << "\nLoading will fail."; - return -1; - } - // DSA's structure is documented here: - // https://www.cryptsoft.com/pkcs11doc/STANDARD/v201-95.pdf in section 11.9. - if (pkcs8Info[1].type() != QAsn1Element::SequenceType) - return -1; - const auto dsaInfo = pkcs8Info[1].toList(); - if (dsaInfo.size() != 3 || dsaInfo[0].type() != QAsn1Element::IntegerType) - return -1; - keyLength = numberOfBits(dsaInfo[0].value()); - } else if (value == DH_ENCRYPTION_OID) { - if (Q_UNLIKELY(that->algorithm != QSsl::Dh)) { - // As above for RSA. - qWarning() << "QSslKey: Found DH when asked to use" << getName(that->algorithm) - << "\nLoading will fail."; - return -1; - } - // DH's structure is documented here: - // https://www.cryptsoft.com/pkcs11doc/STANDARD/v201-95.pdf in section 11.9. - if (pkcs8Info[1].type() != QAsn1Element::SequenceType) - return -1; - const auto dhInfo = pkcs8Info[1].toList(); - if (dhInfo.size() < 2 || dhInfo.size() > 3 || dhInfo[0].type() != QAsn1Element::IntegerType) - return -1; - keyLength = numberOfBits(dhInfo[0].value()); - } else { - // in case of unexpected formats: - qWarning() << "QSslKey: Unsupported PKCS#8 key algorithm:" << value - << "\nFile a bugreport to Qt (include the line above)."; - return -1; - } - return keyLength; -} - -void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhrase, bool deepClear) -{ - clear(deepClear); - - if (der.isEmpty()) - return; - // decryptPkcs8 decrypts if necessary or returns 'der' unaltered - QByteArray decryptedDer = decryptPkcs8(der, passPhrase); - - QAsn1Element elem; - if (!elem.read(decryptedDer) || elem.type() != QAsn1Element::SequenceType) - return; - - if (type == QSsl::PublicKey) { - // key info - QDataStream keyStream(elem.value()); - if (!elem.read(keyStream) || elem.type() != QAsn1Element::SequenceType) - return; - const auto infoItems = elem.toList(); - if (infoItems.size() < 2 || infoItems[0].type() != QAsn1Element::ObjectIdentifierType) - return; - if (algorithm == QSsl::Rsa) { - if (infoItems[0].toObjectId() != RSA_ENCRYPTION_OID) - return; - // key data - if (!elem.read(keyStream) || elem.type() != QAsn1Element::BitStringType || elem.value().isEmpty()) - return; - if (!elem.read(elem.value().mid(1)) || elem.type() != QAsn1Element::SequenceType) - return; - if (!elem.read(elem.value()) || elem.type() != QAsn1Element::IntegerType) - return; - keyLength = numberOfBits(elem.value()); - } else if (algorithm == QSsl::Dsa) { - if (infoItems[0].toObjectId() != DSA_ENCRYPTION_OID) - return; - if (infoItems[1].type() != QAsn1Element::SequenceType) - return; - // key params - const auto params = infoItems[1].toList(); - if (params.isEmpty() || params[0].type() != QAsn1Element::IntegerType) - return; - keyLength = numberOfBits(params[0].value()); - } else if (algorithm == QSsl::Dh) { - if (infoItems[0].toObjectId() != DH_ENCRYPTION_OID) - return; - if (infoItems[1].type() != QAsn1Element::SequenceType) - return; - // key params - const auto params = infoItems[1].toList(); - if (params.isEmpty() || params[0].type() != QAsn1Element::IntegerType) - return; - keyLength = numberOfBits(params[0].value()); - } else if (algorithm == QSsl::Ec) { - if (infoItems[0].toObjectId() != EC_ENCRYPTION_OID) - return; - if (infoItems[1].type() != QAsn1Element::ObjectIdentifierType) - return; - keyLength = curveBits(infoItems[1].toObjectId()); - } - - } else { - const auto items = elem.toList(); - if (items.isEmpty()) - return; - - // version - if (items[0].type() != QAsn1Element::IntegerType) - return; - const QByteArray versionHex = items[0].value().toHex(); - - if (items.size() == 3 && items[1].type() == QAsn1Element::SequenceType - && items[2].type() == QAsn1Element::OctetStringType) { - if (versionHex != "00" && versionHex != "01") - return; - int pkcs8KeyLength = extractPkcs8KeyLength(items, this); - if (pkcs8KeyLength == -1) - return; - isPkcs8 = true; - keyLength = pkcs8KeyLength; - } else if (algorithm == QSsl::Rsa) { - if (versionHex != "00") - return; - if (items.size() != 9 || items[1].type() != QAsn1Element::IntegerType) - return; - keyLength = numberOfBits(items[1].value()); - } else if (algorithm == QSsl::Dsa) { - if (versionHex != "00") - return; - if (items.size() != 6 || items[1].type() != QAsn1Element::IntegerType) - return; - keyLength = numberOfBits(items[1].value()); - } else if (algorithm == QSsl::Dh) { - if (versionHex != "00") - return; - if (items.size() < 5 || items.size() > 6 || items[1].type() != QAsn1Element::IntegerType) - return; - keyLength = numberOfBits(items[1].value()); - } else if (algorithm == QSsl::Ec) { - if (versionHex != "01") - return; - if (items.size() != 4 - || items[1].type() != QAsn1Element::OctetStringType - || items[2].type() != QAsn1Element::Context0Type - || items[3].type() != QAsn1Element::Context1Type) - return; - QAsn1Element oidElem; - if (!oidElem.read(items[2].value()) - || oidElem.type() != QAsn1Element::ObjectIdentifierType) - return; - keyLength = curveBits(oidElem.toObjectId()); - } - } - - derData = decryptedDer; - isNull = false; -} - -void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase, - bool deepClear) -{ - QMap<QByteArray, QByteArray> headers; - QByteArray data = derFromPem(pem, &headers); - if (headers.value("Proc-Type") == "4,ENCRYPTED") { - const QList<QByteArray> dekInfo = headers.value("DEK-Info").split(','); - if (dekInfo.size() != 2) { - clear(deepClear); - return; - } - - Cipher cipher; - if (dekInfo.first() == "DES-CBC") { - cipher = DesCbc; - } else if (dekInfo.first() == "DES-EDE3-CBC") { - cipher = DesEde3Cbc; - } else if (dekInfo.first() == "RC2-CBC") { - cipher = Rc2Cbc; - } else if (dekInfo.first() == "AES-128-CBC") { - cipher = Aes128Cbc; - } else if (dekInfo.first() == "AES-192-CBC") { - cipher = Aes192Cbc; - } else if (dekInfo.first() == "AES-256-CBC") { - cipher = Aes256Cbc; - } else { - clear(deepClear); - return; - } - - const QByteArray iv = QByteArray::fromHex(dekInfo.last()); - const QByteArray key = deriveKey(cipher, passPhrase, iv); - data = decrypt(cipher, data, key, iv); - } - decodeDer(data, passPhrase, deepClear); -} - -int QSslKeyPrivate::length() const -{ - return keyLength; -} - -QByteArray QSslKeyPrivate::toPem(const QByteArray &passPhrase) const -{ - QByteArray data; - QMap<QByteArray, QByteArray> headers; - - if (type == QSsl::PrivateKey && !passPhrase.isEmpty()) { - // ### use a cryptographically secure random number generator - quint64 random = QRandomGenerator::system()->generate64(); - QByteArray iv = QByteArray::fromRawData(reinterpret_cast<const char *>(&random), sizeof(random)); - - Cipher cipher = DesEde3Cbc; - const QByteArray key = deriveKey(cipher, passPhrase, iv); - data = encrypt(cipher, derData, key, iv); - - headers.insert("Proc-Type", "4,ENCRYPTED"); - headers.insert("DEK-Info", "DES-EDE3-CBC," + iv.toHex()); - } else { - data = derData; - } - - return pemFromDer(data, headers); -} - -Qt::HANDLE QSslKeyPrivate::handle() const -{ - return opaque; -} - -// Maps OIDs to the encryption cipher they specify -static const QMap<QByteArray, QSslKeyPrivate::Cipher> oidCipherMap { - {DES_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::DesCbc}, - {DES_EDE3_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::DesEde3Cbc}, - // {PKCS5_MD2_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc}, // No MD2 - {PKCS5_MD5_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc}, - {PKCS5_SHA1_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc}, - // {PKCS5_MD2_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc}, // No MD2 - {PKCS5_MD5_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc}, - {PKCS5_SHA1_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc}, - {RC2_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Rc2Cbc} - // {RC5_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Rc5Cbc}, // No RC5 - // {AES128_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes128}, // no AES - // {AES192_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes192}, - // {AES256_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes256} -}; - -struct EncryptionData -{ - EncryptionData() : initialized(false) - {} - EncryptionData(QSslKeyPrivate::Cipher cipher, QByteArray key, QByteArray iv) - : initialized(true), cipher(cipher), key(key), iv(iv) - {} - bool initialized; - QSslKeyPrivate::Cipher cipher; - QByteArray key; - QByteArray iv; -}; - -static EncryptionData readPbes2(const QList<QAsn1Element> &element, const QByteArray &passPhrase) -{ - // RFC 8018: https://tools.ietf.org/html/rfc8018#section-6.2 - /*** Scheme: *** - * Sequence (scheme-specific info..) - * Sequence (key derivation info) - * Object Identifier (Key derivation algorithm (e.g. PBKDF2)) - * Sequence (salt) - * CHOICE (this entry can be either of the types it contains) - * Octet string (actual salt) - * Object identifier (Anything using this is deferred to a later version of PKCS #5) - * Integer (iteration count) - * Sequence (encryption algorithm info) - * Object identifier (identifier for the algorithm) - * Algorithm dependent, is covered in the switch further down - */ - - static const QMap<QByteArray, QCryptographicHash::Algorithm> pbes2OidHashFunctionMap { - // PBES2/PBKDF2 - {HMAC_WITH_SHA1, QCryptographicHash::Sha1}, - {HMAC_WITH_SHA224, QCryptographicHash::Sha224}, - {HMAC_WITH_SHA256, QCryptographicHash::Sha256}, - {HMAC_WITH_SHA512, QCryptographicHash::Sha512}, - {HMAC_WITH_SHA512_224, QCryptographicHash::Sha512}, - {HMAC_WITH_SHA512_256, QCryptographicHash::Sha512}, - {HMAC_WITH_SHA384, QCryptographicHash::Sha384} - }; - - // Values from their respective sections here: https://tools.ietf.org/html/rfc8018#appendix-B.2 - static const QMap<QSslKeyPrivate::Cipher, int> cipherKeyLengthMap { - {QSslKeyPrivate::Cipher::DesCbc, 8}, - {QSslKeyPrivate::Cipher::DesEde3Cbc, 24}, - // @note: variable key-length (https://tools.ietf.org/html/rfc8018#appendix-B.2.3) - {QSslKeyPrivate::Cipher::Rc2Cbc, 4} - // @todo: AES(, rc5?) - }; - - const QList<QAsn1Element> keyDerivationContainer = element[0].toList(); - if (keyDerivationContainer.size() != 2 - || keyDerivationContainer[0].type() != QAsn1Element::ObjectIdentifierType - || keyDerivationContainer[1].type() != QAsn1Element::SequenceType) { - return {}; - } - - const QByteArray keyDerivationAlgorithm = keyDerivationContainer[0].toObjectId(); - const auto keyDerivationParams = keyDerivationContainer[1].toList(); - - const auto encryptionAlgorithmContainer = element[1].toList(); - if (encryptionAlgorithmContainer.size() != 2 - || encryptionAlgorithmContainer[0].type() != QAsn1Element::ObjectIdentifierType) { - return {}; - } - - auto iterator = oidCipherMap.constFind(encryptionAlgorithmContainer[0].toObjectId()); - if (iterator == oidCipherMap.cend()) { - qWarning() - << "QSslKey: Unsupported encryption cipher OID:" << encryptionAlgorithmContainer[0].toObjectId() - << "\nFile a bugreport to Qt (include the line above)."; - return {}; - } - - QSslKeyPrivate::Cipher cipher = *iterator; - QByteArray key; - QByteArray iv; - switch (cipher) { - case QSslKeyPrivate::Cipher::DesCbc: - case QSslKeyPrivate::Cipher::DesEde3Cbc: - // https://tools.ietf.org/html/rfc8018#appendix-B.2.1 (DES-CBC-PAD) - // https://tools.ietf.org/html/rfc8018#appendix-B.2.2 (DES-EDE3-CBC-PAD) - // @todo https://tools.ietf.org/html/rfc8018#appendix-B.2.5 (AES-CBC-PAD) - /*** Scheme: *** - * Octet string (IV) - */ - if (encryptionAlgorithmContainer[1].type() != QAsn1Element::OctetStringType) - return {}; - - // @note: All AES identifiers should be able to use this branch!! - iv = encryptionAlgorithmContainer[1].value(); - - if (iv.size() != 8) // @note: AES needs 16 bytes - return {}; - break; - case QSslKeyPrivate::Cipher::Rc2Cbc: { - // https://tools.ietf.org/html/rfc8018#appendix-B.2.3 - /*** Scheme: *** - * Sequence (rc2 parameters) - * Integer (rc2 parameter version) - * Octet string (IV) - */ - if (encryptionAlgorithmContainer[1].type() != QAsn1Element::SequenceType) - return {}; - const auto rc2ParametersContainer = encryptionAlgorithmContainer[1].toList(); - if ((rc2ParametersContainer.size() != 1 && rc2ParametersContainer.size() != 2) - || rc2ParametersContainer.back().type() != QAsn1Element::OctetStringType) { - return {}; - } - iv = rc2ParametersContainer.back().value(); - if (iv.size() != 8) - return {}; - break; - } // @todo(?): case (RC5 , AES) - case QSslKeyPrivate::Cipher::Aes128Cbc: - case QSslKeyPrivate::Cipher::Aes192Cbc: - case QSslKeyPrivate::Cipher::Aes256Cbc: - Q_UNREACHABLE(); - } - - if (Q_LIKELY(keyDerivationAlgorithm == PKCS5_PBKDF2_ENCRYPTION_OID)) { - // Definition: https://tools.ietf.org/html/rfc8018#appendix-A.2 - QByteArray salt; - if (keyDerivationParams[0].type() == QAsn1Element::OctetStringType) { - salt = keyDerivationParams[0].value(); - } else if (keyDerivationParams[0].type() == QAsn1Element::ObjectIdentifierType) { - Q_UNIMPLEMENTED(); - /* See paragraph from https://tools.ietf.org/html/rfc8018#appendix-A.2 - which ends with: "such facilities are deferred to a future version of PKCS #5" - */ - return {}; - } else { - return {}; - } - - // Iterations needed to derive the key - int iterationCount = keyDerivationParams[1].toInteger(); - // Optional integer - int keyLength = -1; - int vectorPos = 2; - if (keyDerivationParams.size() > vectorPos - && keyDerivationParams[vectorPos].type() == QAsn1Element::IntegerType) { - keyLength = keyDerivationParams[vectorPos].toInteger(nullptr); - ++vectorPos; - } else { - keyLength = cipherKeyLengthMap[cipher]; - } - - // Optional algorithm identifier (default: HMAC-SHA-1) - QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha1; - if (keyDerivationParams.size() > vectorPos - && keyDerivationParams[vectorPos].type() == QAsn1Element::SequenceType) { - const auto hashAlgorithmContainer = keyDerivationParams[vectorPos].toList(); - hashAlgorithm = pbes2OidHashFunctionMap[hashAlgorithmContainer.front().toObjectId()]; - Q_ASSERT(hashAlgorithmContainer[1].type() == QAsn1Element::NullType); - ++vectorPos; - } - Q_ASSERT(keyDerivationParams.size() == vectorPos); - - key = QPasswordDigestor::deriveKeyPbkdf2(hashAlgorithm, passPhrase, salt, iterationCount, keyLength); - } else { - qWarning() - << "QSslKey: Unsupported key derivation algorithm OID:" << keyDerivationAlgorithm - << "\nFile a bugreport to Qt (include the line above)."; - return {}; - } - return {cipher, key, iv}; -} - -// Maps OIDs to the hash function it specifies -static const QMap<QByteArray, QCryptographicHash::Algorithm> pbes1OidHashFunctionMap { -#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 - // PKCS5 - //{PKCS5_MD2_DES_CBC_OID, QCryptographicHash::Md2}, No MD2 - //{PKCS5_MD2_RC2_CBC_OID, QCryptographicHash::Md2}, - {PKCS5_MD5_DES_CBC_OID, QCryptographicHash::Md5}, - {PKCS5_MD5_RC2_CBC_OID, QCryptographicHash::Md5}, -#endif - {PKCS5_SHA1_DES_CBC_OID, QCryptographicHash::Sha1}, - {PKCS5_SHA1_RC2_CBC_OID, QCryptographicHash::Sha1}, - // PKCS12 (unimplemented) - // {PKCS12_SHA1_RC4_128_OID, QCryptographicHash::Sha1}, // No RC4 - // {PKCS12_SHA1_RC4_40_OID, QCryptographicHash::Sha1}, - // @todo: lacking support. @note: there might be code to do this inside qsslsocket_mac... - // further note that more work may be required for the 3DES variations listed to be available. - // {PKCS12_SHA1_3KEY_3DES_CBC_OID, QCryptographicHash::Sha1}, - // {PKCS12_SHA1_2KEY_3DES_CBC_OID, QCryptographicHash::Sha1}, - // {PKCS12_SHA1_RC2_128_CBC_OID, QCryptographicHash::Sha1}, - // {PKCS12_SHA1_RC2_40_CBC_OID, QCryptographicHash::Sha1} -}; - -static EncryptionData readPbes1(const QList<QAsn1Element> &element, - const QByteArray &encryptionScheme, const QByteArray &passPhrase) -{ - // RFC 8018: https://tools.ietf.org/html/rfc8018#section-6.1 - // Steps refer to this section: https://tools.ietf.org/html/rfc8018#section-6.1.2 - /*** Scheme: *** - * Sequence (PBE Parameter) - * Octet string (salt) - * Integer (iteration counter) - */ - // Step 1 - if (element.size() != 2 - || element[0].type() != QAsn1Element::ElementType::OctetStringType - || element[1].type() != QAsn1Element::ElementType::IntegerType) { - return {}; - } - QByteArray salt = element[0].value(); - if (salt.size() != 8) - return {}; - - int iterationCount = element[1].toInteger(); - if (iterationCount < 0) - return {}; - - // Step 2 - auto iterator = pbes1OidHashFunctionMap.constFind(encryptionScheme); - if (iterator == pbes1OidHashFunctionMap.cend()) { - // Qt was compiled with ONLY_SHA1 (or it's MD2) - return {}; - } - QCryptographicHash::Algorithm hashAlgorithm = *iterator; - QByteArray key = QPasswordDigestor::deriveKeyPbkdf1(hashAlgorithm, passPhrase, salt, iterationCount, 16); - if (key.size() != 16) - return {}; - - // Step 3 - QByteArray iv = key.right(8); // last 8 bytes are used as IV - key.truncate(8); // first 8 bytes are used for the key - - QSslKeyPrivate::Cipher cipher = oidCipherMap[encryptionScheme]; - // Steps 4-6 are done after returning - return {cipher, key, iv}; -} - -QByteArray QSslKeyPrivate::decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase) -{ - // RFC 5958: https://tools.ietf.org/html/rfc5958 - /*** Scheme: *** - * Sequence - * Sequence - * Object Identifier (encryption scheme (currently PBES2, PBES1, @todo PKCS12)) - * Sequence (scheme parameters) - * Octet String (the encrypted data) - */ - QAsn1Element elem; - if (!elem.read(encrypted) || elem.type() != QAsn1Element::SequenceType) - return encrypted; - - const auto items = elem.toList(); - if (items.size() != 2 - || items[0].type() != QAsn1Element::SequenceType - || items[1].type() != QAsn1Element::OctetStringType) { - return encrypted; - } - - const auto encryptionSchemeContainer = items[0].toList(); - - if (encryptionSchemeContainer.size() != 2 - || encryptionSchemeContainer[0].type() != QAsn1Element::ObjectIdentifierType - || encryptionSchemeContainer[1].type() != QAsn1Element::SequenceType) { - return encrypted; - } - - const QByteArray encryptionScheme = encryptionSchemeContainer[0].toObjectId(); - const auto schemeParameterContainer = encryptionSchemeContainer[1].toList(); - - if (schemeParameterContainer.size() != 2 - && schemeParameterContainer[0].type() != QAsn1Element::SequenceType - && schemeParameterContainer[1].type() != QAsn1Element::SequenceType) { - return encrypted; - } - - EncryptionData data; - if (encryptionScheme == PKCS5_PBES2_ENCRYPTION_OID) { - data = readPbes2(schemeParameterContainer, passPhrase); - } else if (pbes1OidHashFunctionMap.contains(encryptionScheme)) { - data = readPbes1(schemeParameterContainer, encryptionScheme, passPhrase); - } else if (encryptionScheme.startsWith(PKCS12_OID)) { - Q_UNIMPLEMENTED(); // this isn't some 'unknown', I know these aren't implemented - return encrypted; - } else { - qWarning() - << "QSslKey: Unsupported encryption scheme OID:" << encryptionScheme - << "\nFile a bugreport to Qt (include the line above)."; - return encrypted; - } - - if (!data.initialized) { - // something went wrong, return - return encrypted; - } - - QByteArray decryptedKey = decrypt(data.cipher, items[1].value(), data.key, data.iv); - // The data is still wrapped in a octet string, so let's unwrap it - QAsn1Element decryptedKeyElement(QAsn1Element::ElementType::OctetStringType, decryptedKey); - return decryptedKeyElement.value(); -} |