File indexing completed on 2024-04-28 04:57:03

0001 /**
0002  * SPDX-FileCopyrightText: 2023 Albert Vaca <albertvaka@gmail.com>
0003  * SPDX-FileCopyrightText: 2023 Edward Kigwana <ekigwana@gmail.com>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006  */
0007 
0008 #include "sslhelper.h"
0009 #include "core_debug.h"
0010 
0011 extern "C" {
0012 #include <openssl/bn.h>
0013 #include <openssl/err.h>
0014 #include <openssl/evp.h>
0015 #include <openssl/pem.h>
0016 #include <openssl/rsa.h>
0017 #include <openssl/x509.h>
0018 }
0019 
0020 namespace SslHelper
0021 {
0022 QString getSslError()
0023 {
0024     char buf[256];
0025     ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
0026     return QString::fromLatin1(buf);
0027 }
0028 
0029 QSslKey generateRsaPrivateKey()
0030 {
0031     // Initialize context.
0032     auto pctxRaw = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr);
0033     auto pctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(pctxRaw, ::EVP_PKEY_CTX_free);
0034     if (!pctx) {
0035         qCWarning(KDECONNECT_CORE) << "Generate RSA Private Key failed to allocate context " << getSslError();
0036         return QSslKey();
0037     }
0038 
0039     if (EVP_PKEY_keygen_init(pctx.get()) <= 0) {
0040         qCWarning(KDECONNECT_CORE) << "Generate RSA Private Key failed to initialize context " << getSslError();
0041         return QSslKey();
0042     }
0043 
0044     // Set key bits.
0045     if (EVP_PKEY_CTX_set_rsa_keygen_bits(pctx.get(), 2048) <= 0) {
0046         qCWarning(KDECONNECT_CORE) << "Generate RSA Private Key failed to set key bits " << getSslError();
0047         return QSslKey();
0048     }
0049 
0050     // Generate private key.
0051     auto pkey = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>(EVP_PKEY_new(), ::EVP_PKEY_free);
0052     if (!pkey) {
0053         qCWarning(KDECONNECT_CORE) << "Generate RSA Private Key failed to allocate private key " << getSslError();
0054         return QSslKey();
0055     }
0056 
0057     auto pkey_raw = pkey.get();
0058     if (EVP_PKEY_keygen(pctx.get(), &pkey_raw) <= 0) {
0059         qCWarning(KDECONNECT_CORE) << "Generate RSA Private Key failed to generate private key " << getSslError();
0060         return QSslKey();
0061     }
0062 
0063     // Convert private key format to PEM as required by QSslKey.
0064     auto bio = std::unique_ptr<BIO, decltype(&::BIO_free_all)>(BIO_new(BIO_s_mem()), ::BIO_free_all);
0065     if (!bio) {
0066         qCWarning(KDECONNECT_CORE) << "Generate RSA Private Key failed to allocate I/O abstraction " << getSslError();
0067         return QSslKey();
0068     }
0069 
0070     if (!PEM_write_bio_PrivateKey(bio.get(), pkey_raw, nullptr, nullptr, 0, nullptr, nullptr)) {
0071         qCWarning(KDECONNECT_CORE) << "Generate RSA Private Key failed write PEM format private key to BIO " << getSslError();
0072         return QSslKey();
0073     }
0074 
0075     BUF_MEM *mem = nullptr;
0076     if (!BIO_get_mem_ptr(bio.get(), &mem)) {
0077         qCWarning(KDECONNECT_CORE) << "Generate RSA Private Key failed get PEM format address " << getSslError();
0078         return QSslKey();
0079     }
0080 
0081     return QSslKey(QByteArray(mem->data, mem->length), QSsl::KeyAlgorithm::Rsa);
0082 }
0083 
0084 QSslCertificate generateSelfSignedCertificate(const QSslKey &qtPrivateKey, const QString &commonName)
0085 {
0086     // Create certificate.
0087     auto x509 = std::unique_ptr<X509, decltype(&::X509_free)>(X509_new(), ::X509_free);
0088     if (!x509) {
0089         qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to allocate certifcate " << getSslError();
0090         return QSslCertificate();
0091     }
0092 
0093     if (!X509_set_version(x509.get(), 2)) {
0094         qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to set version " << getSslError();
0095         return QSslCertificate();
0096     }
0097 
0098     // Generate a random serial number for the certificate.
0099     auto sn = std::unique_ptr<BIGNUM, decltype(&::BN_free)>(BN_new(), ::BN_free);
0100     if (!sn) {
0101         qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to allocate big number structure " << getSslError();
0102         return QSslCertificate();
0103     }
0104 
0105     if (!BN_rand(sn.get(), 160, -1, 0)) { // as per rfc3280, serial numbers must be 20 bytes (160 bits) or less
0106         qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to generate random number " << getSslError();
0107         return QSslCertificate();
0108     }
0109 
0110     if (!BN_to_ASN1_INTEGER(sn.get(), X509_get_serialNumber(x509.get()))) {
0111         qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to convert number structure to integer" << getSslError();
0112         return QSslCertificate();
0113     }
0114 
0115     // Set the certificate subject and issuer (self-signed).
0116     auto name = X509_get_subject_name(x509.get());
0117     QByteArray commonNameBytes = commonName.toLatin1();
0118     const unsigned char *commonNameCStr = reinterpret_cast<const unsigned char *>(commonNameBytes.data());
0119     if (!X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, commonNameCStr, -1, -1, 0)) { // Common Name
0120         qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to set common name to " << commonName << " " << getSslError();
0121         return QSslCertificate();
0122     }
0123 
0124     const unsigned char *organizationCStr = reinterpret_cast<const unsigned char *>("KDE");
0125     if (!X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, organizationCStr, -1, -1, 0)) { // Organization
0126         qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to set organization " << getSslError();
0127         return QSslCertificate();
0128     }
0129 
0130     const unsigned char *organizationalUnitCStr = reinterpret_cast<const unsigned char *>("KDE Connect");
0131     if (!X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, organizationalUnitCStr, -1, -1, 0)) { // Organizational Unit
0132         qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to set organizational unit " << getSslError();
0133         return QSslCertificate();
0134     }
0135 
0136     if (!X509_set_subject_name(x509.get(), name)) {
0137         qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to set subject name" << getSslError();
0138         return QSslCertificate();
0139     }
0140 
0141     if (!X509_set_issuer_name(x509.get(), name)) {
0142         qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to set issuer name" << getSslError();
0143         return QSslCertificate();
0144     }
0145 
0146     // Set the certificate validity period.
0147     int a_year_in_seconds = 356 * 24 * 60 * 60;
0148     X509_gmtime_adj(X509_getm_notBefore(x509.get()), -a_year_in_seconds);
0149     X509_gmtime_adj(X509_getm_notAfter(x509.get()), 10 * a_year_in_seconds);
0150 
0151     // Convert the QSslKey to the OpenSSL private key format.
0152     QByteArray keyPemData = qtPrivateKey.toPem();
0153     auto bio = std::unique_ptr<BIO, decltype(&::BIO_free_all)>(BIO_new_mem_buf(keyPemData.data(), -1), ::BIO_free_all);
0154     if (!bio) {
0155         qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to allocate I/O abstraction " << getSslError();
0156         return QSslCertificate();
0157     }
0158 
0159     auto pkeyRaw = PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr);
0160     auto pkey = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>(pkeyRaw, ::EVP_PKEY_free);
0161     if (!pkey) {
0162         qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to read private key " << getSslError();
0163         return QSslCertificate();
0164     }
0165 
0166     if (!X509_set_pubkey(x509.get(), pkey.get())) {
0167         qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to set private key " << getSslError();
0168         return QSslCertificate();
0169     }
0170 
0171     // Sign the certificate with private key.
0172     if (!X509_sign(x509.get(), pkey.get(), EVP_sha256())) {
0173         qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to sign certificate " << getSslError();
0174         return QSslCertificate();
0175     }
0176 
0177     // Convert to PEM which is the format needed for QSslCertificate.
0178     bio = std::unique_ptr<BIO, decltype(&::BIO_free_all)>(BIO_new(BIO_s_mem()), ::BIO_free_all);
0179     if (!bio) {
0180         qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to allocate I/O abstraction " << getSslError();
0181         return QSslCertificate();
0182     }
0183 
0184     if (!PEM_write_bio_X509(bio.get(), x509.get())) {
0185         qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed write PEM format certificate to BIO " << getSslError();
0186         return QSslCertificate();
0187     }
0188 
0189     BUF_MEM *pem = nullptr;
0190     if (!BIO_get_mem_ptr(bio.get(), &pem)) {
0191         qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed get PEM format address " << getSslError();
0192         return QSslCertificate();
0193     }
0194 
0195     return QSslCertificate(QByteArray(pem->data, pem->length));
0196 }
0197 
0198 } // namespace SslHelper