nfp: Separate nfc tag from amiibo data

This commit is contained in:
Narr the Reg 2022-02-10 10:58:37 -06:00
parent e35c2fd5d0
commit 6a1ad03153
3 changed files with 74 additions and 42 deletions

View File

@ -299,16 +299,21 @@ public:
/** /**
* Sends a specific vibration to the output device * Sends a specific vibration to the output device
* @return returns true if vibration had no errors * @return true if vibration had no errors
*/ */
bool SetVibration(std::size_t device_index, VibrationValue vibration); bool SetVibration(std::size_t device_index, VibrationValue vibration);
/** /**
* Sends a small vibration to the output device * Sends a small vibration to the output device
* @return returns true if SetVibration was successfull * @return true if SetVibration was successfull
*/ */
bool TestVibration(std::size_t device_index); bool TestVibration(std::size_t device_index);
/**
* Sets the desired data to be polled from a controller
* @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
* @return true if SetPollingMode was successfull
*/
bool SetPollingMode(Common::Input::PollingMode polling_mode); bool SetPollingMode(Common::Input::PollingMode polling_mode);
/// Returns the led pattern corresponding to this emulated controller /// Returns the led pattern corresponding to this emulated controller

View File

@ -479,7 +479,7 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
} }
bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
if (buffer.size() < sizeof(AmiiboFile)) { if (buffer.size() < sizeof(NTAG215File)) {
LOG_ERROR(Service_NFP, "Wrong file size"); LOG_ERROR(Service_NFP, "Wrong file size");
return false; return false;
} }
@ -490,12 +490,15 @@ bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
} }
LOG_INFO(Service_NFP, "Amiibo detected"); LOG_INFO(Service_NFP, "Amiibo detected");
std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); std::memcpy(&tag_data, buffer.data(), sizeof(tag_data));
if (!IsAmiiboValid()) { if (!IsAmiiboValid()) {
return false; return false;
} }
// This value can't be dumped from a tag. Generate it
tag_data.PWD = GetTagPassword(tag_data.uuid);
device_state = DeviceState::TagFound; device_state = DeviceState::TagFound;
activate_event->GetWritableEvent().Signal(); activate_event->GetWritableEvent().Signal();
return true; return true;
@ -511,42 +514,43 @@ void Module::Interface::CloseAmiibo() {
} }
bool Module::Interface::IsAmiiboValid() const { bool Module::Interface::IsAmiiboValid() const {
LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", amiibo.uuid_lock); const auto& amiibo_data = tag_data.user_memory;
LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", amiibo.compability_container); LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", tag_data.lock_bytes);
LOG_DEBUG(Service_NFP, "crypto_init=0x{0:x}", amiibo.crypto_init); LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", tag_data.compability_container);
LOG_DEBUG(Service_NFP, "write_count={}", amiibo.write_count); LOG_DEBUG(Service_NFP, "crypto_init=0x{0:x}", amiibo_data.crypto_init);
LOG_DEBUG(Service_NFP, "write_count={}", amiibo_data.write_count);
LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo.model_info.character_id); LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id);
LOG_DEBUG(Service_NFP, "character_variant={}", amiibo.model_info.character_variant); LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant);
LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo.model_info.amiibo_type); LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type);
LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", amiibo.model_info.model_number); LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number);
LOG_DEBUG(Service_NFP, "series={}", amiibo.model_info.series); LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series);
LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo.model_info.fixed); LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.fixed);
LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", amiibo.tag_dynamic_lock); LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", tag_data.dynamic_lock);
LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", amiibo.tag_CFG0); LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", tag_data.CFG0);
LOG_DEBUG(Service_NFP, "tag_CFG1=0x{0:x}", amiibo.tag_CFG1); LOG_DEBUG(Service_NFP, "tag_CFG1=0x{0:x}", tag_data.CFG1);
// Check against all know constants on an amiibo binary // Check against all know constants on an amiibo binary
if (amiibo.uuid_lock != 0xE00F) { if (tag_data.lock_bytes != 0xE00F) {
return false; return false;
} }
if (amiibo.compability_container != 0xEEFF10F1UL) { if (tag_data.compability_container != 0xEEFF10F1U) {
return false; return false;
} }
if ((amiibo.crypto_init & 0xFF) != 0xA5) { if ((amiibo_data.crypto_init & 0xFF) != 0xA5) {
return false; return false;
} }
if (amiibo.model_info.fixed != 0x02) { if (amiibo_data.model_info.fixed != 0x02) {
return false; return false;
} }
if ((amiibo.tag_dynamic_lock & 0xFFFFFF) != 0x0F0001) { if ((tag_data.dynamic_lock & 0xFFFFFF) != 0x0F0001) {
return false; return false;
} }
if (amiibo.tag_CFG0 != 0x04000000UL) { if (tag_data.CFG0 != 0x04000000U) {
return false; return false;
} }
if (amiibo.tag_CFG1 != 0x5F) { if (tag_data.CFG1 != 0x5F) {
return false; return false;
} }
return true; return true;
@ -629,12 +633,11 @@ ResultCode Module::Interface::Unmount() {
ResultCode Module::Interface::GetTagInfo(TagInfo& tag_info) const { ResultCode Module::Interface::GetTagInfo(TagInfo& tag_info) const {
if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) {
// Read this data from the amiibo save file
tag_info = { tag_info = {
.uuid = amiibo.uuid, .uuid = tag_data.uuid,
.uuid_length = static_cast<u8>(amiibo.uuid.size()), .uuid_length = static_cast<u8>(tag_data.uuid.size()),
.protocol = protocol, .protocol = protocol,
.tag_type = static_cast<u32>(amiibo.model_info.amiibo_type), .tag_type = static_cast<u32>(tag_data.user_memory.model_info.amiibo_type),
}; };
return ResultSuccess; return ResultSuccess;
} }
@ -654,7 +657,7 @@ ResultCode Module::Interface::GetCommonInfo(CommonInfo& common_info) const {
.last_write_year = 2022, .last_write_year = 2022,
.last_write_month = 2, .last_write_month = 2,
.last_write_day = 7, .last_write_day = 7,
.write_counter = amiibo.write_count, .write_counter = tag_data.user_memory.write_count,
.version = 1, .version = 1,
.application_area_size = ApplicationAreaSize, .application_area_size = ApplicationAreaSize,
}; };
@ -667,7 +670,7 @@ ResultCode Module::Interface::GetModelInfo(ModelInfo& model_info) const {
return ErrCodes::WrongDeviceState; return ErrCodes::WrongDeviceState;
} }
model_info = amiibo.model_info; model_info = tag_data.user_memory.model_info;
return ResultSuccess; return ResultSuccess;
} }
@ -757,7 +760,7 @@ bool Module::Interface::AmiiboApplicationDataExist(u32 access_id) const {
return false; return false;
} }
const std::vector<u8> Module::Interface::LoadAmiiboApplicationData(u32 access_id) const { std::vector<u8> Module::Interface::LoadAmiiboApplicationData(u32 access_id) const {
// TODO(german77): Read file // TODO(german77): Read file
std::vector<u8> data(ApplicationAreaSize); std::vector<u8> data(ApplicationAreaSize);
return data; return data;
@ -781,6 +784,15 @@ Core::HID::NpadIdType Module::Interface::GetNpadId() const {
return npad_id; return npad_id;
} }
u32 Module::Interface::GetTagPassword(const TagUuid& uuid) const {
// Verifiy that the generated password is correct
u32 password = 0xAA ^ (uuid[1] ^ uuid[3]);
password &= (0x55 ^ (uuid[2] ^ uuid[4])) << 8;
password &= (0xAA ^ (uuid[3] ^ uuid[5])) << 16;
password &= (0x55 ^ (uuid[4] ^ uuid[6])) << 24;
return password;
}
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
auto module = std::make_shared<Module>(); auto module = std::make_shared<Module>();
std::make_shared<NFP_User>(module, system)->InstallAsService(service_manager); std::make_shared<NFP_User>(module, system)->InstallAsService(service_manager);

View File

@ -7,6 +7,7 @@
#include <array> #include <array>
#include <vector> #include <vector>
#include "common/common_funcs.h"
#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
@ -85,8 +86,10 @@ enum class AmiiboSeries : u8 {
Diablo Diablo
}; };
using TagUuid = std::array<u8, 10>;
struct TagInfo { struct TagInfo {
std::array<u8, 10> uuid; TagUuid uuid;
u8 uuid_length; u8 uuid_length;
INSERT_PADDING_BYTES(0x15); INSERT_PADDING_BYTES(0x15);
s32 protocol; s32 protocol;
@ -138,10 +141,7 @@ public:
const char* name); const char* name);
~Interface() override; ~Interface() override;
struct AmiiboFile { struct EncryptedAmiiboFile {
std::array<u8, 10> uuid;
u16 uuid_lock; // Must be 0F E0
u32 compability_container; // Must be F1 10 FF EE
u16 crypto_init; // Must be A5 XX u16 crypto_init; // Must be A5 XX
u16 write_count; // Number of times the amiibo has been written? u16 write_count; // Number of times the amiibo has been written?
INSERT_PADDING_BYTES(0x20); // System crypts INSERT_PADDING_BYTES(0x20); // System crypts
@ -150,11 +150,22 @@ public:
INSERT_PADDING_BYTES(0xC); // SHA256-HMAC INSERT_PADDING_BYTES(0xC); // SHA256-HMAC
INSERT_PADDING_BYTES(0x114); // section 1 encrypted buffer INSERT_PADDING_BYTES(0x114); // section 1 encrypted buffer
INSERT_PADDING_BYTES(0x54); // section 2 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) == 0x214, "AmiiboFile is an invalid size"); static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
struct NTAG215File {
TagUuid uuid; // Unique serial number
u16 lock_bytes; // Set defined pages as read only
u32 compability_container; // Defines available memory
EncryptedAmiiboFile user_memory; // Writable data
u32 dynamic_lock; // Dynamic lock
u32 CFG0; // Defines memory protected by password
u32 CFG1; // Defines number of verification attempts
u32 PWD; // Password to allow write access
u16 PACK; // Password acknowledge reply
u16 RFUI; // Reserved for future use
};
static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size");
void CreateUserInterface(Kernel::HLERequestContext& ctx); void CreateUserInterface(Kernel::HLERequestContext& ctx);
bool LoadAmiibo(const std::vector<u8>& buffer); bool LoadAmiibo(const std::vector<u8>& buffer);
@ -191,17 +202,21 @@ public:
private: private:
/// Validates that the amiibo file is not corrupted /// Validates that the amiibo file is not corrupted
bool IsAmiiboValid() const; bool IsAmiiboValid() const;
bool AmiiboApplicationDataExist(u32 access_id) const; bool AmiiboApplicationDataExist(u32 access_id) const;
const std::vector<u8> LoadAmiiboApplicationData(u32 access_id) const; std::vector<u8> LoadAmiiboApplicationData(u32 access_id) const;
void SaveAmiiboApplicationData(u32 access_id, const std::vector<u8>& data) const; void SaveAmiiboApplicationData(u32 access_id, const std::vector<u8>& data) const;
/// return password needed to allow write access to protected memory
u32 GetTagPassword(const TagUuid& uuid) const;
const Core::HID::NpadIdType npad_id; const Core::HID::NpadIdType npad_id;
DeviceState device_state{DeviceState::Unaviable}; DeviceState device_state{DeviceState::Unaviable};
KernelHelpers::ServiceContext service_context; KernelHelpers::ServiceContext service_context;
Kernel::KEvent* activate_event; Kernel::KEvent* activate_event;
Kernel::KEvent* deactivate_event; Kernel::KEvent* deactivate_event;
AmiiboFile amiibo{}; NTAG215File tag_data{};
s32 protocol; s32 protocol;
bool is_application_area_initialized{}; bool is_application_area_initialized{};
u32 application_area_id; u32 application_area_id;