mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-11-15 22:54:00 +00:00
service: nfc: Accuracy fixes
This commit is contained in:
parent
fb97aec26b
commit
b1b13ddc6b
10 changed files with 193 additions and 111 deletions
|
@ -36,12 +36,12 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
|
||||||
|
|
||||||
// Validate UUID
|
// Validate UUID
|
||||||
constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3`
|
constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3`
|
||||||
if ((CT ^ ntag_file.uuid.uid[0] ^ ntag_file.uuid.uid[1] ^ ntag_file.uuid.uid[2]) !=
|
if ((CT ^ ntag_file.uuid.part1[0] ^ ntag_file.uuid.part1[1] ^ ntag_file.uuid.part1[2]) !=
|
||||||
ntag_file.uuid.uid[3]) {
|
ntag_file.uuid.crc_check1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ((ntag_file.uuid.uid[4] ^ ntag_file.uuid.uid[5] ^ ntag_file.uuid.uid[6] ^
|
if ((ntag_file.uuid.part2[0] ^ ntag_file.uuid.part2[1] ^ ntag_file.uuid.part2[2] ^
|
||||||
ntag_file.uuid.nintendo_id) != ntag_file.uuid.lock_bytes[0]) {
|
ntag_file.uuid.nintendo_id) != ntag_file.uuid_crc_check2) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,8 +74,9 @@ bool IsAmiiboValid(const NTAG215File& ntag_file) {
|
||||||
NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
|
NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
|
||||||
NTAG215File encoded_data{};
|
NTAG215File encoded_data{};
|
||||||
|
|
||||||
encoded_data.uid = nfc_data.uuid.uid;
|
encoded_data.uid = nfc_data.uuid;
|
||||||
encoded_data.nintendo_id = nfc_data.uuid.nintendo_id;
|
encoded_data.uid_crc_check2 = nfc_data.uuid_crc_check2;
|
||||||
|
encoded_data.internal_number = nfc_data.internal_number;
|
||||||
encoded_data.static_lock = nfc_data.static_lock;
|
encoded_data.static_lock = nfc_data.static_lock;
|
||||||
encoded_data.compability_container = nfc_data.compability_container;
|
encoded_data.compability_container = nfc_data.compability_container;
|
||||||
encoded_data.hmac_data = nfc_data.user_memory.hmac_data;
|
encoded_data.hmac_data = nfc_data.user_memory.hmac_data;
|
||||||
|
@ -94,7 +95,6 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
|
||||||
encoded_data.register_info_crc = nfc_data.user_memory.register_info_crc;
|
encoded_data.register_info_crc = nfc_data.user_memory.register_info_crc;
|
||||||
encoded_data.application_area = nfc_data.user_memory.application_area;
|
encoded_data.application_area = nfc_data.user_memory.application_area;
|
||||||
encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag;
|
encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag;
|
||||||
encoded_data.lock_bytes = nfc_data.uuid.lock_bytes;
|
|
||||||
encoded_data.model_info = nfc_data.user_memory.model_info;
|
encoded_data.model_info = nfc_data.user_memory.model_info;
|
||||||
encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt;
|
encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt;
|
||||||
encoded_data.dynamic_lock = nfc_data.dynamic_lock;
|
encoded_data.dynamic_lock = nfc_data.dynamic_lock;
|
||||||
|
@ -108,9 +108,9 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
|
||||||
EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
|
EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
|
||||||
EncryptedNTAG215File nfc_data{};
|
EncryptedNTAG215File nfc_data{};
|
||||||
|
|
||||||
nfc_data.uuid.uid = encoded_data.uid;
|
nfc_data.uuid = encoded_data.uid;
|
||||||
nfc_data.uuid.nintendo_id = encoded_data.nintendo_id;
|
nfc_data.uuid_crc_check2 = encoded_data.uid_crc_check2;
|
||||||
nfc_data.uuid.lock_bytes = encoded_data.lock_bytes;
|
nfc_data.internal_number = encoded_data.internal_number;
|
||||||
nfc_data.static_lock = encoded_data.static_lock;
|
nfc_data.static_lock = encoded_data.static_lock;
|
||||||
nfc_data.compability_container = encoded_data.compability_container;
|
nfc_data.compability_container = encoded_data.compability_container;
|
||||||
nfc_data.user_memory.hmac_data = encoded_data.hmac_data;
|
nfc_data.user_memory.hmac_data = encoded_data.hmac_data;
|
||||||
|
@ -139,23 +139,12 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
|
||||||
return nfc_data;
|
return nfc_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetTagPassword(const TagUuid& uuid) {
|
|
||||||
// Verify that the generated password is correct
|
|
||||||
u32 password = 0xAA ^ (uuid.uid[1] ^ uuid.uid[3]);
|
|
||||||
password &= (0x55 ^ (uuid.uid[2] ^ uuid.uid[4])) << 8;
|
|
||||||
password &= (0xAA ^ (uuid.uid[3] ^ uuid.uid[5])) << 16;
|
|
||||||
password &= (0x55 ^ (uuid.uid[4] ^ uuid.uid[6])) << 24;
|
|
||||||
return password;
|
|
||||||
}
|
|
||||||
|
|
||||||
HashSeed GetSeed(const NTAG215File& data) {
|
HashSeed GetSeed(const NTAG215File& data) {
|
||||||
HashSeed seed{
|
HashSeed seed{
|
||||||
.magic = data.write_counter,
|
.magic = data.write_counter,
|
||||||
.padding = {},
|
.padding = {},
|
||||||
.uid_1 = data.uid,
|
.uid_1 = data.uid,
|
||||||
.nintendo_id_1 = data.nintendo_id,
|
|
||||||
.uid_2 = data.uid,
|
.uid_2 = data.uid,
|
||||||
.nintendo_id_2 = data.nintendo_id,
|
|
||||||
.keygen_salt = data.keygen_salt,
|
.keygen_salt = data.keygen_salt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -177,10 +166,11 @@ std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed
|
||||||
output.insert(output.end(), key.magic_bytes.begin(),
|
output.insert(output.end(), key.magic_bytes.begin(),
|
||||||
key.magic_bytes.begin() + key.magic_length);
|
key.magic_bytes.begin() + key.magic_length);
|
||||||
|
|
||||||
output.insert(output.end(), seed.uid_1.begin(), seed.uid_1.end());
|
std::array<u8, sizeof(NFP::TagUuid)> seed_uuid{};
|
||||||
output.emplace_back(seed.nintendo_id_1);
|
memcpy(seed_uuid.data(), &seed.uid_1, sizeof(NFP::TagUuid));
|
||||||
output.insert(output.end(), seed.uid_2.begin(), seed.uid_2.end());
|
output.insert(output.end(), seed_uuid.begin(), seed_uuid.end());
|
||||||
output.emplace_back(seed.nintendo_id_2);
|
memcpy(seed_uuid.data(), &seed.uid_2, sizeof(NFP::TagUuid));
|
||||||
|
output.insert(output.end(), seed_uuid.begin(), seed_uuid.end());
|
||||||
|
|
||||||
for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) {
|
for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) {
|
||||||
output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i]));
|
output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i]));
|
||||||
|
@ -264,8 +254,8 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou
|
||||||
|
|
||||||
// Copy the rest of the data directly
|
// Copy the rest of the data directly
|
||||||
out_data.uid = in_data.uid;
|
out_data.uid = in_data.uid;
|
||||||
out_data.nintendo_id = in_data.nintendo_id;
|
out_data.uid_crc_check2 = in_data.uid_crc_check2;
|
||||||
out_data.lock_bytes = in_data.lock_bytes;
|
out_data.internal_number = in_data.internal_number;
|
||||||
out_data.static_lock = in_data.static_lock;
|
out_data.static_lock = in_data.static_lock;
|
||||||
out_data.compability_container = in_data.compability_container;
|
out_data.compability_container = in_data.compability_container;
|
||||||
|
|
||||||
|
|
|
@ -24,10 +24,8 @@ using DrgbOutput = std::array<u8, 0x20>;
|
||||||
struct HashSeed {
|
struct HashSeed {
|
||||||
u16_be magic;
|
u16_be magic;
|
||||||
std::array<u8, 0xE> padding;
|
std::array<u8, 0xE> padding;
|
||||||
NFC::UniqueSerialNumber uid_1;
|
TagUuid uid_1;
|
||||||
u8 nintendo_id_1;
|
TagUuid uid_2;
|
||||||
NFC::UniqueSerialNumber uid_2;
|
|
||||||
u8 nintendo_id_2;
|
|
||||||
std::array<u8, 0x20> keygen_salt;
|
std::array<u8, 0x20> keygen_salt;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size");
|
static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size");
|
||||||
|
@ -69,9 +67,6 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data);
|
||||||
/// Converts from encoded file format to encrypted file format
|
/// Converts from encoded file format to encrypted file format
|
||||||
EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data);
|
EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data);
|
||||||
|
|
||||||
/// Returns password needed to allow write access to protected memory
|
|
||||||
u32 GetTagPassword(const TagUuid& uuid);
|
|
||||||
|
|
||||||
// Generates Seed needed for key derivation
|
// Generates Seed needed for key derivation
|
||||||
HashSeed GetSeed(const NTAG215File& data);
|
HashSeed GetSeed(const NTAG215File& data);
|
||||||
|
|
||||||
|
|
|
@ -242,34 +242,39 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
|
||||||
return ResultWrongDeviceState;
|
return ResultWrongDeviceState;
|
||||||
}
|
}
|
||||||
|
|
||||||
UniqueSerialNumber uuid = encrypted_tag_data.uuid.uid;
|
UniqueSerialNumber uuid{};
|
||||||
|
u8 uuid_length{};
|
||||||
// Generate random UUID to bypass amiibo load limits
|
NfcProtocol protocol{NfcProtocol::TypeA};
|
||||||
if (Settings::values.random_amiibo_id) {
|
TagType tag_type{TagType::Type2};
|
||||||
Common::TinyMT rng{};
|
|
||||||
rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
|
|
||||||
rng.GenerateRandomBytes(uuid.data(), sizeof(UniqueSerialNumber));
|
|
||||||
uuid[3] = 0x88 ^ uuid[0] ^ uuid[1] ^ uuid[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_mifare) {
|
if (is_mifare) {
|
||||||
tag_info = {
|
tag_type = TagType::Mifare;
|
||||||
.uuid = uuid,
|
uuid_length = sizeof(NFP::NtagTagUuid);
|
||||||
.uuid_extension = {},
|
memcpy(uuid.data(), mifare_data.data(), uuid_length);
|
||||||
.uuid_length = static_cast<u8>(uuid.size()),
|
} else {
|
||||||
.protocol = NfcProtocol::TypeA,
|
tag_type = TagType::Type2;
|
||||||
.tag_type = TagType::Type4,
|
uuid_length = sizeof(NFP::NtagTagUuid);
|
||||||
|
NFP::NtagTagUuid nUuid{
|
||||||
|
.part1 = encrypted_tag_data.uuid.part1,
|
||||||
|
.part2 = encrypted_tag_data.uuid.part2,
|
||||||
|
.nintendo_id = encrypted_tag_data.uuid.nintendo_id,
|
||||||
};
|
};
|
||||||
return ResultSuccess;
|
memcpy(uuid.data(), &nUuid, uuid_length);
|
||||||
|
|
||||||
|
// Generate random UUID to bypass amiibo load limits
|
||||||
|
if (Settings::values.random_amiibo_id) {
|
||||||
|
Common::TinyMT rng{};
|
||||||
|
rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
|
||||||
|
rng.GenerateRandomBytes(uuid.data(), uuid_length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Protocol and tag type may change here
|
// Protocol and tag type may change here
|
||||||
tag_info = {
|
tag_info = {
|
||||||
.uuid = uuid,
|
.uuid = uuid,
|
||||||
.uuid_extension = {},
|
.uuid_length = uuid_length,
|
||||||
.uuid_length = static_cast<u8>(uuid.size()),
|
.protocol = protocol,
|
||||||
.protocol = NfcProtocol::TypeA,
|
.tag_type = tag_type,
|
||||||
.tag_type = TagType::Type2,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
|
@ -277,8 +282,38 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
|
||||||
|
|
||||||
Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameters,
|
Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameters,
|
||||||
std::span<MifareReadBlockData> read_block_data) const {
|
std::span<MifareReadBlockData> read_block_data) const {
|
||||||
|
if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
|
||||||
|
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
|
||||||
|
if (device_state == DeviceState::TagRemoved) {
|
||||||
|
return ResultTagRemoved;
|
||||||
|
}
|
||||||
|
return ResultWrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
Result result = ResultSuccess;
|
Result result = ResultSuccess;
|
||||||
|
|
||||||
|
TagInfo tag_info{};
|
||||||
|
result = GetTagInfo(tag_info, true);
|
||||||
|
|
||||||
|
if (result.IsError()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag_info.protocol != NfcProtocol::TypeA || tag_info.tag_type != TagType::Mifare) {
|
||||||
|
return ResultInvalidTagType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters.size() == 0) {
|
||||||
|
return ResultInvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto unknown = parameters[0].sector_key.unknown;
|
||||||
|
for (std::size_t i = 0; i < parameters.size(); i++) {
|
||||||
|
if (unknown != parameters[i].sector_key.unknown) {
|
||||||
|
return ResultInvalidArgument;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (std::size_t i = 0; i < parameters.size(); i++) {
|
for (std::size_t i = 0; i < parameters.size(); i++) {
|
||||||
result = ReadMifare(parameters[i], read_block_data[i]);
|
result = ReadMifare(parameters[i], read_block_data[i]);
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
|
@ -293,17 +328,8 @@ Result NfcDevice::ReadMifare(const MifareReadBlockParameter& parameter,
|
||||||
MifareReadBlockData& read_block_data) const {
|
MifareReadBlockData& read_block_data) const {
|
||||||
const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock);
|
const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock);
|
||||||
read_block_data.sector_number = parameter.sector_number;
|
read_block_data.sector_number = parameter.sector_number;
|
||||||
|
|
||||||
if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
|
|
||||||
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
|
|
||||||
if (device_state == DeviceState::TagRemoved) {
|
|
||||||
return ResultTagRemoved;
|
|
||||||
}
|
|
||||||
return ResultWrongDeviceState;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mifare_data.size() < sector_index + sizeof(DataBlock)) {
|
if (mifare_data.size() < sector_index + sizeof(DataBlock)) {
|
||||||
return Mifare::ResultReadError;
|
return ResultMifareError288;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use parameter.sector_key to read encrypted data
|
// TODO: Use parameter.sector_key to read encrypted data
|
||||||
|
@ -315,6 +341,28 @@ Result NfcDevice::ReadMifare(const MifareReadBlockParameter& parameter,
|
||||||
Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> parameters) {
|
Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> parameters) {
|
||||||
Result result = ResultSuccess;
|
Result result = ResultSuccess;
|
||||||
|
|
||||||
|
TagInfo tag_info{};
|
||||||
|
result = GetTagInfo(tag_info, true);
|
||||||
|
|
||||||
|
if (result.IsError()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag_info.protocol != NfcProtocol::TypeA || tag_info.tag_type != TagType::Mifare) {
|
||||||
|
return ResultInvalidTagType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters.size() == 0) {
|
||||||
|
return ResultInvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto unknown = parameters[0].sector_key.unknown;
|
||||||
|
for (std::size_t i = 0; i < parameters.size(); i++) {
|
||||||
|
if (unknown != parameters[i].sector_key.unknown) {
|
||||||
|
return ResultInvalidArgument;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (std::size_t i = 0; i < parameters.size(); i++) {
|
for (std::size_t i = 0; i < parameters.size(); i++) {
|
||||||
result = WriteMifare(parameters[i]);
|
result = WriteMifare(parameters[i]);
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
|
@ -324,7 +372,7 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet
|
||||||
|
|
||||||
if (!npad_device->WriteNfc(mifare_data)) {
|
if (!npad_device->WriteNfc(mifare_data)) {
|
||||||
LOG_ERROR(Service_NFP, "Error writing to file");
|
LOG_ERROR(Service_NFP, "Error writing to file");
|
||||||
return Mifare::ResultReadError;
|
return ResultMifareError288;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -342,7 +390,7 @@ Result NfcDevice::WriteMifare(const MifareWriteBlockParameter& parameter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mifare_data.size() < sector_index + sizeof(DataBlock)) {
|
if (mifare_data.size() < sector_index + sizeof(DataBlock)) {
|
||||||
return Mifare::ResultReadError;
|
return ResultMifareError288;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use parameter.sector_key to encrypt the data
|
// TODO: Use parameter.sector_key to encrypt the data
|
||||||
|
@ -366,7 +414,7 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target
|
||||||
|
|
||||||
if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {
|
if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {
|
||||||
LOG_ERROR(Service_NFP, "Not an amiibo");
|
LOG_ERROR(Service_NFP, "Not an amiibo");
|
||||||
return ResultNotAnAmiibo;
|
return ResultInvalidTagType;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The loaded amiibo is not encrypted
|
// The loaded amiibo is not encrypted
|
||||||
|
@ -381,14 +429,14 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
|
if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
|
||||||
bool has_backup = HasBackup(encrypted_tag_data.uuid.uid).IsSuccess();
|
bool has_backup = HasBackup(encrypted_tag_data.uuid).IsSuccess();
|
||||||
LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup);
|
LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup);
|
||||||
return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData;
|
return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
|
std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
|
||||||
memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
|
memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
|
||||||
WriteBackupData(encrypted_tag_data.uuid.uid, data);
|
WriteBackupData(encrypted_tag_data.uuid, data);
|
||||||
|
|
||||||
device_state = DeviceState::TagMounted;
|
device_state = DeviceState::TagMounted;
|
||||||
mount_target = mount_target_;
|
mount_target = mount_target_;
|
||||||
|
@ -492,7 +540,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
|
memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
|
||||||
WriteBackupData(encrypted_tag_data.uuid.uid, data);
|
WriteBackupData(encrypted_tag_data.uuid, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!npad_device->WriteNfc(data)) {
|
if (!npad_device->WriteNfc(data)) {
|
||||||
|
@ -520,7 +568,7 @@ Result NfcDevice::Restore() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = ReadBackupData(tag_info.uuid, data);
|
result = ReadBackupData(tag_info.uuid, tag_info.uuid_length, data);
|
||||||
|
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
return result;
|
return result;
|
||||||
|
@ -548,7 +596,7 @@ Result NfcDevice::Restore() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!NFP::AmiiboCrypto::IsAmiiboValid(temporary_encrypted_tag_data)) {
|
if (!NFP::AmiiboCrypto::IsAmiiboValid(temporary_encrypted_tag_data)) {
|
||||||
return ResultNotAnAmiibo;
|
return ResultInvalidTagType;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_plain_amiibo) {
|
if (!is_plain_amiibo) {
|
||||||
|
@ -1194,10 +1242,12 @@ Result NfcDevice::BreakTag(NFP::BreakType break_type) {
|
||||||
return FlushWithBreak(break_type);
|
return FlushWithBreak(break_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result NfcDevice::HasBackup(const NFC::UniqueSerialNumber& uid) const {
|
Result NfcDevice::HasBackup(const UniqueSerialNumber& uid, std::size_t uuid_size) const {
|
||||||
|
ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size");
|
||||||
constexpr auto backup_dir = "backup";
|
constexpr auto backup_dir = "backup";
|
||||||
const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
|
const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
|
||||||
const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, ""));
|
const auto file_name =
|
||||||
|
fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, ""));
|
||||||
|
|
||||||
if (!Common::FS::Exists(yuzu_amiibo_dir / backup_dir / file_name)) {
|
if (!Common::FS::Exists(yuzu_amiibo_dir / backup_dir / file_name)) {
|
||||||
return ResultUnableToAccessBackupFile;
|
return ResultUnableToAccessBackupFile;
|
||||||
|
@ -1206,10 +1256,19 @@ Result NfcDevice::HasBackup(const NFC::UniqueSerialNumber& uid) const {
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result NfcDevice::ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const {
|
Result NfcDevice::HasBackup(const NFP::TagUuid& tag_uid) const {
|
||||||
|
UniqueSerialNumber uuid{};
|
||||||
|
memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid));
|
||||||
|
return HasBackup(uuid, sizeof(NFP::TagUuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result NfcDevice::ReadBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size,
|
||||||
|
std::span<u8> data) const {
|
||||||
|
ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size");
|
||||||
constexpr auto backup_dir = "backup";
|
constexpr auto backup_dir = "backup";
|
||||||
const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
|
const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
|
||||||
const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, ""));
|
const auto file_name =
|
||||||
|
fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, ""));
|
||||||
|
|
||||||
const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name,
|
const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name,
|
||||||
Common::FS::FileAccessMode::Read,
|
Common::FS::FileAccessMode::Read,
|
||||||
|
@ -1228,12 +1287,21 @@ Result NfcDevice::ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result NfcDevice::WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data) {
|
Result NfcDevice::ReadBackupData(const NFP::TagUuid& tag_uid, std::span<u8> data) const {
|
||||||
|
UniqueSerialNumber uuid{};
|
||||||
|
memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid));
|
||||||
|
return ReadBackupData(uuid, sizeof(NFP::TagUuid), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result NfcDevice::WriteBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size,
|
||||||
|
std::span<const u8> data) {
|
||||||
|
ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size");
|
||||||
constexpr auto backup_dir = "backup";
|
constexpr auto backup_dir = "backup";
|
||||||
const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
|
const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
|
||||||
const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, ""));
|
const auto file_name =
|
||||||
|
fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, ""));
|
||||||
|
|
||||||
if (HasBackup(uid).IsError()) {
|
if (HasBackup(uid, uuid_size).IsError()) {
|
||||||
if (!Common::FS::CreateDir(yuzu_amiibo_dir / backup_dir)) {
|
if (!Common::FS::CreateDir(yuzu_amiibo_dir / backup_dir)) {
|
||||||
return ResultBackupPathAlreadyExist;
|
return ResultBackupPathAlreadyExist;
|
||||||
}
|
}
|
||||||
|
@ -1260,6 +1328,12 @@ Result NfcDevice::WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result NfcDevice::WriteBackupData(const NFP::TagUuid& tag_uid, std::span<const u8> data) {
|
||||||
|
UniqueSerialNumber uuid{};
|
||||||
|
memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid));
|
||||||
|
return WriteBackupData(uuid, sizeof(NFP::TagUuid), data);
|
||||||
|
}
|
||||||
|
|
||||||
Result NfcDevice::WriteNtf(std::span<const u8> data) {
|
Result NfcDevice::WriteNtf(std::span<const u8> data) {
|
||||||
if (device_state != DeviceState::TagMounted) {
|
if (device_state != DeviceState::TagMounted) {
|
||||||
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
|
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
|
||||||
|
|
|
@ -86,9 +86,14 @@ public:
|
||||||
Result GetAll(NFP::NfpData& data) const;
|
Result GetAll(NFP::NfpData& data) const;
|
||||||
Result SetAll(const NFP::NfpData& data);
|
Result SetAll(const NFP::NfpData& data);
|
||||||
Result BreakTag(NFP::BreakType break_type);
|
Result BreakTag(NFP::BreakType break_type);
|
||||||
Result HasBackup(const NFC::UniqueSerialNumber& uid) const;
|
Result HasBackup(const UniqueSerialNumber& uid, std::size_t uuid_size) const;
|
||||||
Result ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const;
|
Result HasBackup(const NFP::TagUuid& tag_uid) const;
|
||||||
Result WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data);
|
Result ReadBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size,
|
||||||
|
std::span<u8> data) const;
|
||||||
|
Result ReadBackupData(const NFP::TagUuid& tag_uid, std::span<u8> data) const;
|
||||||
|
Result WriteBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size,
|
||||||
|
std::span<const u8> data);
|
||||||
|
Result WriteBackupData(const NFP::TagUuid& tag_uid, std::span<const u8> data);
|
||||||
Result WriteNtf(std::span<const u8> data);
|
Result WriteNtf(std::span<const u8> data);
|
||||||
|
|
||||||
u64 GetHandle() const;
|
u64 GetHandle() const;
|
||||||
|
|
|
@ -550,7 +550,7 @@ Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) cons
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.IsSuccess()) {
|
if (result.IsSuccess()) {
|
||||||
result = device->ReadBackupData(tag_info.uuid, data);
|
result = device->ReadBackupData(tag_info.uuid, tag_info.uuid_length, data);
|
||||||
result = VerifyDeviceResult(device, result);
|
result = VerifyDeviceResult(device, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -569,7 +569,7 @@ Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> dat
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.IsSuccess()) {
|
if (result.IsSuccess()) {
|
||||||
result = device->WriteBackupData(tag_info.uuid, data);
|
result = device->WriteBackupData(tag_info.uuid, tag_info.uuid_length, data);
|
||||||
result = VerifyDeviceResult(device, result);
|
result = VerifyDeviceResult(device, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,6 @@ constexpr Result ResultInvalidArgument(ErrorModule::NFCMifare, 65);
|
||||||
constexpr Result ResultWrongDeviceState(ErrorModule::NFCMifare, 73);
|
constexpr Result ResultWrongDeviceState(ErrorModule::NFCMifare, 73);
|
||||||
constexpr Result ResultNfcDisabled(ErrorModule::NFCMifare, 80);
|
constexpr Result ResultNfcDisabled(ErrorModule::NFCMifare, 80);
|
||||||
constexpr Result ResultTagRemoved(ErrorModule::NFCMifare, 97);
|
constexpr Result ResultTagRemoved(ErrorModule::NFCMifare, 97);
|
||||||
constexpr Result ResultReadError(ErrorModule::NFCMifare, 288);
|
constexpr Result ResultNotAMifare(ErrorModule::NFCMifare, 288);
|
||||||
|
|
||||||
} // namespace Service::NFC::Mifare
|
} // namespace Service::NFC::Mifare
|
||||||
|
|
|
@ -355,7 +355,7 @@ Result NfcInterface::TranslateResultToNfp(Result result) const {
|
||||||
if (result == ResultApplicationAreaExist) {
|
if (result == ResultApplicationAreaExist) {
|
||||||
return NFP::ResultApplicationAreaExist;
|
return NFP::ResultApplicationAreaExist;
|
||||||
}
|
}
|
||||||
if (result == ResultNotAnAmiibo) {
|
if (result == ResultInvalidTagType) {
|
||||||
return NFP::ResultNotAnAmiibo;
|
return NFP::ResultNotAnAmiibo;
|
||||||
}
|
}
|
||||||
if (result == ResultUnableToAccessBackupFile) {
|
if (result == ResultUnableToAccessBackupFile) {
|
||||||
|
@ -381,6 +381,9 @@ Result NfcInterface::TranslateResultToMifare(Result result) const {
|
||||||
if (result == ResultTagRemoved) {
|
if (result == ResultTagRemoved) {
|
||||||
return Mifare::ResultTagRemoved;
|
return Mifare::ResultTagRemoved;
|
||||||
}
|
}
|
||||||
|
if (result == ResultInvalidTagType) {
|
||||||
|
return Mifare::ResultNotAMifare;
|
||||||
|
}
|
||||||
LOG_WARNING(Service_NFC, "Result conversion not handled");
|
LOG_WARNING(Service_NFC, "Result conversion not handled");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,8 @@ constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFC, 136);
|
||||||
constexpr Result ResultCorruptedData(ErrorModule::NFC, 144);
|
constexpr Result ResultCorruptedData(ErrorModule::NFC, 144);
|
||||||
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFC, 152);
|
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFC, 152);
|
||||||
constexpr Result ResultApplicationAreaExist(ErrorModule::NFC, 168);
|
constexpr Result ResultApplicationAreaExist(ErrorModule::NFC, 168);
|
||||||
constexpr Result ResultNotAnAmiibo(ErrorModule::NFC, 178);
|
constexpr Result ResultInvalidTagType(ErrorModule::NFC, 178);
|
||||||
constexpr Result ResultBackupPathAlreadyExist(ErrorModule::NFC, 216);
|
constexpr Result ResultBackupPathAlreadyExist(ErrorModule::NFC, 216);
|
||||||
|
constexpr Result ResultMifareError288(ErrorModule::NFC, 288);
|
||||||
|
|
||||||
} // namespace Service::NFC
|
} // namespace Service::NFC
|
||||||
|
|
|
@ -35,21 +35,27 @@ enum class State : u32 {
|
||||||
|
|
||||||
// This is nn::nfc::TagType
|
// This is nn::nfc::TagType
|
||||||
enum class TagType : u32 {
|
enum class TagType : u32 {
|
||||||
None,
|
None = 0,
|
||||||
Type1, // ISO14443A RW 96-2k bytes 106kbit/s
|
Type1 = 1U << 0, // ISO14443A RW. Topaz
|
||||||
Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
|
Type2 = 1U << 1, // ISO14443A RW. Ultralight, NTAGX, ST25TN
|
||||||
Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s
|
Type3 = 1U << 2, // ISO14443A RW/RO. Sony FeliCa
|
||||||
Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
|
Type4A = 1U << 3, // ISO14443A RW/RO. DESFire
|
||||||
Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
|
Type4B = 1U << 4, // ISO14443B RW/RO. DESFire
|
||||||
|
Type5 = 1U << 5, // ISO15693 RW/RO. SLI, SLIX, ST25TV
|
||||||
|
Mifare = 1U << 6, // Mifare classic. Skylanders
|
||||||
|
All = 0xFFFFFFFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class PackedTagType : u8 {
|
enum class PackedTagType : u8 {
|
||||||
None,
|
None = 0,
|
||||||
Type1, // ISO14443A RW 96-2k bytes 106kbit/s
|
Type1 = 1U << 0, // ISO14443A RW. Topaz
|
||||||
Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
|
Type2 = 1U << 1, // ISO14443A RW. Ultralight, NTAGX, ST25TN
|
||||||
Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s
|
Type3 = 1U << 2, // ISO14443A RW/RO. Sony FeliCa
|
||||||
Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
|
Type4A = 1U << 3, // ISO14443A RW/RO. DESFire
|
||||||
Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
|
Type4B = 1U << 4, // ISO14443B RW/RO. DESFire
|
||||||
|
Type5 = 1U << 5, // ISO15693 RW/RO. SLI, SLIX, ST25TV
|
||||||
|
Mifare = 1U << 6, // Mifare classic. Skylanders
|
||||||
|
All = 0xFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is nn::nfc::NfcProtocol
|
// This is nn::nfc::NfcProtocol
|
||||||
|
@ -69,8 +75,7 @@ enum class TestWaveType : u32 {
|
||||||
Unknown,
|
Unknown,
|
||||||
};
|
};
|
||||||
|
|
||||||
using UniqueSerialNumber = std::array<u8, 7>;
|
using UniqueSerialNumber = std::array<u8, 10>;
|
||||||
using UniqueSerialNumberExtension = std::array<u8, 3>;
|
|
||||||
|
|
||||||
// This is nn::nfc::DeviceHandle
|
// This is nn::nfc::DeviceHandle
|
||||||
using DeviceHandle = u64;
|
using DeviceHandle = u64;
|
||||||
|
@ -78,7 +83,6 @@ using DeviceHandle = u64;
|
||||||
// This is nn::nfc::TagInfo
|
// This is nn::nfc::TagInfo
|
||||||
struct TagInfo {
|
struct TagInfo {
|
||||||
UniqueSerialNumber uuid;
|
UniqueSerialNumber uuid;
|
||||||
UniqueSerialNumberExtension uuid_extension;
|
|
||||||
u8 uuid_length;
|
u8 uuid_length;
|
||||||
INSERT_PADDING_BYTES(0x15);
|
INSERT_PADDING_BYTES(0x15);
|
||||||
NfcProtocol protocol;
|
NfcProtocol protocol;
|
||||||
|
|
|
@ -85,7 +85,7 @@ enum class CabinetMode : u8 {
|
||||||
StartFormatter,
|
StartFormatter,
|
||||||
};
|
};
|
||||||
|
|
||||||
using LockBytes = std::array<u8, 2>;
|
using UuidPart = std::array<u8, 3>;
|
||||||
using HashData = std::array<u8, 0x20>;
|
using HashData = std::array<u8, 0x20>;
|
||||||
using ApplicationArea = std::array<u8, 0xD8>;
|
using ApplicationArea = std::array<u8, 0xD8>;
|
||||||
using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
|
using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
|
||||||
|
@ -93,12 +93,20 @@ using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
|
||||||
// This is nn::nfp::TagInfo
|
// This is nn::nfp::TagInfo
|
||||||
using TagInfo = NFC::TagInfo;
|
using TagInfo = NFC::TagInfo;
|
||||||
|
|
||||||
struct TagUuid {
|
struct NtagTagUuid {
|
||||||
NFC::UniqueSerialNumber uid;
|
UuidPart part1;
|
||||||
|
UuidPart part2;
|
||||||
u8 nintendo_id;
|
u8 nintendo_id;
|
||||||
LockBytes lock_bytes;
|
|
||||||
};
|
};
|
||||||
static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size");
|
static_assert(sizeof(NtagTagUuid) == 7, "NtagTagUuid is an invalid size");
|
||||||
|
|
||||||
|
struct TagUuid {
|
||||||
|
UuidPart part1;
|
||||||
|
u8 crc_check1;
|
||||||
|
UuidPart part2;
|
||||||
|
u8 nintendo_id;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(TagUuid) == 8, "TagUuid is an invalid size");
|
||||||
|
|
||||||
struct WriteDate {
|
struct WriteDate {
|
||||||
u16 year;
|
u16 year;
|
||||||
|
@ -231,7 +239,8 @@ struct EncryptedAmiiboFile {
|
||||||
static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
|
static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
|
||||||
|
|
||||||
struct NTAG215File {
|
struct NTAG215File {
|
||||||
LockBytes lock_bytes; // Tag UUID
|
u8 uid_crc_check2;
|
||||||
|
u8 internal_number;
|
||||||
u16 static_lock; // Set defined pages as read only
|
u16 static_lock; // Set defined pages as read only
|
||||||
u32 compability_container; // Defines available memory
|
u32 compability_container; // Defines available memory
|
||||||
HashData hmac_data; // Hash
|
HashData hmac_data; // Hash
|
||||||
|
@ -250,8 +259,7 @@ struct NTAG215File {
|
||||||
u32_be register_info_crc;
|
u32_be register_info_crc;
|
||||||
ApplicationArea application_area; // Encrypted Game data
|
ApplicationArea application_area; // Encrypted Game data
|
||||||
HashData hmac_tag; // Hash
|
HashData hmac_tag; // Hash
|
||||||
NFC::UniqueSerialNumber uid; // Unique serial number
|
TagUuid uid;
|
||||||
u8 nintendo_id; // Tag UUID
|
|
||||||
AmiiboModelInfo model_info;
|
AmiiboModelInfo model_info;
|
||||||
HashData keygen_salt; // Salt
|
HashData keygen_salt; // Salt
|
||||||
u32 dynamic_lock; // Dynamic lock
|
u32 dynamic_lock; // Dynamic lock
|
||||||
|
@ -264,7 +272,9 @@ static_assert(std::is_trivially_copyable_v<NTAG215File>, "NTAG215File must be tr
|
||||||
#pragma pack()
|
#pragma pack()
|
||||||
|
|
||||||
struct EncryptedNTAG215File {
|
struct EncryptedNTAG215File {
|
||||||
TagUuid uuid; // Unique serial number
|
TagUuid uuid;
|
||||||
|
u8 uuid_crc_check2;
|
||||||
|
u8 internal_number;
|
||||||
u16 static_lock; // Set defined pages as read only
|
u16 static_lock; // Set defined pages as read only
|
||||||
u32 compability_container; // Defines available memory
|
u32 compability_container; // Defines available memory
|
||||||
EncryptedAmiiboFile user_memory; // Writable data
|
EncryptedAmiiboFile user_memory; // Writable data
|
||||||
|
|
Loading…
Reference in a new issue