diff --git a/app/http/identitymanager.cpp b/app/http/identitymanager.cpp index 68ef9e59..91893d88 100644 --- a/app/http/identitymanager.cpp +++ b/app/http/identitymanager.cpp @@ -2,17 +2,17 @@ #include "utils.h" #include -#include -#include -#include -#include -#include +#include #include #include #include #include +#define SER_UNIQUEID "uniqueid" +#define SER_CERT "certificate" +#define SER_KEY "key" + IdentityManager* IdentityManager::s_Im = nullptr; IdentityManager* @@ -27,50 +27,8 @@ IdentityManager::get() return s_Im; } -IdentityManager::IdentityManager() +void IdentityManager::createCredentials(QSettings& settings) { - m_RootDirectory = QDir(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)); - if (!m_RootDirectory.exists()) - { - m_RootDirectory.mkpath("."); - } - - QFile uniqueIdFile(m_RootDirectory.filePath("uniqueid")); - if (uniqueIdFile.open(QIODevice::ReadOnly)) - { - m_CachedUniqueId = QTextStream(&uniqueIdFile).readAll(); - qDebug() << "Loaded cached unique ID: " << m_CachedUniqueId; - } - else - { - for (int i = 0; i < 16; i++) - { - int n = qrand() % 16; - m_CachedUniqueId.append(QString::number(n, 16)); - } - - qDebug() << "Generated new unique ID: " << m_CachedUniqueId; - - uniqueIdFile.open(QIODevice::ReadWrite); - QTextStream(&uniqueIdFile) << m_CachedUniqueId; - } - - QFile certificateFile(m_RootDirectory.filePath("cert")); - QFile privateKeyFile(m_RootDirectory.filePath("key")); - - if (certificateFile.open(QIODevice::ReadOnly) && privateKeyFile.open(QIODevice::ReadOnly)) - { - // Not cached yet, but it's on disk - m_CachedPemCert = certificateFile.readAll(); - m_CachedPrivateKey = privateKeyFile.readAll(); - - qDebug() << "Loaded cached identity key pair from disk"; - return; - } - - privateKeyFile.close(); - certificateFile.close(); - X509* cert = X509_new(); THROW_BAD_ALLOC_IF_NULL(cert); @@ -112,7 +70,9 @@ IdentityManager::IdentityManager() X509_set_pubkey(cert, pk); X509_NAME* name = X509_get_subject_name(cert); - X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, reinterpret_cast(const_cast("NVIDIA GameStream Client")), -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, + reinterpret_cast(const_cast("NVIDIA GameStream Client")), + -1, -1, 0); X509_set_issuer_name(cert, name); X509_sign(cert, pk, EVP_sha1()); @@ -125,17 +85,12 @@ IdentityManager::IdentityManager() THROW_BAD_ALLOC_IF_NULL(biocert); PEM_write_bio_X509(biocert, cert); - privateKeyFile.open(QIODevice::WriteOnly); - certificateFile.open(QIODevice::WriteOnly); - BUF_MEM* mem; BIO_get_mem_ptr(biokey, &mem); m_CachedPrivateKey = QByteArray(mem->data, (int)mem->length); - QTextStream(&privateKeyFile) << m_CachedPrivateKey; BIO_get_mem_ptr(biocert, &mem); m_CachedPemCert = QByteArray(mem->data, (int)mem->length); - QTextStream(&certificateFile) << m_CachedPemCert; X509_free(cert); EVP_PKEY_free(pk); @@ -143,45 +98,107 @@ IdentityManager::IdentityManager() BIO_free(biokey); BIO_free(biocert); - qDebug() << "Wrote new identity credentials to disk"; + settings.setValue(SER_CERT, m_CachedPemCert); + settings.setValue(SER_KEY, m_CachedPrivateKey); + + qDebug() << "Wrote new identity credentials to settings"; +} + +IdentityManager::IdentityManager() +{ + QSettings settings; + + m_CachedPemCert = settings.value(SER_CERT).toByteArray(); + m_CachedPrivateKey = settings.value(SER_KEY).toByteArray(); + + if (m_CachedPemCert.isEmpty() || m_CachedPrivateKey.isEmpty()) { + qDebug() << "No existing credentials found"; + createCredentials(settings); + } + else if (getSslCertificate().isNull()) { + qWarning() << "Certificate is unreadable"; + createCredentials(settings); + } + else if (getSslKey().isNull()) { + qWarning() << "Private key is unreadable"; + createCredentials(settings); + } + + // We should have valid credentials now. If not, we're screwed + if (getSslCertificate().isNull()) { + qFatal("Newly generated certificate is unreadable"); + } + if (getSslKey().isNull()) { + qFatal("Newly generated private key is unreadable"); + } +} + +QSslCertificate +IdentityManager::getSslCertificate() +{ + if (m_CachedSslCert.isNull()) { + m_CachedSslCert = QSslCertificate(m_CachedPemCert); + } + return m_CachedSslCert; +} + +QSslKey +IdentityManager::getSslKey() +{ + if (m_CachedSslKey.isNull()) { + BIO* bio = BIO_new_mem_buf(m_CachedPrivateKey.data(), -1); + THROW_BAD_ALLOC_IF_NULL(bio); + + EVP_PKEY* pk = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr); + BIO_free(bio); + + bio = BIO_new(BIO_s_mem()); + THROW_BAD_ALLOC_IF_NULL(bio); + + // We must write out our PEM in the old PKCS1 format for SecureTransport + // on macOS/iOS to be able to read it. + BUF_MEM* mem; + BIO_get_mem_ptr(bio, &mem); + PEM_write_bio_PrivateKey_traditional(bio, pk, nullptr, nullptr, 0, nullptr, 0); + + m_CachedSslKey = QSslKey(QByteArray::fromRawData(mem->data, (int)mem->length), QSsl::Rsa); + + BIO_free(bio); + EVP_PKEY_free(pk); + } + return m_CachedSslKey; } QSslConfiguration IdentityManager::getSslConfig() { - BIO* bio = BIO_new_mem_buf(m_CachedPrivateKey.data(), -1); - THROW_BAD_ALLOC_IF_NULL(bio); - - EVP_PKEY* pk = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr); - BIO_free(bio); - - bio = BIO_new(BIO_s_mem()); - THROW_BAD_ALLOC_IF_NULL(bio); - - // We must write out our PEM in the old PKCS1 format for SecureTransport - // on macOS/iOS to be able to read it. - BUF_MEM* mem; - BIO_get_mem_ptr(bio, &mem); - PEM_write_bio_PrivateKey_traditional(bio, pk, nullptr, nullptr, 0, nullptr, 0); - - QSslCertificate cert = QSslCertificate(m_CachedPemCert); - QSslKey key = QSslKey(QByteArray::fromRawData(mem->data, (int)mem->length), QSsl::Rsa); - Q_ASSERT(!cert.isNull()); - Q_ASSERT(!key.isNull()); - - BIO_free(bio); - EVP_PKEY_free(pk); - QSslConfiguration sslConfig(QSslConfiguration::defaultConfiguration()); - sslConfig.setLocalCertificate(cert); - sslConfig.setPrivateKey(key); - + sslConfig.setLocalCertificate(getSslCertificate()); + sslConfig.setPrivateKey(getSslKey()); return sslConfig; } QString IdentityManager::getUniqueId() { + if (m_CachedUniqueId.isNull()) { + QSettings settings; + + // Load the unique ID from settings + m_CachedUniqueId = settings.value(SER_UNIQUEID).toString(); + if (!m_CachedUniqueId.isEmpty() && !m_CachedUniqueId.isNull()) { + qDebug() << "Loaded unique ID from settings: " << m_CachedUniqueId; + } + else { + // Generate a new unique ID in base 16 + m_CachedUniqueId = QString::number( + QRandomGenerator64::securelySeeded().generate64(), 16); + + qDebug() << "Generated new unique ID: " << m_CachedUniqueId; + + settings.setValue(SER_UNIQUEID, m_CachedUniqueId); + } + } return m_CachedUniqueId; } diff --git a/app/http/identitymanager.h b/app/http/identitymanager.h index 602f0330..b19ed1b5 100644 --- a/app/http/identitymanager.h +++ b/app/http/identitymanager.h @@ -1,7 +1,9 @@ #pragma once -#include #include +#include +#include +#include class IdentityManager { @@ -25,11 +27,23 @@ public: private: IdentityManager(); - QDir m_RootDirectory; + QSslCertificate + getSslCertificate(); + QSslKey + getSslKey(); + + void + createCredentials(QSettings& settings); + + // Initialized in constructor QByteArray m_CachedPrivateKey; QByteArray m_CachedPemCert; + + // Lazy initialized QString m_CachedUniqueId; + QSslCertificate m_CachedSslCert; + QSslKey m_CachedSslKey; static IdentityManager* s_Im; };