summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/CMakeLists.txt3
-rw-r--r--src/core/hle/service/nfp/amiibo_crypto.cpp455
-rw-r--r--src/core/hle/service/nfp/amiibo_crypto.h102
-rw-r--r--src/core/hle/service/nfp/amiibo_types.h304
-rw-r--r--src/core/hle/service/nfp/nfp.cpp481
-rw-r--r--src/core/hle/service/nfp/nfp.h137
-rw-r--r--src/yuzu/main.cpp21
7 files changed, 1227 insertions, 276 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 806e7ff6c..22ff3d304 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -525,6 +525,9 @@ add_library(core STATIC
525 hle/service/ncm/ncm.h 525 hle/service/ncm/ncm.h
526 hle/service/nfc/nfc.cpp 526 hle/service/nfc/nfc.cpp
527 hle/service/nfc/nfc.h 527 hle/service/nfc/nfc.h
528 hle/service/nfp/amiibo_crypto.cpp
529 hle/service/nfp/amiibo_crypto.h
530 hle/service/nfp/amiibo_types.h
528 hle/service/nfp/nfp.cpp 531 hle/service/nfp/nfp.cpp
529 hle/service/nfp/nfp.h 532 hle/service/nfp/nfp.h
530 hle/service/nfp/nfp_user.cpp 533 hle/service/nfp/nfp_user.cpp
diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp
new file mode 100644
index 000000000..211e518b0
--- /dev/null
+++ b/src/core/hle/service/nfp/amiibo_crypto.cpp
@@ -0,0 +1,455 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4// SPDX-FileCopyrightText: Copyright 2017 socram8888/amiitool
5// SPDX-License-Identifier: MIT
6
7#include <array>
8#include <mbedtls/aes.h>
9#include <mbedtls/hmac_drbg.h>
10
11#include "common/fs/file.h"
12#include "common/fs/path_util.h"
13#include "common/logging/log.h"
14#include "core/hle/service/mii/mii_manager.h"
15#include "core/hle/service/nfp/amiibo_crypto.h"
16
17namespace Service::NFP::AmiiboCrypto {
18
19Service::Mii::MiiInfo AmiiboRegisterInfoToMii(const AmiiboRegisterInfo& mii_info) {
20
21 Service::Mii::MiiManager manager;
22 auto mii = manager.BuildDefault(0);
23
24 // TODO: We are ignoring a bunch of data from the amiibo mii
25
26 mii.gender = static_cast<u8>(mii_info.mii_information.gender);
27 mii.favorite_color = static_cast<u8>(mii_info.mii_information.favorite_color);
28 memcpy(mii.name.data(), mii_info.mii_name.data(), 10);
29 mii.height = mii_info.height;
30 mii.build = mii_info.build;
31
32 mii.faceline_type = mii_info.appearance_bits1.face_shape;
33 mii.faceline_color = mii_info.appearance_bits1.skin_color;
34 mii.faceline_wrinkle = mii_info.appearance_bits2.wrinkles;
35 mii.faceline_make = mii_info.appearance_bits2.makeup;
36
37 mii.hair_type = mii_info.hair_style;
38 mii.hair_color = mii_info.appearance_bits3.hair_color;
39 mii.hair_flip = mii_info.appearance_bits3.flip_hair;
40
41 mii.eye_type = static_cast<u8>(mii_info.appearance_bits4.eye_type);
42 mii.eye_color = static_cast<u8>(mii_info.appearance_bits4.eye_color);
43 mii.eye_scale = static_cast<u8>(mii_info.appearance_bits4.eye_scale);
44 mii.eye_aspect = static_cast<u8>(mii_info.appearance_bits4.eye_vertical_stretch);
45 mii.eye_rotate = static_cast<u8>(mii_info.appearance_bits4.eye_rotation);
46 mii.eye_x = static_cast<u8>(mii_info.appearance_bits4.eye_spacing);
47 mii.eye_y = static_cast<u8>(mii_info.appearance_bits4.eye_y_position);
48
49 mii.eyebrow_type = static_cast<u8>(mii_info.appearance_bits5.eyebrow_style);
50 mii.eyebrow_color = static_cast<u8>(mii_info.appearance_bits5.eyebrow_color);
51 mii.eyebrow_scale = static_cast<u8>(mii_info.appearance_bits5.eyebrow_scale);
52 mii.eyebrow_aspect = static_cast<u8>(mii_info.appearance_bits5.eyebrow_yscale);
53 mii.eyebrow_rotate = static_cast<u8>(mii_info.appearance_bits5.eyebrow_rotation);
54 mii.eyebrow_x = static_cast<u8>(mii_info.appearance_bits5.eyebrow_spacing);
55 mii.eyebrow_y = static_cast<u8>(mii_info.appearance_bits5.eyebrow_y_position);
56
57 mii.nose_type = static_cast<u8>(mii_info.appearance_bits6.nose_type);
58 mii.nose_scale = static_cast<u8>(mii_info.appearance_bits6.nose_scale);
59 mii.nose_y = static_cast<u8>(mii_info.appearance_bits6.nose_y_position);
60
61 mii.mouth_type = static_cast<u8>(mii_info.appearance_bits7.mouth_type);
62 mii.mouth_color = static_cast<u8>(mii_info.appearance_bits7.mouth_color);
63 mii.mouth_scale = static_cast<u8>(mii_info.appearance_bits7.mouth_scale);
64 mii.mouth_aspect = static_cast<u8>(mii_info.appearance_bits7.mouth_horizontal_stretch);
65 mii.mouth_y = static_cast<u8>(mii_info.appearance_bits8.mouth_y_position);
66
67 mii.mustache_type = static_cast<u8>(mii_info.appearance_bits8.mustache_type);
68 mii.mustache_scale = static_cast<u8>(mii_info.appearance_bits9.mustache_scale);
69 mii.mustache_y = static_cast<u8>(mii_info.appearance_bits9.mustache_y_position);
70
71 mii.beard_type = static_cast<u8>(mii_info.appearance_bits9.bear_type);
72 mii.beard_color = static_cast<u8>(mii_info.appearance_bits9.facial_hair_color);
73
74 mii.glasses_type = static_cast<u8>(mii_info.appearance_bits10.glasses_type);
75 mii.glasses_color = static_cast<u8>(mii_info.appearance_bits10.glasses_color);
76 mii.glasses_scale = static_cast<u8>(mii_info.appearance_bits10.glasses_scale);
77 mii.glasses_y = static_cast<u8>(mii_info.appearance_bits10.glasses_y_position);
78
79 mii.mole_type = static_cast<u8>(mii_info.appearance_bits11.mole_enabled);
80 mii.mole_scale = static_cast<u8>(mii_info.appearance_bits11.mole_scale);
81 mii.mole_x = static_cast<u8>(mii_info.appearance_bits11.mole_x_position);
82 mii.mole_y = static_cast<u8>(mii_info.appearance_bits11.mole_y_position);
83
84 // TODO: Validate mii data
85
86 return mii;
87}
88
89bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
90 const auto& amiibo_data = ntag_file.user_memory;
91 LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock);
92 LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container);
93 LOG_INFO(Service_NFP, "write_count={}", amiibo_data.write_counter);
94
95 LOG_INFO(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id);
96 LOG_INFO(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant);
97 LOG_INFO(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type);
98 LOG_INFO(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number);
99 LOG_INFO(Service_NFP, "series={}", amiibo_data.model_info.series);
100 LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.constant_value);
101
102 LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock);
103 LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", ntag_file.CFG0);
104 LOG_DEBUG(Service_NFP, "tag_CFG1=0x{0:x}", ntag_file.CFG1);
105
106 // Validate UUID
107 constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3`
108 if ((CT ^ ntag_file.uuid[0] ^ ntag_file.uuid[1] ^ ntag_file.uuid[2]) != ntag_file.uuid[3]) {
109 return false;
110 }
111 if ((ntag_file.uuid[4] ^ ntag_file.uuid[5] ^ ntag_file.uuid[6] ^ ntag_file.uuid[7]) !=
112 ntag_file.uuid[8]) {
113 return false;
114 }
115
116 // Check against all know constants on an amiibo binary
117 if (ntag_file.static_lock != 0xE00F) {
118 return false;
119 }
120 if (ntag_file.compability_container != 0xEEFF10F1U) {
121 return false;
122 }
123 if (amiibo_data.constant_value != 0xA5) {
124 return false;
125 }
126 if (amiibo_data.model_info.constant_value != 0x02) {
127 return false;
128 }
129 if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001) {
130 return false;
131 }
132 if (ntag_file.CFG0 != 0x04000000U) {
133 return false;
134 }
135 if (ntag_file.CFG1 != 0x5F) {
136 return false;
137 }
138 return true;
139}
140
141NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
142 NTAG215File encoded_data{};
143
144 memcpy(encoded_data.uuid2.data(), nfc_data.uuid.data() + 0x8, 2);
145 encoded_data.static_lock = nfc_data.static_lock;
146 encoded_data.compability_container = nfc_data.compability_container;
147 encoded_data.unfixed_hash = nfc_data.user_memory.unfixed_hash;
148 encoded_data.constant_value = nfc_data.user_memory.constant_value;
149 encoded_data.write_counter = nfc_data.user_memory.write_counter;
150 encoded_data.settings = nfc_data.user_memory.settings;
151 encoded_data.owner_mii = nfc_data.user_memory.owner_mii;
152 encoded_data.title_id = nfc_data.user_memory.title_id;
153 encoded_data.applicaton_write_counter = nfc_data.user_memory.applicaton_write_counter;
154 encoded_data.application_area_id = nfc_data.user_memory.application_area_id;
155 encoded_data.unknown = nfc_data.user_memory.unknown;
156 encoded_data.hash = nfc_data.user_memory.hash;
157 encoded_data.application_area = nfc_data.user_memory.application_area;
158 encoded_data.locked_hash = nfc_data.user_memory.locked_hash;
159 memcpy(encoded_data.uuid.data(), nfc_data.uuid.data(), 8);
160 encoded_data.model_info = nfc_data.user_memory.model_info;
161 encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt;
162 encoded_data.dynamic_lock = nfc_data.dynamic_lock;
163 encoded_data.CFG0 = nfc_data.CFG0;
164 encoded_data.CFG1 = nfc_data.CFG1;
165 encoded_data.password = nfc_data.password;
166
167 return encoded_data;
168}
169
170EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
171 EncryptedNTAG215File nfc_data{};
172
173 memcpy(nfc_data.uuid.data() + 0x8, encoded_data.uuid2.data(), 2);
174 memcpy(nfc_data.uuid.data(), encoded_data.uuid.data(), 8);
175 nfc_data.static_lock = encoded_data.static_lock;
176 nfc_data.compability_container = encoded_data.compability_container;
177 nfc_data.user_memory.unfixed_hash = encoded_data.unfixed_hash;
178 nfc_data.user_memory.constant_value = encoded_data.constant_value;
179 nfc_data.user_memory.write_counter = encoded_data.write_counter;
180 nfc_data.user_memory.settings = encoded_data.settings;
181 nfc_data.user_memory.owner_mii = encoded_data.owner_mii;
182 nfc_data.user_memory.title_id = encoded_data.title_id;
183 nfc_data.user_memory.applicaton_write_counter = encoded_data.applicaton_write_counter;
184 nfc_data.user_memory.application_area_id = encoded_data.application_area_id;
185 nfc_data.user_memory.unknown = encoded_data.unknown;
186 nfc_data.user_memory.hash = encoded_data.hash;
187 nfc_data.user_memory.application_area = encoded_data.application_area;
188 nfc_data.user_memory.locked_hash = encoded_data.locked_hash;
189 nfc_data.user_memory.model_info = encoded_data.model_info;
190 nfc_data.user_memory.keygen_salt = encoded_data.keygen_salt;
191 nfc_data.dynamic_lock = encoded_data.dynamic_lock;
192 nfc_data.CFG0 = encoded_data.CFG0;
193 nfc_data.CFG1 = encoded_data.CFG1;
194 nfc_data.password = encoded_data.password;
195
196 return nfc_data;
197}
198
199u32 GetTagPassword(const TagUuid& uuid) {
200 // Verifiy that the generated password is correct
201 u32 password = 0xAA ^ (uuid[1] ^ uuid[3]);
202 password &= (0x55 ^ (uuid[2] ^ uuid[4])) << 8;
203 password &= (0xAA ^ (uuid[3] ^ uuid[5])) << 16;
204 password &= (0x55 ^ (uuid[4] ^ uuid[6])) << 24;
205 return password;
206}
207
208HashSeed GetSeed(const NTAG215File& data) {
209 HashSeed seed{
210 .data =
211 {
212 .magic = data.write_counter,
213 .padding = {},
214 .uuid1 = {},
215 .uuid2 = {},
216 .keygen_salt = data.keygen_salt,
217 },
218 };
219
220 // Copy the first 8 bytes of uuid
221 memcpy(seed.data.uuid1.data(), data.uuid.data(), sizeof(seed.data.uuid1));
222 memcpy(seed.data.uuid2.data(), data.uuid.data(), sizeof(seed.data.uuid2));
223
224 return seed;
225}
226
227void PreGenerateKey(const InternalKey& key, const HashSeed& seed, u8* output,
228 std::size_t& outputLen) {
229 std::size_t index = 0;
230
231 // Copy whole type string
232 memccpy(output + index, key.type_string.data(), '\0', key.type_string.size());
233 index += key.type_string.size();
234
235 // Append (16 - magic_length) from the input seed
236 std::size_t seedPart1Len = 16 - key.magic_length;
237 memcpy(output + index, &seed, seedPart1Len);
238 index += seedPart1Len;
239
240 // Append all bytes from magicBytes
241 memcpy(output + index, &key.magic_bytes, key.magic_length);
242 index += key.magic_length;
243
244 // Seed 16 bytes at +0x10
245 memcpy(output + index, &seed.raw[0x10], 16);
246 index += 16;
247
248 // 32 bytes at +0x20 from input seed xored with xor pad
249 for (std::size_t i = 0; i < 32; i++)
250 output[index + i] = seed.raw[i + 32] ^ key.xor_pad[i];
251 index += 32;
252
253 outputLen = index;
254}
255
256void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key,
257 const u8* seed, std::size_t seed_size) {
258
259 // Initialize context
260 ctx.used = false;
261 ctx.counter = 0;
262 ctx.buffer_size = sizeof(ctx.counter) + seed_size;
263 memcpy(ctx.buffer.data() + sizeof(u16), seed, seed_size);
264
265 // Initialize HMAC context
266 mbedtls_md_init(&hmac_ctx);
267 mbedtls_md_setup(&hmac_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1);
268 mbedtls_md_hmac_starts(&hmac_ctx, hmac_key.data(), hmac_key.size());
269}
270
271void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output) {
272 // If used at least once, reinitialize the HMAC
273 if (ctx.used) {
274 mbedtls_md_hmac_reset(&hmac_ctx);
275 }
276
277 ctx.used = true;
278
279 // Store counter in big endian, and increment it
280 ctx.buffer[0] = static_cast<u8>(ctx.counter >> 8);
281 ctx.buffer[1] = static_cast<u8>(ctx.counter >> 0);
282 ctx.counter++;
283
284 // Do HMAC magic
285 mbedtls_md_hmac_update(&hmac_ctx, reinterpret_cast<const unsigned char*>(ctx.buffer.data()),
286 ctx.buffer_size);
287 mbedtls_md_hmac_finish(&hmac_ctx, output.data());
288}
289
290DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data) {
291 constexpr std::size_t OUTPUT_SIZE = 512;
292 const auto seed = GetSeed(data);
293
294 // Generate internal seed
295 u8 internal_key[OUTPUT_SIZE];
296 std::size_t internal_key_lenght = 0;
297 PreGenerateKey(key, seed, internal_key, internal_key_lenght);
298
299 // Initialize context
300 CryptoCtx ctx{};
301 mbedtls_md_context_t hmac_ctx;
302 CryptoInit(ctx, hmac_ctx, key.hmac_key, internal_key, internal_key_lenght);
303
304 // Generate derived keys
305 DerivedKeys derived_keys{};
306 std::array<DrgbOutput, 2> temp{};
307 CryptoStep(ctx, hmac_ctx, temp[0]);
308 CryptoStep(ctx, hmac_ctx, temp[1]);
309 memcpy(&derived_keys, temp.data(), sizeof(DerivedKeys));
310
311 // Cleanup context
312 mbedtls_md_free(&hmac_ctx);
313
314 return derived_keys;
315}
316
317void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& out_data) {
318 mbedtls_aes_context aes;
319 std::size_t nc_off = 0;
320 std::array<u8, 0x10> nonce_counter{};
321 std::array<u8, 0x10> stream_block{};
322
323 mbedtls_aes_setkey_enc(&aes, keys.aes_key.data(), 128);
324 memcpy(nonce_counter.data(), keys.aes_iv.data(), sizeof(nonce_counter));
325
326 std::array<u8, sizeof(NTAG215File)> in_data_byes{};
327 std::array<u8, sizeof(NTAG215File)> out_data_bytes{};
328 memcpy(in_data_byes.data(), &in_data, sizeof(NTAG215File));
329 memcpy(out_data_bytes.data(), &out_data, sizeof(NTAG215File));
330
331 mbedtls_aes_crypt_ctr(&aes, 0x188, &nc_off, nonce_counter.data(), stream_block.data(),
332 in_data_byes.data() + 0x2c, out_data_bytes.data() + 0x2c);
333
334 memcpy(out_data_bytes.data(), in_data_byes.data(), 0x008);
335 // Data signature NOT copied
336 memcpy(out_data_bytes.data() + 0x028, in_data_byes.data() + 0x028, 0x004);
337 // Tag signature NOT copied
338 memcpy(out_data_bytes.data() + 0x1D4, in_data_byes.data() + 0x1D4, 0x048);
339
340 memcpy(&out_data, out_data_bytes.data(), sizeof(NTAG215File));
341}
342
343bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) {
344 const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
345
346 const Common::FS::IOFile keys_file{yuzu_keys_dir / "key_retail.bin",
347 Common::FS::FileAccessMode::Read,
348 Common::FS::FileType::BinaryFile};
349
350 if (!keys_file.IsOpen()) {
351 LOG_ERROR(Core, "No keys detected");
352 return false;
353 }
354
355 if (keys_file.Read(unfixed_info) != 1) {
356 LOG_ERROR(Core, "Failed to read unfixed_info");
357 return false;
358 }
359 if (keys_file.Read(locked_secret) != 1) {
360 LOG_ERROR(Core, "Failed to read locked-secret");
361 return false;
362 }
363
364 return true;
365}
366
367bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) {
368 InternalKey locked_secret{};
369 InternalKey unfixed_info{};
370
371 if (!LoadKeys(locked_secret, unfixed_info)) {
372 return false;
373 }
374
375 // Generate keys
376 NTAG215File encoded_data = NfcDataToEncodedData(encrypted_tag_data);
377 const auto data_keys = GenerateKey(unfixed_info, encoded_data);
378 const auto tag_keys = GenerateKey(locked_secret, encoded_data);
379
380 // Decrypt
381 Cipher(data_keys, encoded_data, tag_data);
382
383 std::array<u8, sizeof(NTAG215File)> out{};
384 memcpy(out.data(), &tag_data, sizeof(NTAG215File));
385
386 // Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC!
387 mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(),
388 sizeof(HmacKey), out.data() + 0x1D4, 0x34, out.data() + HMAC_POS_TAG);
389
390 // Regenerate data HMAC
391 mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), data_keys.hmac_key.data(),
392 sizeof(HmacKey), out.data() + 0x29, 0x1DF, out.data() + HMAC_POS_DATA);
393
394 memcpy(&tag_data, out.data(), sizeof(NTAG215File));
395
396 if (memcmp(tag_data.unfixed_hash.data(), encrypted_tag_data.user_memory.unfixed_hash.data(),
397 32) != 0) {
398 return false;
399 }
400
401 if (memcmp(tag_data.locked_hash.data(), encrypted_tag_data.user_memory.locked_hash.data(),
402 32) != 0) {
403 return false;
404 }
405
406 return true;
407}
408
409bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_tag_data) {
410 InternalKey locked_secret{};
411 InternalKey unfixed_info{};
412
413 if (!LoadKeys(locked_secret, unfixed_info)) {
414 return false;
415 }
416
417 // Generate keys
418 const auto data_keys = GenerateKey(unfixed_info, tag_data);
419 const auto tag_keys = GenerateKey(locked_secret, tag_data);
420
421 std::array<u8, sizeof(NTAG215File)> plain{};
422 std::array<u8, sizeof(NTAG215File)> cipher{};
423 memcpy(plain.data(), &tag_data, sizeof(NTAG215File));
424
425 // Generate tag HMAC
426 mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(),
427 sizeof(HmacKey), plain.data() + 0x1D4, 0x34, cipher.data() + HMAC_POS_TAG);
428
429 // Init mbedtls HMAC context
430 mbedtls_md_context_t ctx;
431 mbedtls_md_init(&ctx);
432 mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1);
433
434 // Generate data HMAC
435 mbedtls_md_hmac_starts(&ctx, data_keys.hmac_key.data(), sizeof(HmacKey));
436 mbedtls_md_hmac_update(&ctx, plain.data() + 0x029, 0x18B); // Data
437 mbedtls_md_hmac_update(&ctx, cipher.data() + HMAC_POS_TAG, 0x20); // Tag HMAC
438 mbedtls_md_hmac_update(&ctx, plain.data() + 0x1D4, 0x34);
439 mbedtls_md_hmac_finish(&ctx, cipher.data() + HMAC_POS_DATA);
440
441 // HMAC cleanup
442 mbedtls_md_free(&ctx);
443
444 // Encrypt
445 NTAG215File encoded_tag_data{};
446 memcpy(&encoded_tag_data, cipher.data(), sizeof(NTAG215File));
447 Cipher(data_keys, tag_data, encoded_tag_data);
448
449 // Convert back to hardware
450 encrypted_tag_data = EncodedDataToNfcData(encoded_tag_data);
451
452 return true;
453}
454
455} // namespace Service::NFP::AmiiboCrypto
diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h
new file mode 100644
index 000000000..bfba5dcb2
--- /dev/null
+++ b/src/core/hle/service/nfp/amiibo_crypto.h
@@ -0,0 +1,102 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "core/hle/service/nfp/amiibo_types.h"
9
10struct mbedtls_md_context_t;
11
12namespace Service::NFP::AmiiboCrypto {
13constexpr std::size_t HMAC_POS_DATA = 0x8;
14constexpr std::size_t HMAC_POS_TAG = 0x1B4;
15
16using HmacKey = std::array<u8, 0x10>;
17using DrgbOutput = std::array<u8, 0x20>;
18
19struct HashSeed {
20 union {
21 std::array<u8, 0x40> raw;
22 struct {
23 u16 magic;
24 std::array<u8, 0xE> padding;
25 std::array<u8, 0x8> uuid1;
26 std::array<u8, 0x8> uuid2;
27 std::array<u8, 0x20> keygen_salt;
28 } data;
29 };
30};
31static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size");
32
33struct InternalKey {
34 HmacKey hmac_key;
35 std::array<char, 0xE> type_string;
36 u8 reserved;
37 u8 magic_length;
38 std::array<u8, 0x10> magic_bytes;
39 std::array<u8, 0x20> xor_pad;
40};
41static_assert(sizeof(InternalKey) == 0x50, "InternalKey is an invalid size");
42static_assert(std::is_trivially_copyable_v<InternalKey>, "InternalKey must be trivially copyable.");
43
44struct CryptoCtx {
45 std::array<char, 480> buffer;
46 bool used;
47 std::size_t buffer_size;
48 s16 counter;
49};
50
51struct DerivedKeys {
52 std::array<u8, 0x10> aes_key;
53 std::array<u8, 0x10> aes_iv;
54 std::array<u8, 0x10> hmac_key;
55};
56static_assert(sizeof(DerivedKeys) == 0x30, "DerivedKeys is an invalid size");
57
58/// Converts mii data from nintendo 3ds format to nintendo switch format
59Service::Mii::MiiInfo AmiiboRegisterInfoToMii(const AmiiboRegisterInfo& register_info);
60
61/// Validates that the amiibo file is not corrupted
62bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file);
63
64/// Converts from encrypted file format to encoded file format
65NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data);
66
67/// Converts from encoded file format to encrypted file format
68EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data);
69
70/// Returns password needed to allow write access to protected memory
71u32 GetTagPassword(const TagUuid& uuid);
72
73// Generates Seed needed for key derivation
74HashSeed GetSeed(const NTAG215File& data);
75
76// Middle step on the generation of derived keys
77void PreGenerateKey(const InternalKey& key, const HashSeed& seed, u8* output,
78 std::size_t& outputLen);
79
80// Initializes mbedtls context
81void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key,
82 const u8* seed, std::size_t seed_size);
83
84// Feeds data to mbedtls context to generate the derived key
85void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output);
86
87// Generates the derived key from amiibo data
88DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data);
89
90// Encodes or decodes amiibo data
91void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& out_data);
92
93/// Loads both amiibo keys from key_retail.bin
94bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info);
95
96/// Decodes encripted amiibo data returns true if output is valid
97bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data);
98
99/// Encodes plain amiibo data returns true if output is valid
100bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_tag_data);
101
102} // namespace Service::NFP::AmiiboCrypto
diff --git a/src/core/hle/service/nfp/amiibo_types.h b/src/core/hle/service/nfp/amiibo_types.h
new file mode 100644
index 000000000..49875cff4
--- /dev/null
+++ b/src/core/hle/service/nfp/amiibo_types.h
@@ -0,0 +1,304 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7
8namespace Service::NFP {
9enum class ServiceType : u32 {
10 User,
11 Debug,
12 System,
13};
14
15enum class State : u32 {
16 NonInitialized,
17 Initialized,
18};
19
20enum class DeviceState : u32 {
21 Initialized,
22 SearchingForTag,
23 TagFound,
24 TagRemoved,
25 TagMounted,
26 Unaviable,
27 Finalized,
28};
29
30enum class ModelType : u32 {
31 Amiibo,
32};
33
34enum class MountTarget : u32 {
35 Rom,
36 Ram,
37 All,
38};
39
40enum class AmiiboType : u8 {
41 Figure,
42 Card,
43 Yarn,
44};
45
46enum class AmiiboSeries : u8 {
47 SuperSmashBros,
48 SuperMario,
49 ChibiRobo,
50 YoshiWoollyWorld,
51 Splatoon,
52 AnimalCrossing,
53 EightBitMario,
54 Skylanders,
55 Unknown8,
56 TheLegendOfZelda,
57 ShovelKnight,
58 Unknown11,
59 Kiby,
60 Pokemon,
61 MarioSportsSuperstars,
62 MonsterHunter,
63 BoxBoy,
64 Pikmin,
65 FireEmblem,
66 Metroid,
67 Others,
68 MegaMan,
69 Diablo,
70};
71
72using TagUuid = std::array<u8, 10>;
73using HashData = std::array<u8, 0x20>;
74using ApplicationArea = std::array<u8, 0xD8>;
75
76struct AmiiboDate {
77 union {
78 u16_be raw{};
79
80 BitField<0, 5, u16> day;
81 BitField<5, 4, u16> month;
82 BitField<9, 7, u16> year;
83 };
84};
85static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size");
86
87struct Settings {
88 union {
89 u8 raw{};
90
91 BitField<4, 1, u8> amiibo_initialized;
92 BitField<5, 1, u8> appdata_initialized;
93 };
94};
95static_assert(sizeof(Settings) == 1, "AmiiboDate is an invalid size");
96
97struct AmiiboSettings {
98 Settings settings;
99 u8 country_code_id;
100 u16_be crc_counter; // Incremented each time crc is changed
101 AmiiboDate init_date;
102 AmiiboDate write_date;
103 u32_be crc;
104 std::array<u16_be, 0xA> amiibo_name; // UTF-16 text
105};
106static_assert(sizeof(AmiiboSettings) == 0x20, "AmiiboSettings is an invalid size");
107
108struct AmiiboModelInfo {
109 u16 character_id;
110 u8 character_variant;
111 AmiiboType amiibo_type;
112 u16 model_number;
113 AmiiboSeries series;
114 u8 constant_value; // Must be 02
115 INSERT_PADDING_BYTES(0x4); // Unknown
116};
117static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size");
118
119struct NTAG215Password {
120 u32 PWD; // Password to allow write access
121 u16 PACK; // Password acknowledge reply
122 u16 RFUI; // Reserved for future use
123};
124static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size");
125
126// Based on citra HLE::Applets::MiiData and PretendoNetwork.
127// https://github.com/citra-emu/citra/blob/master/src/core/hle/applets/mii_selector.h#L48
128// https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299
129#pragma pack(1)
130struct AmiiboRegisterInfo {
131 u32_be mii_id;
132 u64_be system_id;
133 u32_be specialness_and_creation_date;
134 std::array<u8, 0x6> creator_mac;
135 u16_be padding;
136 union {
137 u16 raw;
138
139 BitField<0, 1, u16> gender;
140 BitField<1, 4, u16> birth_month;
141 BitField<5, 5, u16> birth_day;
142 BitField<10, 4, u16> favorite_color;
143 BitField<14, 1, u16> favorite;
144 } mii_information;
145 std::array<char16_t, 0xA> mii_name;
146 u8 height;
147 u8 build;
148 union {
149 u8 raw;
150
151 BitField<0, 1, u8> disable_sharing;
152 BitField<1, 4, u8> face_shape;
153 BitField<5, 3, u8> skin_color;
154 } appearance_bits1;
155 union {
156 u8 raw;
157
158 BitField<0, 4, u8> wrinkles;
159 BitField<4, 4, u8> makeup;
160 } appearance_bits2;
161 u8 hair_style;
162 union {
163 u8 raw;
164
165 BitField<0, 3, u8> hair_color;
166 BitField<3, 1, u8> flip_hair;
167 } appearance_bits3;
168 union {
169 u32 raw;
170
171 BitField<0, 6, u32> eye_type;
172 BitField<6, 3, u32> eye_color;
173 BitField<9, 4, u32> eye_scale;
174 BitField<13, 3, u32> eye_vertical_stretch;
175 BitField<16, 5, u32> eye_rotation;
176 BitField<21, 4, u32> eye_spacing;
177 BitField<25, 5, u32> eye_y_position;
178 } appearance_bits4;
179 union {
180 u32 raw;
181
182 BitField<0, 5, u32> eyebrow_style;
183 BitField<5, 3, u32> eyebrow_color;
184 BitField<8, 4, u32> eyebrow_scale;
185 BitField<12, 3, u32> eyebrow_yscale;
186 BitField<16, 4, u32> eyebrow_rotation;
187 BitField<21, 4, u32> eyebrow_spacing;
188 BitField<25, 5, u32> eyebrow_y_position;
189 } appearance_bits5;
190 union {
191 u16 raw;
192
193 BitField<0, 5, u16> nose_type;
194 BitField<5, 4, u16> nose_scale;
195 BitField<9, 5, u16> nose_y_position;
196 } appearance_bits6;
197 union {
198 u16 raw;
199
200 BitField<0, 6, u16> mouth_type;
201 BitField<6, 3, u16> mouth_color;
202 BitField<9, 4, u16> mouth_scale;
203 BitField<13, 3, u16> mouth_horizontal_stretch;
204 } appearance_bits7;
205 union {
206 u8 raw;
207
208 BitField<0, 5, u8> mouth_y_position;
209 BitField<5, 3, u8> mustache_type;
210 } appearance_bits8;
211 u8 allow_copying;
212 union {
213 u16 raw;
214
215 BitField<0, 3, u16> bear_type;
216 BitField<3, 3, u16> facial_hair_color;
217 BitField<6, 4, u16> mustache_scale;
218 BitField<10, 5, u16> mustache_y_position;
219 } appearance_bits9;
220 union {
221 u16 raw;
222
223 BitField<0, 4, u16> glasses_type;
224 BitField<4, 3, u16> glasses_color;
225 BitField<7, 4, u16> glasses_scale;
226 BitField<11, 5, u16> glasses_y_position;
227 } appearance_bits10;
228 union {
229 u16 raw;
230
231 BitField<0, 1, u16> mole_enabled;
232 BitField<1, 4, u16> mole_scale;
233 BitField<5, 5, u16> mole_x_position;
234 BitField<10, 5, u16> mole_y_position;
235 } appearance_bits11;
236
237 std::array<u16_le, 0xA> author_name;
238 INSERT_PADDING_BYTES(0x4);
239};
240static_assert(sizeof(AmiiboRegisterInfo) == 0x60, "AmiiboRegisterInfo is an invalid size");
241
242struct EncryptedAmiiboFile {
243 u8 constant_value; // Must be A5
244 u16 write_counter; // Number of times the amiibo has been written?
245 INSERT_PADDING_BYTES(0x1); // Unknown 1
246 AmiiboSettings settings; // Encrypted amiibo settings
247 HashData locked_hash; // Hash
248 AmiiboModelInfo model_info; // Encrypted amiibo model info
249 HashData keygen_salt; // Salt
250 HashData unfixed_hash; // Hash
251 AmiiboRegisterInfo owner_mii; // Encrypted Mii data
252 u64_be title_id; // Encrypted Game id
253 u16_be applicaton_write_counter; // Encrypted Counter
254 u32_be application_area_id; // Encrypted Game id
255 std::array<u8, 0x2> unknown;
256 HashData hash; // Probably a SHA256-HMAC hash?
257 ApplicationArea application_area; // Encrypted Game data
258};
259static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
260
261struct NTAG215File {
262 std::array<u8, 0x2> uuid2;
263 u16 static_lock; // Set defined pages as read only
264 u32 compability_container; // Defines available memory
265 HashData unfixed_hash; // Hash
266 u8 constant_value; // Must be A5
267 u16 write_counter; // Number of times the amiibo has been written?
268 INSERT_PADDING_BYTES(0x1); // Unknown 1
269 AmiiboSettings settings;
270 AmiiboRegisterInfo owner_mii; // Encrypted Mii data
271 u64_be title_id;
272 u16_be applicaton_write_counter; // Encrypted Counter
273 u32_be application_area_id;
274 std::array<u8, 0x2> unknown;
275 HashData hash; // Probably a SHA256-HMAC hash?
276 ApplicationArea application_area; // Encrypted Game data
277 HashData locked_hash; // Hash
278 std::array<u8, 0x8> uuid;
279 AmiiboModelInfo model_info;
280 HashData keygen_salt; // Salt
281 u32 dynamic_lock; // Dynamic lock
282 u32 CFG0; // Defines memory protected by password
283 u32 CFG1; // Defines number of verification attempts
284 NTAG215Password password; // Password data
285};
286static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size");
287static_assert(std::is_trivially_copyable_v<NTAG215File>, "NTAG215File must be trivially copyable.");
288#pragma pack()
289
290struct EncryptedNTAG215File {
291 TagUuid uuid; // Unique serial number
292 u16 static_lock; // Set defined pages as read only
293 u32 compability_container; // Defines available memory
294 EncryptedAmiiboFile user_memory; // Writable data
295 u32 dynamic_lock; // Dynamic lock
296 u32 CFG0; // Defines memory protected by password
297 u32 CFG1; // Defines number of verification attempts
298 NTAG215Password password; // Password data
299};
300static_assert(sizeof(EncryptedNTAG215File) == 0x21C, "EncryptedNTAG215File is an invalid size");
301static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>,
302 "EncryptedNTAG215File must be trivially copyable.");
303
304} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 6c5b41dd1..4dba05a6a 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -4,6 +4,8 @@
4#include <array> 4#include <array>
5#include <atomic> 5#include <atomic>
6 6
7#include "common/fs/file.h"
8#include "common/fs/path_util.h"
7#include "common/logging/log.h" 9#include "common/logging/log.h"
8#include "core/core.h" 10#include "core/core.h"
9#include "core/hid/emulated_controller.h" 11#include "core/hid/emulated_controller.h"
@@ -12,6 +14,7 @@
12#include "core/hle/ipc_helpers.h" 14#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/k_event.h" 15#include "core/hle/kernel/k_event.h"
14#include "core/hle/service/mii/mii_manager.h" 16#include "core/hle/service/mii/mii_manager.h"
17#include "core/hle/service/nfp/amiibo_crypto.h"
15#include "core/hle/service/nfp/nfp.h" 18#include "core/hle/service/nfp/nfp.h"
16#include "core/hle/service/nfp/nfp_user.h" 19#include "core/hle/service/nfp/nfp_user.h"
17 20
@@ -19,12 +22,13 @@ namespace Service::NFP {
19namespace ErrCodes { 22namespace ErrCodes {
20constexpr Result DeviceNotFound(ErrorModule::NFP, 64); 23constexpr Result DeviceNotFound(ErrorModule::NFP, 64);
21constexpr Result WrongDeviceState(ErrorModule::NFP, 73); 24constexpr Result WrongDeviceState(ErrorModule::NFP, 73);
25constexpr Result NfcDisabled(ErrorModule::NFP, 80);
26constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88);
22constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); 27constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
28constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152);
23constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); 29constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168);
24} // namespace ErrCodes 30} // namespace ErrCodes
25 31
26constexpr u32 ApplicationAreaSize = 0xD8;
27
28IUser::IUser(Module::Interface& nfp_interface_, Core::System& system_) 32IUser::IUser(Module::Interface& nfp_interface_, Core::System& system_)
29 : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name}, 33 : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name},
30 nfp_interface{nfp_interface_} { 34 nfp_interface{nfp_interface_} {
@@ -39,7 +43,7 @@ IUser::IUser(Module::Interface& nfp_interface_, Core::System& system_)
39 {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, 43 {7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
40 {8, &IUser::GetApplicationArea, "GetApplicationArea"}, 44 {8, &IUser::GetApplicationArea, "GetApplicationArea"},
41 {9, &IUser::SetApplicationArea, "SetApplicationArea"}, 45 {9, &IUser::SetApplicationArea, "SetApplicationArea"},
42 {10, nullptr, "Flush"}, 46 {10, &IUser::Flush, "Flush"},
43 {11, nullptr, "Restore"}, 47 {11, nullptr, "Restore"},
44 {12, &IUser::CreateApplicationArea, "CreateApplicationArea"}, 48 {12, &IUser::CreateApplicationArea, "CreateApplicationArea"},
45 {13, &IUser::GetTagInfo, "GetTagInfo"}, 49 {13, &IUser::GetTagInfo, "GetTagInfo"},
@@ -87,11 +91,23 @@ void IUser::Finalize(Kernel::HLERequestContext& ctx) {
87void IUser::ListDevices(Kernel::HLERequestContext& ctx) { 91void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
88 LOG_INFO(Service_NFP, "called"); 92 LOG_INFO(Service_NFP, "called");
89 93
94 if (state == State::NonInitialized) {
95 IPC::ResponseBuilder rb{ctx, 2};
96 rb.Push(ErrCodes::NfcDisabled);
97 return;
98 }
99
90 std::vector<u64> devices; 100 std::vector<u64> devices;
91 101
92 // TODO(german77): Loop through all interfaces 102 // TODO(german77): Loop through all interfaces
93 devices.push_back(nfp_interface.GetHandle()); 103 devices.push_back(nfp_interface.GetHandle());
94 104
105 if (devices.size() == 0) {
106 IPC::ResponseBuilder rb{ctx, 2};
107 rb.Push(ErrCodes::DeviceNotFound);
108 return;
109 }
110
95 ctx.WriteBuffer(devices); 111 ctx.WriteBuffer(devices);
96 112
97 IPC::ResponseBuilder rb{ctx, 3}; 113 IPC::ResponseBuilder rb{ctx, 3};
@@ -105,6 +121,12 @@ void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
105 const auto nfp_protocol{rp.Pop<s32>()}; 121 const auto nfp_protocol{rp.Pop<s32>()};
106 LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); 122 LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
107 123
124 if (state == State::NonInitialized) {
125 IPC::ResponseBuilder rb{ctx, 2};
126 rb.Push(ErrCodes::NfcDisabled);
127 return;
128 }
129
108 // TODO(german77): Loop through all interfaces 130 // TODO(german77): Loop through all interfaces
109 if (device_handle == nfp_interface.GetHandle()) { 131 if (device_handle == nfp_interface.GetHandle()) {
110 const auto result = nfp_interface.StartDetection(nfp_protocol); 132 const auto result = nfp_interface.StartDetection(nfp_protocol);
@@ -124,6 +146,12 @@ void IUser::StopDetection(Kernel::HLERequestContext& ctx) {
124 const auto device_handle{rp.Pop<u64>()}; 146 const auto device_handle{rp.Pop<u64>()};
125 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); 147 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
126 148
149 if (state == State::NonInitialized) {
150 IPC::ResponseBuilder rb{ctx, 2};
151 rb.Push(ErrCodes::NfcDisabled);
152 return;
153 }
154
127 // TODO(german77): Loop through all interfaces 155 // TODO(german77): Loop through all interfaces
128 if (device_handle == nfp_interface.GetHandle()) { 156 if (device_handle == nfp_interface.GetHandle()) {
129 const auto result = nfp_interface.StopDetection(); 157 const auto result = nfp_interface.StopDetection();
@@ -146,6 +174,12 @@ void IUser::Mount(Kernel::HLERequestContext& ctx) {
146 LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle, 174 LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle,
147 model_type, mount_target); 175 model_type, mount_target);
148 176
177 if (state == State::NonInitialized) {
178 IPC::ResponseBuilder rb{ctx, 2};
179 rb.Push(ErrCodes::NfcDisabled);
180 return;
181 }
182
149 // TODO(german77): Loop through all interfaces 183 // TODO(german77): Loop through all interfaces
150 if (device_handle == nfp_interface.GetHandle()) { 184 if (device_handle == nfp_interface.GetHandle()) {
151 const auto result = nfp_interface.Mount(); 185 const auto result = nfp_interface.Mount();
@@ -165,6 +199,12 @@ void IUser::Unmount(Kernel::HLERequestContext& ctx) {
165 const auto device_handle{rp.Pop<u64>()}; 199 const auto device_handle{rp.Pop<u64>()};
166 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); 200 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
167 201
202 if (state == State::NonInitialized) {
203 IPC::ResponseBuilder rb{ctx, 2};
204 rb.Push(ErrCodes::NfcDisabled);
205 return;
206 }
207
168 // TODO(german77): Loop through all interfaces 208 // TODO(german77): Loop through all interfaces
169 if (device_handle == nfp_interface.GetHandle()) { 209 if (device_handle == nfp_interface.GetHandle()) {
170 const auto result = nfp_interface.Unmount(); 210 const auto result = nfp_interface.Unmount();
@@ -186,6 +226,12 @@ void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) {
186 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, access_id={}", device_handle, 226 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, access_id={}", device_handle,
187 access_id); 227 access_id);
188 228
229 if (state == State::NonInitialized) {
230 IPC::ResponseBuilder rb{ctx, 2};
231 rb.Push(ErrCodes::NfcDisabled);
232 return;
233 }
234
189 // TODO(german77): Loop through all interfaces 235 // TODO(german77): Loop through all interfaces
190 if (device_handle == nfp_interface.GetHandle()) { 236 if (device_handle == nfp_interface.GetHandle()) {
191 const auto result = nfp_interface.OpenApplicationArea(access_id); 237 const auto result = nfp_interface.OpenApplicationArea(access_id);
@@ -205,9 +251,15 @@ void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) {
205 const auto device_handle{rp.Pop<u64>()}; 251 const auto device_handle{rp.Pop<u64>()};
206 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); 252 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
207 253
254 if (state == State::NonInitialized) {
255 IPC::ResponseBuilder rb{ctx, 2};
256 rb.Push(ErrCodes::NfcDisabled);
257 return;
258 }
259
208 // TODO(german77): Loop through all interfaces 260 // TODO(german77): Loop through all interfaces
209 if (device_handle == nfp_interface.GetHandle()) { 261 if (device_handle == nfp_interface.GetHandle()) {
210 std::vector<u8> data{}; 262 ApplicationArea data{};
211 const auto result = nfp_interface.GetApplicationArea(data); 263 const auto result = nfp_interface.GetApplicationArea(data);
212 ctx.WriteBuffer(data); 264 ctx.WriteBuffer(data);
213 IPC::ResponseBuilder rb{ctx, 3}; 265 IPC::ResponseBuilder rb{ctx, 3};
@@ -229,6 +281,12 @@ void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) {
229 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}", device_handle, 281 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}", device_handle,
230 data.size()); 282 data.size());
231 283
284 if (state == State::NonInitialized) {
285 IPC::ResponseBuilder rb{ctx, 2};
286 rb.Push(ErrCodes::NfcDisabled);
287 return;
288 }
289
232 // TODO(german77): Loop through all interfaces 290 // TODO(german77): Loop through all interfaces
233 if (device_handle == nfp_interface.GetHandle()) { 291 if (device_handle == nfp_interface.GetHandle()) {
234 const auto result = nfp_interface.SetApplicationArea(data); 292 const auto result = nfp_interface.SetApplicationArea(data);
@@ -243,6 +301,31 @@ void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) {
243 rb.Push(ErrCodes::DeviceNotFound); 301 rb.Push(ErrCodes::DeviceNotFound);
244} 302}
245 303
304void IUser::Flush(Kernel::HLERequestContext& ctx) {
305 IPC::RequestParser rp{ctx};
306 const auto device_handle{rp.Pop<u64>()};
307 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
308
309 if (state == State::NonInitialized) {
310 IPC::ResponseBuilder rb{ctx, 2};
311 rb.Push(ErrCodes::NfcDisabled);
312 return;
313 }
314
315 // TODO(german77): Loop through all interfaces
316 if (device_handle == nfp_interface.GetHandle()) {
317 const auto result = nfp_interface.Flush();
318 IPC::ResponseBuilder rb{ctx, 2};
319 rb.Push(result);
320 return;
321 }
322
323 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
324
325 IPC::ResponseBuilder rb{ctx, 2};
326 rb.Push(ErrCodes::DeviceNotFound);
327}
328
246void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) { 329void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) {
247 IPC::RequestParser rp{ctx}; 330 IPC::RequestParser rp{ctx};
248 const auto device_handle{rp.Pop<u64>()}; 331 const auto device_handle{rp.Pop<u64>()};
@@ -251,6 +334,12 @@ void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) {
251 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}", 334 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}",
252 device_handle, access_id, data.size()); 335 device_handle, access_id, data.size());
253 336
337 if (state == State::NonInitialized) {
338 IPC::ResponseBuilder rb{ctx, 2};
339 rb.Push(ErrCodes::NfcDisabled);
340 return;
341 }
342
254 // TODO(german77): Loop through all interfaces 343 // TODO(german77): Loop through all interfaces
255 if (device_handle == nfp_interface.GetHandle()) { 344 if (device_handle == nfp_interface.GetHandle()) {
256 const auto result = nfp_interface.CreateApplicationArea(access_id, data); 345 const auto result = nfp_interface.CreateApplicationArea(access_id, data);
@@ -270,6 +359,12 @@ void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) {
270 const auto device_handle{rp.Pop<u64>()}; 359 const auto device_handle{rp.Pop<u64>()};
271 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); 360 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
272 361
362 if (state == State::NonInitialized) {
363 IPC::ResponseBuilder rb{ctx, 2};
364 rb.Push(ErrCodes::NfcDisabled);
365 return;
366 }
367
273 // TODO(german77): Loop through all interfaces 368 // TODO(german77): Loop through all interfaces
274 if (device_handle == nfp_interface.GetHandle()) { 369 if (device_handle == nfp_interface.GetHandle()) {
275 TagInfo tag_info{}; 370 TagInfo tag_info{};
@@ -291,6 +386,12 @@ void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) {
291 const auto device_handle{rp.Pop<u64>()}; 386 const auto device_handle{rp.Pop<u64>()};
292 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); 387 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
293 388
389 if (state == State::NonInitialized) {
390 IPC::ResponseBuilder rb{ctx, 2};
391 rb.Push(ErrCodes::NfcDisabled);
392 return;
393 }
394
294 // TODO(german77): Loop through all interfaces 395 // TODO(german77): Loop through all interfaces
295 if (device_handle == nfp_interface.GetHandle()) { 396 if (device_handle == nfp_interface.GetHandle()) {
296 RegisterInfo register_info{}; 397 RegisterInfo register_info{};
@@ -312,6 +413,12 @@ void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) {
312 const auto device_handle{rp.Pop<u64>()}; 413 const auto device_handle{rp.Pop<u64>()};
313 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); 414 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
314 415
416 if (state == State::NonInitialized) {
417 IPC::ResponseBuilder rb{ctx, 2};
418 rb.Push(ErrCodes::NfcDisabled);
419 return;
420 }
421
315 // TODO(german77): Loop through all interfaces 422 // TODO(german77): Loop through all interfaces
316 if (device_handle == nfp_interface.GetHandle()) { 423 if (device_handle == nfp_interface.GetHandle()) {
317 CommonInfo common_info{}; 424 CommonInfo common_info{};
@@ -333,6 +440,12 @@ void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) {
333 const auto device_handle{rp.Pop<u64>()}; 440 const auto device_handle{rp.Pop<u64>()};
334 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); 441 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
335 442
443 if (state == State::NonInitialized) {
444 IPC::ResponseBuilder rb{ctx, 2};
445 rb.Push(ErrCodes::NfcDisabled);
446 return;
447 }
448
336 // TODO(german77): Loop through all interfaces 449 // TODO(german77): Loop through all interfaces
337 if (device_handle == nfp_interface.GetHandle()) { 450 if (device_handle == nfp_interface.GetHandle()) {
338 ModelInfo model_info{}; 451 ModelInfo model_info{};
@@ -354,6 +467,12 @@ void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) {
354 const auto device_handle{rp.Pop<u64>()}; 467 const auto device_handle{rp.Pop<u64>()};
355 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); 468 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
356 469
470 if (state == State::NonInitialized) {
471 IPC::ResponseBuilder rb{ctx, 2};
472 rb.Push(ErrCodes::NfcDisabled);
473 return;
474 }
475
357 // TODO(german77): Loop through all interfaces 476 // TODO(german77): Loop through all interfaces
358 if (device_handle == nfp_interface.GetHandle()) { 477 if (device_handle == nfp_interface.GetHandle()) {
359 IPC::ResponseBuilder rb{ctx, 2, 1}; 478 IPC::ResponseBuilder rb{ctx, 2, 1};
@@ -373,6 +492,12 @@ void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
373 const auto device_handle{rp.Pop<u64>()}; 492 const auto device_handle{rp.Pop<u64>()};
374 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); 493 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
375 494
495 if (state == State::NonInitialized) {
496 IPC::ResponseBuilder rb{ctx, 2};
497 rb.Push(ErrCodes::NfcDisabled);
498 return;
499 }
500
376 // TODO(german77): Loop through all interfaces 501 // TODO(german77): Loop through all interfaces
377 if (device_handle == nfp_interface.GetHandle()) { 502 if (device_handle == nfp_interface.GetHandle()) {
378 IPC::ResponseBuilder rb{ctx, 2, 1}; 503 IPC::ResponseBuilder rb{ctx, 2, 1};
@@ -419,6 +544,12 @@ void IUser::GetNpadId(Kernel::HLERequestContext& ctx) {
419 const auto device_handle{rp.Pop<u64>()}; 544 const auto device_handle{rp.Pop<u64>()};
420 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); 545 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
421 546
547 if (state == State::NonInitialized) {
548 IPC::ResponseBuilder rb{ctx, 2};
549 rb.Push(ErrCodes::NfcDisabled);
550 return;
551 }
552
422 // TODO(german77): Loop through all interfaces 553 // TODO(german77): Loop through all interfaces
423 if (device_handle == nfp_interface.GetHandle()) { 554 if (device_handle == nfp_interface.GetHandle()) {
424 IPC::ResponseBuilder rb{ctx, 3}; 555 IPC::ResponseBuilder rb{ctx, 3};
@@ -442,7 +573,7 @@ void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {
442 if (device_handle == nfp_interface.GetHandle()) { 573 if (device_handle == nfp_interface.GetHandle()) {
443 IPC::ResponseBuilder rb{ctx, 3}; 574 IPC::ResponseBuilder rb{ctx, 3};
444 rb.Push(ResultSuccess); 575 rb.Push(ResultSuccess);
445 rb.Push(ApplicationAreaSize); 576 rb.Push(sizeof(ApplicationArea));
446 return; 577 return;
447 } 578 }
448 579
@@ -455,6 +586,12 @@ void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {
455void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { 586void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
456 LOG_DEBUG(Service_NFP, "(STUBBED) called"); 587 LOG_DEBUG(Service_NFP, "(STUBBED) called");
457 588
589 if (state == State::NonInitialized) {
590 IPC::ResponseBuilder rb{ctx, 2};
591 rb.Push(ErrCodes::NfcDisabled);
592 return;
593 }
594
458 IPC::ResponseBuilder rb{ctx, 2, 1}; 595 IPC::ResponseBuilder rb{ctx, 2, 1};
459 rb.Push(ResultSuccess); 596 rb.Push(ResultSuccess);
460 rb.PushCopyObjects(availability_change_event->GetReadableEvent()); 597 rb.PushCopyObjects(availability_change_event->GetReadableEvent());
@@ -478,36 +615,42 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
478 rb.PushIpcInterface<IUser>(*this, system); 615 rb.PushIpcInterface<IUser>(*this, system);
479} 616}
480 617
481bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { 618bool Module::Interface::LoadAmiiboFile(const std::string& filename) {
482 if (device_state != DeviceState::SearchingForTag) {
483 LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state);
484 return false;
485 }
486
487 constexpr auto tag_size = sizeof(NTAG215File);
488 constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password); 619 constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password);
620 const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read,
621 Common::FS::FileType::BinaryFile};
489 622
490 std::vector<u8> amiibo_buffer = buffer; 623 if (!amiibo_file.IsOpen()) {
624 LOG_ERROR(Core, "Amiibo is already on use");
625 return false;
626 }
491 627
492 if (amiibo_buffer.size() < tag_size_without_password) { 628 // Workaround for files with missing password data
493 LOG_ERROR(Service_NFP, "Wrong file size {}", buffer.size()); 629 std::array<u8, sizeof(EncryptedNTAG215File)> buffer{};
630 if (amiibo_file.Read(buffer) < tag_size_without_password) {
631 LOG_ERROR(Core, "Failed to read amiibo file");
494 return false; 632 return false;
495 } 633 }
634 memcpy(&encrypted_tag_data, buffer.data(), sizeof(EncryptedNTAG215File));
496 635
497 // Ensure it has the correct size 636 if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {
498 if (amiibo_buffer.size() != tag_size) { 637 LOG_INFO(Service_NFP, "Invalid amiibo");
499 amiibo_buffer.resize(tag_size, 0); 638 return false;
500 } 639 }
501 640
502 LOG_INFO(Service_NFP, "Amiibo detected"); 641 file_path = filename;
503 std::memcpy(&tag_data, buffer.data(), tag_size); 642 return true;
643}
504 644
505 if (!IsAmiiboValid()) { 645bool Module::Interface::LoadAmiibo(const std::string& filename) {
646 if (device_state != DeviceState::SearchingForTag) {
647 LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state);
506 return false; 648 return false;
507 } 649 }
508 650
509 // This value can't be dumped from a tag. Generate it 651 if (!LoadAmiiboFile(filename)) {
510 tag_data.password.PWD = GetTagPassword(tag_data.uuid); 652 return false;
653 }
511 654
512 device_state = DeviceState::TagFound; 655 device_state = DeviceState::TagFound;
513 activate_event->GetWritableEvent().Signal(); 656 activate_event->GetWritableEvent().Signal();
@@ -517,55 +660,13 @@ bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
517void Module::Interface::CloseAmiibo() { 660void Module::Interface::CloseAmiibo() {
518 LOG_INFO(Service_NFP, "Remove amiibo"); 661 LOG_INFO(Service_NFP, "Remove amiibo");
519 device_state = DeviceState::TagRemoved; 662 device_state = DeviceState::TagRemoved;
663 is_data_decoded = false;
520 is_application_area_initialized = false; 664 is_application_area_initialized = false;
521 application_area_id = 0; 665 encrypted_tag_data = {};
522 application_area_data.clear(); 666 tag_data = {};
523 deactivate_event->GetWritableEvent().Signal(); 667 deactivate_event->GetWritableEvent().Signal();
524} 668}
525 669
526bool Module::Interface::IsAmiiboValid() const {
527 const auto& amiibo_data = tag_data.user_memory;
528 LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", tag_data.lock_bytes);
529 LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", tag_data.compability_container);
530 LOG_DEBUG(Service_NFP, "crypto_init=0x{0:x}", amiibo_data.crypto_init);
531 LOG_DEBUG(Service_NFP, "write_count={}", amiibo_data.write_count);
532
533 LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id);
534 LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant);
535 LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type);
536 LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number);
537 LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series);
538 LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.fixed);
539
540 LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", tag_data.dynamic_lock);
541 LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", tag_data.CFG0);
542 LOG_DEBUG(Service_NFP, "tag_CFG1=0x{0:x}", tag_data.CFG1);
543
544 // Check against all know constants on an amiibo binary
545 if (tag_data.lock_bytes != 0xE00F) {
546 return false;
547 }
548 if (tag_data.compability_container != 0xEEFF10F1U) {
549 return false;
550 }
551 if ((amiibo_data.crypto_init & 0xFF) != 0xA5) {
552 return false;
553 }
554 if (amiibo_data.model_info.fixed != 0x02) {
555 return false;
556 }
557 if ((tag_data.dynamic_lock & 0xFFFFFF) != 0x0F0001) {
558 return false;
559 }
560 if (tag_data.CFG0 != 0x04000000U) {
561 return false;
562 }
563 if (tag_data.CFG1 != 0x5F) {
564 return false;
565 }
566 return true;
567}
568
569Kernel::KReadableEvent& Module::Interface::GetActivateEvent() const { 670Kernel::KReadableEvent& Module::Interface::GetActivateEvent() const {
570 return activate_event->GetReadableEvent(); 671 return activate_event->GetReadableEvent();
571} 672}
@@ -576,13 +677,20 @@ Kernel::KReadableEvent& Module::Interface::GetDeactivateEvent() const {
576 677
577void Module::Interface::Initialize() { 678void Module::Interface::Initialize() {
578 device_state = DeviceState::Initialized; 679 device_state = DeviceState::Initialized;
680 is_data_decoded = false;
681 is_application_area_initialized = false;
682 encrypted_tag_data = {};
683 tag_data = {};
579} 684}
580 685
581void Module::Interface::Finalize() { 686void Module::Interface::Finalize() {
687 if (device_state == DeviceState::TagMounted) {
688 Unmount();
689 }
690 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
691 StopDetection();
692 }
582 device_state = DeviceState::Unaviable; 693 device_state = DeviceState::Unaviable;
583 is_application_area_initialized = false;
584 application_area_id = 0;
585 application_area_data.clear();
586} 694}
587 695
588Result Module::Interface::StartDetection(s32 protocol_) { 696Result Module::Interface::StartDetection(s32 protocol_) {
@@ -618,42 +726,102 @@ Result Module::Interface::StopDetection() {
618 return ErrCodes::WrongDeviceState; 726 return ErrCodes::WrongDeviceState;
619} 727}
620 728
621Result Module::Interface::Mount() { 729Result Module::Interface::Flush() {
622 if (device_state == DeviceState::TagFound) { 730 // Ignore write command if we can't encrypt the data
623 device_state = DeviceState::TagMounted; 731 if (!is_data_decoded) {
624 return ResultSuccess; 732 return ResultSuccess;
625 } 733 }
626 734
627 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 735 constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password);
628 return ErrCodes::WrongDeviceState; 736 EncryptedNTAG215File tmp_encrypted_tag_data{};
737 const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite,
738 Common::FS::FileType::BinaryFile};
739
740 if (!amiibo_file.IsOpen()) {
741 LOG_ERROR(Core, "Amiibo is already on use");
742 return ErrCodes::WriteAmiiboFailed;
743 }
744
745 // Workaround for files with missing password data
746 std::array<u8, sizeof(EncryptedNTAG215File)> buffer{};
747 if (amiibo_file.Read(buffer) < tag_size_without_password) {
748 LOG_ERROR(Core, "Failed to read amiibo file");
749 return ErrCodes::WriteAmiiboFailed;
750 }
751 memcpy(&tmp_encrypted_tag_data, buffer.data(), sizeof(EncryptedNTAG215File));
752
753 if (!AmiiboCrypto::IsAmiiboValid(tmp_encrypted_tag_data)) {
754 LOG_INFO(Service_NFP, "Invalid amiibo");
755 return ErrCodes::WriteAmiiboFailed;
756 }
757
758 bool is_uuid_equal = memcmp(tmp_encrypted_tag_data.uuid.data(), tag_data.uuid.data(), 8) == 0;
759 bool is_character_equal = tmp_encrypted_tag_data.user_memory.model_info.character_id ==
760 tag_data.model_info.character_id;
761 if (!is_uuid_equal || !is_character_equal) {
762 LOG_ERROR(Core, "Not the same amiibo");
763 return ErrCodes::WriteAmiiboFailed;
764 }
765
766 if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) {
767 LOG_ERROR(Core, "Failed to encode data");
768 return ErrCodes::WriteAmiiboFailed;
769 }
770
771 // Return to the start of the file
772 if (!amiibo_file.Seek(0)) {
773 LOG_ERROR(Service_NFP, "Error writting to file");
774 return ErrCodes::WriteAmiiboFailed;
775 }
776
777 if (!amiibo_file.Write(encrypted_tag_data)) {
778 LOG_ERROR(Service_NFP, "Error writting to file");
779 return ErrCodes::WriteAmiiboFailed;
780 }
781
782 return ResultSuccess;
783}
784
785Result Module::Interface::Mount() {
786 if (device_state != DeviceState::TagFound) {
787 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
788 return ErrCodes::WrongDeviceState;
789 }
790
791 is_data_decoded = AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data);
792 LOG_INFO(Service_NFP, "Is amiibo decoded {}", is_data_decoded);
793
794 is_application_area_initialized = false;
795 device_state = DeviceState::TagMounted;
796 return ResultSuccess;
629} 797}
630 798
631Result Module::Interface::Unmount() { 799Result Module::Interface::Unmount() {
632 if (device_state == DeviceState::TagMounted) { 800 if (device_state != DeviceState::TagMounted) {
633 is_application_area_initialized = false; 801 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
634 application_area_id = 0; 802 return ErrCodes::WrongDeviceState;
635 application_area_data.clear();
636 device_state = DeviceState::TagFound;
637 return ResultSuccess;
638 } 803 }
639 804
640 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 805 is_data_decoded = false;
641 return ErrCodes::WrongDeviceState; 806 is_application_area_initialized = false;
807 device_state = DeviceState::TagFound;
808 return ResultSuccess;
642} 809}
643 810
644Result Module::Interface::GetTagInfo(TagInfo& tag_info) const { 811Result Module::Interface::GetTagInfo(TagInfo& tag_info) const {
645 if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { 812 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
646 tag_info = { 813 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
647 .uuid = tag_data.uuid, 814 return ErrCodes::WrongDeviceState;
648 .uuid_length = static_cast<u8>(tag_data.uuid.size()),
649 .protocol = protocol,
650 .tag_type = static_cast<u32>(tag_data.user_memory.model_info.amiibo_type),
651 };
652 return ResultSuccess;
653 } 815 }
654 816
655 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 817 tag_info = {
656 return ErrCodes::WrongDeviceState; 818 .uuid = encrypted_tag_data.uuid,
819 .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.size()),
820 .protocol = protocol,
821 .tag_type = static_cast<u32>(encrypted_tag_data.user_memory.model_info.amiibo_type),
822 };
823
824 return ResultSuccess;
657} 825}
658 826
659Result Module::Interface::GetCommonInfo(CommonInfo& common_info) const { 827Result Module::Interface::GetCommonInfo(CommonInfo& common_info) const {
@@ -662,14 +830,28 @@ Result Module::Interface::GetCommonInfo(CommonInfo& common_info) const {
662 return ErrCodes::WrongDeviceState; 830 return ErrCodes::WrongDeviceState;
663 } 831 }
664 832
665 // Read this data from the amiibo save file 833 if (is_data_decoded) {
834 const auto& settings = tag_data.settings;
835 // TODO: Validate this data
836 common_info = {
837 .last_write_year = static_cast<u16>(settings.write_date.year.Value()),
838 .last_write_month = static_cast<u8>(settings.write_date.month.Value()),
839 .last_write_day = static_cast<u8>(settings.write_date.day.Value()),
840 .write_counter = settings.crc_counter,
841 .version = 1,
842 .application_area_size = sizeof(ApplicationArea),
843 };
844 return ResultSuccess;
845 }
846
847 // Generate a generic answer
666 common_info = { 848 common_info = {
667 .last_write_year = 2022, 849 .last_write_year = 2022,
668 .last_write_month = 2, 850 .last_write_month = 2,
669 .last_write_day = 7, 851 .last_write_day = 7,
670 .write_counter = tag_data.user_memory.write_count, 852 .write_counter = 0,
671 .version = 1, 853 .version = 1,
672 .application_area_size = ApplicationAreaSize, 854 .application_area_size = sizeof(ApplicationArea),
673 }; 855 };
674 return ResultSuccess; 856 return ResultSuccess;
675} 857}
@@ -680,7 +862,15 @@ Result Module::Interface::GetModelInfo(ModelInfo& model_info) const {
680 return ErrCodes::WrongDeviceState; 862 return ErrCodes::WrongDeviceState;
681 } 863 }
682 864
683 model_info = tag_data.user_memory.model_info; 865 const auto& model_info_data = encrypted_tag_data.user_memory.model_info;
866 model_info = {
867 .character_id = model_info_data.character_id,
868 .character_variant = model_info_data.character_variant,
869 .amiibo_type = model_info_data.amiibo_type,
870 .model_number = model_info_data.model_number,
871 .series = model_info_data.series,
872 .constant_value = model_info_data.constant_value,
873 };
684 return ResultSuccess; 874 return ResultSuccess;
685} 875}
686 876
@@ -690,9 +880,30 @@ Result Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const {
690 return ErrCodes::WrongDeviceState; 880 return ErrCodes::WrongDeviceState;
691 } 881 }
692 882
693 Service::Mii::MiiManager manager; 883 if (is_data_decoded) {
884 const auto& settings = tag_data.settings;
885
886 // Amiibo name is u16 while the register info is u8. Figure out how to handle this properly
887 std::array<u8, 11> amiibo_name{};
888 for (std::size_t i = 0; i < sizeof(amiibo_name) - 1; ++i) {
889 amiibo_name[i] = static_cast<u8>(settings.amiibo_name[i]);
890 }
891
892 // TODO: Validate this data
893 register_info = {
894 .mii_char_info = AmiiboCrypto::AmiiboRegisterInfoToMii(tag_data.owner_mii),
895 .first_write_year = static_cast<u16>(settings.init_date.year.Value()),
896 .first_write_month = static_cast<u8>(settings.init_date.month.Value()),
897 .first_write_day = static_cast<u8>(settings.init_date.day.Value()),
898 .amiibo_name = amiibo_name,
899 .unknown = {},
900 };
901
902 return ResultSuccess;
903 }
694 904
695 // Read this data from the amiibo save file 905 // Generate a generic answer
906 Service::Mii::MiiManager manager;
696 register_info = { 907 register_info = {
697 .mii_char_info = manager.BuildDefault(0), 908 .mii_char_info = manager.BuildDefault(0),
698 .first_write_year = 2022, 909 .first_write_year = 2022,
@@ -709,29 +920,39 @@ Result Module::Interface::OpenApplicationArea(u32 access_id) {
709 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 920 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
710 return ErrCodes::WrongDeviceState; 921 return ErrCodes::WrongDeviceState;
711 } 922 }
712 if (AmiiboApplicationDataExist(access_id)) { 923
713 application_area_data = LoadAmiiboApplicationData(access_id); 924 // Fallback for lack of amiibo keys
714 application_area_id = access_id; 925 if (!is_data_decoded) {
715 is_application_area_initialized = true; 926 LOG_WARNING(Service_NFP, "Application area is not initialized");
927 return ErrCodes::ApplicationAreaIsNotInitialized;
716 } 928 }
717 if (!is_application_area_initialized) { 929
930 if (tag_data.settings.settings.appdata_initialized == 0) {
718 LOG_WARNING(Service_NFP, "Application area is not initialized"); 931 LOG_WARNING(Service_NFP, "Application area is not initialized");
719 return ErrCodes::ApplicationAreaIsNotInitialized; 932 return ErrCodes::ApplicationAreaIsNotInitialized;
720 } 933 }
934
935 if (tag_data.application_area_id != access_id) {
936 LOG_WARNING(Service_NFP, "Wrong application area id");
937 return ErrCodes::WrongApplicationAreaId;
938 }
939
940 is_application_area_initialized = true;
721 return ResultSuccess; 941 return ResultSuccess;
722} 942}
723 943
724Result Module::Interface::GetApplicationArea(std::vector<u8>& data) const { 944Result Module::Interface::GetApplicationArea(ApplicationArea& data) const {
725 if (device_state != DeviceState::TagMounted) { 945 if (device_state != DeviceState::TagMounted) {
726 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 946 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
727 return ErrCodes::WrongDeviceState; 947 return ErrCodes::WrongDeviceState;
728 } 948 }
949
729 if (!is_application_area_initialized) { 950 if (!is_application_area_initialized) {
730 LOG_ERROR(Service_NFP, "Application area is not initialized"); 951 LOG_ERROR(Service_NFP, "Application area is not initialized");
731 return ErrCodes::ApplicationAreaIsNotInitialized; 952 return ErrCodes::ApplicationAreaIsNotInitialized;
732 } 953 }
733 954
734 data = application_area_data; 955 data = tag_data.application_area;
735 956
736 return ResultSuccess; 957 return ResultSuccess;
737} 958}
@@ -741,12 +962,18 @@ Result Module::Interface::SetApplicationArea(const std::vector<u8>& data) {
741 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 962 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
742 return ErrCodes::WrongDeviceState; 963 return ErrCodes::WrongDeviceState;
743 } 964 }
965
744 if (!is_application_area_initialized) { 966 if (!is_application_area_initialized) {
745 LOG_ERROR(Service_NFP, "Application area is not initialized"); 967 LOG_ERROR(Service_NFP, "Application area is not initialized");
746 return ErrCodes::ApplicationAreaIsNotInitialized; 968 return ErrCodes::ApplicationAreaIsNotInitialized;
747 } 969 }
748 application_area_data = data; 970
749 SaveAmiiboApplicationData(application_area_id, application_area_data); 971 if (data.size() != sizeof(ApplicationArea)) {
972 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
973 return ResultUnknown;
974 }
975
976 std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea));
750 return ResultSuccess; 977 return ResultSuccess;
751} 978}
752 979
@@ -755,30 +982,21 @@ Result Module::Interface::CreateApplicationArea(u32 access_id, const std::vector
755 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 982 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
756 return ErrCodes::WrongDeviceState; 983 return ErrCodes::WrongDeviceState;
757 } 984 }
758 if (AmiiboApplicationDataExist(access_id)) { 985
986 if (tag_data.settings.settings.appdata_initialized != 0) {
759 LOG_ERROR(Service_NFP, "Application area already exist"); 987 LOG_ERROR(Service_NFP, "Application area already exist");
760 return ErrCodes::ApplicationAreaExist; 988 return ErrCodes::ApplicationAreaExist;
761 } 989 }
762 application_area_data = data;
763 application_area_id = access_id;
764 SaveAmiiboApplicationData(application_area_id, application_area_data);
765 return ResultSuccess;
766}
767 990
768bool Module::Interface::AmiiboApplicationDataExist(u32 access_id) const { 991 if (data.size() != sizeof(ApplicationArea)) {
769 // TODO(german77): Check if file exist 992 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
770 return false; 993 return ResultUnknown;
771} 994 }
772 995
773std::vector<u8> Module::Interface::LoadAmiiboApplicationData(u32 access_id) const { 996 std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea));
774 // TODO(german77): Read file 997 tag_data.application_area_id = access_id;
775 std::vector<u8> data(ApplicationAreaSize);
776 return data;
777}
778 998
779void Module::Interface::SaveAmiiboApplicationData(u32 access_id, 999 return ResultSuccess;
780 const std::vector<u8>& data) const {
781 // TODO(german77): Save file
782} 1000}
783 1001
784u64 Module::Interface::GetHandle() const { 1002u64 Module::Interface::GetHandle() const {
@@ -794,15 +1012,6 @@ Core::HID::NpadIdType Module::Interface::GetNpadId() const {
794 return npad_id; 1012 return npad_id;
795} 1013}
796 1014
797u32 Module::Interface::GetTagPassword(const TagUuid& uuid) const {
798 // Verifiy that the generated password is correct
799 u32 password = 0xAA ^ (uuid[1] ^ uuid[3]);
800 password &= (0x55 ^ (uuid[2] ^ uuid[4])) << 8;
801 password &= (0xAA ^ (uuid[3] ^ uuid[5])) << 16;
802 password &= (0x55 ^ (uuid[4] ^ uuid[6])) << 24;
803 return password;
804}
805
806void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 1015void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
807 auto module = std::make_shared<Module>(); 1016 auto module = std::make_shared<Module>();
808 std::make_shared<NFP_User>(module, system)->InstallAsService(service_manager); 1017 std::make_shared<NFP_User>(module, system)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index 0fc808781..3410dcfb0 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -9,6 +9,7 @@
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "core/hle/service/kernel_helpers.h" 10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/mii/types.h" 11#include "core/hle/service/mii/types.h"
12#include "core/hle/service/nfp/amiibo_types.h"
12#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
13 14
14namespace Kernel { 15namespace Kernel {
@@ -21,72 +22,6 @@ enum class NpadIdType : u32;
21} // namespace Core::HID 22} // namespace Core::HID
22 23
23namespace Service::NFP { 24namespace Service::NFP {
24
25enum class ServiceType : u32 {
26 User,
27 Debug,
28 System,
29};
30
31enum class State : u32 {
32 NonInitialized,
33 Initialized,
34};
35
36enum class DeviceState : u32 {
37 Initialized,
38 SearchingForTag,
39 TagFound,
40 TagRemoved,
41 TagMounted,
42 Unaviable,
43 Finalized,
44};
45
46enum class ModelType : u32 {
47 Amiibo,
48};
49
50enum class MountTarget : u32 {
51 Rom,
52 Ram,
53 All,
54};
55
56enum class AmiiboType : u8 {
57 Figure,
58 Card,
59 Yarn,
60};
61
62enum class AmiiboSeries : u8 {
63 SuperSmashBros,
64 SuperMario,
65 ChibiRobo,
66 YoshiWoollyWorld,
67 Splatoon,
68 AnimalCrossing,
69 EightBitMario,
70 Skylanders,
71 Unknown8,
72 TheLegendOfZelda,
73 ShovelKnight,
74 Unknown11,
75 Kiby,
76 Pokemon,
77 MarioSportsSuperstars,
78 MonsterHunter,
79 BoxBoy,
80 Pikmin,
81 FireEmblem,
82 Metroid,
83 Others,
84 MegaMan,
85 Diablo
86};
87
88using TagUuid = std::array<u8, 10>;
89
90struct TagInfo { 25struct TagInfo {
91 TagUuid uuid; 26 TagUuid uuid;
92 u8 uuid_length; 27 u8 uuid_length;
@@ -114,10 +49,8 @@ struct ModelInfo {
114 AmiiboType amiibo_type; 49 AmiiboType amiibo_type;
115 u16 model_number; 50 u16 model_number;
116 AmiiboSeries series; 51 AmiiboSeries series;
117 u8 fixed; // Must be 02 52 u8 constant_value; // Must be 02
118 INSERT_PADDING_BYTES(0x4); // Unknown 53 INSERT_PADDING_BYTES(0x38); // Unknown
119 INSERT_PADDING_BYTES(0x20); // Probably a SHA256-(HMAC?) hash
120 INSERT_PADDING_BYTES(0x14); // SHA256-HMAC
121}; 54};
122static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); 55static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
123 56
@@ -126,7 +59,7 @@ struct RegisterInfo {
126 u16 first_write_year; 59 u16 first_write_year;
127 u8 first_write_month; 60 u8 first_write_month;
128 u8 first_write_day; 61 u8 first_write_day;
129 std::array<u8, 11> amiibo_name; 62 std::array<u8, 0xA + 1> amiibo_name;
130 u8 unknown; 63 u8 unknown;
131 INSERT_PADDING_BYTES(0x98); 64 INSERT_PADDING_BYTES(0x98);
132}; 65};
@@ -140,39 +73,9 @@ public:
140 const char* name); 73 const char* name);
141 ~Interface() override; 74 ~Interface() override;
142 75
143 struct EncryptedAmiiboFile {
144 u16 crypto_init; // Must be A5 XX
145 u16 write_count; // Number of times the amiibo has been written?
146 INSERT_PADDING_BYTES(0x20); // System crypts
147 INSERT_PADDING_BYTES(0x20); // SHA256-(HMAC?) hash
148 ModelInfo model_info; // This struct is bigger than documentation
149 INSERT_PADDING_BYTES(0xC); // SHA256-HMAC
150 INSERT_PADDING_BYTES(0x114); // section 1 encrypted buffer
151 INSERT_PADDING_BYTES(0x54); // section 2 encrypted buffer
152 };
153 static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
154
155 struct NTAG215Password {
156 u32 PWD; // Password to allow write access
157 u16 PACK; // Password acknowledge reply
158 u16 RFUI; // Reserved for future use
159 };
160 static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size");
161
162 struct NTAG215File {
163 TagUuid uuid; // Unique serial number
164 u16 lock_bytes; // Set defined pages as read only
165 u32 compability_container; // Defines available memory
166 EncryptedAmiiboFile user_memory; // Writable data
167 u32 dynamic_lock; // Dynamic lock
168 u32 CFG0; // Defines memory protected by password
169 u32 CFG1; // Defines number of verification attempts
170 NTAG215Password password; // Password data
171 };
172 static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size");
173
174 void CreateUserInterface(Kernel::HLERequestContext& ctx); 76 void CreateUserInterface(Kernel::HLERequestContext& ctx);
175 bool LoadAmiibo(const std::vector<u8>& buffer); 77 bool LoadAmiibo(const std::string& filename);
78 bool LoadAmiiboFile(const std::string& filename);
176 void CloseAmiibo(); 79 void CloseAmiibo();
177 80
178 void Initialize(); 81 void Initialize();
@@ -182,6 +85,7 @@ public:
182 Result StopDetection(); 85 Result StopDetection();
183 Result Mount(); 86 Result Mount();
184 Result Unmount(); 87 Result Unmount();
88 Result Flush();
185 89
186 Result GetTagInfo(TagInfo& tag_info) const; 90 Result GetTagInfo(TagInfo& tag_info) const;
187 Result GetCommonInfo(CommonInfo& common_info) const; 91 Result GetCommonInfo(CommonInfo& common_info) const;
@@ -189,7 +93,7 @@ public:
189 Result GetRegisterInfo(RegisterInfo& register_info) const; 93 Result GetRegisterInfo(RegisterInfo& register_info) const;
190 94
191 Result OpenApplicationArea(u32 access_id); 95 Result OpenApplicationArea(u32 access_id);
192 Result GetApplicationArea(std::vector<u8>& data) const; 96 Result GetApplicationArea(ApplicationArea& data) const;
193 Result SetApplicationArea(const std::vector<u8>& data); 97 Result SetApplicationArea(const std::vector<u8>& data);
194 Result CreateApplicationArea(u32 access_id, const std::vector<u8>& data); 98 Result CreateApplicationArea(u32 access_id, const std::vector<u8>& data);
195 99
@@ -204,27 +108,19 @@ public:
204 std::shared_ptr<Module> module; 108 std::shared_ptr<Module> module;
205 109
206 private: 110 private:
207 /// Validates that the amiibo file is not corrupted
208 bool IsAmiiboValid() const;
209
210 bool AmiiboApplicationDataExist(u32 access_id) const;
211 std::vector<u8> LoadAmiiboApplicationData(u32 access_id) const;
212 void SaveAmiiboApplicationData(u32 access_id, const std::vector<u8>& data) const;
213
214 /// return password needed to allow write access to protected memory
215 u32 GetTagPassword(const TagUuid& uuid) const;
216
217 const Core::HID::NpadIdType npad_id; 111 const Core::HID::NpadIdType npad_id;
218 112
219 DeviceState device_state{DeviceState::Unaviable}; 113 bool is_data_decoded{};
220 KernelHelpers::ServiceContext service_context; 114 bool is_application_area_initialized{};
115 s32 protocol;
116 std::string file_path{};
221 Kernel::KEvent* activate_event; 117 Kernel::KEvent* activate_event;
222 Kernel::KEvent* deactivate_event; 118 Kernel::KEvent* deactivate_event;
119 DeviceState device_state{DeviceState::Unaviable};
120 KernelHelpers::ServiceContext service_context;
121
223 NTAG215File tag_data{}; 122 NTAG215File tag_data{};
224 s32 protocol; 123 EncryptedNTAG215File encrypted_tag_data{};
225 bool is_application_area_initialized{};
226 u32 application_area_id;
227 std::vector<u8> application_area_data;
228 }; 124 };
229}; 125};
230 126
@@ -243,6 +139,7 @@ private:
243 void OpenApplicationArea(Kernel::HLERequestContext& ctx); 139 void OpenApplicationArea(Kernel::HLERequestContext& ctx);
244 void GetApplicationArea(Kernel::HLERequestContext& ctx); 140 void GetApplicationArea(Kernel::HLERequestContext& ctx);
245 void SetApplicationArea(Kernel::HLERequestContext& ctx); 141 void SetApplicationArea(Kernel::HLERequestContext& ctx);
142 void Flush(Kernel::HLERequestContext& ctx);
246 void CreateApplicationArea(Kernel::HLERequestContext& ctx); 143 void CreateApplicationArea(Kernel::HLERequestContext& ctx);
247 void GetTagInfo(Kernel::HLERequestContext& ctx); 144 void GetTagInfo(Kernel::HLERequestContext& ctx);
248 void GetRegisterInfo(Kernel::HLERequestContext& ctx); 145 void GetRegisterInfo(Kernel::HLERequestContext& ctx);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index a85adc072..fa9548fed 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -3254,26 +3254,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) {
3254 return; 3254 return;
3255 } 3255 }
3256 3256
3257 QFile nfc_file{filename}; 3257 if (!nfc->LoadAmiibo(filename.toStdString())) {
3258 if (!nfc_file.open(QIODevice::ReadOnly)) {
3259 QMessageBox::warning(this, tr("Error opening Amiibo data file"),
3260 tr("Unable to open Amiibo file \"%1\" for reading.").arg(filename));
3261 return;
3262 }
3263
3264 const u64 nfc_file_size = nfc_file.size();
3265 std::vector<u8> buffer(nfc_file_size);
3266 const u64 read_size = nfc_file.read(reinterpret_cast<char*>(buffer.data()), nfc_file_size);
3267 if (nfc_file_size != read_size) {
3268 QMessageBox::warning(this, tr("Error reading Amiibo data file"),
3269 tr("Unable to fully read Amiibo data. Expected to read %1 bytes, but "
3270 "was only able to read %2 bytes.")
3271 .arg(nfc_file_size)
3272 .arg(read_size));
3273 return;
3274 }
3275
3276 if (!nfc->LoadAmiibo(buffer)) {
3277 QMessageBox::warning(this, tr("Error loading Amiibo data"), 3258 QMessageBox::warning(this, tr("Error loading Amiibo data"),
3278 tr("Unable to load Amiibo data.")); 3259 tr("Unable to load Amiibo data."));
3279 } 3260 }