patch_manager: handle sha256 checksum

This commit is contained in:
Megamouse 2020-09-07 14:10:57 +02:00
parent ca07605835
commit 570eee3202
4 changed files with 89 additions and 28 deletions

View File

@ -7,6 +7,8 @@
#include "curl_handle.h"
#include "progress_dialog.h"
#include "Crypto/sha256.h"
LOG_CHANNEL(network_log, "NETWORK");
size_t curl_write_cb_compat(char* ptr, size_t /*size*/, size_t nmemb, void* userdata)
@ -120,6 +122,27 @@ progress_dialog* downloader::get_progress_dialog() const
return m_progress_dialog;
}
std::string downloader::get_hash(const char* data, size_t size, bool lower_case)
{
u8 res_hash[32];
mbedtls_sha256_context ctx;
mbedtls_sha256_init(&ctx);
mbedtls_sha256_starts_ret(&ctx, 0);
mbedtls_sha256_update_ret(&ctx, reinterpret_cast<const unsigned char*>(data), size);
mbedtls_sha256_finish_ret(&ctx, res_hash);
std::string res_hash_string("0000000000000000000000000000000000000000000000000000000000000000");
for (size_t index = 0; index < 32; index++)
{
const auto pal = lower_case ? "0123456789abcdef" : "0123456789ABCDEF";
res_hash_string[index * 2] = pal[res_hash[index] >> 4];
res_hash_string[(index * 2) + 1] = pal[res_hash[index] & 15];
}
return res_hash_string;
}
size_t downloader::update_buffer(char* data, size_t size)
{
if (m_curl_abort)

View File

@ -22,6 +22,8 @@ public:
progress_dialog* get_progress_dialog() const;
static std::string get_hash(const char* data, size_t size, bool lower_case);
private Q_SLOTS:
void handle_buffer_update(int size, int max);

View File

@ -832,7 +832,26 @@ void patch_manager_dialog::dragLeaveEvent(QDragLeaveEvent* event)
void patch_manager_dialog::download_update()
{
m_downloader->start("https://rpcs3.net/compatibility?patch&api=v1", true, true, tr("Downloading latest patches"));
patch_log.notice("Patch download triggered");
const std::string path = patch_engine::get_patches_path() + "patch.yml";
std::string url = "https://rpcs3.net/compatibility?patch&api=v1&v=" + patch_engine_version;
if (fs::is_file(path))
{
if (fs::file patch_file{path})
{
const std::string hash = downloader::get_hash(patch_file.to_string().c_str(), patch_file.size(), true);
url += "&sha256=" + hash;
}
else
{
patch_log.error("Could not open patch file: %s", path);
return;
}
}
m_downloader->start(url, true, true, tr("Downloading latest patches"));
}
bool patch_manager_dialog::handle_json(const QByteArray& data)
@ -845,8 +864,9 @@ bool patch_manager_dialog::handle_json(const QByteArray& data)
std::string error_message;
switch (return_code)
{
case -1: error_message = "Hash not found"; break;
case -1: error_message = "No patches found for the specified version"; break;
case -2: error_message = "Server Error - Maintenance Mode"; break;
case -3: error_message = "Server Error - Illegal Search"; break;
case -255: error_message = "Server Error - Return code not found"; break;
default: error_message = "Server Error - Unknown Error"; break;
}
@ -859,6 +879,19 @@ bool patch_manager_dialog::handle_json(const QByteArray& data)
return false;
}
if (return_code == 1)
{
patch_log.notice("Patch download: No newer patches found");
QMessageBox::information(this, tr("Download successful"), tr("Your patch file is already up to date."));
return true;
}
if (return_code != 0)
{
patch_log.error("Patch download error: unknown return code: %d", return_code);
return false;
}
const QJsonValue& version_obj = json_data["version"];
if (!version_obj.isString())
@ -874,6 +907,14 @@ bool patch_manager_dialog::handle_json(const QByteArray& data)
return false;
}
const QJsonValue& hash_obj = json_data["sha256"];
if (!hash_obj.isString())
{
patch_log.error("JSON doesn't contain sha256");
return false;
}
const QJsonValue& patch = json_data["patch"];
if (!patch.isString() || patch.toString().isEmpty())
@ -887,18 +928,26 @@ bool patch_manager_dialog::handle_json(const QByteArray& data)
const std::string content = patch.toString().toStdString();
if (hash_obj.toString().toStdString() != downloader::get_hash(content.c_str(), content.size(), true))
{
patch_log.error("JSON content does not match the provided checksum");
return false;
}
if (patch_engine::load(patches, "From Download", content, true, &log_message))
{
patch_log.success("Successfully validated downloaded patch file");
patch_log.notice("Successfully validated downloaded patch file");
const std::string path = patch_engine::get_patches_path() + "patch.yml";
const std::string path = patch_engine::get_patches_path() + "patch.yml";
const std::string path_old = path + ".old";
// Back up current patch file
if (!fs::copy_file(path, path_old, true))
// Back up current patch file if possible
if (fs::is_file(path))
{
patch_log.error("Could not back up current patches to %s", path_old);
return true;
if (const std::string path_old = path + ".old";
!fs::copy_file(path, path_old, true))
{
patch_log.error("Could not back up current patches to %s", path_old);
return false;
}
}
// Overwrite current patch file
@ -909,7 +958,7 @@ bool patch_manager_dialog::handle_json(const QByteArray& data)
else
{
patch_log.error("Could not save new patches to %s", path);
return true;
return false;
}
refresh();
@ -920,5 +969,7 @@ bool patch_manager_dialog::handle_json(const QByteArray& data)
QMessageBox::critical(this, tr("Validation failed"), tr("Errors were found in the downloaded patch file.\n\nLog:\n%0").arg(QString::fromStdString(log_message.str())));
}
patch_log.success("Successfully downloaded latest patch file");
QMessageBox::information(this, tr("Download successful"), tr("Your patch file is now up to date"));
return true;
}

View File

@ -5,7 +5,6 @@
#include "rpcs3_version.h"
#include "downloader.h"
#include "Utilities/StrUtil.h"
#include "Crypto/sha256.h"
#include "Emu/System.h"
#include <QApplication>
@ -249,22 +248,8 @@ bool update_manager::handle_rpcs3(const QByteArray& data)
return false;
}
u8 res_hash[32];
mbedtls_sha256_context ctx;
mbedtls_sha256_init(&ctx);
mbedtls_sha256_starts_ret(&ctx, 0);
mbedtls_sha256_update_ret(&ctx, reinterpret_cast<const unsigned char*>(data.data()), data.size());
mbedtls_sha256_finish_ret(&ctx, res_hash);
std::string res_hash_string("0000000000000000000000000000000000000000000000000000000000000000");
for (size_t index = 0; index < 32; index++)
{
constexpr auto pal = "0123456789ABCDEF";
res_hash_string[index * 2] = pal[res_hash[index] >> 4];
res_hash_string[(index * 2) + 1] = pal[res_hash[index] & 15];
}
if (m_expected_hash != res_hash_string)
if (const std::string res_hash_string = downloader::get_hash(data.data(), data.size(), false);
m_expected_hash != res_hash_string)
{
update_log.error("Hash mismatch: %s expected: %s", res_hash_string, m_expected_hash);
return false;