From bf1f70db0a4b9af8c4515a91519dfb7cd89b7d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 9 Feb 2017 14:07:36 +0100 Subject: [PATCH 1/4] Move the ticket code to ESFormats This moves some parsing code for tickets and ticket views to ESFormats instead of duplicating it over DiscIO and Core. --- Source/Core/Core/Boot/Boot_BS2Emu.cpp | 2 +- Source/Core/Core/IOS/ES/ES.cpp | 153 +++++------------- Source/Core/Core/IOS/ES/ES.h | 2 +- Source/Core/Core/IOS/ES/Formats.cpp | 108 ++++++++++++- Source/Core/Core/IOS/ES/Formats.h | 84 +++++++++- Source/Core/Core/IOS/WFS/WFSI.cpp | 8 +- Source/Core/Core/IOS/WFS/WFSI.h | 2 +- Source/Core/DiscIO/NANDContentLoader.cpp | 100 ++---------- Source/Core/DiscIO/NANDContentLoader.h | 16 +- Source/Core/DiscIO/WiiWad.cpp | 2 +- Source/Core/DiscIO/WiiWad.h | 5 +- .../DolphinWX/ISOProperties/InfoPanel.cpp | 2 +- 12 files changed, 254 insertions(+), 230 deletions(-) diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index a11b1658d5..0dedb770bb 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -348,7 +348,7 @@ bool CBoot::EmulatedBS2_Wii() std::vector tmd = DVDInterface::GetVolume().GetTMD(); - IOS::HLE::TMDReader tmd_reader{std::move(tmd)}; + ES::TMDReader tmd_reader{std::move(tmd)}; if (!SetupWiiMemory(tmd_reader.GetIOSId())) return false; diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp index d0b2c43298..5e6a06d278 100644 --- a/Source/Core/Core/IOS/ES/ES.cpp +++ b/Source/Core/Core/IOS/ES/ES.cpp @@ -2,35 +2,6 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -// ======================================================= -// File description -// ------------- -/* Here we handle /dev/es requests. We have cases for these functions, the exact - DevKitPro/libogc name is in parenthesis: - - 0x20 GetTitleID (ES_GetTitleID) (Input: none, Output: 8 bytes) - 0x1d GetDataDir (ES_GetDataDir) (Input: 8 bytes, Output: 30 bytes) - - 0x1b DiGetTicketView (Input: none, Output: 216 bytes) - 0x16 GetConsumption (Input: 8 bytes, Output: 0 bytes, 4 bytes) // there are two output buffers - - 0x12 GetNumTicketViews (ES_GetNumTicketViews) (Input: 8 bytes, Output: 4 bytes) - 0x14 GetTMDViewSize (ES_GetTMDViewSize) (Input: ?, Output: ?) // I don't get this anymore, - it used to come after 0x12 - - but only the first two are correctly supported. For the other four we ignore any potential - input and only write zero to the out buffer. However, most games only use first two, - but some Nintendo developed games use the other ones to: - - 0x1b: Mario Galaxy, Mario Kart, SSBB - 0x16: Mario Galaxy, Mario Kart, SSBB - 0x12: Mario Kart - 0x14: Mario Kart: But only if we don't return a zeroed out buffer for the 0x12 question, - and instead answer for example 1 will this question appear. - -*/ -// ============= - #include #include #include @@ -347,7 +318,8 @@ IPCCommandResult ES::AddTicket(const IOCtlVRequest& request) INFO_LOG(IOS_ES, "IOCTL_ES_ADDTICKET"); std::vector ticket(request.in_vectors[0].size); Memory::CopyFromEmu(ticket.data(), request.in_vectors[0].address, request.in_vectors[0].size); - DiscIO::AddTicket(ticket); + + DiscIO::AddTicket(::ES::TicketReader{std::move(ticket)}); return GetDefaultReply(IPC_SUCCESS); } @@ -441,18 +413,18 @@ IPCCommandResult ES::AddContentFinish(const IOCtlVRequest& request) INFO_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTFINISH: content fd %08x", content_fd); // Try to find the title key from a pre-installed ticket. - std::vector ticket = DiscIO::FindSignedTicket(m_addtitle_tmd.GetTitleId()); - if (ticket.size() == 0) + ::ES::TicketReader ticket = DiscIO::FindSignedTicket(m_addtitle_tmd.GetTitleId()); + if (!ticket.IsValid()) { return GetDefaultReply(ES_NO_TICKET_INSTALLED); } mbedtls_aes_context aes_ctx; - mbedtls_aes_setkey_dec(&aes_ctx, DiscIO::GetKeyFromTicket(ticket).data(), 128); + mbedtls_aes_setkey_dec(&aes_ctx, ticket.GetTitleKey().data(), 128); // The IV for title content decryption is the lower two bytes of the // content index, zero extended. - TMDReader::Content content_info; + ::ES::TMDReader::Content content_info; if (!m_addtitle_tmd.FindContentById(m_addtitle_content_id, &content_info)) { return GetDefaultReply(ES_INVALID_TMD); @@ -777,46 +749,24 @@ IPCCommandResult ES::GetViewCount(const IOCtlVRequest& request) u64 TitleID = Memory::Read_U64(request.in_vectors[0].address); - u32 retVal = 0; const DiscIO::CNANDContentLoader& Loader = AccessContentDevice(TitleID); - u32 ViewCount = - static_cast(Loader.GetTicket().size()) / DiscIO::CNANDContentLoader::TICKET_SIZE; - if (!ViewCount) + size_t view_count = 0; + if (TitleID >> 32 == 0x00000001 && TitleID != TITLEID_SYSMENU) { - std::string TicketFilename = Common::GetTicketFileName(TitleID, Common::FROM_SESSION_ROOT); - if (File::Exists(TicketFilename)) - { - u32 FileSize = (u32)File::GetSize(TicketFilename); - _dbg_assert_msg_(IOS_ES, (FileSize % DiscIO::CNANDContentLoader::TICKET_SIZE) == 0, - "IOCTL_ES_GETVIEWCNT ticket file size seems to be wrong"); - - ViewCount = FileSize / DiscIO::CNANDContentLoader::TICKET_SIZE; - _dbg_assert_msg_(IOS_ES, (ViewCount > 0) && (ViewCount <= 4), - "IOCTL_ES_GETVIEWCNT ticket count seems to be wrong"); - } - else if (TitleID >> 32 == 0x00000001) - { - // Fake a ticket view to make IOS reload work. - ViewCount = 1; - } - else - { - ViewCount = 0; - if (TitleID == TITLEID_SYSMENU) - { - PanicAlertT("There must be a ticket for 00000001/00000002. Your NAND dump is probably " - "incomplete."); - } - // retVal = ES_NO_TICKET_INSTALLED; - } + // Fake a ticket view to make IOS reload work. + view_count = 1; + } + else if (Loader.IsValid() && Loader.GetTicket().IsValid()) + { + view_count = Loader.GetTicket().GetNumberOfTickets(); } - INFO_LOG(IOS_ES, "IOCTL_ES_GETVIEWCNT for titleID: %08x/%08x (View Count = %i)", - (u32)(TitleID >> 32), (u32)TitleID, ViewCount); + INFO_LOG(IOS_ES, "IOCTL_ES_GETVIEWCNT for titleID: %08x/%08x (View Count = %zu)", + static_cast(TitleID >> 32), static_cast(TitleID), view_count); - Memory::Write_U32(ViewCount, request.io_vectors[0].address); - return GetDefaultReply(retVal); + Memory::Write_U32(static_cast(view_count), request.io_vectors[0].address); + return GetDefaultReply(IPC_SUCCESS); } IPCCommandResult ES::GetViews(const IOCtlVRequest& request) @@ -826,68 +776,37 @@ IPCCommandResult ES::GetViews(const IOCtlVRequest& request) u64 TitleID = Memory::Read_U64(request.in_vectors[0].address); u32 maxViews = Memory::Read_U32(request.in_vectors[1].address); - u32 retVal = 0; const DiscIO::CNANDContentLoader& Loader = AccessContentDevice(TitleID); - const std::vector& ticket = Loader.GetTicket(); - - if (ticket.empty()) + if (TitleID >> 32 == 0x00000001 && TitleID != TITLEID_SYSMENU) { - std::string TicketFilename = Common::GetTicketFileName(TitleID, Common::FROM_SESSION_ROOT); - if (File::Exists(TicketFilename)) - { - File::IOFile pFile(TicketFilename, "rb"); - if (pFile) - { - u8 FileTicket[DiscIO::CNANDContentLoader::TICKET_SIZE]; - for (unsigned int View = 0; - View != maxViews && - pFile.ReadBytes(FileTicket, DiscIO::CNANDContentLoader::TICKET_SIZE); - ++View) - { - Memory::Write_U32(View, request.io_vectors[0].address + View * 0xD8); - Memory::CopyToEmu(request.io_vectors[0].address + 4 + View * 0xD8, FileTicket + 0x1D0, - 212); - } - } - } - else if (TitleID >> 32 == 0x00000001) - { - // For IOS titles, the ticket view isn't normally parsed by either the - // SDK or libogc, just passed to LaunchTitle, so this - // shouldn't matter at all. Just fill out some fields just - // to be on the safe side. - u32 Address = request.io_vectors[0].address; - Memory::Memset(Address, 0, 0xD8); - Memory::Write_U64(TitleID, Address + 4 + (0x1dc - 0x1d0)); // title ID - Memory::Write_U16(0xffff, Address + 4 + (0x1e4 - 0x1d0)); // unnnown - Memory::Write_U32(0xff00, Address + 4 + (0x1ec - 0x1d0)); // access mask - Memory::Memset(Address + 4 + (0x222 - 0x1d0), 0xff, 0x20); // content permissions - } - else - { - // retVal = ES_NO_TICKET_INSTALLED; - PanicAlertT("IOCTL_ES_GETVIEWS: Tried to get data from an unknown ticket: %08x/%08x", - (u32)(TitleID >> 32), (u32)TitleID); - } + // For IOS titles, the ticket view isn't normally parsed by either the + // SDK or libogc, just passed to LaunchTitle, so this + // shouldn't matter at all. Just fill out some fields just + // to be on the safe side. + u32 Address = request.io_vectors[0].address; + Memory::Memset(Address, 0, 0xD8); + Memory::Write_U64(TitleID, Address + 4 + (0x1dc - 0x1d0)); // title ID + Memory::Write_U16(0xffff, Address + 4 + (0x1e4 - 0x1d0)); // unnnown + Memory::Write_U32(0xff00, Address + 4 + (0x1ec - 0x1d0)); // access mask + Memory::Memset(Address + 4 + (0x222 - 0x1d0), 0xff, 0x20); // content permissions } - else + else if (Loader.IsValid() && Loader.GetTicket().IsValid()) { - u32 view_count = - static_cast(Loader.GetTicket().size()) / DiscIO::CNANDContentLoader::TICKET_SIZE; - for (unsigned int view = 0; view != maxViews && view < view_count; ++view) + u32 number_of_views = std::min(maxViews, Loader.GetTicket().GetNumberOfTickets()); + for (u32 view = 0; view < number_of_views; ++view) { - Memory::Write_U32(view, request.io_vectors[0].address + view * 0xD8); - Memory::CopyToEmu(request.io_vectors[0].address + 4 + view * 0xD8, - &ticket[0x1D0 + (view * DiscIO::CNANDContentLoader::TICKET_SIZE)], 212); + const std::vector ticket_view = Loader.GetTicket().GetRawTicketView(view); + Memory::CopyToEmu(request.io_vectors[0].address + view * sizeof(::ES::TicketView), + ticket_view.data(), ticket_view.size()); } } INFO_LOG(IOS_ES, "IOCTL_ES_GETVIEWS for titleID: %08x/%08x (MaxViews = %i)", (u32)(TitleID >> 32), (u32)TitleID, maxViews); - return GetDefaultReply(retVal); + return GetDefaultReply(IPC_SUCCESS); } IPCCommandResult ES::GetTMDViewCount(const IOCtlVRequest& request) diff --git a/Source/Core/Core/IOS/ES/ES.h b/Source/Core/Core/IOS/ES/ES.h index 86f447e9a0..aa934100f2 100644 --- a/Source/Core/Core/IOS/ES/ES.h +++ b/Source/Core/Core/IOS/ES/ES.h @@ -203,7 +203,7 @@ private: u32 m_AccessIdentID = 0; // For title installation (ioctls IOCTL_ES_ADDTITLE*). - TMDReader m_addtitle_tmd; + ::ES::TMDReader m_addtitle_tmd; u32 m_addtitle_content_id = 0xFFFFFFFF; std::vector m_addtitle_content_buffer; }; diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index 6a9053fbee..1eaf375070 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -5,17 +5,30 @@ #include "Core/IOS/ES/Formats.h" #include +#include +#include #include #include +#include + #include "Common/ChunkFile.h" #include "Common/CommonFuncs.h" #include "Common/CommonTypes.h" -namespace IOS +namespace ES { -namespace HLE +std::vector AESDecode(const u8* key, u8* iv, const u8* src, u32 size) { + mbedtls_aes_context aes_ctx; + std::vector buffer(size); + + mbedtls_aes_setkey_dec(&aes_ctx, key, 128); + mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, size, iv, src, buffer.data()); + + return buffer; +} + TMDReader::TMDReader(const std::vector& bytes) : m_bytes(bytes) { } @@ -103,5 +116,92 @@ void TMDReader::DoState(PointerWrap& p) { p.Do(m_bytes); } -} // namespace HLE -} // namespace IOS + +TicketReader::TicketReader(const std::vector& bytes) : m_bytes(bytes) +{ +} + +TicketReader::TicketReader(std::vector&& bytes) : m_bytes(std::move(bytes)) +{ +} + +void TicketReader::SetBytes(const std::vector& bytes) +{ + m_bytes = bytes; +} + +void TicketReader::SetBytes(std::vector&& bytes) +{ + m_bytes = std::move(bytes); +} + +bool TicketReader::IsValid() const +{ + // Too small for the signature type. + if (m_bytes.size() < sizeof(u32)) + return false; + + u32 ticket_offset = GetOffset(); + if (ticket_offset == 0) + return false; + + // Too small for the ticket itself. + if (m_bytes.size() < ticket_offset + sizeof(Ticket)) + return false; + + return true; +} + +u32 TicketReader::GetNumberOfTickets() const +{ + return static_cast(m_bytes.size() / (GetOffset() + sizeof(Ticket))); +} + +u32 TicketReader::GetOffset() const +{ + u32 signature_type = Common::swap32(m_bytes.data()); + if (signature_type == 0x10000) // RSA4096 + return 576; + if (signature_type == 0x10001) // RSA2048 + return 320; + if (signature_type == 0x10002) // ECDSA + return 128; + + ERROR_LOG(COMMON, "Invalid ticket signature type: %08x", signature_type); + return 0; +} + +const std::vector& TicketReader::GetRawTicket() const +{ + return m_bytes; +} + +std::vector TicketReader::GetRawTicketView(u32 ticket_num) const +{ + // A ticket view is composed of a view ID + part of a ticket starting from the ticket_id field. + std::vector view{sizeof(TicketView)}; + + u32 view_id = Common::swap32(ticket_num); + std::memcpy(view.data(), &view_id, sizeof(view_id)); + + const size_t ticket_start = (GetOffset() + sizeof(Ticket)) * ticket_num; + const size_t view_start = ticket_start + offsetof(Ticket, ticket_id); + std::memcpy(view.data() + sizeof(view_id), &m_bytes[view_start], sizeof(view) - sizeof(view_id)); + + return view; +} + +u64 TicketReader::GetTitleId() const +{ + return Common::swap64(m_bytes.data() + GetOffset() + offsetof(Ticket, title_id)); +} + +std::vector TicketReader::GetTitleKey() const +{ + const u8 common_key[16] = {0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, + 0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7}; + u8 iv[16] = {}; + std::copy_n(&m_bytes[GetOffset() + offsetof(Ticket, title_id)], sizeof(Ticket::title_id), iv); + return AESDecode(common_key, iv, &m_bytes[GetOffset() + offsetof(Ticket, title_key)], 16); +} +} // namespace ES diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h index 775f602451..f161d1974a 100644 --- a/Source/Core/Core/IOS/ES/Formats.h +++ b/Source/Core/Core/IOS/ES/Formats.h @@ -13,10 +13,10 @@ #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" -namespace IOS -{ -namespace HLE +namespace ES { +std::vector AESDecode(const u8* key, u8* iv, const u8* src, u32 size); + class TMDReader final { public: @@ -49,5 +49,79 @@ public: private: std::vector m_bytes; }; -} // namespace HLE -} // namespace IOS + +#pragma pack(push, 4) +struct TimeLimit +{ + u32 enabled; + u32 seconds; +}; + +struct TicketView +{ + u32 view; + u64 ticket_id; + u32 device_id; + u64 title_id; + u16 access_mask; + u32 permitted_title_id; + u32 permitted_title_mask; + u8 title_export_allowed; + u8 common_key_index; + u8 unknown2[0x30]; + u8 content_access_permissions[0x40]; + TimeLimit time_limits[8]; +}; +static_assert(sizeof(TicketView) == 0xd8, "TicketView has the wrong size"); + +struct Ticket +{ + u8 signature_issuer[0x40]; + u8 ecdh_key[0x3c]; + u8 unknown[0x03]; + u8 title_key[0x10]; + u64 ticket_id; + u32 device_id; + u64 title_id; + u16 access_mask; + u16 ticket_version; + u32 permitted_title_id; + u32 permitted_title_mask; + u8 title_export_allowed; + u8 common_key_index; + u8 unknown2[0x30]; + u8 content_access_permissions[0x40]; + TimeLimit time_limits[8]; +}; +static_assert(sizeof(Ticket) == 356, "Ticket has the wrong size"); +#pragma pack(pop) + +class TicketReader final +{ +public: + TicketReader() = default; + explicit TicketReader(const std::vector& bytes); + explicit TicketReader(std::vector&& bytes); + + void SetBytes(const std::vector& bytes); + void SetBytes(std::vector&& bytes); + + bool IsValid() const; + + const std::vector& GetRawTicket() const; + u32 GetNumberOfTickets() const; + u32 GetOffset() const; + + // Returns a "raw" ticket view, without byte swapping. Intended for use from ES. + // Theoretically, a ticket file can contain one or more tickets. In practice, most (all?) + // official titles only have one ticket, but IOS *does* have code to handle ticket files with + // more than just one ticket and generate ticket views for them, so we implement it too. + std::vector GetRawTicketView(u32 ticket_num) const; + + u64 GetTitleId() const; + std::vector GetTitleKey() const; + +private: + std::vector m_bytes; +}; +} // namespace ES diff --git a/Source/Core/Core/IOS/WFS/WFSI.cpp b/Source/Core/Core/IOS/WFS/WFSI.cpp index 5ca881bb28..4c885fee72 100644 --- a/Source/Core/Core/IOS/WFS/WFSI.cpp +++ b/Source/Core/Core/IOS/WFS/WFSI.cpp @@ -109,14 +109,14 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request) Memory::CopyFromEmu(tmd_bytes.data(), tmd_addr, tmd_size); m_tmd.SetBytes(std::move(tmd_bytes)); - std::vector ticket = DiscIO::FindSignedTicket(m_tmd.GetTitleId()); - if (ticket.size() == 0) + ES::TicketReader ticket = DiscIO::FindSignedTicket(m_tmd.GetTitleId()); + if (!ticket.IsValid()) { return_error_code = -11028; break; } - memcpy(m_aes_key, DiscIO::GetKeyFromTicket(ticket).data(), sizeof(m_aes_key)); + memcpy(m_aes_key, ticket.GetTitleKey().data(), sizeof(m_aes_key)); mbedtls_aes_setkey_dec(&m_aes_ctx, m_aes_key, 128); break; @@ -134,7 +134,7 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request) // Initializes the IV from the index of the content in the TMD contents. u32 content_id = Memory::Read_U32(request.buffer_in + 8); - TMDReader::Content content_info; + ES::TMDReader::Content content_info; if (!m_tmd.FindContentById(content_id, &content_info)) { WARN_LOG(IOS, "%s: Content id %08x not found", ioctl_name, content_id); diff --git a/Source/Core/Core/IOS/WFS/WFSI.h b/Source/Core/Core/IOS/WFS/WFSI.h index 0c1973184e..6388121680 100644 --- a/Source/Core/Core/IOS/WFS/WFSI.h +++ b/Source/Core/Core/IOS/WFS/WFSI.h @@ -50,7 +50,7 @@ private: u8 m_aes_key[0x10] = {}; u8 m_aes_iv[0x10] = {}; - TMDReader m_tmd; + ::ES::TMDReader m_tmd; std::string m_base_extract_path; ARCUnpacker m_arc_unpacker; diff --git a/Source/Core/DiscIO/NANDContentLoader.cpp b/Source/Core/DiscIO/NANDContentLoader.cpp index fc1f600aa1..dcd875388a 100644 --- a/Source/Core/DiscIO/NANDContentLoader.cpp +++ b/Source/Core/DiscIO/NANDContentLoader.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -29,50 +28,6 @@ namespace DiscIO { -namespace -{ -// Strips the signature part of a ticket, which has variable size based on -// signature type. Returns a new vector which has only the ticket structure -// itself. -std::vector SignedTicketToTicket(const std::vector& signed_ticket) -{ - u32 signature_type = Common::swap32(signed_ticket.data()); - u32 entry_offset; - if (signature_type == 0x10000) // RSA4096 - { - entry_offset = 576; - } - else if (signature_type == 0x10001) // RSA2048 - { - entry_offset = 320; - } - else if (signature_type == 0x10002) // ECDSA - { - entry_offset = 128; - } - else - { - ERROR_LOG(DISCIO, "Invalid ticket signature type: %08x", signature_type); - return std::vector(); - } - - std::vector ticket(signed_ticket.size() - entry_offset); - std::copy(signed_ticket.begin() + entry_offset, signed_ticket.end(), ticket.begin()); - return ticket; -} - -std::vector AESDecode(const u8* key, u8* iv, const u8* src, u32 size) -{ - mbedtls_aes_context aes_ctx; - std::vector buffer(size); - - mbedtls_aes_setkey_dec(&aes_ctx, key, 128); - mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, size, iv, src, buffer.data()); - - return buffer; -} -} - CNANDContentData::~CNANDContentData() = default; CSharedContent::CSharedContent(Common::FromWhichRoot root) : m_root(root) @@ -222,13 +177,11 @@ bool CNANDContentLoader::Initialize(const std::string& name) WiiWAD wad(name); std::vector data_app; std::vector tmd; - std::vector decrypted_title_key; if (wad.IsValid()) { m_IsWAD = true; - m_Ticket = wad.GetTicket(); - decrypted_title_key = GetKeyFromTicket(m_Ticket); + m_ticket = wad.GetTicket(); tmd = wad.GetTMD(); data_app = wad.GetDataApp(); } @@ -265,12 +218,14 @@ bool CNANDContentLoader::Initialize(const std::string& name) if (m_Country == 2) // SYSMENU m_Country = GetSysMenuRegion(m_TitleVersion); - InitializeContentEntries(tmd, decrypted_title_key, data_app); + if (!m_IsWAD) + m_ticket = FindSignedTicket(m_TitleID); + + InitializeContentEntries(tmd, data_app); return true; } void CNANDContentLoader::InitializeContentEntries(const std::vector& tmd, - const std::vector& decrypted_title_key, const std::vector& data_app) { m_Content.resize(m_NumEntries); @@ -305,8 +260,8 @@ void CNANDContentLoader::InitializeContentEntries(const std::vector& tmd, iv.fill(0); std::copy(&tmd[entry_offset + 0x01E8], &tmd[entry_offset + 0x01E8 + 2], iv.begin()); - content.m_Data = std::make_unique(AESDecode( - decrypted_title_key.data(), iv.data(), &data_app[data_app_offset], rounded_size)); + content.m_Data = std::make_unique(ES::AESDecode( + m_ticket.GetTitleKey().data(), iv.data(), &data_app[data_app_offset], rounded_size)); data_app_offset += rounded_size; continue; @@ -533,14 +488,14 @@ u64 CNANDContentManager::Install_WiiWAD(const std::string& filename) return title_id; } -bool AddTicket(const std::vector& signed_ticket) +bool AddTicket(const ES::TicketReader& signed_ticket) { - std::vector ticket = SignedTicketToTicket(signed_ticket); - if (ticket.empty()) + if (!signed_ticket.IsValid()) { return false; } - u64 title_id = Common::swap64(ticket.data() + 0x9c); + + u64 title_id = signed_ticket.GetTitleId(); std::string ticket_filename = Common::GetTicketFileName(title_id, Common::FROM_CONFIGURED_ROOT); File::CreateFullPath(ticket_filename); @@ -549,46 +504,25 @@ bool AddTicket(const std::vector& signed_ticket) if (!ticket_file) return false; - return ticket_file.WriteBytes(signed_ticket.data(), signed_ticket.size()); + const std::vector& raw_ticket = signed_ticket.GetRawTicket(); + return ticket_file.WriteBytes(raw_ticket.data(), raw_ticket.size()); } -std::vector FindSignedTicket(u64 title_id) +ES::TicketReader FindSignedTicket(u64 title_id) { std::string ticket_filename = Common::GetTicketFileName(title_id, Common::FROM_CONFIGURED_ROOT); File::IOFile ticket_file(ticket_filename, "rb"); if (!ticket_file) { - return std::vector(); + return ES::TicketReader{}; } std::vector signed_ticket(ticket_file.GetSize()); if (!ticket_file.ReadBytes(signed_ticket.data(), signed_ticket.size())) { - return std::vector(); + return ES::TicketReader{}; } - return signed_ticket; + return ES::TicketReader{std::move(signed_ticket)}; } - -std::vector FindTicket(u64 title_id) -{ - std::vector signed_ticket = FindSignedTicket(title_id); - if (signed_ticket.empty()) - { - return std::vector(); - } - - return SignedTicketToTicket(signed_ticket); -} - -std::vector GetKeyFromTicket(const std::vector& signed_ticket) -{ - const u8 common_key[16] = {0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, - 0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7}; - u8 iv[16] = {}; - - std::copy(&signed_ticket[0x01DC], &signed_ticket[0x01DC + 8], iv); - return AESDecode(common_key, iv, &signed_ticket[0x01BF], 16); -} - } // namespace end diff --git a/Source/Core/DiscIO/NANDContentLoader.h b/Source/Core/DiscIO/NANDContentLoader.h index 632ac0ef3e..654a123b5d 100644 --- a/Source/Core/DiscIO/NANDContentLoader.h +++ b/Source/Core/DiscIO/NANDContentLoader.h @@ -12,6 +12,7 @@ #include "Common/CommonTypes.h" #include "Common/NandPaths.h" +#include "Core/IOS/ES/Formats.h" namespace File { @@ -22,10 +23,8 @@ namespace DiscIO { enum class Region; -bool AddTicket(const std::vector& signed_ticket); -std::vector FindSignedTicket(u64 title_id); -std::vector FindTicket(u64 title_id); -std::vector GetKeyFromTicket(const std::vector& ticket); +bool AddTicket(const ES::TicketReader& signed_ticket); +ES::TicketReader FindSignedTicket(u64 title_id); class CNANDContentData { @@ -93,7 +92,7 @@ public: const SNANDContent* GetContentByIndex(int index) const; const u8* GetTMDView() const { return m_TMDView; } const u8* GetTMDHeader() const { return m_TMDHeader; } - const std::vector& GetTicket() const { return m_Ticket; } + const ES::TicketReader& GetTicket() const { return m_ticket; } const std::vector& GetContent() const { return m_Content; } u16 GetTitleVersion() const { return m_TitleVersion; } u16 GetNumEntries() const { return m_NumEntries; } @@ -104,14 +103,11 @@ public: TMD_VIEW_SIZE = 0x58, TMD_HEADER_SIZE = 0x1E4, CONTENT_HEADER_SIZE = 0x24, - TICKET_SIZE = 0x2A4 }; private: bool Initialize(const std::string& name); - void InitializeContentEntries(const std::vector& tmd, - const std::vector& decrypted_title_key, - const std::vector& data_app); + void InitializeContentEntries(const std::vector& tmd, const std::vector& data_app); bool m_Valid; bool m_IsWAD; @@ -123,7 +119,7 @@ private: u16 m_TitleVersion; u8 m_TMDView[TMD_VIEW_SIZE]; u8 m_TMDHeader[TMD_HEADER_SIZE]; - std::vector m_Ticket; + ES::TicketReader m_ticket; u8 m_Country; std::vector m_Content; diff --git a/Source/Core/DiscIO/WiiWad.cpp b/Source/Core/DiscIO/WiiWad.cpp index 9e85669204..04e4ff8a7c 100644 --- a/Source/Core/DiscIO/WiiWad.cpp +++ b/Source/Core/DiscIO/WiiWad.cpp @@ -88,7 +88,7 @@ bool WiiWAD::ParseWAD(IBlobReader& reader) u32 offset = 0x40; m_certificate_chain = CreateWADEntry(reader, certificate_chain_size, offset); offset += Common::AlignUp(certificate_chain_size, 0x40); - m_ticket = CreateWADEntry(reader, ticket_size, offset); + m_ticket.SetBytes(CreateWADEntry(reader, ticket_size, offset)); offset += Common::AlignUp(ticket_size, 0x40); m_tmd = CreateWADEntry(reader, tmd_size, offset); offset += Common::AlignUp(tmd_size, 0x40); diff --git a/Source/Core/DiscIO/WiiWad.h b/Source/Core/DiscIO/WiiWad.h index 21f8b746bd..ea0c3de43a 100644 --- a/Source/Core/DiscIO/WiiWad.h +++ b/Source/Core/DiscIO/WiiWad.h @@ -8,6 +8,7 @@ #include #include "Common/CommonTypes.h" +#include "Core/IOS/ES/Formats.h" namespace DiscIO { @@ -22,7 +23,7 @@ public: bool IsValid() const { return m_valid; } const std::vector& GetCertificateChain() const { return m_certificate_chain; } - const std::vector& GetTicket() const { return m_ticket; } + const ES::TicketReader& GetTicket() const { return m_ticket; } const std::vector& GetTMD() const { return m_tmd; } const std::vector& GetDataApp() const { return m_data_app; } const std::vector& GetFooter() const { return m_footer; } @@ -32,7 +33,7 @@ private: bool m_valid; std::vector m_certificate_chain; - std::vector m_ticket; + ES::TicketReader m_ticket; std::vector m_tmd; std::vector m_data_app; std::vector m_footer; diff --git a/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp b/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp index e534f88395..6a839c32be 100644 --- a/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp +++ b/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp @@ -182,7 +182,7 @@ void InfoPanel::LoadISODetails() m_fst->SetValue(StrToWxStr(std::to_string(m_opened_iso->GetFSTSize()))); if (m_ios_version) { - IOS::HLE::TMDReader tmd{m_opened_iso->GetTMD()}; + ES::TMDReader tmd{m_opened_iso->GetTMD()}; if (tmd.IsValid()) m_ios_version->SetValue(StringFromFormat("IOS%u", static_cast(tmd.GetIOSId()))); } From c1a139e8ace17e45e8ca6977b6f6b19a1a4195f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 11 Feb 2017 08:57:47 +0100 Subject: [PATCH 2/4] Use ESFormats for TMDs We already have a TMDReader, so let's actually use it. And move ESFormats to IOS::ES, since it's definitely part of IOS. This adds a DiscIO dependency on Core which will be fixed in a follow-up PR. --- Source/Core/Core/Boot/Boot.cpp | 8 +- Source/Core/Core/Boot/Boot_BS2Emu.cpp | 6 +- Source/Core/Core/Boot/Boot_WiiWAD.cpp | 7 +- Source/Core/Core/ConfigManager.cpp | 4 +- Source/Core/Core/IOS/DI/DI.cpp | 8 +- Source/Core/Core/IOS/ES/ES.cpp | 148 ++++++----------- Source/Core/Core/IOS/ES/ES.h | 4 +- Source/Core/Core/IOS/ES/Formats.cpp | 85 ++++++++-- Source/Core/Core/IOS/ES/Formats.h | 112 +++++++++---- Source/Core/Core/IOS/IPC.cpp | 2 +- Source/Core/Core/IOS/IPC.h | 7 +- Source/Core/Core/IOS/MIOS.cpp | 3 +- Source/Core/Core/IOS/WFS/WFSI.cpp | 2 +- Source/Core/Core/IOS/WFS/WFSI.h | 2 +- Source/Core/DiscIO/NANDContentLoader.cpp | 152 +++++++----------- Source/Core/DiscIO/NANDContentLoader.h | 49 ++---- Source/Core/DiscIO/Volume.h | 3 +- Source/Core/DiscIO/VolumeWad.cpp | 43 +++-- Source/Core/DiscIO/VolumeWad.h | 4 +- Source/Core/DiscIO/VolumeWiiCrypted.cpp | 4 +- Source/Core/DiscIO/VolumeWiiCrypted.h | 3 +- Source/Core/DiscIO/WiiWad.cpp | 2 +- Source/Core/DiscIO/WiiWad.h | 8 +- Source/Core/DolphinWX/FrameTools.cpp | 18 +-- .../DolphinWX/ISOProperties/InfoPanel.cpp | 4 +- Source/Core/DolphinWX/MainMenuBar.cpp | 5 +- 26 files changed, 337 insertions(+), 356 deletions(-) diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index 42d3327946..46176ecf5c 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -102,7 +102,7 @@ bool CBoot::FindMapFile(std::string* existing_map_file, std::string* writable_ma DiscIO::CNANDContentManager::Access().GetNANDLoader(_StartupPara.m_strFilename); if (Loader.IsValid()) { - u64 TitleID = Loader.GetTitleID(); + u64 TitleID = Loader.GetTMD().GetTitleId(); title_id_str = StringFromFormat("%08X_%08X", (u32)(TitleID >> 32) & 0xFFFFFFFF, (u32)TitleID & 0xFFFFFFFF); } @@ -278,11 +278,7 @@ bool CBoot::BootUp() PanicAlertT("Warning - starting ISO in wrong console mode!"); } - std::vector tmd_buffer = pVolume.GetTMD(); - if (!tmd_buffer.empty()) - { - IOS::HLE::ES_DIVerify(tmd_buffer); - } + IOS::HLE::ES_DIVerify(pVolume.GetTMD()); _StartupPara.bWii = pVolume.GetVolumeType() == DiscIO::Platform::WII_DISC; diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index 0dedb770bb..605579f597 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -346,11 +346,9 @@ bool CBoot::EmulatedBS2_Wii() PowerPC::ppcState.gpr[1] = 0x816ffff0; // StackPointer - std::vector tmd = DVDInterface::GetVolume().GetTMD(); + IOS::ES::TMDReader tmd = DVDInterface::GetVolume().GetTMD(); - ES::TMDReader tmd_reader{std::move(tmd)}; - - if (!SetupWiiMemory(tmd_reader.GetIOSId())) + if (!SetupWiiMemory(tmd.GetIOSId())) return false; // Execute the apploader diff --git a/Source/Core/Core/Boot/Boot_WiiWAD.cpp b/Source/Core/Core/Boot/Boot_WiiWAD.cpp index 2cef0b6ad6..5b3be7c4dc 100644 --- a/Source/Core/Core/Boot/Boot_WiiWAD.cpp +++ b/Source/Core/Core/Boot/Boot_WiiWAD.cpp @@ -78,7 +78,7 @@ bool CBoot::Boot_WiiWAD(const std::string& _pFilename) if (!ContentLoader.IsValid()) return false; - u64 titleID = ContentLoader.GetTitleID(); + u64 titleID = ContentLoader.GetTMD().GetTitleId(); // create data directory File::CreateFullPath(Common::GetTitleDataPath(titleID, Common::FROM_SESSION_ROOT)); @@ -86,12 +86,11 @@ bool CBoot::Boot_WiiWAD(const std::string& _pFilename) IOS::HLE::CreateVirtualFATFilesystem(); // setup Wii memory - u64 ios_title_id = 0x0000000100000000ULL | ContentLoader.GetIosVersion(); - if (!SetupWiiMemory(ios_title_id)) + if (!SetupWiiMemory(ContentLoader.GetTMD().GetIOSId())) return false; // DOL const DiscIO::SNANDContent* pContent = - ContentLoader.GetContentByIndex(ContentLoader.GetBootIndex()); + ContentLoader.GetContentByIndex(ContentLoader.GetTMD().GetBootIndex()); if (pContent == nullptr) return false; diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 8b64441f7f..bc246bfc20 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -922,7 +922,7 @@ bool SConfig::AutoSetup(EBootBS2 _BootBS2) const DiscIO::CNANDContentLoader& ContentLoader = DiscIO::CNANDContentManager::Access().GetNANDLoader(m_strFilename); - if (ContentLoader.GetContentByIndex(ContentLoader.GetBootIndex()) == nullptr) + if (ContentLoader.GetContentByIndex(ContentLoader.GetTMD().GetBootIndex()) == nullptr) { // WAD is valid yet cannot be booted. Install instead. u64 installed = DiscIO::CNANDContentManager::Access().Install_WiiWAD(m_strFilename); @@ -931,7 +931,7 @@ bool SConfig::AutoSetup(EBootBS2 _BootBS2) return false; // do not boot } - SetRegion(ContentLoader.GetRegion(), &set_region_dir); + SetRegion(ContentLoader.GetTMD().GetRegion(), &set_region_dir); bWii = true; m_BootType = BOOT_WII_NAND; diff --git a/Source/Core/Core/IOS/DI/DI.cpp b/Source/Core/Core/IOS/DI/DI.cpp index f7d3006fff..bd91eb11d5 100644 --- a/Source/Core/Core/IOS/DI/DI.cpp +++ b/Source/Core/Core/IOS/DI/DI.cpp @@ -14,6 +14,7 @@ #include "Core/HW/DVDInterface.h" #include "Core/HW/Memmap.h" #include "Core/IOS/DI/DI.h" +#include "Core/IOS/ES/Formats.h" #include "Core/IOS/IPC.h" #include "DiscIO/Volume.h" @@ -105,9 +106,10 @@ IPCCommandResult DI::IOCtlV(const IOCtlVRequest& request) INFO_LOG(IOS_DI, "DVDLowOpenPartition: partition_offset 0x%016" PRIx64, partition_offset); // Read TMD to the buffer - std::vector tmd_buffer = DVDInterface::GetVolume().GetTMD(); - Memory::CopyToEmu(request.io_vectors[0].address, tmd_buffer.data(), tmd_buffer.size()); - ES_DIVerify(tmd_buffer); + const ES::TMDReader tmd = DVDInterface::GetVolume().GetTMD(); + const std::vector raw_tmd = tmd.GetRawTMD(); + Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size()); + ES_DIVerify(tmd); return_value = 1; break; diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp index 5e6a06d278..a496837c63 100644 --- a/Source/Core/Core/IOS/ES/ES.cpp +++ b/Source/Core/Core/IOS/ES/ES.cpp @@ -86,7 +86,7 @@ void ES::OpenInternal() // check for cd ... if (contentLoader.IsValid()) { - m_TitleID = contentLoader.GetTitleID(); + m_TitleID = contentLoader.GetTMD().GetTitleId(); m_TitleIDs.clear(); DiscIO::cUIDsys uid_sys{Common::FromWhichRoot::FROM_SESSION_ROOT}; @@ -201,8 +201,8 @@ u32 ES::OpenTitleContent(u32 CFD, u64 TitleID, u16 Index) SContentAccess Access; Access.m_Position = 0; - Access.m_Index = pContent->m_Index; - Access.m_Size = pContent->m_Size; + Access.m_Index = pContent->m_metadata.index; + Access.m_Size = static_cast(pContent->m_metadata.size); Access.m_TitleID = TitleID; pContent->m_Data->Open(); @@ -319,7 +319,7 @@ IPCCommandResult ES::AddTicket(const IOCtlVRequest& request) std::vector ticket(request.in_vectors[0].size); Memory::CopyFromEmu(ticket.data(), request.in_vectors[0].address, request.in_vectors[0].size); - DiscIO::AddTicket(::ES::TicketReader{std::move(ticket)}); + DiscIO::AddTicket(IOS::ES::TicketReader{std::move(ticket)}); return GetDefaultReply(IPC_SUCCESS); } @@ -413,7 +413,7 @@ IPCCommandResult ES::AddContentFinish(const IOCtlVRequest& request) INFO_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTFINISH: content fd %08x", content_fd); // Try to find the title key from a pre-installed ticket. - ::ES::TicketReader ticket = DiscIO::FindSignedTicket(m_addtitle_tmd.GetTitleId()); + IOS::ES::TicketReader ticket = DiscIO::FindSignedTicket(m_addtitle_tmd.GetTitleId()); if (!ticket.IsValid()) { return GetDefaultReply(ES_NO_TICKET_INSTALLED); @@ -424,7 +424,7 @@ IPCCommandResult ES::AddContentFinish(const IOCtlVRequest& request) // The IV for title content decryption is the lower two bytes of the // content index, zero extended. - ::ES::TMDReader::Content content_info; + IOS::ES::Content content_info; if (!m_addtitle_tmd.FindContentById(m_addtitle_content_id, &content_info)) { return GetDefaultReply(ES_INVALID_TMD); @@ -471,28 +471,21 @@ IPCCommandResult ES::GetTitleContentsCount(const IOCtlVRequest& request) u64 TitleID = Memory::Read_U64(request.in_vectors[0].address); - const DiscIO::CNANDContentLoader& rNANDContent = AccessContentDevice(TitleID); - u16 NumberOfPrivateContent = 0; - s32 return_value = IPC_SUCCESS; - if (rNANDContent.IsValid()) // Not sure if dolphin will ever fail this check - { - NumberOfPrivateContent = rNANDContent.GetNumEntries(); + const DiscIO::CNANDContentLoader& nand_content = AccessContentDevice(TitleID); + if (!nand_content.IsValid()) + return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT); - if ((u32)(TitleID >> 32) == 0x00010000) - Memory::Write_U32(0, request.io_vectors[0].address); - else - Memory::Write_U32(NumberOfPrivateContent, request.io_vectors[0].address); - } + const u16 num_contents = nand_content.GetTMD().GetNumContents(); + + if ((u32)(TitleID >> 32) == 0x00010000) + Memory::Write_U32(0, request.io_vectors[0].address); else - { - return_value = static_cast(rNANDContent.GetContentSize()); - } + Memory::Write_U32(num_contents, request.io_vectors[0].address); INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLECONTENTSCNT: TitleID: %08x/%08x content count %i", - (u32)(TitleID >> 32), (u32)TitleID, - rNANDContent.IsValid() ? NumberOfPrivateContent : (u32)rNANDContent.GetContentSize()); + (u32)(TitleID >> 32), (u32)TitleID, num_contents); - return GetDefaultReply(return_value); + return GetDefaultReply(IPC_SUCCESS); } IPCCommandResult ES::GetTitleContents(const IOCtlVRequest& request) @@ -505,25 +498,17 @@ IPCCommandResult ES::GetTitleContents(const IOCtlVRequest& request) u64 TitleID = Memory::Read_U64(request.in_vectors[0].address); const DiscIO::CNANDContentLoader& rNANDContent = AccessContentDevice(TitleID); - s32 return_value = IPC_SUCCESS; - if (rNANDContent.IsValid()) // Not sure if dolphin will ever fail this check + if (!rNANDContent.IsValid()) + return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT); + + for (const auto& content : rNANDContent.GetContent()) { - for (u16 i = 0; i < rNANDContent.GetNumEntries(); i++) - { - Memory::Write_U32(rNANDContent.GetContentByIndex(i)->m_ContentID, - request.io_vectors[0].address + i * 4); - INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLECONTENTS: Index %d: %08x", i, - rNANDContent.GetContentByIndex(i)->m_ContentID); - } - } - else - { - return_value = static_cast(rNANDContent.GetContentSize()); - ERROR_LOG(IOS_ES, "IOCTL_ES_GETTITLECONTENTS: Unable to open content %zu", - rNANDContent.GetContentSize()); + const u16 index = content.m_metadata.index; + Memory::Write_U32(content.m_metadata.id, request.io_vectors[0].address + index * 4); + INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLECONTENTS: Index %d: %08x", index, content.m_metadata.id); } - return GetDefaultReply(return_value); + return GetDefaultReply(IPC_SUCCESS); } IPCCommandResult ES::OpenTitleContent(const IOCtlVRequest& request) @@ -798,7 +783,7 @@ IPCCommandResult ES::GetViews(const IOCtlVRequest& request) for (u32 view = 0; view < number_of_views; ++view) { const std::vector ticket_view = Loader.GetTicket().GetRawTicketView(view); - Memory::CopyToEmu(request.io_vectors[0].address + view * sizeof(::ES::TicketView), + Memory::CopyToEmu(request.io_vectors[0].address + view * sizeof(IOS::ES::TicketView), ticket_view.data(), ticket_view.size()); } } @@ -809,6 +794,7 @@ IPCCommandResult ES::GetViews(const IOCtlVRequest& request) return GetDefaultReply(IPC_SUCCESS); } +// TODO: rename this to GetTMDViewSize. There is only one TMD, so the name doesn't make sense. IPCCommandResult ES::GetTMDViewCount(const IOCtlVRequest& request) { _dbg_assert_msg_(IOS_ES, request.in_vectors.size() == 1, "IOCTL_ES_GETTMDVIEWCNT no in buffer"); @@ -818,18 +804,13 @@ IPCCommandResult ES::GetTMDViewCount(const IOCtlVRequest& request) const DiscIO::CNANDContentLoader& Loader = AccessContentDevice(TitleID); - u32 TMDViewCnt = 0; + u32 view_size = 0; if (Loader.IsValid()) - { - TMDViewCnt += DiscIO::CNANDContentLoader::TMD_VIEW_SIZE; - TMDViewCnt += 2; // title version - TMDViewCnt += 2; // num entries - TMDViewCnt += (u32)Loader.GetContentSize() * (4 + 2 + 2 + 8); // content id, index, type, size - } - Memory::Write_U32(TMDViewCnt, request.io_vectors[0].address); + view_size = static_cast(Loader.GetTMD().GetRawView().size()); + Memory::Write_U32(view_size, request.io_vectors[0].address); INFO_LOG(IOS_ES, "IOCTL_ES_GETTMDVIEWCNT: title: %08x/%08x (view size %i)", (u32)(TitleID >> 32), - (u32)TitleID, TMDViewCnt); + (u32)TitleID, view_size); return GetDefaultReply(IPC_SUCCESS); } @@ -848,30 +829,11 @@ IPCCommandResult ES::GetTMDViews(const IOCtlVRequest& request) if (Loader.IsValid()) { - u32 Address = request.io_vectors[0].address; + const std::vector raw_view = Loader.GetTMD().GetRawView(); + if (raw_view.size() != request.io_vectors[0].size) + return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT); - Memory::CopyToEmu(Address, Loader.GetTMDView(), DiscIO::CNANDContentLoader::TMD_VIEW_SIZE); - Address += DiscIO::CNANDContentLoader::TMD_VIEW_SIZE; - - Memory::Write_U16(Loader.GetTitleVersion(), Address); - Address += 2; - Memory::Write_U16(Loader.GetNumEntries(), Address); - Address += 2; - - const std::vector& rContent = Loader.GetContent(); - for (size_t i = 0; i < Loader.GetContentSize(); i++) - { - Memory::Write_U32(rContent[i].m_ContentID, Address); - Address += 4; - Memory::Write_U16(rContent[i].m_Index, Address); - Address += 2; - Memory::Write_U16(rContent[i].m_Type, Address); - Address += 2; - Memory::Write_U64(rContent[i].m_Size, Address); - Address += 8; - } - - _dbg_assert_(IOS_ES, (Address - request.io_vectors[0].address) == request.io_vectors[0].size); + Memory::CopyToEmu(request.io_vectors[0].address, raw_view.data(), raw_view.size()); } INFO_LOG(IOS_ES, "IOCTL_ES_GETTMDVIEWS: title: %08x/%08x (buffer size: %i)", (u32)(TitleID >> 32), @@ -923,18 +885,15 @@ IPCCommandResult ES::GetStoredTMDSize(const IOCtlVRequest& request) const DiscIO::CNANDContentLoader& Loader = AccessContentDevice(TitleID); _dbg_assert_(IOS_ES, Loader.IsValid()); - u32 TMDCnt = 0; + u32 tmd_size = 0; if (Loader.IsValid()) - { - TMDCnt += DiscIO::CNANDContentLoader::TMD_HEADER_SIZE; - TMDCnt += (u32)Loader.GetContentSize() * DiscIO::CNANDContentLoader::CONTENT_HEADER_SIZE; - } + tmd_size = static_cast(Loader.GetTMD().GetRawTMD().size()); if (request.io_vectors.size()) - Memory::Write_U32(TMDCnt, request.io_vectors[0].address); + Memory::Write_U32(tmd_size, request.io_vectors[0].address); INFO_LOG(IOS_ES, "IOCTL_ES_GETSTOREDTMDSIZE: title: %08x/%08x (view size %i)", - (u32)(TitleID >> 32), (u32)TitleID, TMDCnt); + (u32)(TitleID >> 32), (u32)TitleID, tmd_size); return GetDefaultReply(IPC_SUCCESS); } @@ -962,20 +921,11 @@ IPCCommandResult ES::GetStoredTMD(const IOCtlVRequest& request) if (Loader.IsValid() && request.io_vectors.size()) { - u32 Address = request.io_vectors[0].address; + const std::vector raw_tmd = Loader.GetTMD().GetRawTMD(); + if (raw_tmd.size() != request.io_vectors[0].size) + return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT); - Memory::CopyToEmu(Address, Loader.GetTMDHeader(), DiscIO::CNANDContentLoader::TMD_HEADER_SIZE); - Address += DiscIO::CNANDContentLoader::TMD_HEADER_SIZE; - - const std::vector& rContent = Loader.GetContent(); - for (size_t i = 0; i < Loader.GetContentSize(); i++) - { - Memory::CopyToEmu(Address, rContent[i].m_Header, - DiscIO::CNANDContentLoader::CONTENT_HEADER_SIZE); - Address += DiscIO::CNANDContentLoader::CONTENT_HEADER_SIZE; - } - - _dbg_assert_(IOS_ES, (Address - request.io_vectors[0].address) == request.io_vectors[0].size); + Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size()); } INFO_LOG(IOS_ES, "IOCTL_ES_GETSTOREDTMD: title: %08x/%08x (buffer size: %i)", @@ -1050,9 +1000,9 @@ IPCCommandResult ES::Launch(const IOCtlVRequest& request) const DiscIO::CNANDContentLoader& ContentLoader = AccessContentDevice(TitleID); if (ContentLoader.IsValid()) { - ios_to_load = 0x0000000100000000ULL | ContentLoader.GetIosVersion(); + ios_to_load = ContentLoader.GetTMD().GetIOSId(); - u32 bootInd = ContentLoader.GetBootIndex(); + u32 bootInd = ContentLoader.GetTMD().GetBootIndex(); const DiscIO::SNANDContent* pContent = ContentLoader.GetContentByIndex(bootInd); if (pContent) { @@ -1194,10 +1144,13 @@ const DiscIO::CNANDContentLoader& ES::AccessContentDevice(u64 title_id) return DiscIO::CNANDContentManager::Access().GetNANDLoader(title_id, Common::FROM_SESSION_ROOT); } -u32 ES::ES_DIVerify(const std::vector& tmd) +u32 ES::ES_DIVerify(const IOS::ES::TMDReader& tmd) { + if (!tmd.IsValid()) + return -1; + u64 title_id = 0xDEADBEEFDEADBEEFull; - u64 tmd_title_id = Common::swap64(&tmd[0x18C]); + u64 tmd_title_id = tmd.GetTitleId(); DVDInterface::GetVolume().GetTitleID(&title_id); if (title_id != tmd_title_id) @@ -1211,7 +1164,8 @@ u32 ES::ES_DIVerify(const std::vector& tmd) if (!File::Exists(tmd_path)) { File::IOFile tmd_file(tmd_path, "wb"); - if (!tmd_file.WriteBytes(tmd.data(), tmd.size())) + const std::vector& tmd_bytes = tmd.GetRawTMD(); + if (!tmd_file.WriteBytes(tmd_bytes.data(), tmd_bytes.size())) ERROR_LOG(IOS_ES, "DIVerify failed to write disc TMD to NAND."); } DiscIO::cUIDsys uid_sys{Common::FromWhichRoot::FROM_SESSION_ROOT}; diff --git a/Source/Core/Core/IOS/ES/ES.h b/Source/Core/Core/IOS/ES/ES.h index aa934100f2..045fc75f36 100644 --- a/Source/Core/Core/IOS/ES/ES.h +++ b/Source/Core/Core/IOS/ES/ES.h @@ -46,7 +46,7 @@ public: void Close() override; IPCCommandResult IOCtlV(const IOCtlVRequest& request) override; - static u32 ES_DIVerify(const std::vector& tmd); + static u32 ES_DIVerify(const IOS::ES::TMDReader& tmd); // This should only be cleared on power reset static std::string m_ContentFile; @@ -203,7 +203,7 @@ private: u32 m_AccessIdentID = 0; // For title installation (ioctls IOCTL_ES_ADDTITLE*). - ::ES::TMDReader m_addtitle_tmd; + IOS::ES::TMDReader m_addtitle_tmd; u32 m_addtitle_content_id = 0xFFFFFFFF; std::vector m_addtitle_content_buffer; }; diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index 1eaf375070..1e66ba4f51 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -16,8 +16,12 @@ #include "Common/CommonFuncs.h" #include "Common/CommonTypes.h" +namespace IOS +{ namespace ES { +constexpr size_t CONTENT_VIEW_SIZE = 0x10; + std::vector AESDecode(const u8* key, u8* iv, const u8* src, u32 size) { mbedtls_aes_context aes_ctx; @@ -49,13 +53,13 @@ void TMDReader::SetBytes(std::vector&& bytes) bool TMDReader::IsValid() const { - if (m_bytes.size() < 0x1E4) + if (m_bytes.size() < sizeof(TMDHeader)) { // TMD is too small to contain its base fields. return false; } - if (m_bytes.size() < 0x1E4 + GetNumContents() * 36u) + if (m_bytes.size() < sizeof(TMDHeader) + GetNumContents() * sizeof(Content)) { // TMD is too small to contain all its expected content entries. return false; @@ -64,19 +68,69 @@ bool TMDReader::IsValid() const return true; } +const std::vector& TMDReader::GetRawTMD() const +{ + return m_bytes; +} + +std::vector TMDReader::GetRawHeader() const +{ + return std::vector(m_bytes.begin(), m_bytes.begin() + sizeof(TMDHeader)); +} + +std::vector TMDReader::GetRawView() const +{ + // Base fields + std::vector view(m_bytes.cbegin() + offsetof(TMDHeader, tmd_version), + m_bytes.cbegin() + offsetof(TMDHeader, access_rights)); + + const auto version = m_bytes.cbegin() + offsetof(TMDHeader, title_version); + view.insert(view.end(), version, version + sizeof(TMDHeader::title_version)); + + const auto num_contents = m_bytes.cbegin() + offsetof(TMDHeader, num_contents); + view.insert(view.end(), num_contents, num_contents + sizeof(TMDHeader::num_contents)); + + // Content views (same as Content, but without the hash) + for (size_t i = 0; i < GetNumContents(); ++i) + { + const auto content_iterator = m_bytes.cbegin() + sizeof(TMDHeader) + i * sizeof(Content); + view.insert(view.end(), content_iterator, content_iterator + CONTENT_VIEW_SIZE); + } + + return view; +} + +u16 TMDReader::GetBootIndex() const +{ + return Common::swap16(m_bytes.data() + offsetof(TMDHeader, boot_index)); +} + u64 TMDReader::GetIOSId() const { - return Common::swap64(m_bytes.data() + 0x184); + return Common::swap64(m_bytes.data() + offsetof(TMDHeader, ios_id)); +} + +DiscIO::Region TMDReader::GetRegion() const +{ + if (GetTitleId() == 0x0000000100000002) + return DiscIO::RegionSwitchWii(DiscIO::GetSysMenuRegion(GetTitleVersion())); + + return DiscIO::RegionSwitchWii(static_cast(GetTitleId() & 0xff)); } u64 TMDReader::GetTitleId() const { - return Common::swap64(m_bytes.data() + 0x18C); + return Common::swap64(m_bytes.data() + offsetof(TMDHeader, title_id)); +} + +u16 TMDReader::GetTitleVersion() const +{ + return Common::swap16(m_bytes.data() + offsetof(TMDHeader, title_version)); } u16 TMDReader::GetNumContents() const { - return Common::swap16(m_bytes.data() + 0x1DE); + return Common::swap16(m_bytes.data() + offsetof(TMDHeader, num_contents)); } bool TMDReader::GetContent(u16 index, Content* content) const @@ -86,16 +140,24 @@ bool TMDReader::GetContent(u16 index, Content* content) const return false; } - const u8* content_base = m_bytes.data() + 0x1E4 + index * 36; - content->id = Common::swap32(content_base); - content->index = Common::swap16(content_base + 4); - content->type = Common::swap16(content_base + 6); - content->size = Common::swap64(content_base + 8); - std::copy(content_base + 16, content_base + 36, content->sha1.begin()); + const u8* content_base = m_bytes.data() + sizeof(TMDHeader) + index * sizeof(Content); + content->id = Common::swap32(content_base + offsetof(Content, id)); + content->index = Common::swap16(content_base + offsetof(Content, index)); + content->type = Common::swap16(content_base + offsetof(Content, type)); + content->size = Common::swap64(content_base + offsetof(Content, size)); + std::copy_n(content_base + offsetof(Content, sha1), content->sha1.size(), content->sha1.begin()); return true; } +std::vector TMDReader::GetContents() const +{ + std::vector contents(GetNumContents()); + for (size_t i = 0; i < contents.size(); ++i) + GetContent(static_cast(i), &contents[i]); + return contents; +} + bool TMDReader::FindContentById(u32 id, Content* content) const { for (u16 index = 0; index < GetNumContents(); ++index) @@ -205,3 +267,4 @@ std::vector TicketReader::GetTitleKey() const return AESDecode(common_key, iv, &m_bytes[GetOffset() + offsetof(Ticket, title_key)], 16); } } // namespace ES +} // namespace IOS diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h index f161d1974a..64fd6c3a46 100644 --- a/Source/Core/Core/IOS/ES/Formats.h +++ b/Source/Core/Core/IOS/ES/Formats.h @@ -12,45 +12,50 @@ #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" +#include "DiscIO/Enums.h" +namespace IOS +{ namespace ES { -std::vector AESDecode(const u8* key, u8* iv, const u8* src, u32 size); - -class TMDReader final -{ -public: - TMDReader() = default; - explicit TMDReader(const std::vector& bytes); - explicit TMDReader(std::vector&& bytes); - - void SetBytes(const std::vector& bytes); - void SetBytes(std::vector&& bytes); - - bool IsValid() const; - - u64 GetIOSId() const; - u64 GetTitleId() const; - - struct Content - { - u32 id; - u16 index; - u16 type; - u64 size; - std::array sha1; - }; - u16 GetNumContents() const; - bool GetContent(u16 index, Content* content) const; - bool FindContentById(u32 id, Content* content) const; - - void DoState(PointerWrap& p); - -private: - std::vector m_bytes; -}; - #pragma pack(push, 4) +struct TMDHeader +{ + u32 signature_type; + u8 rsa_2048_signature[256]; + u8 fill[60]; + u8 issuer[64]; + u8 tmd_version; + u8 ca_crl_version; + u8 signer_crl_version; + u64 ios_id; + u64 title_id; + u32 title_type; + u16 group_id; + u16 zero; + u16 region; + u8 ratings[16]; + u8 reserved[12]; + u8 ipc_mask[12]; + u8 reserved2[18]; + u32 access_rights; + u16 title_version; + u16 num_contents; + u16 boot_index; + u16 fill2; +}; +static_assert(sizeof(TMDHeader) == 0x1e4, "TMDHeader has the wrong size"); + +struct Content +{ + u32 id; + u16 index; + u16 type; + u64 size; + std::array sha1; +}; +static_assert(sizeof(Content) == 36, "Content has the wrong size"); + struct TimeLimit { u32 enabled; @@ -96,6 +101,42 @@ struct Ticket static_assert(sizeof(Ticket) == 356, "Ticket has the wrong size"); #pragma pack(pop) +std::vector AESDecode(const u8* key, u8* iv, const u8* src, u32 size); + +class TMDReader final +{ +public: + TMDReader() = default; + explicit TMDReader(const std::vector& bytes); + explicit TMDReader(std::vector&& bytes); + + void SetBytes(const std::vector& bytes); + void SetBytes(std::vector&& bytes); + + bool IsValid() const; + + // Returns the TMD or parts of it without any kind of parsing. Intended for use by ES. + const std::vector& GetRawTMD() const; + std::vector GetRawHeader() const; + std::vector GetRawView() const; + + u16 GetBootIndex() const; + u64 GetIOSId() const; + DiscIO::Region GetRegion() const; + u64 GetTitleId() const; + u16 GetTitleVersion() const; + + u16 GetNumContents() const; + bool GetContent(u16 index, Content* content) const; + std::vector GetContents() const; + bool FindContentById(u32 id, Content* content) const; + + void DoState(PointerWrap& p); + +private: + std::vector m_bytes; +}; + class TicketReader final { public: @@ -125,3 +166,4 @@ private: std::vector m_bytes; }; } // namespace ES +} // namespace IOS diff --git a/Source/Core/Core/IOS/IPC.cpp b/Source/Core/Core/IOS/IPC.cpp index 5a334f6c82..b74ed1c996 100644 --- a/Source/Core/Core/IOS/IPC.cpp +++ b/Source/Core/Core/IOS/IPC.cpp @@ -658,7 +658,7 @@ void SetDefaultContentFile(const std::string& file_name) es->LoadWAD(file_name); } -void ES_DIVerify(const std::vector& tmd) +void ES_DIVerify(const ES::TMDReader& tmd) { Device::ES::ES_DIVerify(tmd); } diff --git a/Source/Core/Core/IOS/IPC.h b/Source/Core/Core/IOS/IPC.h index 09b3c1179c..5bbd10befd 100644 --- a/Source/Core/Core/IOS/IPC.h +++ b/Source/Core/Core/IOS/IPC.h @@ -16,6 +16,11 @@ class PointerWrap; namespace IOS { +namespace ES +{ +class TMDReader; +} + namespace HLE { namespace Device @@ -61,7 +66,7 @@ void DoState(PointerWrap& p); // Set default content file void SetDefaultContentFile(const std::string& file_name); -void ES_DIVerify(const std::vector& tmd); +void ES_DIVerify(const ES::TMDReader& tmd); void SDIO_EventNotify(); diff --git a/Source/Core/Core/IOS/MIOS.cpp b/Source/Core/Core/IOS/MIOS.cpp index 6fa93f6d68..2a9e55402f 100644 --- a/Source/Core/Core/IOS/MIOS.cpp +++ b/Source/Core/Core/IOS/MIOS.cpp @@ -23,6 +23,7 @@ #include "Core/HW/DVDThread.h" #include "Core/HW/Memmap.h" #include "Core/HW/SystemTimers.h" +#include "Core/IOS/ES/Formats.h" #include "Core/IOS/MIOS.h" #include "Core/PowerPC/PPCSymbolDB.h" #include "Core/PowerPC/PowerPC.h" @@ -97,7 +98,7 @@ static std::vector GetMIOSBinary() if (!loader.IsValid()) return {}; - const auto* content = loader.GetContentByIndex(loader.GetBootIndex()); + const auto* content = loader.GetContentByIndex(loader.GetTMD().GetBootIndex()); if (!content) return {}; diff --git a/Source/Core/Core/IOS/WFS/WFSI.cpp b/Source/Core/Core/IOS/WFS/WFSI.cpp index 4c885fee72..16e97055d6 100644 --- a/Source/Core/Core/IOS/WFS/WFSI.cpp +++ b/Source/Core/Core/IOS/WFS/WFSI.cpp @@ -134,7 +134,7 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request) // Initializes the IV from the index of the content in the TMD contents. u32 content_id = Memory::Read_U32(request.buffer_in + 8); - ES::TMDReader::Content content_info; + ES::Content content_info; if (!m_tmd.FindContentById(content_id, &content_info)) { WARN_LOG(IOS, "%s: Content id %08x not found", ioctl_name, content_id); diff --git a/Source/Core/Core/IOS/WFS/WFSI.h b/Source/Core/Core/IOS/WFS/WFSI.h index 6388121680..82dfded277 100644 --- a/Source/Core/Core/IOS/WFS/WFSI.h +++ b/Source/Core/Core/IOS/WFS/WFSI.h @@ -50,7 +50,7 @@ private: u8 m_aes_key[0x10] = {}; u8 m_aes_iv[0x10] = {}; - ::ES::TMDReader m_tmd; + IOS::ES::TMDReader m_tmd; std::string m_base_extract_path; ARCUnpacker m_arc_unpacker; diff --git a/Source/Core/DiscIO/NANDContentLoader.cpp b/Source/Core/DiscIO/NANDContentLoader.cpp index dcd875388a..0344fe1c46 100644 --- a/Source/Core/DiscIO/NANDContentLoader.cpp +++ b/Source/Core/DiscIO/NANDContentLoader.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -146,7 +147,6 @@ bool CNANDContentDataBuffer::GetRange(u32 start, u32 size, u8* buffer) } CNANDContentLoader::CNANDContentLoader(const std::string& content_name) - : m_Valid(false), m_IsWAD(false), m_TitleID(-1), m_IosVersion(0x09), m_BootIndex(-1) { m_Valid = Initialize(content_name); } @@ -159,7 +159,7 @@ const SNANDContent* CNANDContentLoader::GetContentByIndex(int index) const { for (auto& Content : m_Content) { - if (Content.m_Index == index) + if (Content.m_metadata.index == index) { return &Content; } @@ -176,13 +176,12 @@ bool CNANDContentLoader::Initialize(const std::string& name) WiiWAD wad(name); std::vector data_app; - std::vector tmd; if (wad.IsValid()) { m_IsWAD = true; m_ticket = wad.GetTicket(); - tmd = wad.GetTMD(); + m_tmd = wad.GetTMD(); data_app = wad.GetDataApp(); } else @@ -201,94 +200,64 @@ bool CNANDContentLoader::Initialize(const std::string& name) return false; } - tmd.resize(static_cast(File::GetSize(tmd_filename))); - tmd_file.ReadBytes(tmd.data(), tmd.size()); + std::vector bytes(File::GetSize(tmd_filename)); + tmd_file.ReadBytes(bytes.data(), bytes.size()); + m_tmd.SetBytes(std::move(bytes)); + + m_ticket = FindSignedTicket(m_tmd.GetTitleId()); } - std::copy(&tmd[0], &tmd[TMD_HEADER_SIZE], m_TMDHeader); - std::copy(&tmd[0x180], &tmd[0x180 + TMD_VIEW_SIZE], m_TMDView); - - m_TitleVersion = Common::swap16(&tmd[0x01DC]); - m_NumEntries = Common::swap16(&tmd[0x01DE]); - m_BootIndex = Common::swap16(&tmd[0x01E0]); - m_TitleID = Common::swap64(&tmd[0x018C]); - m_IosVersion = Common::swap16(&tmd[0x018A]); - m_Country = static_cast(m_TitleID & 0xFF); - - if (m_Country == 2) // SYSMENU - m_Country = GetSysMenuRegion(m_TitleVersion); - - if (!m_IsWAD) - m_ticket = FindSignedTicket(m_TitleID); - - InitializeContentEntries(tmd, data_app); + InitializeContentEntries(data_app); return true; } -void CNANDContentLoader::InitializeContentEntries(const std::vector& tmd, - const std::vector& data_app) +void CNANDContentLoader::InitializeContentEntries(const std::vector& data_app) { - m_Content.resize(m_NumEntries); + if (!m_ticket.IsValid()) + { + ERROR_LOG(IOS_ES, "No valid ticket for title %016" PRIx64, m_tmd.GetTitleId()); + return; + } + + const std::vector contents = m_tmd.GetContents(); + m_Content.resize(contents.size()); - std::array iv; u32 data_app_offset = 0; - + const std::vector title_key = m_ticket.GetTitleKey(); CSharedContent shared_content{Common::FromWhichRoot::FROM_SESSION_ROOT}; - for (u32 i = 0; i < m_NumEntries; i++) + for (size_t i = 0; i < contents.size(); ++i) { - const u32 entry_offset = 0x24 * i; - - SNANDContent& content = m_Content[i]; - content.m_ContentID = Common::swap32(&tmd[entry_offset + 0x01E4]); - content.m_Index = Common::swap16(&tmd[entry_offset + 0x01E8]); - content.m_Type = Common::swap16(&tmd[entry_offset + 0x01EA]); - content.m_Size = static_cast(Common::swap64(&tmd[entry_offset + 0x01EC])); - - const auto header_begin = std::next(tmd.begin(), entry_offset + 0x01E4); - const auto header_end = std::next(header_begin, ArraySize(content.m_Header)); - std::copy(header_begin, header_end, content.m_Header); - - const auto hash_begin = std::next(tmd.begin(), entry_offset + 0x01F4); - const auto hash_end = std::next(hash_begin, ArraySize(content.m_SHA1Hash)); - std::copy(hash_begin, hash_end, content.m_SHA1Hash); + const auto& content = contents.at(i); if (m_IsWAD) { - u32 rounded_size = Common::AlignUp(content.m_Size, 0x40); + // The content index is used as IV (2 bytes); the remaining 14 bytes are zeroes. + std::array iv{}; + iv[0] = static_cast(content.index >> 8) & 0xFF; + iv[1] = static_cast(content.index) & 0xFF; - iv.fill(0); - std::copy(&tmd[entry_offset + 0x01E8], &tmd[entry_offset + 0x01E8 + 2], iv.begin()); - - content.m_Data = std::make_unique(ES::AESDecode( - m_ticket.GetTitleKey().data(), iv.data(), &data_app[data_app_offset], rounded_size)); + u32 rounded_size = Common::AlignUp(static_cast(content.size), 0x40); + m_Content[i].m_Data = std::make_unique(IOS::ES::AESDecode( + title_key.data(), iv.data(), &data_app[data_app_offset], rounded_size)); data_app_offset += rounded_size; - continue; + } + else + { + std::string filename; + if (content.type & 0x8000) // shared app + filename = shared_content.GetFilenameFromSHA1(content.sha1.data()); + else + filename = StringFromFormat("%s/%08x.app", m_Path.c_str(), content.id); + + m_Content[i].m_Data = std::make_unique(filename); } - std::string filename; - if (content.m_Type & 0x8000) // shared app - filename = shared_content.GetFilenameFromSHA1(content.m_SHA1Hash); - else - filename = StringFromFormat("%s/%08x.app", m_Path.c_str(), content.m_ContentID); - - content.m_Data = std::make_unique(filename); - - // Be graceful about incorrect TMDs. - if (File::Exists(filename)) - content.m_Size = static_cast(File::GetSize(filename)); + m_Content[i].m_metadata = std::move(content); } } -DiscIO::Region CNANDContentLoader::GetRegion() const -{ - if (!IsValid()) - return DiscIO::Region::UNKNOWN_REGION; - - return RegionSwitchWii(m_Country); -} - CNANDContentManager::~CNANDContentManager() { } @@ -327,18 +296,18 @@ void CNANDContentManager::ClearCache() void CNANDContentLoader::RemoveTitle() const { - INFO_LOG(DISCIO, "RemoveTitle %08x/%08x", (u32)(m_TitleID >> 32), (u32)m_TitleID); + const u64 title_id = m_tmd.GetTitleId(); + INFO_LOG(DISCIO, "RemoveTitle %08x/%08x", (u32)(title_id >> 32), (u32)title_id); if (IsValid()) { // remove TMD? - for (u32 i = 0; i < m_NumEntries; i++) + for (const auto& content : m_Content) { - if (!(m_Content[i].m_Type & 0x8000)) // skip shared apps + if (!(content.m_metadata.type & 0x8000)) // skip shared apps { - std::string filename = - StringFromFormat("%s/%08x.app", m_Path.c_str(), m_Content[i].m_ContentID); - INFO_LOG(DISCIO, "Delete %s", filename.c_str()); - File::Delete(filename); + std::string path = StringFromFormat("%s/%08x.app", m_Path.c_str(), content.m_metadata.id); + INFO_LOG(DISCIO, "Delete %s", path.c_str()); + File::Delete(path); } } CNANDContentManager::Access().ClearCache(); // deletes 'this' @@ -425,7 +394,7 @@ u64 CNANDContentManager::Install_WiiWAD(const std::string& filename) if (content_loader.IsValid() == false) return 0; - u64 title_id = content_loader.GetTitleID(); + const u64 title_id = content_loader.GetTMD().GetTitleId(); // copy WAD's TMD header and contents to content directory @@ -440,20 +409,17 @@ u64 CNANDContentManager::Install_WiiWAD(const std::string& filename) return 0; } - tmd_file.WriteBytes(content_loader.GetTMDHeader(), CNANDContentLoader::TMD_HEADER_SIZE); + const auto& raw_tmd = content_loader.GetTMD().GetRawTMD(); + tmd_file.WriteBytes(raw_tmd.data(), raw_tmd.size()); CSharedContent shared_content{Common::FromWhichRoot::FROM_CONFIGURED_ROOT}; - for (u32 i = 0; i < content_loader.GetContentSize(); i++) + for (const auto& content : content_loader.GetContent()) { - const SNANDContent& content = content_loader.GetContent()[i]; - - tmd_file.WriteBytes(content.m_Header, CNANDContentLoader::CONTENT_HEADER_SIZE); - std::string app_filename; - if (content.m_Type & 0x8000) // shared - app_filename = shared_content.AddSharedContent(content.m_SHA1Hash); + if (content.m_metadata.type & 0x8000) // shared + app_filename = shared_content.AddSharedContent(content.m_metadata.sha1.data()); else - app_filename = StringFromFormat("%s%08x.app", content_path.c_str(), content.m_ContentID); + app_filename = StringFromFormat("%s%08x.app", content_path.c_str(), content.m_metadata.id); if (!File::Exists(app_filename)) { @@ -465,7 +431,7 @@ u64 CNANDContentManager::Install_WiiWAD(const std::string& filename) return 0; } - app_file.WriteBytes(content.m_Data->Get().data(), content.m_Size); + app_file.WriteBytes(content.m_Data->Get().data(), content.m_metadata.size); } else { @@ -488,7 +454,7 @@ u64 CNANDContentManager::Install_WiiWAD(const std::string& filename) return title_id; } -bool AddTicket(const ES::TicketReader& signed_ticket) +bool AddTicket(const IOS::ES::TicketReader& signed_ticket) { if (!signed_ticket.IsValid()) { @@ -508,21 +474,21 @@ bool AddTicket(const ES::TicketReader& signed_ticket) return ticket_file.WriteBytes(raw_ticket.data(), raw_ticket.size()); } -ES::TicketReader FindSignedTicket(u64 title_id) +IOS::ES::TicketReader FindSignedTicket(u64 title_id) { std::string ticket_filename = Common::GetTicketFileName(title_id, Common::FROM_CONFIGURED_ROOT); File::IOFile ticket_file(ticket_filename, "rb"); if (!ticket_file) { - return ES::TicketReader{}; + return IOS::ES::TicketReader{}; } std::vector signed_ticket(ticket_file.GetSize()); if (!ticket_file.ReadBytes(signed_ticket.data(), signed_ticket.size())) { - return ES::TicketReader{}; + return IOS::ES::TicketReader{}; } - return ES::TicketReader{std::move(signed_ticket)}; + return IOS::ES::TicketReader{std::move(signed_ticket)}; } } // namespace end diff --git a/Source/Core/DiscIO/NANDContentLoader.h b/Source/Core/DiscIO/NANDContentLoader.h index 654a123b5d..e945e7dcb5 100644 --- a/Source/Core/DiscIO/NANDContentLoader.h +++ b/Source/Core/DiscIO/NANDContentLoader.h @@ -23,8 +23,8 @@ namespace DiscIO { enum class Region; -bool AddTicket(const ES::TicketReader& signed_ticket); -ES::TicketReader FindSignedTicket(u64 title_id); +bool AddTicket(const IOS::ES::TicketReader& signed_ticket); +IOS::ES::TicketReader FindSignedTicket(u64 title_id); class CNANDContentData { @@ -66,13 +66,7 @@ private: struct SNANDContent { - u32 m_ContentID; - u16 m_Index; - u16 m_Type; - u32 m_Size; - u8 m_SHA1Hash[20]; - u8 m_Header[36]; // all of the above - + IOS::ES::Content m_metadata; std::unique_ptr m_Data; }; @@ -85,42 +79,19 @@ public: bool IsValid() const { return m_Valid; } void RemoveTitle() const; - u64 GetTitleID() const { return m_TitleID; } - u16 GetIosVersion() const { return m_IosVersion; } - u32 GetBootIndex() const { return m_BootIndex; } - size_t GetContentSize() const { return m_Content.size(); } const SNANDContent* GetContentByIndex(int index) const; - const u8* GetTMDView() const { return m_TMDView; } - const u8* GetTMDHeader() const { return m_TMDHeader; } - const ES::TicketReader& GetTicket() const { return m_ticket; } + const IOS::ES::TMDReader& GetTMD() const { return m_tmd; } + const IOS::ES::TicketReader& GetTicket() const { return m_ticket; } const std::vector& GetContent() const { return m_Content; } - u16 GetTitleVersion() const { return m_TitleVersion; } - u16 GetNumEntries() const { return m_NumEntries; } - DiscIO::Region GetRegion() const; - u8 GetCountryChar() const { return m_Country; } - enum - { - TMD_VIEW_SIZE = 0x58, - TMD_HEADER_SIZE = 0x1E4, - CONTENT_HEADER_SIZE = 0x24, - }; - private: bool Initialize(const std::string& name); - void InitializeContentEntries(const std::vector& tmd, const std::vector& data_app); + void InitializeContentEntries(const std::vector& data_app); - bool m_Valid; - bool m_IsWAD; + bool m_Valid = false; + bool m_IsWAD = false; std::string m_Path; - u64 m_TitleID; - u16 m_IosVersion; - u32 m_BootIndex; - u16 m_NumEntries; - u16 m_TitleVersion; - u8 m_TMDView[TMD_VIEW_SIZE]; - u8 m_TMDHeader[TMD_HEADER_SIZE]; - ES::TicketReader m_ticket; - u8 m_Country; + IOS::ES::TMDReader m_tmd; + IOS::ES::TicketReader m_ticket; std::vector m_Content; }; diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index 966bbe2ae1..1190565004 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -12,6 +12,7 @@ #include "Common/CommonFuncs.h" #include "Common/CommonTypes.h" #include "Common/StringUtil.h" +#include "Core/IOS/ES/Formats.h" #include "DiscIO/Enums.h" namespace DiscIO @@ -36,7 +37,7 @@ public: } virtual bool GetTitleID(u64*) const { return false; } - virtual std::vector GetTMD() const { return {}; } + virtual IOS::ES::TMDReader GetTMD() const { return {}; } virtual u64 PartitionOffsetToRawOffset(u64 offset) const { return offset; } virtual std::string GetGameID() const = 0; virtual std::string GetMakerID() const = 0; diff --git a/Source/Core/DiscIO/VolumeWad.cpp b/Source/Core/DiscIO/VolumeWad.cpp index 29f543586d..01080c7b52 100644 --- a/Source/Core/DiscIO/VolumeWad.cpp +++ b/Source/Core/DiscIO/VolumeWad.cpp @@ -36,6 +36,16 @@ CVolumeWAD::CVolumeWAD(std::unique_ptr reader) : m_reader(std::move Common::AlignUp(m_tick_size, 0x40); m_opening_bnr_offset = m_tmd_offset + Common::AlignUp(m_tmd_size, 0x40) + Common::AlignUp(m_data_size, 0x40); + + if (m_tmd_size > 1024 * 1024 * 4) + { + ERROR_LOG(DISCIO, "TMD is too large: %u bytes", m_tmd_size); + return; + } + + std::vector tmd_buffer(m_tmd_size); + Read(m_tmd_offset, m_tmd_size, tmd_buffer.data(), false); + m_tmd.SetBytes(std::move(tmd_buffer)); } CVolumeWAD::~CVolumeWAD() @@ -55,40 +65,26 @@ bool CVolumeWAD::Read(u64 offset, u64 length, u8* buffer, bool decrypt) const Region CVolumeWAD::GetRegion() const { - u8 country_code; - if (!Read(m_tmd_offset + 0x0193, 1, &country_code)) + if (!m_tmd.IsValid()) return Region::UNKNOWN_REGION; - - return RegionSwitchWii(country_code); + return m_tmd.GetRegion(); } Country CVolumeWAD::GetCountry() const { - // read the last digit of the titleID in the ticket - u8 country_code; - if (!Read(m_tmd_offset + 0x0193, 1, &country_code)) + if (!m_tmd.IsValid()) return Country::COUNTRY_UNKNOWN; + u8 country_code = static_cast(m_tmd.GetTitleId() & 0xff); if (country_code == 2) // SYSMENU - { - u16 title_version = 0; - Read(m_tmd_offset + 0x01dc, 2, (u8*)&title_version); - country_code = GetSysMenuRegion(Common::swap16(title_version)); - } + country_code = GetSysMenuRegion(m_tmd.GetTitleVersion()); return CountrySwitch(country_code); } -std::vector CVolumeWAD::GetTMD() const +IOS::ES::TMDReader CVolumeWAD::GetTMD() const { - if (m_tmd_size > 1024 * 1024 * 4) - { - ERROR_LOG(DISCIO, "TMD is too large: %u bytes", m_tmd_size); - return {}; - } - std::vector buffer(m_tmd_size); - Read(m_tmd_offset, m_tmd_size, buffer.data(), false); - return buffer; + return m_tmd; } std::string CVolumeWAD::GetGameID() const @@ -125,11 +121,10 @@ bool CVolumeWAD::GetTitleID(u64* buffer) const u16 CVolumeWAD::GetRevision() const { - u16 revision; - if (!m_reader->Read(m_tmd_offset + 0x1dc, 2, (u8*)&revision)) + if (!m_tmd.IsValid()) return 0; - return Common::swap16(revision); + return m_tmd.GetTitleVersion(); } Platform CVolumeWAD::GetVolumeType() const diff --git a/Source/Core/DiscIO/VolumeWad.h b/Source/Core/DiscIO/VolumeWad.h index c93315f72b..f84af08796 100644 --- a/Source/Core/DiscIO/VolumeWad.h +++ b/Source/Core/DiscIO/VolumeWad.h @@ -10,6 +10,7 @@ #include #include "Common/CommonTypes.h" +#include "Core/IOS/ES/Formats.h" #include "DiscIO/Volume.h" // --- this volume type is used for Wad files --- @@ -32,7 +33,7 @@ public: ~CVolumeWAD(); bool Read(u64 offset, u64 length, u8* buffer, bool decrypt = false) const override; bool GetTitleID(u64* buffer) const override; - std::vector GetTMD() const override; + IOS::ES::TMDReader GetTMD() const override; std::string GetGameID() const override; std::string GetMakerID() const override; u16 GetRevision() const override; @@ -51,6 +52,7 @@ public: private: std::unique_ptr m_reader; + IOS::ES::TMDReader m_tmd; u32 m_offset = 0; u32 m_tmd_offset = 0; u32 m_opening_bnr_offset = 0; diff --git a/Source/Core/DiscIO/VolumeWiiCrypted.cpp b/Source/Core/DiscIO/VolumeWiiCrypted.cpp index ae174604cc..ef291fa237 100644 --- a/Source/Core/DiscIO/VolumeWiiCrypted.cpp +++ b/Source/Core/DiscIO/VolumeWiiCrypted.cpp @@ -114,7 +114,7 @@ bool CVolumeWiiCrypted::GetTitleID(u64* buffer) const return true; } -std::vector CVolumeWiiCrypted::GetTMD() const +IOS::ES::TMDReader CVolumeWiiCrypted::GetTMD() const { u32 tmd_size; u32 tmd_address; @@ -137,7 +137,7 @@ std::vector CVolumeWiiCrypted::GetTMD() const std::vector buffer(tmd_size); Read(m_VolumeOffset + tmd_address, tmd_size, buffer.data(), false); - return buffer; + return IOS::ES::TMDReader{std::move(buffer)}; } u64 CVolumeWiiCrypted::PartitionOffsetToRawOffset(u64 offset) const diff --git a/Source/Core/DiscIO/VolumeWiiCrypted.h b/Source/Core/DiscIO/VolumeWiiCrypted.h index 7d8943ea8a..dfeb74ad66 100644 --- a/Source/Core/DiscIO/VolumeWiiCrypted.h +++ b/Source/Core/DiscIO/VolumeWiiCrypted.h @@ -11,6 +11,7 @@ #include #include "Common/CommonTypes.h" +#include "Core/IOS/ES/Formats.h" #include "DiscIO/Volume.h" // --- this volume type is used for encrypted Wii images --- @@ -32,7 +33,7 @@ public: ~CVolumeWiiCrypted(); bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt) const override; bool GetTitleID(u64* buffer) const override; - std::vector GetTMD() const override; + IOS::ES::TMDReader GetTMD() const override; u64 PartitionOffsetToRawOffset(u64 offset) const override; std::string GetGameID() const override; std::string GetMakerID() const override; diff --git a/Source/Core/DiscIO/WiiWad.cpp b/Source/Core/DiscIO/WiiWad.cpp index 04e4ff8a7c..2576dac41f 100644 --- a/Source/Core/DiscIO/WiiWad.cpp +++ b/Source/Core/DiscIO/WiiWad.cpp @@ -90,7 +90,7 @@ bool WiiWAD::ParseWAD(IBlobReader& reader) offset += Common::AlignUp(certificate_chain_size, 0x40); m_ticket.SetBytes(CreateWADEntry(reader, ticket_size, offset)); offset += Common::AlignUp(ticket_size, 0x40); - m_tmd = CreateWADEntry(reader, tmd_size, offset); + m_tmd.SetBytes(CreateWADEntry(reader, tmd_size, offset)); offset += Common::AlignUp(tmd_size, 0x40); m_data_app = CreateWADEntry(reader, data_app_size, offset); offset += Common::AlignUp(data_app_size, 0x40); diff --git a/Source/Core/DiscIO/WiiWad.h b/Source/Core/DiscIO/WiiWad.h index ea0c3de43a..46480fdaa6 100644 --- a/Source/Core/DiscIO/WiiWad.h +++ b/Source/Core/DiscIO/WiiWad.h @@ -23,8 +23,8 @@ public: bool IsValid() const { return m_valid; } const std::vector& GetCertificateChain() const { return m_certificate_chain; } - const ES::TicketReader& GetTicket() const { return m_ticket; } - const std::vector& GetTMD() const { return m_tmd; } + const IOS::ES::TicketReader& GetTicket() const { return m_ticket; } + const IOS::ES::TMDReader& GetTMD() const { return m_tmd; } const std::vector& GetDataApp() const { return m_data_app; } const std::vector& GetFooter() const { return m_footer; } private: @@ -33,8 +33,8 @@ private: bool m_valid; std::vector m_certificate_chain; - ES::TicketReader m_ticket; - std::vector m_tmd; + IOS::ES::TicketReader m_ticket; + IOS::ES::TMDReader m_tmd; std::vector m_data_app; std::vector m_footer; }; diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index e1a9271744..535dc568aa 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -1225,23 +1225,7 @@ void CFrame::OnInstallWAD(wxCommandEvent& event) void CFrame::UpdateLoadWiiMenuItem() const { - auto* const menu_item = GetMenuBar()->FindItem(IDM_LOAD_WII_MENU); - - const auto& sys_menu_loader = DiscIO::CNANDContentManager::Access().GetNANDLoader( - TITLEID_SYSMENU, Common::FROM_CONFIGURED_ROOT); - - if (sys_menu_loader.IsValid()) - { - const int version = sys_menu_loader.GetTitleVersion(); - const char region = sys_menu_loader.GetCountryChar(); - menu_item->Enable(); - menu_item->SetItemLabel(wxString::Format(_("Load Wii System Menu %d%c"), version, region)); - } - else - { - menu_item->Enable(false); - menu_item->SetItemLabel(_("Load Wii System Menu")); - } + GetMenuBar()->Refresh(true, nullptr); } void CFrame::OnFifoPlayer(wxCommandEvent& WXUNUSED(event)) diff --git a/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp b/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp index 6a839c32be..3fc9efc14d 100644 --- a/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp +++ b/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp @@ -182,7 +182,7 @@ void InfoPanel::LoadISODetails() m_fst->SetValue(StrToWxStr(std::to_string(m_opened_iso->GetFSTSize()))); if (m_ios_version) { - ES::TMDReader tmd{m_opened_iso->GetTMD()}; + const IOS::ES::TMDReader tmd = m_opened_iso->GetTMD(); if (tmd.IsValid()) m_ios_version->SetValue(StringFromFormat("IOS%u", static_cast(tmd.GetIOSId()))); } @@ -223,7 +223,7 @@ wxStaticBoxSizer* InfoPanel::CreateISODetailsSizer() {_("Apploader Date:"), m_date}, {_("FST Size:"), m_fst}, }}; - if (!m_opened_iso->GetTMD().empty()) + if (m_opened_iso->GetTMD().IsValid()) controls.emplace_back(_("IOS Version:"), m_ios_version); const int space_10 = FromDIP(10); diff --git a/Source/Core/DolphinWX/MainMenuBar.cpp b/Source/Core/DolphinWX/MainMenuBar.cpp index 6cf1240b30..a0ec767fff 100644 --- a/Source/Core/DolphinWX/MainMenuBar.cpp +++ b/Source/Core/DolphinWX/MainMenuBar.cpp @@ -12,6 +12,7 @@ #include "Core/Core.h" #include "Core/PowerPC/PowerPC.h" #include "Core/State.h" +#include "DiscIO/Enums.h" #include "DiscIO/NANDContentLoader.h" #include "DolphinWX/Globals.h" #include "DolphinWX/WxUtils.h" @@ -546,8 +547,8 @@ void MainMenuBar::RefreshWiiSystemMenuLabel() const if (sys_menu_loader.IsValid()) { - const auto sys_menu_version = sys_menu_loader.GetTitleVersion(); - const auto sys_menu_region = sys_menu_loader.GetCountryChar(); + const u16 sys_menu_version = sys_menu_loader.GetTMD().GetTitleVersion(); + const char sys_menu_region = DiscIO::GetSysMenuRegion(sys_menu_version); item->Enable(); item->SetItemLabel( wxString::Format(_("Load Wii System Menu %u%c"), sys_menu_version, sys_menu_region)); From 5104caf6a647dac47509a8e0540a2eb04c1b1ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 12 Feb 2017 11:50:35 +0100 Subject: [PATCH 3/4] Move AES code to Common/Crypto --- Source/Core/Common/CMakeLists.txt | 1 + Source/Core/Common/Common.vcxproj | 2 ++ Source/Core/Common/Common.vcxproj.filters | 6 ++++++ Source/Core/Common/Crypto/AES.cpp | 24 +++++++++++++++++++++++ Source/Core/Common/Crypto/AES.h | 18 +++++++++++++++++ Source/Core/Core/IOS/ES/Formats.cpp | 17 +++------------- Source/Core/Core/IOS/ES/Formats.h | 2 -- Source/Core/DiscIO/NANDContentLoader.cpp | 3 ++- 8 files changed, 56 insertions(+), 17 deletions(-) create mode 100644 Source/Core/Common/Crypto/AES.cpp create mode 100644 Source/Core/Common/Crypto/AES.h diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index 5f910a1670..9feeb4ad2e 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -30,6 +30,7 @@ set(SRCS Analytics.cpp x64ABI.cpp x64Emitter.cpp MD5.cpp + Crypto/AES.cpp Crypto/bn.cpp Crypto/ec.cpp Logging/LogManager.cpp) diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj index 831dcad4a7..ac2c2617aa 100644 --- a/Source/Core/Common/Common.vcxproj +++ b/Source/Core/Common/Common.vcxproj @@ -139,6 +139,7 @@ + @@ -188,6 +189,7 @@ + diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters index 3c9b952742..aac1b5a2ec 100644 --- a/Source/Core/Common/Common.vcxproj.filters +++ b/Source/Core/Common/Common.vcxproj.filters @@ -75,6 +75,9 @@ Logging + + Crypto + Crypto @@ -259,6 +262,9 @@ + + Crypto + Crypto diff --git a/Source/Core/Common/Crypto/AES.cpp b/Source/Core/Common/Crypto/AES.cpp new file mode 100644 index 0000000000..e12ba2481c --- /dev/null +++ b/Source/Core/Common/Crypto/AES.cpp @@ -0,0 +1,24 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include "Common/Crypto/AES.h" + +namespace Common +{ +namespace AES +{ +std::vector Decrypt(const u8* key, u8* iv, const u8* src, size_t size) +{ + mbedtls_aes_context aes_ctx; + std::vector buffer(size); + + mbedtls_aes_setkey_dec(&aes_ctx, key, 128); + mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, size, iv, src, buffer.data()); + + return buffer; +} +} // namespace AES +} // namespace Common diff --git a/Source/Core/Common/Crypto/AES.h b/Source/Core/Common/Crypto/AES.h new file mode 100644 index 0000000000..54d88727ac --- /dev/null +++ b/Source/Core/Common/Crypto/AES.h @@ -0,0 +1,18 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "Common/CommonTypes.h" + +namespace Common +{ +namespace AES +{ +std::vector Decrypt(const u8* key, u8* iv, const u8* src, size_t size); +} // namespace AES +} // namespace Common diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index 1e66ba4f51..f90bd765b2 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -10,11 +10,10 @@ #include #include -#include - #include "Common/ChunkFile.h" #include "Common/CommonFuncs.h" #include "Common/CommonTypes.h" +#include "Common/Crypto/AES.h" namespace IOS { @@ -22,17 +21,6 @@ namespace ES { constexpr size_t CONTENT_VIEW_SIZE = 0x10; -std::vector AESDecode(const u8* key, u8* iv, const u8* src, u32 size) -{ - mbedtls_aes_context aes_ctx; - std::vector buffer(size); - - mbedtls_aes_setkey_dec(&aes_ctx, key, 128); - mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, size, iv, src, buffer.data()); - - return buffer; -} - TMDReader::TMDReader(const std::vector& bytes) : m_bytes(bytes) { } @@ -264,7 +252,8 @@ std::vector TicketReader::GetTitleKey() const 0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7}; u8 iv[16] = {}; std::copy_n(&m_bytes[GetOffset() + offsetof(Ticket, title_id)], sizeof(Ticket::title_id), iv); - return AESDecode(common_key, iv, &m_bytes[GetOffset() + offsetof(Ticket, title_key)], 16); + return Common::AES::Decrypt(common_key, iv, &m_bytes[GetOffset() + offsetof(Ticket, title_key)], + 16); } } // namespace ES } // namespace IOS diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h index 64fd6c3a46..b6d2985cc2 100644 --- a/Source/Core/Core/IOS/ES/Formats.h +++ b/Source/Core/Core/IOS/ES/Formats.h @@ -101,8 +101,6 @@ struct Ticket static_assert(sizeof(Ticket) == 356, "Ticket has the wrong size"); #pragma pack(pop) -std::vector AESDecode(const u8* key, u8* iv, const u8* src, u32 size); - class TMDReader final { public: diff --git a/Source/Core/DiscIO/NANDContentLoader.cpp b/Source/Core/DiscIO/NANDContentLoader.cpp index 0344fe1c46..62e2e174cb 100644 --- a/Source/Core/DiscIO/NANDContentLoader.cpp +++ b/Source/Core/DiscIO/NANDContentLoader.cpp @@ -17,6 +17,7 @@ #include "Common/Align.h" #include "Common/CommonFuncs.h" #include "Common/CommonTypes.h" +#include "Common/Crypto/AES.h" #include "Common/FileUtil.h" #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" @@ -239,7 +240,7 @@ void CNANDContentLoader::InitializeContentEntries(const std::vector& data_ap u32 rounded_size = Common::AlignUp(static_cast(content.size), 0x40); - m_Content[i].m_Data = std::make_unique(IOS::ES::AESDecode( + m_Content[i].m_Data = std::make_unique(Common::AES::Decrypt( title_key.data(), iv.data(), &data_app[data_app_offset], rounded_size)); data_app_offset += rounded_size; } From 44a3db21e4b7fe20ee2ed6253990f5981b3f12f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Tue, 14 Feb 2017 13:15:02 +0100 Subject: [PATCH 4/4] ES: Make sure the TMD and ticket are valid before use --- Source/Core/Core/IOS/ES/ES.cpp | 12 ++++++------ Source/Core/DiscIO/NANDContentLoader.cpp | 5 +++++ Source/Core/DiscIO/NANDContentLoader.h | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp index a496837c63..57a430e7a3 100644 --- a/Source/Core/Core/IOS/ES/ES.cpp +++ b/Source/Core/Core/IOS/ES/ES.cpp @@ -186,7 +186,7 @@ u32 ES::OpenTitleContent(u32 CFD, u64 TitleID, u16 Index) { const DiscIO::CNANDContentLoader& Loader = AccessContentDevice(TitleID); - if (!Loader.IsValid()) + if (!Loader.IsValid() || !Loader.GetTicket().IsValid()) { WARN_LOG(IOS_ES, "ES: loader not valid for %" PRIx64, TitleID); return 0xffffffff; @@ -501,11 +501,11 @@ IPCCommandResult ES::GetTitleContents(const IOCtlVRequest& request) if (!rNANDContent.IsValid()) return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT); - for (const auto& content : rNANDContent.GetContent()) + for (const auto& content : rNANDContent.GetTMD().GetContents()) { - const u16 index = content.m_metadata.index; - Memory::Write_U32(content.m_metadata.id, request.io_vectors[0].address + index * 4); - INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLECONTENTS: Index %d: %08x", index, content.m_metadata.id); + const u16 index = content.index; + Memory::Write_U32(content.id, request.io_vectors[0].address + index * 4); + INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLECONTENTS: Index %d: %08x", index, content.id); } return GetDefaultReply(IPC_SUCCESS); @@ -568,7 +568,7 @@ IPCCommandResult ES::ReadContent(const IOCtlVRequest& request) { const DiscIO::CNANDContentLoader& ContentLoader = AccessContentDevice(rContent.m_TitleID); // ContentLoader should never be invalid; rContent has been created by it. - if (ContentLoader.IsValid()) + if (ContentLoader.IsValid() && ContentLoader.GetTicket().IsValid()) { const DiscIO::SNANDContent* pContent = ContentLoader.GetContentByIndex(rContent.m_Index); if (!pContent->m_Data->GetRange(rContent.m_Position, Size, pDest)) diff --git a/Source/Core/DiscIO/NANDContentLoader.cpp b/Source/Core/DiscIO/NANDContentLoader.cpp index 62e2e174cb..1204550fab 100644 --- a/Source/Core/DiscIO/NANDContentLoader.cpp +++ b/Source/Core/DiscIO/NANDContentLoader.cpp @@ -156,6 +156,11 @@ CNANDContentLoader::~CNANDContentLoader() { } +bool CNANDContentLoader::IsValid() const +{ + return m_Valid && m_tmd.IsValid(); +} + const SNANDContent* CNANDContentLoader::GetContentByIndex(int index) const { for (auto& Content : m_Content) diff --git a/Source/Core/DiscIO/NANDContentLoader.h b/Source/Core/DiscIO/NANDContentLoader.h index e945e7dcb5..ca9b8fe78d 100644 --- a/Source/Core/DiscIO/NANDContentLoader.h +++ b/Source/Core/DiscIO/NANDContentLoader.h @@ -77,7 +77,7 @@ public: explicit CNANDContentLoader(const std::string& content_name); ~CNANDContentLoader(); - bool IsValid() const { return m_Valid; } + bool IsValid() const; void RemoveTitle() const; const SNANDContent* GetContentByIndex(int index) const; const IOS::ES::TMDReader& GetTMD() const { return m_tmd; }