mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Add console unique data (SecureInfo, LocalFriendCodeSeed, CTCert) (#6)
* Add console unique secure data * Add CTCert and DeviceID support * Fix AM_U::GetDeviceID Co-authored-by: Daniel López Guimaraes <112760654+DaniElectra@users.noreply.github.com> * Update to latest master changes. --------- Co-authored-by: Daniel López Guimaraes <112760654+DaniElectra@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									f112d975b9
								
							
						
					
					
						commit
						813d0c2a30
					
				
					 12 changed files with 597 additions and 14 deletions
				
			
		|  | @ -80,6 +80,35 @@ struct TicketInfo { | |||
| 
 | ||||
| static_assert(sizeof(TicketInfo) == 0x18, "Ticket info structure size is wrong"); | ||||
| 
 | ||||
| bool CTCert::IsValid() const { | ||||
|     constexpr std::string_view expected_issuer_prod = "Nintendo CA - G3_NintendoCTR2prod"; | ||||
|     constexpr std::string_view expected_issuer_dev = "Nintendo CA - G3_NintendoCTR2dev"; | ||||
|     constexpr u32 expected_signature_type = 0x010005; | ||||
| 
 | ||||
|     return signature_type == expected_signature_type && | ||||
|            (std::string(issuer.data()) == expected_issuer_prod || | ||||
|             std::string(issuer.data()) == expected_issuer_dev); | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| u32 CTCert::GetDeviceID() const { | ||||
|     constexpr std::string_view key_id_prefix = "CT"; | ||||
| 
 | ||||
|     const std::string key_id_str(key_id.data()); | ||||
|     if (key_id_str.starts_with(key_id_prefix)) { | ||||
|         const std::string device_id = | ||||
|             key_id_str.substr(key_id_prefix.size(), key_id_str.find('-') - key_id_prefix.size()); | ||||
|         char* end_ptr; | ||||
|         const u32 device_id_value = std::strtoul(device_id.c_str(), &end_ptr, 16); | ||||
|         if (*end_ptr == '\0') { | ||||
|             return device_id_value; | ||||
|         } | ||||
|     } | ||||
|     // Error
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| class CIAFile::DecryptionState { | ||||
| public: | ||||
|     std::vector<CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption> content; | ||||
|  | @ -1213,6 +1242,21 @@ void Module::Interface::GetTicketList(Kernel::HLERequestContext& ctx) { | |||
|                 ticket_list_count, ticket_index); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::GetDeviceID(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
| 
 | ||||
|     const u32 deviceID = am->ct_cert.IsValid() ? am->ct_cert.GetDeviceID() : 0; | ||||
| 
 | ||||
|     if (deviceID == 0) { | ||||
|         LOG_ERROR(Service_AM, "Invalid or missing CTCert"); | ||||
|     } | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(0); | ||||
|     rb.Push(deviceID); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::NeedsCleanup(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
|     const auto media_type = rp.Pop<u8>(); | ||||
|  | @ -1802,13 +1846,66 @@ void Module::serialize(Archive& ar, const unsigned int) { | |||
| } | ||||
| SERIALIZE_IMPL(Module) | ||||
| 
 | ||||
| Module::Module(Core::System& system) : system(system) { | ||||
| void Module::Interface::GetDeviceCert(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
|     [[maybe_unused]] u32 size = rp.Pop<u32>(); | ||||
|     auto buffer = rp.PopMappedBuffer(); | ||||
| 
 | ||||
|     if (!am->ct_cert.IsValid()) { | ||||
|         LOG_ERROR(Service_AM, "Invalid or missing CTCert"); | ||||
|     } | ||||
| 
 | ||||
|     buffer.Write(&am->ct_cert, 0, std::min(sizeof(CTCert), buffer.GetSize())); | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(0); | ||||
|     rb.PushMappedBuffer(buffer); | ||||
| } | ||||
| 
 | ||||
| std::string Module::GetCTCertPath() { | ||||
|     return FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + "CTCert.bin"; | ||||
| } | ||||
| 
 | ||||
| CTCertLoadStatus Module::LoadCTCertFile(CTCert& output) { | ||||
|     if (output.IsValid()) { | ||||
|         return CTCertLoadStatus::Loaded; | ||||
|     } | ||||
|     std::string file_path = GetCTCertPath(); | ||||
|     if (!FileUtil::Exists(file_path)) { | ||||
|         return CTCertLoadStatus::NotFound; | ||||
|     } | ||||
|     FileUtil::IOFile file(file_path, "rb"); | ||||
|     if (!file.IsOpen()) { | ||||
|         return CTCertLoadStatus::IOError; | ||||
|     } | ||||
|     if (file.GetSize() != sizeof(CTCert)) { | ||||
|         return CTCertLoadStatus::Invalid; | ||||
|     } | ||||
|     if (file.ReadBytes(&output, sizeof(CTCert)) != sizeof(CTCert)) { | ||||
|         return CTCertLoadStatus::IOError; | ||||
|     } | ||||
|     if (!output.IsValid()) { | ||||
|         output = CTCert(); | ||||
|         return CTCertLoadStatus::Invalid; | ||||
|     } | ||||
|     return CTCertLoadStatus::Loaded; | ||||
| } | ||||
| 
 | ||||
| Module::Module(Core::System& _system) : system(_system) { | ||||
|     ScanForAllTitles(); | ||||
|     LoadCTCertFile(ct_cert); | ||||
|     system_updater_mutex = system.Kernel().CreateMutex(false, "AM::SystemUpdaterMutex"); | ||||
| } | ||||
| 
 | ||||
| Module::~Module() = default; | ||||
| 
 | ||||
| std::shared_ptr<Module> GetModule(Core::System& system) { | ||||
|     auto am = system.ServiceManager().GetService<Service::AM::Module::Interface>("am:u"); | ||||
|     if (!am) | ||||
|         return nullptr; | ||||
|     return am->GetModule(); | ||||
| } | ||||
| 
 | ||||
| void InstallInterfaces(Core::System& system) { | ||||
|     auto& service_manager = system.ServiceManager(); | ||||
|     auto am = std::make_shared<Module>(system); | ||||
|  |  | |||
|  | @ -68,6 +68,31 @@ enum class InstallStatus : u32 { | |||
|     ErrorEncrypted, | ||||
| }; | ||||
| 
 | ||||
| enum class CTCertLoadStatus { | ||||
|     Loaded, | ||||
|     NotFound, | ||||
|     Invalid, | ||||
|     IOError, | ||||
| }; | ||||
| 
 | ||||
| struct CTCert { | ||||
|     u32_be signature_type{}; | ||||
|     std::array<u8, 0x1E> signature_r{}; | ||||
|     std::array<u8, 0x1E> signature_s{}; | ||||
|     INSERT_PADDING_BYTES(0x40){}; | ||||
|     std::array<char, 0x40> issuer{}; | ||||
|     u32_be key_type{}; | ||||
|     std::array<char, 0x40> key_id{}; | ||||
|     u32_be expiration_time{}; | ||||
|     std::array<u8, 0x1E> public_key_x{}; | ||||
|     std::array<u8, 0x1E> public_key_y{}; | ||||
|     INSERT_PADDING_BYTES(0x3C){}; | ||||
| 
 | ||||
|     bool IsValid() const; | ||||
|     u32 GetDeviceID() const; | ||||
| }; | ||||
| static_assert(sizeof(CTCert) == 0x180, "Invalid CTCert size."); | ||||
| 
 | ||||
| // Title ID valid length
 | ||||
| constexpr std::size_t TITLE_ID_VALID_LENGTH = 16; | ||||
| 
 | ||||
|  | @ -216,6 +241,10 @@ public: | |||
|         Interface(std::shared_ptr<Module> am, const char* name, u32 max_session); | ||||
|         ~Interface(); | ||||
| 
 | ||||
|         std::shared_ptr<Module> GetModule() const { | ||||
|             return am; | ||||
|         } | ||||
| 
 | ||||
|     protected: | ||||
|         /**
 | ||||
|          * AM::GetNumPrograms service function | ||||
|  | @ -415,6 +444,16 @@ public: | |||
|          */ | ||||
|         void GetTicketList(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|         /**
 | ||||
|          * AM::GetDeviceID service function | ||||
|          *  Inputs: | ||||
|          *  Outputs: | ||||
|          *      1 : Result, 0 on success, otherwise error code | ||||
|          *      2 : Unknown | ||||
|          *      3 : DeviceID | ||||
|          */ | ||||
|         void GetDeviceID(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|         /**
 | ||||
|          * AM::NeedsCleanup service function | ||||
|          *  Inputs: | ||||
|  | @ -702,10 +741,32 @@ public: | |||
|          */ | ||||
|         void EndImportTicket(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|         /**
 | ||||
|          * AM::GetDeviceCert service function | ||||
|          *  Inputs: | ||||
|          *  Outputs: | ||||
|          *      1 : Result, 0 on success, otherwise error code | ||||
|          *      2 : Unknown | ||||
|          *      3-4 : Device cert | ||||
|          */ | ||||
|         void GetDeviceCert(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|     protected: | ||||
|         std::shared_ptr<Module> am; | ||||
|     }; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Gets the CTCert.bin path in the host filesystem | ||||
|      * @returns std::string CTCert.bin path in the host filesystem | ||||
|      */ | ||||
|     static std::string GetCTCertPath(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Loads the CTCert.bin file from the filesystem. | ||||
|      * @returns CTCertLoadStatus indicating the file load status. | ||||
|      */ | ||||
|     static CTCertLoadStatus LoadCTCertFile(CTCert& output); | ||||
| 
 | ||||
| private: | ||||
|     /**
 | ||||
|      * Scans the for titles in a storage medium for listing. | ||||
|  | @ -722,12 +783,15 @@ private: | |||
|     bool cia_installing = false; | ||||
|     std::array<std::vector<u64_le>, 3> am_title_list; | ||||
|     std::shared_ptr<Kernel::Mutex> system_updater_mutex; | ||||
|     CTCert ct_cert{}; | ||||
| 
 | ||||
|     template <class Archive> | ||||
|     void serialize(Archive& ar, const unsigned int); | ||||
|     friend class boost::serialization::access; | ||||
| }; | ||||
| 
 | ||||
| std::shared_ptr<Module> GetModule(Core::System& system); | ||||
| 
 | ||||
| void InstallInterfaces(Core::System& system); | ||||
| 
 | ||||
| } // namespace Service::AM
 | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ AM_NET::AM_NET(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "a | |||
|         {0x0007, &AM_NET::DeleteTicket, "DeleteTicket"}, | ||||
|         {0x0008, &AM_NET::GetNumTickets, "GetNumTickets"}, | ||||
|         {0x0009, &AM_NET::GetTicketList, "GetTicketList"}, | ||||
|         {0x000A, nullptr, "GetDeviceID"}, | ||||
|         {0x000A, &AM_NET::GetDeviceID, "GetDeviceID"}, | ||||
|         {0x000B, nullptr, "GetNumImportTitleContexts"}, | ||||
|         {0x000C, nullptr, "GetImportTitleContextList"}, | ||||
|         {0x000D, nullptr, "GetImportTitleContexts"}, | ||||
|  | @ -103,7 +103,7 @@ AM_NET::AM_NET(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "a | |||
|         {0x0815, nullptr, "GetCurrentImportContentContexts"}, | ||||
|         {0x0816, nullptr, "Sign"}, | ||||
|         {0x0817, nullptr, "Verify"}, | ||||
|         {0x0818, nullptr, "GetDeviceCert"}, | ||||
|         {0x0818, &AM_NET::GetDeviceCert, "GetDeviceCert"}, | ||||
|         {0x0819, nullptr, "ImportCertificates"}, | ||||
|         {0x081A, nullptr, "ImportCertificate"}, | ||||
|         {0x081B, nullptr, "CommitImportTitlesAndUpdateFirmwareAuto"}, | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ AM_SYS::AM_SYS(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "a | |||
|         {0x0007, &AM_SYS::DeleteTicket, "DeleteTicket"}, | ||||
|         {0x0008, &AM_SYS::GetNumTickets, "GetNumTickets"}, | ||||
|         {0x0009, &AM_SYS::GetTicketList, "GetTicketList"}, | ||||
|         {0x000A, nullptr, "GetDeviceID"}, | ||||
|         {0x000A, &AM_SYS::GetDeviceID, "GetDeviceID"}, | ||||
|         {0x000B, nullptr, "GetNumImportTitleContexts"}, | ||||
|         {0x000C, nullptr, "GetImportTitleContextList"}, | ||||
|         {0x000D, nullptr, "GetImportTitleContexts"}, | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ AM_U::AM_U(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "am:u" | |||
|         {0x0007, &AM_U::DeleteTicket, "DeleteTicket"}, | ||||
|         {0x0008, &AM_U::GetNumTickets, "GetNumTickets"}, | ||||
|         {0x0009, &AM_U::GetTicketList, "GetTicketList"}, | ||||
|         {0x000A, nullptr, "GetDeviceID"}, | ||||
|         {0x000A, &AM_U::GetDeviceID, "GetDeviceID"}, | ||||
|         {0x000B, nullptr, "GetNumImportTitleContexts"}, | ||||
|         {0x000C, nullptr, "GetImportTitleContextList"}, | ||||
|         {0x000D, nullptr, "GetImportTitleContexts"}, | ||||
|  |  | |||
|  | @ -217,12 +217,39 @@ void Module::Interface::GetRegion(Kernel::HLERequestContext& ctx) { | |||
| void Module::Interface::SecureInfoGetByte101(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_CFG, "(STUBBED) called"); | ||||
|     u8 ret = 0; | ||||
|     if (cfg->secure_info_a_loaded) { | ||||
|         ret = cfg->secure_info_a.unknown; | ||||
|     } | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(ResultSuccess); | ||||
|     // According to 3dbrew this is normally 0.
 | ||||
|     rb.Push<u8>(0); | ||||
|     rb.Push<u8>(ret); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::SecureInfoGetSerialNo(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
|     [[maybe_unused]] u32 out_size = rp.Pop<u32>(); | ||||
|     auto out_buffer = rp.PopMappedBuffer(); | ||||
| 
 | ||||
|     if (out_buffer.GetSize() < sizeof(SecureInfoA::serial_number)) { | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|         rb.Push(Result(ErrorDescription::InvalidSize, ErrorModule::Config, | ||||
|                        ErrorSummary::WrongArgument, ErrorLevel::Permanent)); | ||||
|     } | ||||
|     // Never happens on real hardware, but may happen if user didn't supply a dump.
 | ||||
|     // Always make sure to have available both secure data kinds or error otherwise.
 | ||||
|     if (!cfg->secure_info_a_loaded || !cfg->local_friend_code_seed_b_loaded) { | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|         rb.Push(Result(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::InvalidState, | ||||
|                        ErrorLevel::Permanent)); | ||||
|     } | ||||
| 
 | ||||
|     out_buffer.Write(&cfg->secure_info_a.serial_number, 0, sizeof(SecureInfoA::serial_number)); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.PushMappedBuffer(out_buffer); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::SetUUIDClockSequence(Kernel::HLERequestContext& ctx) { | ||||
|  | @ -365,6 +392,43 @@ void Module::Interface::UpdateConfigNANDSavegame(Kernel::HLERequestContext& ctx) | |||
|     rb.Push(cfg->UpdateConfigNANDSavegame()); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::GetLocalFriendCodeSeedData(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
|     [[maybe_unused]] u32 out_size = rp.Pop<u32>(); | ||||
|     auto out_buffer = rp.PopMappedBuffer(); | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
| 
 | ||||
|     if (out_buffer.GetSize() < sizeof(LocalFriendCodeSeedB)) { | ||||
|         rb.Push(Result(ErrorDescription::InvalidSize, ErrorModule::Config, | ||||
|                        ErrorSummary::WrongArgument, ErrorLevel::Permanent)); | ||||
|     } | ||||
|     // Never happens on real hardware, but may happen if user didn't supply a dump.
 | ||||
|     // Always make sure to have available both secure data kinds or error otherwise.
 | ||||
|     if (!cfg->secure_info_a_loaded || !cfg->local_friend_code_seed_b_loaded) { | ||||
|         rb.Push(Result(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::InvalidState, | ||||
|                        ErrorLevel::Permanent)); | ||||
|     } | ||||
| 
 | ||||
|     out_buffer.Write(&cfg->local_friend_code_seed_b, 0, sizeof(LocalFriendCodeSeedB)); | ||||
|     rb.Push(ResultSuccess); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::GetLocalFriendCodeSeed(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
| 
 | ||||
|     // Never happens on real hardware, but may happen if user didn't supply a dump.
 | ||||
|     // Always make sure to have available both secure data kinds or error otherwise.
 | ||||
|     if (!cfg->secure_info_a_loaded || !cfg->local_friend_code_seed_b_loaded) { | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|         rb.Push(Result(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::InvalidState, | ||||
|                        ErrorLevel::Permanent)); | ||||
|     } | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push<u64>(cfg->local_friend_code_seed_b.friend_code_seed); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::FormatConfig(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|  | @ -506,6 +570,14 @@ Result Module::UpdateConfigNANDSavegame() { | |||
|     return ResultSuccess; | ||||
| } | ||||
| 
 | ||||
| std::string Module::GetLocalFriendCodeSeedBPath() { | ||||
|     return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "rw/sys/LocalFriendCodeSeed_B"; | ||||
| } | ||||
| 
 | ||||
| std::string Module::GetSecureInfoAPath() { | ||||
|     return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "rw/sys/SecureInfo_A"; | ||||
| } | ||||
| 
 | ||||
| Result Module::FormatConfig() { | ||||
|     Result res = DeleteConfigNANDSaveFile(); | ||||
|     // The delete command fails if the file doesn't exist, so we have to check that too
 | ||||
|  | @ -579,6 +651,55 @@ Result Module::LoadConfigNANDSaveFile() { | |||
|     return FormatConfig(); | ||||
| } | ||||
| 
 | ||||
| void Module::InvalidateSecureData() { | ||||
|     secure_info_a_loaded = local_friend_code_seed_b_loaded = false; | ||||
| } | ||||
| 
 | ||||
| SecureDataLoadStatus Module::LoadSecureInfoAFile() { | ||||
|     if (secure_info_a_loaded) { | ||||
|         return SecureDataLoadStatus::Loaded; | ||||
|     } | ||||
|     std::string file_path = GetSecureInfoAPath(); | ||||
|     if (!FileUtil::Exists(file_path)) { | ||||
|         return SecureDataLoadStatus::NotFound; | ||||
|     } | ||||
|     FileUtil::IOFile file(file_path, "rb"); | ||||
|     if (!file.IsOpen()) { | ||||
|         return SecureDataLoadStatus::IOError; | ||||
|     } | ||||
|     if (file.GetSize() != sizeof(SecureInfoA)) { | ||||
|         return SecureDataLoadStatus::Invalid; | ||||
|     } | ||||
|     if (file.ReadBytes(&secure_info_a, sizeof(SecureInfoA)) != sizeof(SecureInfoA)) { | ||||
|         return SecureDataLoadStatus::IOError; | ||||
|     } | ||||
|     secure_info_a_loaded = true; | ||||
|     return SecureDataLoadStatus::Loaded; | ||||
| } | ||||
| 
 | ||||
| SecureDataLoadStatus Module::LoadLocalFriendCodeSeedBFile() { | ||||
|     if (local_friend_code_seed_b_loaded) { | ||||
|         return SecureDataLoadStatus::Loaded; | ||||
|     } | ||||
|     std::string file_path = GetLocalFriendCodeSeedBPath(); | ||||
|     if (!FileUtil::Exists(file_path)) { | ||||
|         return SecureDataLoadStatus::NotFound; | ||||
|     } | ||||
|     FileUtil::IOFile file(file_path, "rb"); | ||||
|     if (!file.IsOpen()) { | ||||
|         return SecureDataLoadStatus::IOError; | ||||
|     } | ||||
|     if (file.GetSize() != sizeof(LocalFriendCodeSeedB)) { | ||||
|         return SecureDataLoadStatus::Invalid; | ||||
|     } | ||||
|     if (file.ReadBytes(&local_friend_code_seed_b, sizeof(LocalFriendCodeSeedB)) != | ||||
|         sizeof(LocalFriendCodeSeedB)) { | ||||
|         return SecureDataLoadStatus::IOError; | ||||
|     } | ||||
|     local_friend_code_seed_b_loaded = true; | ||||
|     return SecureDataLoadStatus::Loaded; | ||||
| } | ||||
| 
 | ||||
| void Module::LoadMCUConfig() { | ||||
|     FileUtil::IOFile mcu_data_file( | ||||
|         fmt::format("{}/mcu.dat", FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir)), "rb"); | ||||
|  | @ -616,6 +737,8 @@ Module::Module(Core::System& system_) : system(system_) { | |||
|         SetEULAVersion(default_version); | ||||
|         UpdateConfigNANDSavegame(); | ||||
|     } | ||||
|     LoadSecureInfoAFile(); | ||||
|     LoadLocalFriendCodeSeedBFile(); | ||||
| } | ||||
| 
 | ||||
| Module::~Module() = default; | ||||
|  |  | |||
|  | @ -176,6 +176,28 @@ enum class AccessFlag : u16 { | |||
| }; | ||||
| DECLARE_ENUM_FLAG_OPERATORS(AccessFlag); | ||||
| 
 | ||||
| struct SecureInfoA { | ||||
|     std::array<u8, 0x100> signature; | ||||
|     u8 region; | ||||
|     u8 unknown; | ||||
|     std::array<u8, 0xF> serial_number; | ||||
| }; | ||||
| static_assert(sizeof(SecureInfoA) == 0x111); | ||||
| 
 | ||||
| struct LocalFriendCodeSeedB { | ||||
|     std::array<u8, 0x100> signature; | ||||
|     u64 unknown; | ||||
|     u64 friend_code_seed; | ||||
| }; | ||||
| static_assert(sizeof(LocalFriendCodeSeedB) == 0x110); | ||||
| 
 | ||||
| enum class SecureDataLoadStatus { | ||||
|     Loaded, | ||||
|     NotFound, | ||||
|     Invalid, | ||||
|     IOError, | ||||
| }; | ||||
| 
 | ||||
| class Module final { | ||||
| public: | ||||
|     Module(Core::System& system_); | ||||
|  | @ -230,6 +252,18 @@ public: | |||
|          */ | ||||
|         void SecureInfoGetByte101(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|         /**
 | ||||
|          * CFG::SecureInfoGetSerialNo service function | ||||
|          *  Inputs: | ||||
|          *      1 : Buffer Size | ||||
|          *      2-3: Output mapped buffer | ||||
|          *  Outputs: | ||||
|          *      0 : Result Header code | ||||
|          *      1 : Result of function, 0 on success, otherwise error code | ||||
|          *      2-3 : Output mapped buffer | ||||
|          */ | ||||
|         void SecureInfoGetSerialNo(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|         /**
 | ||||
|          * CFG::SetUUIDClockSequence service function | ||||
|          *  Inputs: | ||||
|  | @ -345,6 +379,27 @@ public: | |||
|          */ | ||||
|         void UpdateConfigNANDSavegame(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|         /**
 | ||||
|          * CFG::GetLocalFriendCodeSeedData service function | ||||
|          *  Inputs: | ||||
|          *      1 : Buffer Size | ||||
|          *      2-3: Output mapped buffer | ||||
|          *  Outputs: | ||||
|          *      0 : Result Header code | ||||
|          *      1 : Result of function, 0 on success, otherwise error code | ||||
|          */ | ||||
|         void GetLocalFriendCodeSeedData(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|         /**
 | ||||
|          * CFG::GetLocalFriendCodeSeed service function | ||||
|          *  Inputs: | ||||
|          *  Outputs: | ||||
|          *      0 : Result Header code | ||||
|          *      1 : Result of function, 0 on success, otherwise error code | ||||
|          *      2-3 : Friend code seed | ||||
|          */ | ||||
|         void GetLocalFriendCodeSeed(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|         /**
 | ||||
|          * CFG::FormatConfig service function | ||||
|          *  Inputs: | ||||
|  | @ -575,6 +630,34 @@ public: | |||
|      */ | ||||
|     Result UpdateConfigNANDSavegame(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Invalidates the loaded secure data so that it is loaded again. | ||||
|      */ | ||||
|     void InvalidateSecureData(); | ||||
|     /**
 | ||||
|      * Loads the LocalFriendCodeSeed_B file from NAND. | ||||
|      * @returns LocalFriendCodeSeedBLoadStatus indicating the file load status. | ||||
|      */ | ||||
|     SecureDataLoadStatus LoadSecureInfoAFile(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Loads the LocalFriendCodeSeed_B file from NAND. | ||||
|      * @returns LocalFriendCodeSeedBLoadStatus indicating the file load status. | ||||
|      */ | ||||
|     SecureDataLoadStatus LoadLocalFriendCodeSeedBFile(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Gets the SecureInfo_A path in the host filesystem | ||||
|      * @returns std::string SecureInfo_A path in the host filesystem | ||||
|      */ | ||||
|     std::string GetSecureInfoAPath(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Gets the LocalFriendCodeSeed_B path in the host filesystem | ||||
|      * @returns std::string LocalFriendCodeSeed_B path in the host filesystem | ||||
|      */ | ||||
|     std::string GetLocalFriendCodeSeedBPath(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Saves MCU specific data | ||||
|      */ | ||||
|  | @ -590,6 +673,10 @@ private: | |||
|     std::array<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer; | ||||
|     std::unique_ptr<FileSys::ArchiveBackend> cfg_system_save_data_archive; | ||||
|     u32 preferred_region_code = 0; | ||||
|     bool secure_info_a_loaded = false; | ||||
|     SecureInfoA secure_info_a; | ||||
|     bool local_friend_code_seed_b_loaded = false; | ||||
|     LocalFriendCodeSeedB local_friend_code_seed_b; | ||||
|     bool preferred_region_chosen = false; | ||||
|     MCUData mcu_data{}; | ||||
| 
 | ||||
|  |  | |||
|  | @ -28,11 +28,11 @@ CFG_I::CFG_I(std::shared_ptr<Module> cfg) : Module::Interface(std::move(cfg), "c | |||
|         {0x0401, &CFG_I::GetSystemConfig, "GetSystemConfig"}, | ||||
|         {0x0402, &CFG_I::SetSystemConfig, "SetSystemConfig"}, | ||||
|         {0x0403, &CFG_I::UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, | ||||
|         {0x0404, nullptr, "GetLocalFriendCodeSeedData"}, | ||||
|         {0x0405, nullptr, "GetLocalFriendCodeSeed"}, | ||||
|         {0x0404, &CFG_I::GetLocalFriendCodeSeedData, "GetLocalFriendCodeSeedData"}, | ||||
|         {0x0405, &CFG_I::GetLocalFriendCodeSeed, "GetLocalFriendCodeSeed"}, | ||||
|         {0x0406, &CFG_I::GetRegion, "GetRegion"}, | ||||
|         {0x0407, &CFG_I::SecureInfoGetByte101, "SecureInfoGetByte101"}, | ||||
|         {0x0408, nullptr, "SecureInfoGetSerialNo"}, | ||||
|         {0x0408, &CFG_I::SecureInfoGetSerialNo, "SecureInfoGetSerialNo"}, | ||||
|         {0x0409, nullptr, "UpdateConfigBlk00040003"}, | ||||
|         // cfg:i
 | ||||
|         {0x0801, &CFG_I::GetSystemConfig, "GetSystemConfig"}, | ||||
|  |  | |||
|  | @ -28,11 +28,11 @@ CFG_S::CFG_S(std::shared_ptr<Module> cfg) : Module::Interface(std::move(cfg), "c | |||
|         {0x0401, &CFG_S::GetSystemConfig, "GetSystemConfig"}, | ||||
|         {0x0402, &CFG_S::SetSystemConfig, "SetSystemConfig"}, | ||||
|         {0x0403, &CFG_S::UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, | ||||
|         {0x0404, nullptr, "GetLocalFriendCodeSeedData"}, | ||||
|         {0x0405, nullptr, "GetLocalFriendCodeSeed"}, | ||||
|         {0x0404, &CFG_S::GetLocalFriendCodeSeedData, "GetLocalFriendCodeSeedData"}, | ||||
|         {0x0405, &CFG_S::GetLocalFriendCodeSeed, "GetLocalFriendCodeSeed"}, | ||||
|         {0x0406, &CFG_S::GetRegion, "GetRegion"}, | ||||
|         {0x0407, &CFG_S::SecureInfoGetByte101, "SecureInfoGetByte101"}, | ||||
|         {0x0408, nullptr, "SecureInfoGetSerialNo"}, | ||||
|         {0x0408, &CFG_S::SecureInfoGetSerialNo, "SecureInfoGetSerialNo"}, | ||||
|         {0x0409, nullptr, "UpdateConfigBlk00040003"}, | ||||
|         {0x040D, &CFG_S::SetUUIDClockSequence, "SetUUIDClockSequence"}, | ||||
|         {0x040E, &CFG_S::GetUUIDClockSequence, "GetUUIDClockSequence"}, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue