From 29f9a454eb2597eb50db68c31cb655c51de72dae Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Tue, 8 Feb 2022 14:09:30 -0600 Subject: [PATCH] nfp: Validate amiibo files --- src/core/hle/service/nfp/nfp.cpp | 115 ++++++++++++++++++++++--------- src/core/hle/service/nfp/nfp.h | 71 ++++++++++++++++--- 2 files changed, 145 insertions(+), 41 deletions(-) diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 7f1fb3a71..1e91aa340 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -108,7 +108,7 @@ void IUser::StartDetection(Kernel::HLERequestContext& ctx) { // TODO(german77): Loop through all interfaces if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.StartDetection(); + const auto result = nfp_interface.StartDetection(nfp_protocol); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); return; @@ -209,7 +209,6 @@ void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) { if (device_handle == nfp_interface.GetHandle()) { std::vector data{}; const auto result = nfp_interface.GetApplicationArea(data); - ctx.WriteBuffer(data); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(result); @@ -232,7 +231,6 @@ void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) { // TODO(german77): Loop through all interfaces if (device_handle == nfp_interface.GetHandle()) { const auto result = nfp_interface.SetApplicationArea(data); - IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); return; @@ -255,7 +253,6 @@ void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) { // TODO(german77): Loop through all interfaces if (device_handle == nfp_interface.GetHandle()) { const auto result = nfp_interface.CreateApplicationArea(access_id, data); - IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); return; @@ -390,7 +387,7 @@ void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { } void IUser::GetState(Kernel::HLERequestContext& ctx) { - LOG_INFO(Service_NFC, "called"); + LOG_DEBUG(Service_NFC, "called"); IPC::ResponseBuilder rb{ctx, 3, 0}; rb.Push(ResultSuccess); @@ -400,7 +397,7 @@ void IUser::GetState(Kernel::HLERequestContext& ctx) { void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); // TODO(german77): Loop through all interfaces if (device_handle == nfp_interface.GetHandle()) { @@ -419,7 +416,7 @@ void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) { void IUser::GetNpadId(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); // TODO(german77): Loop through all interfaces if (device_handle == nfp_interface.GetHandle()) { @@ -438,7 +435,7 @@ void IUser::GetNpadId(Kernel::HLERequestContext& ctx) { void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); // TODO(german77): Loop through all interfaces if (device_handle == nfp_interface.GetHandle()) { @@ -493,6 +490,11 @@ bool Module::Interface::LoadAmiibo(const std::vector& buffer) { LOG_INFO(Service_NFP, "New Amiibo detected"); std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); + + if (!IsAmiiboValid()) { + return false; + } + device_state = DeviceState::TagFound; activate_event->GetWritableEvent().Signal(); return true; @@ -501,13 +503,54 @@ bool Module::Interface::LoadAmiibo(const std::vector& buffer) { void Module::Interface::CloseAmiibo() { LOG_INFO(Service_NFP, "Remove amiibo"); device_state = DeviceState::TagRemoved; - write_counter = 0; is_application_area_initialized = false; application_area_id = 0; application_area_data.clear(); deactivate_event->GetWritableEvent().Signal(); } +bool Module::Interface::IsAmiiboValid() const { + LOG_INFO(Service_NFP, "uuid_lock=0x{0:x}", amiibo.uuid_lock); + LOG_INFO(Service_NFP, "compability_container=0x{0:x}", amiibo.compability_container); + LOG_INFO(Service_NFP, "crypto_init=0x{0:x}", amiibo.crypto_init); + LOG_INFO(Service_NFP, "write_count={}", amiibo.write_count); + + LOG_INFO(Service_NFP, "character_id=0x{0:x}", amiibo.model_info.character_id); + LOG_INFO(Service_NFP, "character_variant={}", amiibo.model_info.character_variant); + LOG_INFO(Service_NFP, "amiibo_type={}", amiibo.model_info.amiibo_type); + LOG_INFO(Service_NFP, "model_number=0x{0:x}", amiibo.model_info.model_number); + LOG_INFO(Service_NFP, "series={}", amiibo.model_info.series); + LOG_INFO(Service_NFP, "fixed_value=0x{0:x}", amiibo.model_info.fixed); + + LOG_INFO(Service_NFP, "tag_dynamic_lock=0x{0:x}", amiibo.tag_dynamic_lock); + LOG_INFO(Service_NFP, "tag_CFG0=0x{0:x}", amiibo.tag_CFG0); + LOG_INFO(Service_NFP, "tag_CFG1=0x{0:x}", amiibo.tag_CFG1); + + // Check against all know constants on an amiibo binary + if (amiibo.uuid_lock != 0xE00F) { + return false; + } + if (amiibo.compability_container != 0xEEFF10F1UL) { + return false; + } + if ((amiibo.crypto_init & 0xFF) != 0xA5) { + return false; + } + if (amiibo.model_info.fixed != 0x02) { + return false; + } + if ((amiibo.tag_dynamic_lock & 0xFFFFFF) != 0x0F0001) { + return false; + } + if (amiibo.tag_CFG0 != 0x04000000UL) { + return false; + } + if (amiibo.tag_CFG1 != 0x5F) { + return false; + } + return true; +} + Kernel::KReadableEvent& Module::Interface::GetActivateEvent() const { return activate_event->GetReadableEvent(); } @@ -522,13 +565,12 @@ void Module::Interface::Initialize() { void Module::Interface::Finalize() { device_state = DeviceState::Unaviable; - write_counter = 0; is_application_area_initialized = false; application_area_id = 0; application_area_data.clear(); } -ResultCode Module::Interface::StartDetection() { +ResultCode Module::Interface::StartDetection(s32 protocol_) { auto npad_device = system.HIDCore().GetEmulatedController(npad_id); // TODO(german77): Add callback for when nfc data is available @@ -536,6 +578,7 @@ ResultCode Module::Interface::StartDetection() { if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { npad_device->SetPollingMode(Common::Input::PollingMode::NFC); device_state = DeviceState::SearchingForTag; + protocol = protocol_; return ResultSuccess; } @@ -589,8 +632,8 @@ ResultCode Module::Interface::GetTagInfo(TagInfo& tag_info) const { tag_info = { .uuid = amiibo.uuid, .uuid_length = static_cast(amiibo.uuid.size()), - .protocol = 0xFFFFFFFF, // TODO(ogniK): Figure out actual values - .tag_type = 0xFFFFFFFF, + .protocol = protocol, + .tag_type = static_cast(amiibo.model_info.amiibo_type), }; return ResultSuccess; } @@ -610,7 +653,7 @@ ResultCode Module::Interface::GetCommonInfo(CommonInfo& common_info) const { .last_write_year = 2022, .last_write_month = 2, .last_write_day = 7, - .write_counter = write_counter, + .write_counter = amiibo.write_count, .version = 1, .application_area_size = ApplicationAreaSize, }; @@ -652,11 +695,11 @@ ResultCode Module::Interface::OpenApplicationArea(u32 access_id) { LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); return ErrCodes::WrongDeviceState; } - // if (AmiiboApplicationDataExist(access_id)) { - // application_area_data = LoadAmiiboApplicationData(access_id); - // application_area_id = access_id; - // is_application_area_initialized = true; - // } + if (AmiiboApplicationDataExist(access_id)) { + application_area_data = LoadAmiiboApplicationData(access_id); + application_area_id = access_id; + is_application_area_initialized = true; + } if (!is_application_area_initialized) { LOG_ERROR(Service_NFP, "Application area is not initialized"); return ErrCodes::ApplicationAreaIsNotInitialized; @@ -689,8 +732,7 @@ ResultCode Module::Interface::SetApplicationArea(const std::vector& data) { return ErrCodes::ApplicationAreaIsNotInitialized; } application_area_data = data; - write_counter++; - // SaveAmiiboApplicationData(application_area_id,application_area_data); + SaveAmiiboApplicationData(application_area_id, application_area_data); return ResultSuccess; } @@ -699,21 +741,32 @@ ResultCode Module::Interface::CreateApplicationArea(u32 access_id, const std::ve LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); return ErrCodes::WrongDeviceState; } - // if (AmiiboApplicationDataExist(access_id)) { - // LOG_ERROR(Service_NFP, "Application area already exist"); - // return ErrCodes::ApplicationAreaExist; - // } - // if (LoadAmiiboApplicationData(access_id,data)) { - // is_application_area_initialized = true; - // application_area_id = access_id; - // } + if (AmiiboApplicationDataExist(access_id)) { + LOG_ERROR(Service_NFP, "Application area already exist"); + return ErrCodes::ApplicationAreaExist; + } application_area_data = data; application_area_id = access_id; - write_counter = 0; - // SaveAmiiboApplicationData(application_area_id,application_area_data); + SaveAmiiboApplicationData(application_area_id, application_area_data); return ResultSuccess; } +bool Module::Interface::AmiiboApplicationDataExist(u32 access_id) const { + // TODO(german77): Check if file exist + return false; +} + +const std::vector Module::Interface::LoadAmiiboApplicationData(u32 access_id) const { + // TODO(german77): Read file + std::vector data(ApplicationAreaSize); + return data; +} + +void Module::Interface::SaveAmiiboApplicationData(u32 access_id, + const std::vector& data) const { + // TODO(german77): Save file +} + u64 Module::Interface::GetHandle() const { // Generate a handle based of the npad id return static_cast(npad_id); diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 4642ed634..633539dcc 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h @@ -53,11 +53,43 @@ enum class MountTarget : u32 { All, }; +enum class AmiiboType : u8 { + Figure, + Card, + Yarn, +}; + +enum class AmiiboSeries : u8 { + SuperSmashBros, + SuperMario, + ChibiRobo, + YoshiWoollyWorld, + Splatoon, + AnimalCrossing, + EightBitMario, + Skylanders, + Unknown8, + TheLegendOfZelda, + ShovelKnight, + Unknown11, + Kiby, + Pokemon, + MarioSportsSuperstars, + MonsterHunter, + BoxBoy, + Pikmin, + FireEmblem, + Metroid, + Others, + MegaMan, + Diablo +}; + struct TagInfo { std::array uuid; u8 uuid_length; INSERT_PADDING_BYTES(0x15); - u32 protocol; + s32 protocol; u32 tag_type; INSERT_PADDING_BYTES(0x30); }; @@ -77,10 +109,13 @@ static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); struct ModelInfo { u16 character_id; u8 character_variant; - u8 figure_type; + AmiiboType amiibo_type; u16 model_number; - u8 series; - INSERT_PADDING_BYTES(0x39); + AmiiboSeries series; + u8 fixed; // Must be 02 + INSERT_PADDING_BYTES(0x4); // Unknown + INSERT_PADDING_BYTES(0x20); // Probably a SHA256-(HMAC?) hash + INSERT_PADDING_BYTES(0x14); // SHA256-HMAC }; static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); @@ -105,11 +140,21 @@ public: struct AmiiboFile { std::array uuid; - INSERT_PADDING_BYTES(0x4); // Compability container - INSERT_PADDING_BYTES(0x46); - ModelInfo model_info; + u16 uuid_lock; // Must be 0F E0 + u32 compability_container; // Must be F1 10 FF EE + u16 crypto_init; // Must be A5 XX + u16 write_count; // Number of times the amiibo has been written? + INSERT_PADDING_BYTES(0x20); // System crypts + INSERT_PADDING_BYTES(0x20); // SHA256-(HMAC?) hash + ModelInfo model_info; // This struct is bigger than documentation + INSERT_PADDING_BYTES(0xC); // SHA256-HMAC + INSERT_PADDING_BYTES(0x114); // section 1 encrypted buffer + INSERT_PADDING_BYTES(0x54); // section 2 encrypted buffer + u32 tag_dynamic_lock; // Must be 01 00 0F XX + u32 tag_CFG0; // Must be 00 00 00 04 + u32 tag_CFG1; // Must be 50 00 00 00 }; - static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size"); + static_assert(sizeof(AmiiboFile) == 0x214, "AmiiboFile is an invalid size"); void CreateUserInterface(Kernel::HLERequestContext& ctx); bool LoadAmiibo(const std::vector& buffer); @@ -118,7 +163,7 @@ public: void Initialize(); void Finalize(); - ResultCode StartDetection(); + ResultCode StartDetection(s32 protocol_); ResultCode StopDetection(); ResultCode Mount(); ResultCode Unmount(); @@ -144,6 +189,12 @@ public: std::shared_ptr module; private: + /// Validates that the amiibo file is not corrupted + bool IsAmiiboValid() const; + bool AmiiboApplicationDataExist(u32 access_id) const; + const std::vector LoadAmiiboApplicationData(u32 access_id) const; + void SaveAmiiboApplicationData(u32 access_id, const std::vector& data) const; + const Core::HID::NpadIdType npad_id; DeviceState device_state{DeviceState::Unaviable}; @@ -151,7 +202,7 @@ public: Kernel::KEvent* activate_event; Kernel::KEvent* deactivate_event; AmiiboFile amiibo{}; - u16 write_counter{}; + s32 protocol; bool is_application_area_initialized{}; u32 application_area_id; std::vector application_area_data;