forked from suyu/suyu
nfp: Multiple fixes against HW
This commit is contained in:
parent
3ce0ef04dd
commit
673de3995b
9 changed files with 163 additions and 62 deletions
|
@ -431,8 +431,7 @@ CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const {
|
||||||
Service::Mii::MiiManager manager;
|
Service::Mii::MiiManager manager;
|
||||||
auto mii = manager.BuildDefault(0);
|
auto mii = manager.BuildDefault(0);
|
||||||
|
|
||||||
// Check if mii data exist
|
if (!ValidateV3Info(mii_v3)) {
|
||||||
if (mii_v3.version == 0) {
|
|
||||||
return mii;
|
return mii;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -576,6 +575,71 @@ Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const {
|
||||||
return mii_v3;
|
return mii_v3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const {
|
||||||
|
bool is_valid = mii_v3.version == 0 || mii_v3.version == 3;
|
||||||
|
|
||||||
|
is_valid = is_valid && (mii_v3.mii_name[0] != 0);
|
||||||
|
|
||||||
|
is_valid = is_valid && (mii_v3.mii_information.birth_month < 13);
|
||||||
|
is_valid = is_valid && (mii_v3.mii_information.birth_day < 32);
|
||||||
|
is_valid = is_valid && (mii_v3.mii_information.favorite_color < 12);
|
||||||
|
is_valid = is_valid && (mii_v3.height < 128);
|
||||||
|
is_valid = is_valid && (mii_v3.build < 128);
|
||||||
|
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits1.face_shape < 12);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits1.skin_color < 7);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits2.wrinkles < 12);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits2.makeup < 12);
|
||||||
|
|
||||||
|
is_valid = is_valid && (mii_v3.hair_style < 132);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits3.hair_color < 8);
|
||||||
|
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits4.eye_type < 60);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits4.eye_color < 6);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits4.eye_scale < 8);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits4.eye_vertical_stretch < 7);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits4.eye_rotation < 8);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits4.eye_spacing < 13);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits4.eye_y_position < 19);
|
||||||
|
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_style < 25);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_color < 8);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_scale < 9);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_yscale < 7);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_rotation < 12);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_spacing < 12);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_y_position < 19);
|
||||||
|
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits6.nose_type < 18);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits6.nose_scale < 9);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits6.nose_y_position < 19);
|
||||||
|
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits7.mouth_type < 36);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits7.mouth_color < 5);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits7.mouth_scale < 9);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits7.mouth_horizontal_stretch < 7);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits8.mouth_y_position < 19);
|
||||||
|
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits8.mustache_type < 6);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits9.mustache_scale < 7);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits9.mustache_y_position < 17);
|
||||||
|
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits9.bear_type < 6);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits9.facial_hair_color < 8);
|
||||||
|
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits10.glasses_type < 9);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits10.glasses_color < 6);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits10.glasses_scale < 8);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits10.glasses_y_position < 21);
|
||||||
|
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits11.mole_enabled < 2);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits11.mole_scale < 9);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits11.mole_x_position < 17);
|
||||||
|
is_valid = is_valid && (mii_v3.appearance_bits11.mole_y_position < 31);
|
||||||
|
|
||||||
|
return is_valid;
|
||||||
|
}
|
||||||
|
|
||||||
ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {
|
ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {
|
||||||
std::vector<MiiInfoElement> result;
|
std::vector<MiiInfoElement> result;
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ public:
|
||||||
CharInfo BuildDefault(std::size_t index);
|
CharInfo BuildDefault(std::size_t index);
|
||||||
CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const;
|
CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const;
|
||||||
Ver3StoreData ConvertCharInfoToV3(const CharInfo& mii) const;
|
Ver3StoreData ConvertCharInfoToV3(const CharInfo& mii) const;
|
||||||
|
bool ValidateV3Info(const Ver3StoreData& mii_v3) const;
|
||||||
ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag);
|
ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag);
|
||||||
Result GetIndex(const CharInfo& info, u32& index);
|
Result GetIndex(const CharInfo& info, u32& index);
|
||||||
|
|
||||||
|
|
|
@ -106,10 +106,10 @@ public:
|
||||||
{1, &IUser::FinalizeOld, "FinalizeOld"},
|
{1, &IUser::FinalizeOld, "FinalizeOld"},
|
||||||
{2, &IUser::GetStateOld, "GetStateOld"},
|
{2, &IUser::GetStateOld, "GetStateOld"},
|
||||||
{3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"},
|
{3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"},
|
||||||
{400, nullptr, "Initialize"},
|
{400, &IUser::InitializeOld, "Initialize"},
|
||||||
{401, nullptr, "Finalize"},
|
{401, &IUser::FinalizeOld, "Finalize"},
|
||||||
{402, nullptr, "GetState"},
|
{402, &IUser::GetStateOld, "GetState"},
|
||||||
{403, nullptr, "IsNfcEnabled"},
|
{403, &IUser::IsNfcEnabledOld, "IsNfcEnabled"},
|
||||||
{404, nullptr, "ListDevices"},
|
{404, nullptr, "ListDevices"},
|
||||||
{405, nullptr, "GetDeviceState"},
|
{405, nullptr, "GetDeviceState"},
|
||||||
{406, nullptr, "GetNpadId"},
|
{406, nullptr, "GetNpadId"},
|
||||||
|
|
|
@ -25,7 +25,8 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
|
||||||
LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.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_data.model_info.character_variant);
|
LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant);
|
||||||
LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.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_data.model_info.model_number);
|
LOG_DEBUG(Service_NFP, "model_number=0x{0:x}",
|
||||||
|
static_cast<u16>(amiibo_data.model_info.model_number));
|
||||||
LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series);
|
LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series);
|
||||||
LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.constant_value);
|
LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.constant_value);
|
||||||
|
|
||||||
|
@ -35,11 +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[0] ^ ntag_file.uuid[1] ^ ntag_file.uuid[2]) != ntag_file.uuid[3]) {
|
if ((CT ^ ntag_file.uuid.uid[0] ^ ntag_file.uuid.uid[1] ^ ntag_file.uuid.uid[2]) !=
|
||||||
|
ntag_file.uuid.uid[3]) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ((ntag_file.uuid[4] ^ ntag_file.uuid[5] ^ ntag_file.uuid[6] ^ ntag_file.uuid[7]) !=
|
if ((ntag_file.uuid.uid[4] ^ ntag_file.uuid.uid[5] ^ ntag_file.uuid.uid[6] ^
|
||||||
ntag_file.uuid[8]) {
|
ntag_file.uuid.nintendo_id) != ntag_file.uuid.lock_bytes[0]) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +72,8 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
|
||||||
NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
|
NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
|
||||||
NTAG215File encoded_data{};
|
NTAG215File encoded_data{};
|
||||||
|
|
||||||
memcpy(encoded_data.uuid2.data(), nfc_data.uuid.data() + 0x8, sizeof(encoded_data.uuid2));
|
encoded_data.uid = nfc_data.uuid.uid;
|
||||||
|
encoded_data.nintendo_id = nfc_data.uuid.nintendo_id;
|
||||||
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;
|
||||||
|
@ -85,7 +88,7 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
|
||||||
encoded_data.hash = nfc_data.user_memory.hash;
|
encoded_data.hash = nfc_data.user_memory.hash;
|
||||||
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;
|
||||||
memcpy(encoded_data.uuid.data(), nfc_data.uuid.data(), sizeof(encoded_data.uuid));
|
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;
|
||||||
|
@ -99,8 +102,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{};
|
||||||
|
|
||||||
memcpy(nfc_data.uuid.data() + 0x8, encoded_data.uuid2.data(), sizeof(encoded_data.uuid2));
|
nfc_data.uuid.uid = encoded_data.uid;
|
||||||
memcpy(nfc_data.uuid.data(), encoded_data.uuid.data(), sizeof(encoded_data.uuid));
|
nfc_data.uuid.nintendo_id = encoded_data.nintendo_id;
|
||||||
|
nfc_data.uuid.lock_bytes = encoded_data.lock_bytes;
|
||||||
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;
|
||||||
|
@ -127,10 +131,10 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
|
||||||
|
|
||||||
u32 GetTagPassword(const TagUuid& uuid) {
|
u32 GetTagPassword(const TagUuid& uuid) {
|
||||||
// Verifiy that the generated password is correct
|
// Verifiy that the generated password is correct
|
||||||
u32 password = 0xAA ^ (uuid[1] ^ uuid[3]);
|
u32 password = 0xAA ^ (uuid.uid[1] ^ uuid.uid[3]);
|
||||||
password &= (0x55 ^ (uuid[2] ^ uuid[4])) << 8;
|
password &= (0x55 ^ (uuid.uid[2] ^ uuid.uid[4])) << 8;
|
||||||
password &= (0xAA ^ (uuid[3] ^ uuid[5])) << 16;
|
password &= (0xAA ^ (uuid.uid[3] ^ uuid.uid[5])) << 16;
|
||||||
password &= (0x55 ^ (uuid[4] ^ uuid[6])) << 24;
|
password &= (0x55 ^ (uuid.uid[4] ^ uuid.uid[6])) << 24;
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,15 +142,13 @@ HashSeed GetSeed(const NTAG215File& data) {
|
||||||
HashSeed seed{
|
HashSeed seed{
|
||||||
.magic = data.write_counter,
|
.magic = data.write_counter,
|
||||||
.padding = {},
|
.padding = {},
|
||||||
.uuid1 = {},
|
.uid_1 = data.uid,
|
||||||
.uuid2 = {},
|
.nintendo_id_1 = data.nintendo_id,
|
||||||
|
.uid_2 = data.uid,
|
||||||
|
.nintendo_id_2 = data.nintendo_id,
|
||||||
.keygen_salt = data.keygen_salt,
|
.keygen_salt = data.keygen_salt,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Copy the first 8 bytes of uuid
|
|
||||||
memcpy(seed.uuid1.data(), data.uuid.data(), sizeof(seed.uuid1));
|
|
||||||
memcpy(seed.uuid2.data(), data.uuid.data(), sizeof(seed.uuid2));
|
|
||||||
|
|
||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,8 +167,10 @@ 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.uuid1.begin(), seed.uuid1.end());
|
output.insert(output.end(), seed.uid_1.begin(), seed.uid_1.end());
|
||||||
output.insert(output.end(), seed.uuid2.begin(), seed.uuid2.end());
|
output.emplace_back(seed.nintendo_id_1);
|
||||||
|
output.insert(output.end(), seed.uid_2.begin(), seed.uid_2.end());
|
||||||
|
output.emplace_back(seed.nintendo_id_2);
|
||||||
|
|
||||||
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]));
|
||||||
|
@ -250,14 +254,15 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou
|
||||||
reinterpret_cast<unsigned char*>(&out_data.settings));
|
reinterpret_cast<unsigned char*>(&out_data.settings));
|
||||||
|
|
||||||
// Copy the rest of the data directly
|
// Copy the rest of the data directly
|
||||||
out_data.uuid2 = in_data.uuid2;
|
out_data.uid = in_data.uid;
|
||||||
|
out_data.nintendo_id = in_data.nintendo_id;
|
||||||
|
out_data.lock_bytes = in_data.lock_bytes;
|
||||||
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;
|
||||||
|
|
||||||
out_data.constant_value = in_data.constant_value;
|
out_data.constant_value = in_data.constant_value;
|
||||||
out_data.write_counter = in_data.write_counter;
|
out_data.write_counter = in_data.write_counter;
|
||||||
|
|
||||||
out_data.uuid = in_data.uuid;
|
|
||||||
out_data.model_info = in_data.model_info;
|
out_data.model_info = in_data.model_info;
|
||||||
out_data.keygen_salt = in_data.keygen_salt;
|
out_data.keygen_salt = in_data.keygen_salt;
|
||||||
out_data.dynamic_lock = in_data.dynamic_lock;
|
out_data.dynamic_lock = in_data.dynamic_lock;
|
||||||
|
@ -309,7 +314,7 @@ bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& t
|
||||||
// Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC!
|
// Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC!
|
||||||
constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START;
|
constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START;
|
||||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(),
|
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(),
|
||||||
sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uuid),
|
sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uid),
|
||||||
input_length, reinterpret_cast<unsigned char*>(&tag_data.hmac_tag));
|
input_length, reinterpret_cast<unsigned char*>(&tag_data.hmac_tag));
|
||||||
|
|
||||||
// Regenerate data HMAC
|
// Regenerate data HMAC
|
||||||
|
@ -350,7 +355,7 @@ bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_t
|
||||||
constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START;
|
constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START;
|
||||||
constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START;
|
constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START;
|
||||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(),
|
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(),
|
||||||
sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uuid),
|
sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uid),
|
||||||
input_length, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag));
|
input_length, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag));
|
||||||
|
|
||||||
// Init mbedtls HMAC context
|
// Init mbedtls HMAC context
|
||||||
|
@ -364,7 +369,7 @@ bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_t
|
||||||
input_length2); // Data
|
input_length2); // Data
|
||||||
mbedtls_md_hmac_update(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag),
|
mbedtls_md_hmac_update(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag),
|
||||||
sizeof(HashData)); // Tag HMAC
|
sizeof(HashData)); // Tag HMAC
|
||||||
mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.uuid),
|
mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.uid),
|
||||||
input_length);
|
input_length);
|
||||||
mbedtls_md_hmac_finish(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_data));
|
mbedtls_md_hmac_finish(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_data));
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,10 @@ 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;
|
||||||
std::array<u8, 0x8> uuid1;
|
UniqueSerialNumber uid_1;
|
||||||
std::array<u8, 0x8> uuid2;
|
u8 nintendo_id_1;
|
||||||
|
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");
|
||||||
|
|
|
@ -22,6 +22,9 @@
|
||||||
#include "core/hle/service/nfp/nfp_device.h"
|
#include "core/hle/service/nfp/nfp_device.h"
|
||||||
#include "core/hle/service/nfp/nfp_result.h"
|
#include "core/hle/service/nfp/nfp_result.h"
|
||||||
#include "core/hle/service/nfp/nfp_user.h"
|
#include "core/hle/service/nfp/nfp_user.h"
|
||||||
|
#include "core/hle/service/time/time_manager.h"
|
||||||
|
#include "core/hle/service/time/time_zone_content_manager.h"
|
||||||
|
#include "core/hle/service/time/time_zone_types.h"
|
||||||
|
|
||||||
namespace Service::NFP {
|
namespace Service::NFP {
|
||||||
NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
|
NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
|
||||||
|
@ -39,6 +42,9 @@ NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
|
||||||
};
|
};
|
||||||
is_controller_set = true;
|
is_controller_set = true;
|
||||||
callback_key = npad_device->SetCallback(engine_callback);
|
callback_key = npad_device->SetCallback(engine_callback);
|
||||||
|
|
||||||
|
auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()};
|
||||||
|
current_posix_time = standard_steady_clock.GetCurrentTimePoint(system).time_point;
|
||||||
}
|
}
|
||||||
|
|
||||||
NfpDevice::~NfpDevice() {
|
NfpDevice::~NfpDevice() {
|
||||||
|
@ -98,6 +104,7 @@ bool NfpDevice::LoadAmiibo(const std::vector<u8>& data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
device_state = DeviceState::TagFound;
|
device_state = DeviceState::TagFound;
|
||||||
|
deactivate_event->GetReadableEvent().Clear();
|
||||||
activate_event->GetWritableEvent().Signal();
|
activate_event->GetWritableEvent().Signal();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -112,6 +119,7 @@ void NfpDevice::CloseAmiibo() {
|
||||||
device_state = DeviceState::TagRemoved;
|
device_state = DeviceState::TagRemoved;
|
||||||
encrypted_tag_data = {};
|
encrypted_tag_data = {};
|
||||||
tag_data = {};
|
tag_data = {};
|
||||||
|
activate_event->GetReadableEvent().Clear();
|
||||||
deactivate_event->GetWritableEvent().Signal();
|
deactivate_event->GetWritableEvent().Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,8 +148,6 @@ void NfpDevice::Finalize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result NfpDevice::StartDetection(s32 protocol_) {
|
Result NfpDevice::StartDetection(s32 protocol_) {
|
||||||
// TODO(german77): Add callback for when nfc data is available
|
|
||||||
|
|
||||||
if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
|
if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
|
||||||
npad_device->SetPollingMode(Common::Input::PollingMode::NFC);
|
npad_device->SetPollingMode(Common::Input::PollingMode::NFC);
|
||||||
device_state = DeviceState::SearchingForTag;
|
device_state = DeviceState::SearchingForTag;
|
||||||
|
@ -172,11 +178,9 @@ Result NfpDevice::StopDetection() {
|
||||||
Result NfpDevice::Flush() {
|
Result NfpDevice::Flush() {
|
||||||
auto& settings = tag_data.settings;
|
auto& settings = tag_data.settings;
|
||||||
|
|
||||||
if (settings.write_date.raw_date != settings.write_date.raw_date) {
|
const auto& current_date = GetAmiiboDate(current_posix_time);
|
||||||
// TODO: Read current system date
|
if (settings.write_date.raw_date != current_date.raw_date) {
|
||||||
settings.write_date.SetYear(2022);
|
settings.write_date = current_date;
|
||||||
settings.write_date.SetMonth(9);
|
|
||||||
settings.write_date.SetDay(9);
|
|
||||||
settings.crc_counter++;
|
settings.crc_counter++;
|
||||||
// TODO: Find how to calculate the crc check
|
// TODO: Find how to calculate the crc check
|
||||||
// settings.crc = CalculateCRC(settings);
|
// settings.crc = CalculateCRC(settings);
|
||||||
|
@ -239,10 +243,10 @@ Result NfpDevice::GetTagInfo(TagInfo& tag_info) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
tag_info = {
|
tag_info = {
|
||||||
.uuid = encrypted_tag_data.uuid,
|
.uuid = encrypted_tag_data.uuid.uid,
|
||||||
.uuid_length = static_cast<u8>(encrypted_tag_data.uuid.size()),
|
.uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
|
||||||
.protocol = protocol,
|
.protocol = 1,
|
||||||
.tag_type = static_cast<u32>(encrypted_tag_data.user_memory.model_info.amiibo_type),
|
.tag_type = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
|
@ -255,8 +259,6 @@ Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& settings = tag_data.settings;
|
const auto& settings = tag_data.settings;
|
||||||
const u32 application_area_size =
|
|
||||||
tag_data.settings.settings.appdata_initialized == 0 ? 0 : sizeof(ApplicationArea);
|
|
||||||
|
|
||||||
// TODO: Validate this data
|
// TODO: Validate this data
|
||||||
common_info = {
|
common_info = {
|
||||||
|
@ -267,8 +269,8 @@ Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const {
|
||||||
settings.write_date.GetDay(),
|
settings.write_date.GetDay(),
|
||||||
},
|
},
|
||||||
.write_counter = tag_data.write_counter,
|
.write_counter = tag_data.write_counter,
|
||||||
.version = 1,
|
.version = 0,
|
||||||
.application_area_size = application_area_size,
|
.application_area_size = sizeof(ApplicationArea),
|
||||||
};
|
};
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
@ -334,13 +336,8 @@ Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) {
|
||||||
Service::Mii::MiiManager manager;
|
Service::Mii::MiiManager manager;
|
||||||
auto& settings = tag_data.settings;
|
auto& settings = tag_data.settings;
|
||||||
|
|
||||||
// TODO: Read current system date
|
settings.init_date = GetAmiiboDate(current_posix_time);
|
||||||
settings.init_date.SetYear(2022);
|
settings.write_date = GetAmiiboDate(current_posix_time);
|
||||||
settings.init_date.SetMonth(9);
|
|
||||||
settings.init_date.SetDay(9);
|
|
||||||
settings.write_date.SetYear(2022);
|
|
||||||
settings.write_date.SetMonth(9);
|
|
||||||
settings.write_date.SetDay(9);
|
|
||||||
settings.crc_counter++;
|
settings.crc_counter++;
|
||||||
// TODO: Find how to calculate the crc check
|
// TODO: Find how to calculate the crc check
|
||||||
// settings.crc = CalculateCRC(settings);
|
// settings.crc = CalculateCRC(settings);
|
||||||
|
@ -570,4 +567,23 @@ void NfpDevice::SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AmiiboDate NfpDevice::GetAmiiboDate(s64 posix_time) const {
|
||||||
|
const auto& time_zone_manager =
|
||||||
|
system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager();
|
||||||
|
Time::TimeZone::CalendarInfo calendar_info{};
|
||||||
|
AmiiboDate amiibo_date{};
|
||||||
|
|
||||||
|
amiibo_date.SetYear(2000);
|
||||||
|
amiibo_date.SetMonth(1);
|
||||||
|
amiibo_date.SetDay(1);
|
||||||
|
|
||||||
|
if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) {
|
||||||
|
amiibo_date.SetYear(calendar_info.time.year);
|
||||||
|
amiibo_date.SetMonth(calendar_info.time.month);
|
||||||
|
amiibo_date.SetDay(calendar_info.time.day);
|
||||||
|
}
|
||||||
|
|
||||||
|
return amiibo_date;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Service::NFP
|
} // namespace Service::NFP
|
||||||
|
|
|
@ -75,6 +75,7 @@ private:
|
||||||
|
|
||||||
AmiiboName GetAmiiboName(const AmiiboSettings& settings) const;
|
AmiiboName GetAmiiboName(const AmiiboSettings& settings) const;
|
||||||
void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name);
|
void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name);
|
||||||
|
AmiiboDate GetAmiiboDate(s64 posix_time) const;
|
||||||
|
|
||||||
bool is_controller_set{};
|
bool is_controller_set{};
|
||||||
int callback_key;
|
int callback_key;
|
||||||
|
@ -88,6 +89,7 @@ private:
|
||||||
|
|
||||||
bool is_data_moddified{};
|
bool is_data_moddified{};
|
||||||
s32 protocol{};
|
s32 protocol{};
|
||||||
|
s64 current_posix_time{};
|
||||||
DeviceState device_state{DeviceState::Unavailable};
|
DeviceState device_state{DeviceState::Unavailable};
|
||||||
|
|
||||||
NTAG215File tag_data{};
|
NTAG215File tag_data{};
|
||||||
|
|
|
@ -17,5 +17,6 @@ constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
|
||||||
constexpr Result CorruptedData(ErrorModule::NFP, 144);
|
constexpr Result CorruptedData(ErrorModule::NFP, 144);
|
||||||
constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152);
|
constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152);
|
||||||
constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168);
|
constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168);
|
||||||
|
constexpr Result NotAnAmiibo(ErrorModule::NFP, 178);
|
||||||
|
|
||||||
} // namespace Service::NFP
|
} // namespace Service::NFP
|
||||||
|
|
|
@ -75,11 +75,19 @@ enum class AmiiboSeries : u8 {
|
||||||
Diablo,
|
Diablo,
|
||||||
};
|
};
|
||||||
|
|
||||||
using TagUuid = std::array<u8, 10>;
|
using UniqueSerialNumber = std::array<u8, 7>;
|
||||||
|
using LockBytes = std::array<u8, 2>;
|
||||||
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>;
|
||||||
|
|
||||||
|
struct TagUuid {
|
||||||
|
UniqueSerialNumber uid;
|
||||||
|
u8 nintendo_id;
|
||||||
|
LockBytes lock_bytes;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size");
|
||||||
|
|
||||||
struct AmiiboDate {
|
struct AmiiboDate {
|
||||||
u16 raw_date{};
|
u16 raw_date{};
|
||||||
|
|
||||||
|
@ -91,7 +99,7 @@ struct AmiiboDate {
|
||||||
return static_cast<u16>(((GetValue() & 0xFE00) >> 9) + 2000);
|
return static_cast<u16>(((GetValue() & 0xFE00) >> 9) + 2000);
|
||||||
}
|
}
|
||||||
u8 GetMonth() const {
|
u8 GetMonth() const {
|
||||||
return static_cast<u8>(((GetValue() & 0x01E0) >> 5) - 1);
|
return static_cast<u8>((GetValue() & 0x01E0) >> 5);
|
||||||
}
|
}
|
||||||
u8 GetDay() const {
|
u8 GetDay() const {
|
||||||
return static_cast<u8>(GetValue() & 0x001F);
|
return static_cast<u8>(GetValue() & 0x001F);
|
||||||
|
@ -102,7 +110,7 @@ struct AmiiboDate {
|
||||||
raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted);
|
raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted);
|
||||||
}
|
}
|
||||||
void SetMonth(u8 month) {
|
void SetMonth(u8 month) {
|
||||||
const u16 month_converted = static_cast<u16>((month + 1) << 5);
|
const u16 month_converted = static_cast<u16>(month << 5);
|
||||||
raw_date = Common::swap16((GetValue() & ~0x01E0) | month_converted);
|
raw_date = Common::swap16((GetValue() & ~0x01E0) | month_converted);
|
||||||
}
|
}
|
||||||
void SetDay(u8 day) {
|
void SetDay(u8 day) {
|
||||||
|
@ -137,7 +145,7 @@ struct AmiiboModelInfo {
|
||||||
u16 character_id;
|
u16 character_id;
|
||||||
u8 character_variant;
|
u8 character_variant;
|
||||||
AmiiboType amiibo_type;
|
AmiiboType amiibo_type;
|
||||||
u16 model_number;
|
u16_be model_number;
|
||||||
AmiiboSeries series;
|
AmiiboSeries series;
|
||||||
u8 constant_value; // Must be 02
|
u8 constant_value; // Must be 02
|
||||||
INSERT_PADDING_BYTES(0x4); // Unknown
|
INSERT_PADDING_BYTES(0x4); // Unknown
|
||||||
|
@ -172,7 +180,7 @@ 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 {
|
||||||
std::array<u8, 0x2> uuid2;
|
LockBytes lock_bytes; // Tag UUID
|
||||||
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
|
||||||
|
@ -188,7 +196,8 @@ struct NTAG215File {
|
||||||
HashData hash; // Probably a SHA256-HMAC hash?
|
HashData hash; // Probably a SHA256-HMAC hash?
|
||||||
ApplicationArea application_area; // Encrypted Game data
|
ApplicationArea application_area; // Encrypted Game data
|
||||||
HashData hmac_tag; // Hash
|
HashData hmac_tag; // Hash
|
||||||
std::array<u8, 0x8> uuid;
|
UniqueSerialNumber uid; // Unique serial number
|
||||||
|
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
|
||||||
|
@ -215,7 +224,8 @@ static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>,
|
||||||
"EncryptedNTAG215File must be trivially copyable.");
|
"EncryptedNTAG215File must be trivially copyable.");
|
||||||
|
|
||||||
struct TagInfo {
|
struct TagInfo {
|
||||||
TagUuid uuid;
|
UniqueSerialNumber uuid;
|
||||||
|
INSERT_PADDING_BYTES(0x3);
|
||||||
u8 uuid_length;
|
u8 uuid_length;
|
||||||
INSERT_PADDING_BYTES(0x15);
|
INSERT_PADDING_BYTES(0x15);
|
||||||
s32 protocol;
|
s32 protocol;
|
||||||
|
|
Loading…
Reference in a new issue