mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2024-12-13 21:02:28 +00:00
more work
This commit is contained in:
parent
a38bef6bc4
commit
a5d50cd4f0
8 changed files with 322 additions and 97 deletions
|
@ -1,55 +1,34 @@
|
|||
#include "identitymanager.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/conf.h>
|
||||
#include <openssl/rsa.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(directory.filePath("uniqueid"));
|
||||
if (uniqueIdFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
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;
|
||||
}
|
||||
m_CachedUniqueId = QTextStream(&uniqueIdFile).readAll();
|
||||
qDebug() << "Loaded cached unique ID: " << m_CachedUniqueId;
|
||||
}
|
||||
|
||||
return m_CachedUniqueId;
|
||||
}
|
||||
|
||||
void
|
||||
IdentityManager::loadKeyPair()
|
||||
{
|
||||
if (m_CachedPemCert != nullptr && m_CachedPrivateKey != nullptr)
|
||||
else
|
||||
{
|
||||
// Already have cached data
|
||||
return;
|
||||
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"));
|
||||
|
@ -66,48 +45,58 @@ IdentityManager::loadKeyPair()
|
|||
}
|
||||
|
||||
X509* cert = X509_new();
|
||||
if (cert == nullptr)
|
||||
{
|
||||
throw new std::bad_alloc();
|
||||
}
|
||||
THROW_BAD_ALLOC_IF_NULL(cert);
|
||||
|
||||
EVP_PKEY* pk = EVP_PKEY_new();
|
||||
if (pk == nullptr)
|
||||
{
|
||||
X509_free(cert);
|
||||
throw new std::bad_alloc();
|
||||
}
|
||||
THROW_BAD_ALLOC_IF_NULL(pk);
|
||||
|
||||
EVP_PKEY_assign_RSA(pk, RSA_generate_key(2048, RSA_F4, nullptr, nullptr));
|
||||
BIGNUM* bne = BN_new();
|
||||
THROW_BAD_ALLOC_IF_NULL(bne);
|
||||
|
||||
RSA* rsa = RSA_new();
|
||||
THROW_BAD_ALLOC_IF_NULL(rsa);
|
||||
|
||||
BN_set_word(bne, RSA_F4);
|
||||
RSA_generate_key_ex(rsa, 2048, bne, nullptr);
|
||||
|
||||
EVP_PKEY_assign_RSA(pk, rsa);
|
||||
|
||||
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_gmtime_adj(X509_get_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);
|
||||
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, reinterpret_cast<unsigned char *>(const_cast<char*>("NVIDIA GameStream Client")), -1, -1, 0);
|
||||
X509_set_issuer_name(cert, name);
|
||||
|
||||
privateKeyFile.open(QIODevice::ReadWrite);
|
||||
PEM_write_PrivateKey(privateKeyFile.handle(), pk, nullptr, nullptr, 0, nullptr, nullptr);
|
||||
PEM_write_PrivateKey(fdopen(privateKeyFile.handle(), "w"), pk, nullptr, nullptr, 0, nullptr, nullptr);
|
||||
|
||||
certificateFile.open(QIODevice::ReadWrite);
|
||||
PEM_write_X509(certificateFile.handle(), cert);
|
||||
PEM_write_X509(fdopen(certificateFile.handle(), "w"), cert);
|
||||
|
||||
X509_free(cert);
|
||||
EVP_PKEY_free(pk);
|
||||
BN_free(bne);
|
||||
|
||||
qDebug() << "Wrote new identity credentials to disk";
|
||||
}
|
||||
|
||||
QString
|
||||
IdentityManager::getUniqueId()
|
||||
{
|
||||
return m_CachedUniqueId;
|
||||
}
|
||||
|
||||
QByteArray
|
||||
IdentityManager::getCertificate()
|
||||
{
|
||||
loadKeyPair();
|
||||
return m_CachedPemCert;
|
||||
}
|
||||
|
||||
QByteArray
|
||||
IdentityManager::getPrivateKey()
|
||||
{
|
||||
loadKeyPair();
|
||||
return m_CachedPrivateKey;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include <QDir>
|
||||
|
||||
class IdentityManager
|
||||
{
|
||||
public:
|
||||
IdentityManager(QDir directory);
|
||||
|
||||
QString
|
||||
getUniqueId();
|
||||
|
||||
QByteArray
|
||||
getCertificate();
|
||||
|
||||
QByteArray
|
||||
getPrivateKey();
|
||||
|
||||
private:
|
||||
QDir m_RootDirectory;
|
||||
|
||||
QString m_CachedUniqueId;
|
||||
QByteArray m_CachedPemCert;
|
||||
QByteArray m_CachedPrivateKey;
|
||||
QByteArray m_CachedPemCert;
|
||||
QString m_CachedUniqueId;
|
||||
};
|
||||
|
|
|
@ -34,11 +34,12 @@ HEADERS += \
|
|||
mainwindow.h \
|
||||
nvhttp.h \
|
||||
nvpairingmanager.h \
|
||||
identitymanager.h
|
||||
identitymanager.h \
|
||||
utils.h
|
||||
|
||||
FORMS += \
|
||||
mainwindow.ui
|
||||
|
||||
OPENSSL_LIBS='-L/usr/lib -lssl -lcrypto'
|
||||
|
||||
QMAKE_CXXFLAGS += -openssl-linked
|
||||
LIBS += \
|
||||
-lssl \
|
||||
-lcrypto
|
||||
|
|
26
nvhttp.cpp
26
nvhttp.cpp
|
@ -8,8 +8,9 @@
|
|||
|
||||
#define REQUEST_TIMEOUT_MS 5000
|
||||
|
||||
NvHTTP::NvHTTP(QString address) :
|
||||
m_Address(address)
|
||||
NvHTTP::NvHTTP(QString address, IdentityManager im) :
|
||||
m_Address(address),
|
||||
m_Im(im)
|
||||
{
|
||||
m_BaseUrlHttp.setScheme("http");
|
||||
m_BaseUrlHttps.setScheme("https");
|
||||
|
@ -69,6 +70,8 @@ NvHTTP::getServerVersionQuad(QString serverInfo)
|
|||
{
|
||||
ret.append(parts.at(i).toInt());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -104,7 +107,7 @@ NvHTTP::getServerInfo()
|
|||
// Throws if the request failed
|
||||
verifyResponseStatus(serverInfo);
|
||||
}
|
||||
catch (GfeHttpResponseException& e)
|
||||
catch (const GfeHttpResponseException& e)
|
||||
{
|
||||
if (e.getStatusCode() == 401)
|
||||
{
|
||||
|
@ -115,6 +118,11 @@ NvHTTP::getServerInfo()
|
|||
true);
|
||||
verifyResponseStatus(serverInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rethrow real errors
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return serverInfo;
|
||||
|
@ -185,10 +193,10 @@ NvHTTP::openConnection(QUrl baseUrl,
|
|||
{
|
||||
// Build a URL for the request
|
||||
QUrl url(baseUrl);
|
||||
url.setPath(command +
|
||||
"?uniqueid=" + "0" +
|
||||
"&uuid=" + QUuid::createUuid().toString() +
|
||||
((arguments != nullptr) ? (arguments + "&") : ""));
|
||||
url.setPath("/" + command);
|
||||
url.setQuery("uniqueid=" + m_Im.getUniqueId() +
|
||||
"&uuid=" + QUuid::createUuid().toRfc4122().toHex() +
|
||||
((arguments != nullptr) ? ("&" + arguments) : ""));
|
||||
|
||||
QNetworkReply* reply = m_Nam.get(QNetworkRequest(url));
|
||||
|
||||
|
@ -215,8 +223,8 @@ NvHTTP::openConnection(QUrl baseUrl,
|
|||
// Handle error
|
||||
if (reply->error() != QNetworkReply::NoError)
|
||||
{
|
||||
qDebug() << command << " request for failed with error " << reply->error();
|
||||
GfeHttpResponseException* exception = new GfeHttpResponseException(reply->error(), reply->errorString());
|
||||
qDebug() << command << " request failed with error " << reply->error();
|
||||
GfeHttpResponseException exception(reply->error(), reply->errorString());
|
||||
delete reply;
|
||||
throw exception;
|
||||
}
|
||||
|
|
29
nvhttp.h
29
nvhttp.h
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "identitymanager.h"
|
||||
|
||||
#include <QUrl>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
|
||||
|
@ -15,15 +17,15 @@ public:
|
|||
|
||||
const char* what() const throw()
|
||||
{
|
||||
return m_StatusMessage.toStdString().c_str();
|
||||
return m_StatusMessage.toLatin1();
|
||||
}
|
||||
|
||||
const char* getStatusMessage()
|
||||
const char* getStatusMessage() const
|
||||
{
|
||||
return m_StatusMessage.toStdString().c_str();
|
||||
return m_StatusMessage.toLatin1();
|
||||
}
|
||||
|
||||
int getStatusCode()
|
||||
int getStatusCode() const
|
||||
{
|
||||
return m_StatusCode;
|
||||
}
|
||||
|
@ -65,14 +67,7 @@ public:
|
|||
class NvHTTP
|
||||
{
|
||||
public:
|
||||
NvHTTP(QString address);
|
||||
|
||||
private:
|
||||
QNetworkReply*
|
||||
openConnection(QUrl baseUrl,
|
||||
QString command,
|
||||
QString arguments,
|
||||
bool enableTimeout);
|
||||
NvHTTP(QString address, IdentityManager im);
|
||||
|
||||
NvComputer
|
||||
getComputerInfo();
|
||||
|
@ -99,8 +94,16 @@ private:
|
|||
QVector<int>
|
||||
getServerVersionQuad(QString serverInfo);
|
||||
|
||||
QString m_Address;
|
||||
QUrl m_BaseUrlHttp;
|
||||
QUrl m_BaseUrlHttps;
|
||||
private:
|
||||
QNetworkReply*
|
||||
openConnection(QUrl baseUrl,
|
||||
QString command,
|
||||
QString arguments,
|
||||
bool enableTimeout);
|
||||
|
||||
QString m_Address;
|
||||
QNetworkAccessManager m_Nam;
|
||||
IdentityManager m_Im;
|
||||
};
|
||||
|
|
|
@ -1,13 +1,44 @@
|
|||
#include "nvpairingmanager.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <QRandomGenerator>
|
||||
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
NvPairingManager::NvPairingManager(QString address) :
|
||||
m_Http(address)
|
||||
NvPairingManager::NvPairingManager(QString address, IdentityManager im) :
|
||||
m_Http(address, im),
|
||||
m_Im(im)
|
||||
{
|
||||
BIO *bio = BIO_new_mem_buf(m_Im.getCertificate().toStdString().c_str(), -1);
|
||||
THROW_BAD_ALLOC_IF_NULL(bio);
|
||||
|
||||
m_Cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
|
||||
BIO_free_all(bio);
|
||||
if (m_Cert == nullptr)
|
||||
{
|
||||
throw new std::runtime_error("Unable to load certificate");
|
||||
}
|
||||
|
||||
bio = BIO_new_mem_buf(m_Im.getPrivateKey().toStdString().c_str(), -1);
|
||||
THROW_BAD_ALLOC_IF_NULL(bio);
|
||||
|
||||
PEM_read_bio_PrivateKey(bio, &m_PrivateKey, nullptr, nullptr);
|
||||
BIO_free_all(bio);
|
||||
if (m_Cert == nullptr)
|
||||
{
|
||||
throw new std::runtime_error("Unable to load private key");
|
||||
}
|
||||
}
|
||||
|
||||
NvPairingManager::~NvPairingManager()
|
||||
{
|
||||
X509_free(m_Cert);
|
||||
EVP_PKEY_free(m_PrivateKey);
|
||||
}
|
||||
|
||||
QString
|
||||
|
@ -19,20 +50,88 @@ NvPairingManager::generatePinString()
|
|||
QByteArray
|
||||
NvPairingManager::generateRandomBytes(int length)
|
||||
{
|
||||
QByteArray array(length);
|
||||
char* data = static_cast<char*>(alloca(length));
|
||||
RAND_bytes(reinterpret_cast<unsigned char*>(data), length);
|
||||
return QByteArray(data, length);
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
QByteArray
|
||||
NvPairingManager::encrypt(QByteArray plaintext, AES_KEY* key)
|
||||
{
|
||||
QByteArray ciphertext(plaintext.size(), 0);
|
||||
|
||||
for (int i = 0; i < plaintext.size(); i += 16)
|
||||
{
|
||||
array.data()[i] = static_cast<char>(QRandomGenerator::global()->bounded(256));
|
||||
AES_encrypt(reinterpret_cast<unsigned char*>(&plaintext.data()[i]),
|
||||
reinterpret_cast<unsigned char*>(&ciphertext.data()[i]),
|
||||
key);
|
||||
}
|
||||
|
||||
return array;
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
QByteArray
|
||||
NvPairingManager::decrypt(QByteArray ciphertext, AES_KEY* key)
|
||||
{
|
||||
QByteArray plaintext(ciphertext.size(), 0);
|
||||
|
||||
for (int i = 0; i < plaintext.size(); i += 16)
|
||||
{
|
||||
AES_decrypt(reinterpret_cast<unsigned char*>(&ciphertext.data()[i]),
|
||||
reinterpret_cast<unsigned char*>(&plaintext.data()[i]),
|
||||
key);
|
||||
}
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
bool
|
||||
NvPairingManager::verifySignature(QByteArray data, QByteArray signature)
|
||||
{
|
||||
EVP_PKEY* pubKey = X509_get_pubkey(m_Cert);
|
||||
THROW_BAD_ALLOC_IF_NULL(pubKey);
|
||||
|
||||
EVP_MD_CTX* mdctx = EVP_MD_CTX_create();
|
||||
THROW_BAD_ALLOC_IF_NULL(mdctx);
|
||||
|
||||
EVP_DigestVerifyInit(mdctx, nullptr, EVP_sha256(), nullptr, pubKey);
|
||||
EVP_DigestVerifyUpdate(mdctx, data.data(), data.length());
|
||||
int result = EVP_DigestVerifyFinal(mdctx, reinterpret_cast<unsigned char*>(signature.data()), signature.length());
|
||||
|
||||
EVP_PKEY_free(pubKey);
|
||||
EVP_MD_CTX_destroy(mdctx);
|
||||
|
||||
return result > 0;
|
||||
}
|
||||
|
||||
QByteArray
|
||||
NvPairingManager::signMessage(QByteArray message)
|
||||
{
|
||||
EVP_MD_CTX *ctx = EVP_MD_CTX_create();
|
||||
THROW_BAD_ALLOC_IF_NULL(ctx);
|
||||
|
||||
const EVP_MD *md = EVP_get_digestbyname("SHA256");
|
||||
THROW_BAD_ALLOC_IF_NULL(md);
|
||||
|
||||
EVP_DigestInit_ex(ctx, md, NULL);
|
||||
EVP_DigestSignInit(ctx, NULL, md, NULL, m_PrivateKey);
|
||||
EVP_DigestSignUpdate(ctx, reinterpret_cast<unsigned char*>(message.data()), message.length());
|
||||
|
||||
size_t signatureLength = 0;
|
||||
EVP_DigestSignFinal(ctx, NULL, &signatureLength);
|
||||
|
||||
QByteArray signature(signatureLength, 0);
|
||||
EVP_DigestSignFinal(ctx, reinterpret_cast<unsigned char*>(signature.data()), &signatureLength);
|
||||
|
||||
EVP_MD_CTX_destroy(ctx);
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
QByteArray
|
||||
NvPairingManager::saltPin(QByteArray salt, QString pin)
|
||||
{
|
||||
return QByteArray().append(salt).append(pin.toStdString().c_str());
|
||||
return QByteArray().append(salt).append(pin.toLatin1());
|
||||
}
|
||||
|
||||
NvPairingManager::PairState
|
||||
|
@ -42,26 +141,109 @@ NvPairingManager::pair(QString serverInfo, QString pin)
|
|||
qDebug() << "Pairing with server generation: " << serverMajorVersion;
|
||||
|
||||
QCryptographicHash::Algorithm hashAlgo;
|
||||
int hashLength;
|
||||
if (serverMajorVersion >= 7)
|
||||
{
|
||||
// Gen 7+ uses SHA-256 hashing
|
||||
hashAlgo = QCryptographicHash::Sha256;
|
||||
hashLength = 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prior to Gen 7 uses SHA-1 hashing
|
||||
hashAlgo = QCryptographicHash::Sha1;
|
||||
hashLength = 20;
|
||||
}
|
||||
|
||||
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);
|
||||
AES_set_decrypt_key(reinterpret_cast<const unsigned char*>(QCryptographicHash::hash(saltedPin, hashAlgo).data()), 128, &decKey);
|
||||
AES_set_encrypt_key(reinterpret_cast<const unsigned char*>(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=" + )
|
||||
"devicename=roth&updateState=1&phrase=getservercert&salt=" +
|
||||
salt.toHex() + "&clientcert=" + m_Im.getCertificate().toHex(),
|
||||
false);
|
||||
if (m_Http.getXmlString(getCert, "paired") != "1")
|
||||
{
|
||||
qDebug() << "Failed pairing at stage #1";
|
||||
return PairState::FAILED;
|
||||
}
|
||||
|
||||
QByteArray encryptedChallenge = encrypt(generateRandomBytes(16), &encKey);
|
||||
QString challengeXml = m_Http.openConnectionToString(m_Http.m_BaseUrlHttp,
|
||||
"pair",
|
||||
"devicename=roth&updateState=1&clientchallenge=" +
|
||||
encryptedChallenge.toHex(),
|
||||
true);
|
||||
if (m_Http.getXmlString(challengeXml, "paired") != "1")
|
||||
{
|
||||
qDebug() << "Failed pairing at stage #2";
|
||||
return PairState::FAILED;
|
||||
}
|
||||
|
||||
QByteArray challengeResponseData = decrypt(
|
||||
QByteArray::fromHex(m_Http.getXmlString(challengeXml, "challengeresponse").toLatin1()),
|
||||
&decKey);
|
||||
QByteArray clientSecretData = generateRandomBytes(16);
|
||||
QByteArray challengeResponse;
|
||||
|
||||
const ASN1_BIT_STRING *asnSignature;
|
||||
X509_get0_signature(&asnSignature, NULL, m_Cert);
|
||||
|
||||
challengeResponse.append(challengeResponseData.data() + hashLength, 16);
|
||||
challengeResponse.append(reinterpret_cast<char*>(asnSignature->data), 256);
|
||||
challengeResponse.append(clientSecretData);
|
||||
|
||||
QByteArray encryptedChallengeResponseHash = encrypt(QCryptographicHash::hash(challengeResponse, hashAlgo), &encKey);
|
||||
QString respXml = m_Http.openConnectionToString(m_Http.m_BaseUrlHttp,
|
||||
"pair",
|
||||
"devicename=roth&updateState=1&serverchallengeresp=" +
|
||||
encryptedChallengeResponseHash.toHex(),
|
||||
true);
|
||||
if (m_Http.getXmlString(respXml, "paired") != "1")
|
||||
{
|
||||
qDebug() << "Failed pairing at stage #3";
|
||||
return PairState::FAILED;
|
||||
}
|
||||
|
||||
QByteArray pairingSecret =
|
||||
QByteArray::fromHex(m_Http.getXmlString(challengeXml, "pairingsecret").toLatin1());
|
||||
|
||||
if (!verifySignature(QByteArray(pairingSecret.data(), 16),
|
||||
QByteArray(&pairingSecret.data()[16], 256)))
|
||||
{
|
||||
qDebug() << "MITM detected";
|
||||
return PairState::FAILED;
|
||||
}
|
||||
|
||||
QByteArray clientPairingSecret;
|
||||
clientPairingSecret.append(clientSecretData);
|
||||
clientPairingSecret.append(signMessage(clientSecretData));
|
||||
|
||||
QString secretRespXml = m_Http.openConnectionToString(m_Http.m_BaseUrlHttp,
|
||||
"pair",
|
||||
"devicename=roth&updateState=1&clientpairingsecret=" +
|
||||
clientPairingSecret.toHex(),
|
||||
true);
|
||||
if (m_Http.getXmlString(secretRespXml, "paired") != "1")
|
||||
{
|
||||
qDebug() << "Failed pairing at stage #4";
|
||||
return PairState::FAILED;
|
||||
}
|
||||
|
||||
QString pairChallengeXml = m_Http.openConnectionToString(m_Http.m_BaseUrlHttps,
|
||||
"pair",
|
||||
"devicename=roth&updateState=1&phase=pairchallenge",
|
||||
true);
|
||||
if (m_Http.getXmlString(pairChallengeXml, "paired") != "1")
|
||||
{
|
||||
qDebug() << "Failed pairing at stage #5";
|
||||
return PairState::FAILED;
|
||||
}
|
||||
|
||||
return PairState::PAIRED;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <identitymanager.h>
|
||||
#include <nvhttp.h>
|
||||
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
class NvPairingManager
|
||||
{
|
||||
public:
|
||||
|
@ -14,7 +19,9 @@ public:
|
|||
ALREADY_IN_PROGRESS
|
||||
};
|
||||
|
||||
NvPairingManager(QString address);
|
||||
NvPairingManager(QString address, IdentityManager im);
|
||||
|
||||
~NvPairingManager();
|
||||
|
||||
QString
|
||||
generatePinString();
|
||||
|
@ -23,5 +30,26 @@ public:
|
|||
pair(QString serverInfo, QString pin);
|
||||
|
||||
private:
|
||||
QByteArray
|
||||
generateRandomBytes(int length);
|
||||
|
||||
QByteArray
|
||||
saltPin(QByteArray salt, QString pin);
|
||||
|
||||
QByteArray
|
||||
encrypt(QByteArray plaintext, AES_KEY* key);
|
||||
|
||||
QByteArray
|
||||
decrypt(QByteArray ciphertext, AES_KEY* key);
|
||||
|
||||
bool
|
||||
verifySignature(QByteArray data, QByteArray signature);
|
||||
|
||||
QByteArray
|
||||
signMessage(QByteArray message);
|
||||
|
||||
NvHTTP m_Http;
|
||||
IdentityManager m_Im;
|
||||
X509* m_Cert;
|
||||
EVP_PKEY* m_PrivateKey;
|
||||
};
|
||||
|
|
4
utils.h
Normal file
4
utils.h
Normal file
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
|
||||
#define THROW_BAD_ALLOC_IF_NULL(x) \
|
||||
if ((x) == nullptr) throw new std::bad_alloc()
|
Loading…
Reference in a new issue