pairing stuff

This commit is contained in:
Cameron Gutman 2018-04-28 19:01:00 -07:00
parent 3ee554cd91
commit a38bef6bc4
7 changed files with 259 additions and 10 deletions

113
identitymanager.cpp Normal file
View file

@ -0,0 +1,113 @@
#include "identitymanager.h"
#include <QFile>
#include <openssl/pem.h>
#include <openssl/conf.h>
#include <openssl/pkcs12.h>
IdentityManager::IdentityManager(QDir directory)
: m_RootDirectory(directory),
m_CachedPrivateKey(nullptr),
m_CachedPemCert(nullptr),
m_CachedUniqueId(nullptr)
{
}
QString
IdentityManager::getUniqueId()
{
if (m_CachedUniqueId == nullptr)
{
QFile uniqueIdFile(m_RootDirectory.filePath("uniqueid"));
if (uniqueIdFile.open(QIODevice::ReadOnly))
{
m_CachedUniqueId = QTextStream(uniqueIdFile.open(QIODevice::ReadOnly)).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;
}
}
return m_CachedUniqueId;
}
void
IdentityManager::loadKeyPair()
{
if (m_CachedPemCert != nullptr && m_CachedPrivateKey != nullptr)
{
// Already have cached data
return;
}
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 credentials from disk";
return;
}
X509* cert = X509_new();
if (cert == nullptr)
{
throw new std::bad_alloc();
}
EVP_PKEY* pk = EVP_PKEY_new();
if (pk == nullptr)
{
X509_free(cert);
throw new std::bad_alloc();
}
EVP_PKEY_assign_RSA(pk, RSA_generate_key(2048, RSA_F4, nullptr, nullptr));
X509_set_version(cert, 2);
X509_gmtime_adj(X509_get_notBefore(cert), 0);
X509_gmtime_adj(X509_get0_notAfter(cert), 60 * 60 * 24 * 365 * 20); // 20 yrs
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<unsigned char *>("NVIDIA GameStream Client"), -1, -1, 0);
X509_set_issuer_name(x, name);
privateKeyFile.open(QIODevice::ReadWrite);
PEM_write_PrivateKey(privateKeyFile.handle(), pk, nullptr, nullptr, 0, nullptr, nullptr);
certificateFile.open(QIODevice::ReadWrite);
PEM_write_X509(certificateFile.handle(), cert);
qDebug() << "Wrote new identity credentials to disk";
}
QByteArray
IdentityManager::getCertificate()
{
loadKeyPair();
return m_CachedPemCert;
}
QByteArray
IdentityManager::getPrivateKey()
{
loadKeyPair();
return m_CachedPrivateKey;
}

15
identitymanager.h Normal file
View file

@ -0,0 +1,15 @@
#pragma once
class IdentityManager
{
public:
IdentityManager(QDir directory);
private:
QDir m_RootDirectory;
QString m_CachedUniqueId;
QByteArray m_CachedPemCert;
QByteArray m_CachedPrivateKey;
};

View file

@ -20,17 +20,25 @@ DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp \
nvhttp.cpp
main.cpp \
mainwindow.cpp \
nvhttp.cpp \
nvpairingmanager.cpp \
identitymanager.cpp
HEADERS += \
mainwindow.h \
nvhttp.h
mainwindow.h \
nvhttp.h \
nvpairingmanager.h \
identitymanager.h
FORMS += \
mainwindow.ui
mainwindow.ui
OPENSSL_LIBS='-L/usr/lib -lssl -lcrypto'
QMAKE_CXXFLAGS += -openssl-linked

View file

@ -58,6 +58,19 @@ NvHTTP::getComputerInfo()
return computer;
}
QVector<int>
NvHTTP::getServerVersionQuad(QString serverInfo)
{
QString quad = getXmlString(serverInfo, "appversion");
QStringList parts = quad.split(".");
QVector<int> ret;
for (int i = 0; i < 4; i++)
{
ret.append(parts.at(i).toInt());
}
}
int
NvHTTP::getCurrentGame(QString serverInfo)
{
@ -65,10 +78,12 @@ NvHTTP::getCurrentGame(QString serverInfo)
// has the semantics that its name would indicate. To contain the effects of this change as much
// as possible, we'll force the current game to zero if the server isn't in a streaming session.
QString serverState = getXmlString(serverInfo, "state");
if (serverState != nullptr && serverState.endsWith("_SERVER_BUSY")) {
if (serverState != nullptr && serverState.endsWith("_SERVER_BUSY"))
{
return getXmlString(serverInfo, "currentgame").toInt();
}
else {
else
{
return 0;
}
}
@ -201,8 +216,9 @@ NvHTTP::openConnection(QUrl baseUrl,
if (reply->error() != QNetworkReply::NoError)
{
qDebug() << command << " request for failed with error " << reply->error();
GfeHttpResponseException* exception = new GfeHttpResponseException(reply->error(), reply->errorString());
delete reply;
throw new GfeHttpResponseException(reply->error(), reply->errorString());
throw exception;
}
return reply;

View file

@ -96,6 +96,9 @@ private:
QString arguments,
bool enableTimeout);
QVector<int>
getServerVersionQuad(QString serverInfo);
QString m_Address;
QUrl m_BaseUrlHttp;
QUrl m_BaseUrlHttps;

67
nvpairingmanager.cpp Normal file
View file

@ -0,0 +1,67 @@
#include "nvpairingmanager.h"
#include <QRandomGenerator>
#include <openssl/aes.h>
NvPairingManager::NvPairingManager(QString address) :
m_Http(address)
{
}
QString
NvPairingManager::generatePinString()
{
return QString::number(QRandomGenerator::global()->bounded(10000));
}
QByteArray
NvPairingManager::generateRandomBytes(int length)
{
QByteArray array(length);
for (int i = 0; i < length; i++)
{
array.data()[i] = static_cast<char>(QRandomGenerator::global()->bounded(256));
}
return array;
}
QByteArray
NvPairingManager::saltPin(QByteArray salt, QString pin)
{
return QByteArray().append(salt).append(pin.toStdString().c_str());
}
NvPairingManager::PairState
NvPairingManager::pair(QString serverInfo, QString pin)
{
int serverMajorVersion = m_Http.getServerVersionQuad(serverInfo).at(0);
qDebug() << "Pairing with server generation: " << serverMajorVersion;
QCryptographicHash::Algorithm hashAlgo;
if (serverMajorVersion >= 7)
{
// Gen 7+ uses SHA-256 hashing
hashAlgo = QCryptographicHash::Sha256;
}
else
{
// Prior to Gen 7 uses SHA-1 hashing
hashAlgo = QCryptographicHash::Sha1;
}
QByteArray salt = generateRandomBytes(16);
QByteArray saltedPin = saltPin(salt, pin);
AES_KEY encKey, decKey;
AES_set_decrypt_key(QCryptographicHash::hash(saltedPin, hashAlgo).data(), 128, &decKey);
AES_set_encrypt_key(QCryptographicHash::hash(saltedPin, hashAlgo).data(), 128, &encKey);
QString getCert = m_Http.openConnectionToString(m_Http.m_BaseUrlHttp,
"pair",
"&devicename=roth&updateState=1&phrase=getservercert&salt=" +
salt.toHex() + "&clientcert=" + )
}

27
nvpairingmanager.h Normal file
View file

@ -0,0 +1,27 @@
#pragma once
#include <nvhttp.h>
class NvPairingManager
{
public:
enum PairState
{
NOT_PAIRED,
PAIRED,
PIN_WRONG,
FAILED,
ALREADY_IN_PROGRESS
};
NvPairingManager(QString address);
QString
generatePinString();
PairState
pair(QString serverInfo, QString pin);
private:
NvHTTP m_Http;
};