Add console unique secure data

This commit is contained in:
PabloMK7 2023-10-07 20:04:19 +02:00
parent 72c1075402
commit f46d637332
6 changed files with 284 additions and 12 deletions

View file

@ -3,12 +3,14 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <cstring> #include <cstring>
#include <QFileDialog>
#include <QFutureWatcher> #include <QFutureWatcher>
#include <QMessageBox> #include <QMessageBox>
#include <QProgressDialog> #include <QProgressDialog>
#include <QtConcurrent/QtConcurrentMap> #include <QtConcurrent/QtConcurrentMap>
#include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configuration_shared.h"
#include "citra_qt/configuration/configure_system.h" #include "citra_qt/configuration/configure_system.h"
#include "common/file_util.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/am/am.h" #include "core/hle/service/am/am.h"
@ -236,6 +238,24 @@ ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent)
&ConfigureSystem::RefreshConsoleID); &ConfigureSystem::RefreshConsoleID);
connect(ui->button_start_download, &QPushButton::clicked, this, connect(ui->button_start_download, &QPushButton::clicked, this,
&ConfigureSystem::DownloadFromNUS); &ConfigureSystem::DownloadFromNUS);
connect(ui->button_secure_info, &QPushButton::clicked, this, [this] {
ui->button_secure_info->setEnabled(false);
const QString file_path_qtstr =
QFileDialog::getOpenFileName(this, tr("Select SecureInfo_A"), QString(),
tr("SecureInfo_A (SecureInfo_A);;All Files (*.*)"));
ui->button_secure_info->setEnabled(true);
InstallSecureData(file_path_qtstr.toStdString(), cfg->GetSecureInfoAPath());
});
connect(ui->button_friend_code_seed, &QPushButton::clicked, this, [this] {
ui->button_friend_code_seed->setEnabled(false);
const QString file_path_qtstr = QFileDialog::getOpenFileName(
this, tr("Select LocalFriendCodeSeed_B"), QString(),
tr("LocalFriendCodeSeed_B (LocalFriendCodeSeed_B);;All Files (*.*)"));
ui->button_friend_code_seed->setEnabled(true);
InstallSecureData(file_path_qtstr.toStdString(), cfg->GetLocalFriendCodeSeedBPath());
});
for (u8 i = 0; i < country_names.size(); i++) { for (u8 i = 0; i < country_names.size(); i++) {
if (std::strcmp(country_names.at(i), "") != 0) { if (std::strcmp(country_names.at(i), "") != 0) {
ui->combo_country->addItem(tr(country_names.at(i)), i); ui->combo_country->addItem(tr(country_names.at(i)), i);
@ -304,6 +324,7 @@ void ConfigureSystem::SetConfiguration() {
ReadSystemSettings(); ReadSystemSettings();
ui->group_system_settings->setEnabled(enabled); ui->group_system_settings->setEnabled(enabled);
ui->group_real_console_unique_data->setEnabled(enabled);
if (enabled) { if (enabled) {
ui->label_disable_info->hide(); ui->label_disable_info->hide();
} }
@ -353,6 +374,9 @@ void ConfigureSystem::ReadSystemSettings() {
// set firmware download region // set firmware download region
ui->combo_download_region->setCurrentIndex(static_cast<int>(cfg->GetRegionValue())); ui->combo_download_region->setCurrentIndex(static_cast<int>(cfg->GetRegionValue()));
// Refresh secure data status
RefreshSecureDataStatus();
} }
void ConfigureSystem::ApplyConfiguration() { void ConfigureSystem::ApplyConfiguration() {
@ -518,6 +542,41 @@ void ConfigureSystem::RefreshConsoleID() {
tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper()));
} }
void ConfigureSystem::InstallSecureData(const std::string& from_path, const std::string& to_path) {
std::string from =
FileUtil::SanitizePath(from_path, FileUtil::DirectorySeparator::PlatformDefault);
std::string to = FileUtil::SanitizePath(to_path, FileUtil::DirectorySeparator::PlatformDefault);
if (from.empty() || from == to) {
return;
}
FileUtil::CreateFullPath(to_path);
FileUtil::Copy(from, to);
cfg->InvalidateSecureData();
RefreshSecureDataStatus();
}
void ConfigureSystem::RefreshSecureDataStatus() {
auto status_to_str = [](Service::CFG::SecureDataLoadStatus status) {
switch (status) {
case Service::CFG::SecureDataLoadStatus::Loaded:
return "Loaded";
case Service::CFG::SecureDataLoadStatus::NotFound:
return "Not Found";
case Service::CFG::SecureDataLoadStatus::Invalid:
return "Invalid";
case Service::CFG::SecureDataLoadStatus::IOError:
return "IO Error";
default:
return "";
}
};
ui->label_secure_info_status->setText(
tr((std::string("Status: ") + status_to_str(cfg->LoadSecureInfoAFile())).c_str()));
ui->label_friend_code_seed_status->setText(
tr((std::string("Status: ") + status_to_str(cfg->LoadLocalFriendCodeSeedBFile())).c_str()));
}
void ConfigureSystem::RetranslateUI() { void ConfigureSystem::RetranslateUI() {
ui->retranslateUi(this); ui->retranslateUi(this);
} }

View file

@ -46,6 +46,9 @@ private:
void UpdateInitTicks(int init_ticks_type); void UpdateInitTicks(int init_ticks_type);
void RefreshConsoleID(); void RefreshConsoleID();
void InstallSecureData(const std::string& from_path, const std::string& to_path);
void RefreshSecureDataStatus();
void SetupPerGameUI(); void SetupPerGameUI();
void DownloadFromNUS(); void DownloadFromNUS();

View file

@ -217,12 +217,39 @@ void Module::Interface::GetRegion(Kernel::HLERequestContext& ctx) {
void Module::Interface::SecureInfoGetByte101(Kernel::HLERequestContext& ctx) { void Module::Interface::SecureInfoGetByte101(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx); IPC::RequestParser rp(ctx);
LOG_DEBUG(Service_CFG, "(STUBBED) called"); u8 ret = 0;
if (cfg->secure_info_a_loaded) {
ret = cfg->secure_info_a.unknown;
}
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(ResultSuccess); rb.Push(RESULT_SUCCESS);
// According to 3dbrew this is normally 0. rb.Push<u8>(ret);
rb.Push<u8>(0); }
void Module::Interface::SecureInfoGetSerialNo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
[[maybe_unused]] u32 out_size = rp.Pop<u32>();
auto out_buffer = rp.PopMappedBuffer();
if (out_buffer.GetSize() < sizeof(SecureInfoA::serial_number)) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrorDescription::InvalidSize, ErrorModule::Config,
ErrorSummary::WrongArgument, ErrorLevel::Permanent));
}
// Never happens on real hardware, but may happen if user didn't supply a dump.
// Always make sure to have available both secure data kinds or error otherwise.
if (!cfg->secure_info_a_loaded || !cfg->local_friend_code_seed_b_loaded) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Config,
ErrorSummary::InvalidState, ErrorLevel::Permanent));
}
out_buffer.Write(&cfg->secure_info_a.serial_number, 0, sizeof(SecureInfoA::serial_number));
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(out_buffer);
} }
void Module::Interface::SetUUIDClockSequence(Kernel::HLERequestContext& ctx) { void Module::Interface::SetUUIDClockSequence(Kernel::HLERequestContext& ctx) {
@ -365,6 +392,43 @@ void Module::Interface::UpdateConfigNANDSavegame(Kernel::HLERequestContext& ctx)
rb.Push(cfg->UpdateConfigNANDSavegame()); rb.Push(cfg->UpdateConfigNANDSavegame());
} }
void Module::Interface::GetLocalFriendCodeSeedData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
[[maybe_unused]] u32 out_size = rp.Pop<u32>();
auto out_buffer = rp.PopMappedBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
if (out_buffer.GetSize() < sizeof(LocalFriendCodeSeedB)) {
rb.Push(ResultCode(ErrorDescription::InvalidSize, ErrorModule::Config,
ErrorSummary::WrongArgument, ErrorLevel::Permanent));
}
// Never happens on real hardware, but may happen if user didn't supply a dump.
// Always make sure to have available both secure data kinds or error otherwise.
if (!cfg->secure_info_a_loaded || !cfg->local_friend_code_seed_b_loaded) {
rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Config,
ErrorSummary::InvalidState, ErrorLevel::Permanent));
}
out_buffer.Write(&cfg->local_friend_code_seed_b, 0, sizeof(LocalFriendCodeSeedB));
rb.Push(RESULT_SUCCESS);
}
void Module::Interface::GetLocalFriendCodeSeed(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
// Never happens on real hardware, but may happen if user didn't supply a dump.
// Always make sure to have available both secure data kinds or error otherwise.
if (!cfg->secure_info_a_loaded || !cfg->local_friend_code_seed_b_loaded) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Config,
ErrorSummary::InvalidState, ErrorLevel::Permanent));
}
IPC::RequestBuilder rb = rp.MakeBuilder(3, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(cfg->local_friend_code_seed_b.friend_code_seed);
}
void Module::Interface::FormatConfig(Kernel::HLERequestContext& ctx) { void Module::Interface::FormatConfig(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx); IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
@ -506,8 +570,16 @@ Result Module::UpdateConfigNANDSavegame() {
return ResultSuccess; return ResultSuccess;
} }
Result Module::FormatConfig() { std::string Module::GetLocalFriendCodeSeedBPath() {
Result res = DeleteConfigNANDSaveFile(); return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "rw/sys/LocalFriendCodeSeed_B";
}
std::string Module::GetSecureInfoAPath() {
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "rw/sys/SecureInfo_A";
}
ResultCode Module::FormatConfig() {
ResultCode res = DeleteConfigNANDSaveFile();
// The delete command fails if the file doesn't exist, so we have to check that too // The delete command fails if the file doesn't exist, so we have to check that too
if (!res.IsSuccess() && res != FileSys::ResultFileNotFound) { if (!res.IsSuccess() && res != FileSys::ResultFileNotFound) {
return res; return res;
@ -579,6 +651,55 @@ Result Module::LoadConfigNANDSaveFile() {
return FormatConfig(); return FormatConfig();
} }
void Module::InvalidateSecureData() {
secure_info_a_loaded = local_friend_code_seed_b_loaded = false;
}
SecureDataLoadStatus Module::LoadSecureInfoAFile() {
if (secure_info_a_loaded) {
return SecureDataLoadStatus::Loaded;
}
std::string file_path = GetSecureInfoAPath();
if (!FileUtil::Exists(file_path)) {
return SecureDataLoadStatus::NotFound;
}
FileUtil::IOFile file(file_path, "rb");
if (!file.IsOpen()) {
return SecureDataLoadStatus::IOError;
}
if (file.GetSize() != sizeof(SecureInfoA)) {
return SecureDataLoadStatus::Invalid;
}
if (file.ReadBytes(&secure_info_a, sizeof(SecureInfoA)) != sizeof(SecureInfoA)) {
return SecureDataLoadStatus::IOError;
}
secure_info_a_loaded = true;
return SecureDataLoadStatus::Loaded;
}
SecureDataLoadStatus Module::LoadLocalFriendCodeSeedBFile() {
if (local_friend_code_seed_b_loaded) {
return SecureDataLoadStatus::Loaded;
}
std::string file_path = GetLocalFriendCodeSeedBPath();
if (!FileUtil::Exists(file_path)) {
return SecureDataLoadStatus::NotFound;
}
FileUtil::IOFile file(file_path, "rb");
if (!file.IsOpen()) {
return SecureDataLoadStatus::IOError;
}
if (file.GetSize() != sizeof(LocalFriendCodeSeedB)) {
return SecureDataLoadStatus::Invalid;
}
if (file.ReadBytes(&local_friend_code_seed_b, sizeof(LocalFriendCodeSeedB)) !=
sizeof(LocalFriendCodeSeedB)) {
return SecureDataLoadStatus::IOError;
}
local_friend_code_seed_b_loaded = true;
return SecureDataLoadStatus::Loaded;
}
void Module::LoadMCUConfig() { void Module::LoadMCUConfig() {
FileUtil::IOFile mcu_data_file( FileUtil::IOFile mcu_data_file(
fmt::format("{}/mcu.dat", FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir)), "rb"); fmt::format("{}/mcu.dat", FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir)), "rb");
@ -616,6 +737,8 @@ Module::Module(Core::System& system_) : system(system_) {
SetEULAVersion(default_version); SetEULAVersion(default_version);
UpdateConfigNANDSavegame(); UpdateConfigNANDSavegame();
} }
LoadSecureInfoAFile();
LoadLocalFriendCodeSeedBFile();
} }
Module::~Module() = default; Module::~Module() = default;

View file

@ -176,6 +176,28 @@ enum class AccessFlag : u16 {
}; };
DECLARE_ENUM_FLAG_OPERATORS(AccessFlag); DECLARE_ENUM_FLAG_OPERATORS(AccessFlag);
struct SecureInfoA {
std::array<u8, 0x100> signature;
u8 region;
u8 unknown;
std::array<u8, 0xF> serial_number;
};
static_assert(sizeof(SecureInfoA) == 0x111);
struct LocalFriendCodeSeedB {
std::array<u8, 0x100> signature;
u64 unknown;
u64 friend_code_seed;
};
static_assert(sizeof(LocalFriendCodeSeedB) == 0x110);
enum class SecureDataLoadStatus {
Loaded,
NotFound,
Invalid,
IOError,
};
class Module final { class Module final {
public: public:
Module(Core::System& system_); Module(Core::System& system_);
@ -230,6 +252,18 @@ public:
*/ */
void SecureInfoGetByte101(Kernel::HLERequestContext& ctx); void SecureInfoGetByte101(Kernel::HLERequestContext& ctx);
/**
* CFG::SecureInfoGetSerialNo service function
* Inputs:
* 1 : Buffer Size
* 2-3: Output mapped buffer
* Outputs:
* 0 : Result Header code
* 1 : Result of function, 0 on success, otherwise error code
* 2-3 : Output mapped buffer
*/
void SecureInfoGetSerialNo(Kernel::HLERequestContext& ctx);
/** /**
* CFG::SetUUIDClockSequence service function * CFG::SetUUIDClockSequence service function
* Inputs: * Inputs:
@ -345,6 +379,27 @@ public:
*/ */
void UpdateConfigNANDSavegame(Kernel::HLERequestContext& ctx); void UpdateConfigNANDSavegame(Kernel::HLERequestContext& ctx);
/**
* CFG::GetLocalFriendCodeSeedData service function
* Inputs:
* 1 : Buffer Size
* 2-3: Output mapped buffer
* Outputs:
* 0 : Result Header code
* 1 : Result of function, 0 on success, otherwise error code
*/
void GetLocalFriendCodeSeedData(Kernel::HLERequestContext& ctx);
/**
* CFG::GetLocalFriendCodeSeed service function
* Inputs:
* Outputs:
* 0 : Result Header code
* 1 : Result of function, 0 on success, otherwise error code
* 2-3 : Friend code seed
*/
void GetLocalFriendCodeSeed(Kernel::HLERequestContext& ctx);
/** /**
* CFG::FormatConfig service function * CFG::FormatConfig service function
* Inputs: * Inputs:
@ -575,6 +630,34 @@ public:
*/ */
Result UpdateConfigNANDSavegame(); Result UpdateConfigNANDSavegame();
/**
* Invalidates the loaded secure data so that it is loaded again.
*/
void InvalidateSecureData();
/**
* Loads the LocalFriendCodeSeed_B file from NAND.
* @returns LocalFriendCodeSeedBLoadStatus indicating the file load status.
*/
SecureDataLoadStatus LoadSecureInfoAFile();
/**
* Loads the LocalFriendCodeSeed_B file from NAND.
* @returns LocalFriendCodeSeedBLoadStatus indicating the file load status.
*/
SecureDataLoadStatus LoadLocalFriendCodeSeedBFile();
/**
* Gets the SecureInfo_A path in the host filesystem
* @returns std::string SecureInfo_A path in the host filesystem
*/
std::string GetSecureInfoAPath();
/**
* Gets the LocalFriendCodeSeed_B path in the host filesystem
* @returns std::string LocalFriendCodeSeed_B path in the host filesystem
*/
std::string GetLocalFriendCodeSeedBPath();
/** /**
* Saves MCU specific data * Saves MCU specific data
*/ */
@ -590,6 +673,10 @@ private:
std::array<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer; std::array<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer;
std::unique_ptr<FileSys::ArchiveBackend> cfg_system_save_data_archive; std::unique_ptr<FileSys::ArchiveBackend> cfg_system_save_data_archive;
u32 preferred_region_code = 0; u32 preferred_region_code = 0;
bool secure_info_a_loaded = false;
SecureInfoA secure_info_a;
bool local_friend_code_seed_b_loaded = false;
LocalFriendCodeSeedB local_friend_code_seed_b;
bool preferred_region_chosen = false; bool preferred_region_chosen = false;
MCUData mcu_data{}; MCUData mcu_data{};

View file

@ -28,11 +28,11 @@ CFG_I::CFG_I(std::shared_ptr<Module> cfg) : Module::Interface(std::move(cfg), "c
{0x0401, &CFG_I::GetSystemConfig, "GetSystemConfig"}, {0x0401, &CFG_I::GetSystemConfig, "GetSystemConfig"},
{0x0402, &CFG_I::SetSystemConfig, "SetSystemConfig"}, {0x0402, &CFG_I::SetSystemConfig, "SetSystemConfig"},
{0x0403, &CFG_I::UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, {0x0403, &CFG_I::UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
{0x0404, nullptr, "GetLocalFriendCodeSeedData"}, {0x0404, &CFG_I::GetLocalFriendCodeSeedData, "GetLocalFriendCodeSeedData"},
{0x0405, nullptr, "GetLocalFriendCodeSeed"}, {0x0405, &CFG_I::GetLocalFriendCodeSeed, "GetLocalFriendCodeSeed"},
{0x0406, &CFG_I::GetRegion, "GetRegion"}, {0x0406, &CFG_I::GetRegion, "GetRegion"},
{0x0407, &CFG_I::SecureInfoGetByte101, "SecureInfoGetByte101"}, {0x0407, &CFG_I::SecureInfoGetByte101, "SecureInfoGetByte101"},
{0x0408, nullptr, "SecureInfoGetSerialNo"}, {0x0408, &CFG_I::SecureInfoGetSerialNo, "SecureInfoGetSerialNo"},
{0x0409, nullptr, "UpdateConfigBlk00040003"}, {0x0409, nullptr, "UpdateConfigBlk00040003"},
// cfg:i // cfg:i
{0x0801, &CFG_I::GetSystemConfig, "GetSystemConfig"}, {0x0801, &CFG_I::GetSystemConfig, "GetSystemConfig"},

View file

@ -28,11 +28,11 @@ CFG_S::CFG_S(std::shared_ptr<Module> cfg) : Module::Interface(std::move(cfg), "c
{0x0401, &CFG_S::GetSystemConfig, "GetSystemConfig"}, {0x0401, &CFG_S::GetSystemConfig, "GetSystemConfig"},
{0x0402, &CFG_S::SetSystemConfig, "SetSystemConfig"}, {0x0402, &CFG_S::SetSystemConfig, "SetSystemConfig"},
{0x0403, &CFG_S::UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, {0x0403, &CFG_S::UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
{0x0404, nullptr, "GetLocalFriendCodeSeedData"}, {0x0404, &CFG_S::GetLocalFriendCodeSeedData, "GetLocalFriendCodeSeedData"},
{0x0405, nullptr, "GetLocalFriendCodeSeed"}, {0x0405, &CFG_S::GetLocalFriendCodeSeed, "GetLocalFriendCodeSeed"},
{0x0406, &CFG_S::GetRegion, "GetRegion"}, {0x0406, &CFG_S::GetRegion, "GetRegion"},
{0x0407, &CFG_S::SecureInfoGetByte101, "SecureInfoGetByte101"}, {0x0407, &CFG_S::SecureInfoGetByte101, "SecureInfoGetByte101"},
{0x0408, nullptr, "SecureInfoGetSerialNo"}, {0x0408, &CFG_S::SecureInfoGetSerialNo, "SecureInfoGetSerialNo"},
{0x0409, nullptr, "UpdateConfigBlk00040003"}, {0x0409, nullptr, "UpdateConfigBlk00040003"},
{0x040D, &CFG_S::SetUUIDClockSequence, "SetUUIDClockSequence"}, {0x040D, &CFG_S::SetUUIDClockSequence, "SetUUIDClockSequence"},
{0x040E, &CFG_S::GetUUIDClockSequence, "GetUUIDClockSequence"}, {0x040E, &CFG_S::GetUUIDClockSequence, "GetUUIDClockSequence"},