Improve handling of malformed XML responses

This commit is contained in:
Cameron Gutman
2026-05-23 19:16:14 -05:00
parent 459dcd7b2c
commit 63c48be6d4
2 changed files with 51 additions and 38 deletions
+26 -28
View File
@@ -109,7 +109,7 @@ NvHTTP::getCurrentGame(QString serverInfo)
// has the semantics that its name would indicate. To contain the effects of this change as much // 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. // as possible, we'll force the current game to zero if the server isn't in a streaming session.
QString serverState = getXmlString(serverInfo, "state"); QString serverState = getXmlString(serverInfo, "state");
if (serverState != nullptr && serverState.endsWith("_SERVER_BUSY")) if (serverState.endsWith("_SERVER_BUSY"))
{ {
return getXmlString(serverInfo, "currentgame").toInt(); return getXmlString(serverInfo, "currentgame").toInt();
} }
@@ -269,14 +269,16 @@ NvHTTP::getDisplayModeList(QString serverInfo)
if (name == QString("DisplayMode")) { if (name == QString("DisplayMode")) {
modes.append(NvDisplayMode()); modes.append(NvDisplayMode());
} }
else if (name == QString("Width")) { else if (!modes.isEmpty()) {
modes.last().width = xmlReader.readElementText().toInt(); if (name == QString("Width")) {
} modes.last().width = xmlReader.readElementText().toInt();
else if (name == QString("Height")) { }
modes.last().height = xmlReader.readElementText().toInt(); else if (name == QString("Height")) {
} modes.last().height = xmlReader.readElementText().toInt();
else if (name == QString("RefreshRate")) { }
modes.last().refreshRate = xmlReader.readElementText().toInt(); else if (name == QString("RefreshRate")) {
modes.last().refreshRate = xmlReader.readElementText().toInt();
}
} }
} }
} }
@@ -307,17 +309,19 @@ NvHTTP::getAppList()
} }
apps.append(NvApp()); apps.append(NvApp());
} }
else if (name == QString("AppTitle")) { else if (!apps.isEmpty()) {
apps.last().name = xmlReader.readElementText(); if (name == QString("AppTitle")) {
} apps.last().name = xmlReader.readElementText();
else if (name == QString("ID")) { }
apps.last().id = xmlReader.readElementText().toInt(); else if (name == QString("ID")) {
} apps.last().id = xmlReader.readElementText().toInt();
else if (name == QString("IsHdrSupported")) { }
apps.last().hdrSupported = xmlReader.readElementText() == "1"; else if (name == QString("IsHdrSupported")) {
} apps.last().hdrSupported = xmlReader.readElementText() == "1";
else if (name == QString("IsAppCollectorGame")) { }
apps.last().isAppCollectorGame = xmlReader.readElementText() == "1"; else if (name == QString("IsAppCollectorGame")) {
apps.last().isAppCollectorGame = xmlReader.readElementText() == "1";
}
} }
} }
} }
@@ -383,13 +387,7 @@ QByteArray
NvHTTP::getXmlStringFromHex(QString xml, NvHTTP::getXmlStringFromHex(QString xml,
QString tagName) QString tagName)
{ {
QString str = getXmlString(xml, tagName); return QByteArray::fromHex(getXmlString(xml, tagName).toUtf8());
if (str == nullptr)
{
return nullptr;
}
return QByteArray::fromHex(str.toUtf8());
} }
QString QString
@@ -411,7 +409,7 @@ NvHTTP::getXmlString(QString xml,
} }
} }
return nullptr; return QString();
} }
void NvHTTP::handleSslErrors(QNetworkReply* reply, const QList<QSslError>& errors) void NvHTTP::handleSslErrors(QNetworkReply* reply, const QList<QSslError>& errors)
+25 -10
View File
@@ -54,16 +54,18 @@ NvPairingManager::generateRandomBytes(int length)
QByteArray QByteArray
NvPairingManager::encrypt(const QByteArray& plaintext, const QByteArray& key) NvPairingManager::encrypt(const QByteArray& plaintext, const QByteArray& key)
{ {
QByteArray ciphertext(plaintext.size(), 0); if (plaintext.isEmpty()) {
EVP_CIPHER_CTX* cipher; return QByteArray();
int ciphertextLen; }
cipher = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX* cipher = EVP_CIPHER_CTX_new();
THROW_BAD_ALLOC_IF_NULL(cipher); THROW_BAD_ALLOC_IF_NULL(cipher);
EVP_EncryptInit(cipher, EVP_aes_128_ecb(), reinterpret_cast<const unsigned char*>(key.data()), NULL); EVP_EncryptInit(cipher, EVP_aes_128_ecb(), reinterpret_cast<const unsigned char*>(key.data()), NULL);
EVP_CIPHER_CTX_set_padding(cipher, 0); EVP_CIPHER_CTX_set_padding(cipher, 0);
QByteArray ciphertext(plaintext.size(), 0);
int ciphertextLen;
EVP_EncryptUpdate(cipher, EVP_EncryptUpdate(cipher,
reinterpret_cast<unsigned char*>(ciphertext.data()), reinterpret_cast<unsigned char*>(ciphertext.data()),
&ciphertextLen, &ciphertextLen,
@@ -79,16 +81,18 @@ NvPairingManager::encrypt(const QByteArray& plaintext, const QByteArray& key)
QByteArray QByteArray
NvPairingManager::decrypt(const QByteArray& ciphertext, const QByteArray& key) NvPairingManager::decrypt(const QByteArray& ciphertext, const QByteArray& key)
{ {
QByteArray plaintext(ciphertext.size(), 0); if (ciphertext.isEmpty()) {
EVP_CIPHER_CTX* cipher; return QByteArray();
int plaintextLen; }
cipher = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX* cipher = EVP_CIPHER_CTX_new();
THROW_BAD_ALLOC_IF_NULL(cipher); THROW_BAD_ALLOC_IF_NULL(cipher);
EVP_DecryptInit(cipher, EVP_aes_128_ecb(), reinterpret_cast<const unsigned char*>(key.data()), NULL); EVP_DecryptInit(cipher, EVP_aes_128_ecb(), reinterpret_cast<const unsigned char*>(key.data()), NULL);
EVP_CIPHER_CTX_set_padding(cipher, 0); EVP_CIPHER_CTX_set_padding(cipher, 0);
QByteArray plaintext(ciphertext.size(), 0);
int plaintextLen;
EVP_DecryptUpdate(cipher, EVP_DecryptUpdate(cipher,
reinterpret_cast<unsigned char*>(plaintext.data()), reinterpret_cast<unsigned char*>(plaintext.data()),
&plaintextLen, &plaintextLen,
@@ -239,8 +243,7 @@ NvPairingManager::pair(QString appVersion, QString pin, QSslCertificate& serverC
} }
QByteArray serverCertStr = NvHTTP::getXmlStringFromHex(getCert, "plaincert"); QByteArray serverCertStr = NvHTTP::getXmlStringFromHex(getCert, "plaincert");
if (serverCertStr == nullptr) if (serverCertStr.isEmpty()) {
{
qCritical() << "Server likely already pairing"; qCritical() << "Server likely already pairing";
m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, REQUEST_TIMEOUT_MS); m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, REQUEST_TIMEOUT_MS);
return PairState::ALREADY_IN_PROGRESS; return PairState::ALREADY_IN_PROGRESS;
@@ -275,6 +278,12 @@ NvPairingManager::pair(QString appVersion, QString pin, QSslCertificate& serverC
} }
QByteArray challengeResponseData = decrypt(m_Http.getXmlStringFromHex(challengeXml, "challengeresponse"), aesKey); QByteArray challengeResponseData = decrypt(m_Http.getXmlStringFromHex(challengeXml, "challengeresponse"), aesKey);
if (challengeResponseData.size() < hashLength) {
qCritical() << "Invalid challengeresponse at stage #2";
m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, REQUEST_TIMEOUT_MS);
return PairState::FAILED;
}
QByteArray clientSecretData = generateRandomBytes(16); QByteArray clientSecretData = generateRandomBytes(16);
QByteArray challengeResponse; QByteArray challengeResponse;
QByteArray serverResponse(challengeResponseData.data(), hashLength); QByteArray serverResponse(challengeResponseData.data(), hashLength);
@@ -300,6 +309,12 @@ NvPairingManager::pair(QString appVersion, QString pin, QSslCertificate& serverC
} }
QByteArray pairingSecret = NvHTTP::getXmlStringFromHex(respXml, "pairingsecret"); QByteArray pairingSecret = NvHTTP::getXmlStringFromHex(respXml, "pairingsecret");
if (pairingSecret.size() <= 16) {
qCritical() << "Invalid pairingsecret at stage #3";
m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, REQUEST_TIMEOUT_MS);
return PairState::FAILED;
}
QByteArray serverSecret = pairingSecret.left(16); QByteArray serverSecret = pairingSecret.left(16);
QByteArray serverSignature = pairingSecret.mid(16); QByteArray serverSignature = pairingSecret.mid(16);