From 2ec04675c9c440010cb3bb5b8c60079a99255954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 11 Jun 2017 13:11:09 +0200 Subject: [PATCH 1/6] IOS/ESFormats: Add SignedBlobReader Allows common signature parsing code to not be duplicated. --- Source/Core/Core/IOS/ES/Formats.cpp | 100 ++++++++++++++++++++++++++++ Source/Core/Core/IOS/ES/Formats.h | 27 ++++++++ 2 files changed, 127 insertions(+) diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index 158eebd7f0..e68d28109f 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -59,6 +59,106 @@ bool Content::IsShared() const return (type & 0x8000) != 0; } +SignedBlobReader::SignedBlobReader(const std::vector& bytes) : m_bytes(bytes) +{ +} + +SignedBlobReader::SignedBlobReader(std::vector&& bytes) : m_bytes(std::move(bytes)) +{ +} + +const std::vector& SignedBlobReader::GetBytes() const +{ + return m_bytes; +} + +void SignedBlobReader::SetBytes(const std::vector& bytes) +{ + m_bytes = bytes; +} + +void SignedBlobReader::SetBytes(std::vector&& bytes) +{ + m_bytes = std::move(bytes); +} + +bool SignedBlobReader::IsSignatureValid() const +{ + // Too small for the certificate type. + if (m_bytes.size() < sizeof(Cert::type)) + return false; + + // Too small to contain the whole signature data. + const size_t signature_size = GetSignatureSize(); + if (signature_size == 0 || m_bytes.size() < signature_size) + return false; + + return true; +} + +SignatureType SignedBlobReader::GetSignatureType() const +{ + return static_cast(Common::swap32(m_bytes.data())); +} + +std::vector SignedBlobReader::GetSignatureData() const +{ + switch (GetSignatureType()) + { + case SignatureType::RSA4096: + { + const auto signature_begin = m_bytes.begin() + offsetof(SignatureRSA4096, sig); + return std::vector(signature_begin, signature_begin + sizeof(SignatureRSA4096::sig)); + } + case SignatureType::RSA2048: + { + const auto signature_begin = m_bytes.begin() + offsetof(SignatureRSA2048, sig); + return std::vector(signature_begin, signature_begin + sizeof(SignatureRSA2048::sig)); + } + default: + return {}; + } +} + +size_t SignedBlobReader::GetSignatureSize() const +{ + switch (GetSignatureType()) + { + case SignatureType::RSA4096: + return sizeof(SignatureRSA4096); + case SignatureType::RSA2048: + return sizeof(SignatureRSA2048); + default: + return 0; + } +} + +std::string SignedBlobReader::GetIssuer() const +{ + switch (GetSignatureType()) + { + case SignatureType::RSA4096: + { + const char* issuer = + reinterpret_cast(m_bytes.data() + offsetof(SignatureRSA4096, issuer)); + return std::string(issuer, strnlen(issuer, sizeof(SignatureRSA4096::issuer))); + } + case SignatureType::RSA2048: + { + const char* issuer = + reinterpret_cast(m_bytes.data() + offsetof(SignatureRSA2048, issuer)); + return std::string(issuer, strnlen(issuer, sizeof(SignatureRSA2048::issuer))); + } + default: + return ""; + } +} + +void SignedBlobReader::DoState(PointerWrap& p) +{ + p.Do(m_bytes); +} + bool IsValidTMDSize(size_t size) { return size <= 0x49e4; diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h index 178fc643fa..83666f726c 100644 --- a/Source/Core/Core/IOS/ES/Formats.h +++ b/Source/Core/Core/IOS/ES/Formats.h @@ -140,6 +140,33 @@ struct Ticket static_assert(sizeof(Ticket) == 0x2A4, "Ticket has the wrong size"); #pragma pack(pop) +class SignedBlobReader +{ +public: + SignedBlobReader() = default; + explicit SignedBlobReader(const std::vector& bytes); + explicit SignedBlobReader(std::vector&& bytes); + + const std::vector& GetBytes() const; + void SetBytes(const std::vector& bytes); + void SetBytes(std::vector&& bytes); + + // Only checks whether the signature data could be parsed. The signature is not verified. + bool IsSignatureValid() const; + + SignatureType GetSignatureType() const; + std::vector GetSignatureData() const; + size_t GetSignatureSize() const; + // Returns the whole issuer chain. + // Example: Root-CA00000001 if the blob was signed by CA00000001, which is signed by the Root. + std::string GetIssuer() const; + + void DoState(PointerWrap& p); + +protected: + std::vector m_bytes; +}; + bool IsValidTMDSize(size_t size); class TMDReader final From 6c3069be970965d12bed927181e64068d6b96c3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 11 Jun 2017 13:24:45 +0200 Subject: [PATCH 2/6] IOS/ESFormats: Use SignedBlobReader for TMDs and tickets --- Source/Core/Core/IOS/DI/DI.cpp | 2 +- Source/Core/Core/IOS/ES/ES.cpp | 2 +- Source/Core/Core/IOS/ES/Formats.cpp | 60 +++----------------- Source/Core/Core/IOS/ES/Formats.h | 24 +------- Source/Core/Core/IOS/ES/NandUtils.cpp | 2 +- Source/Core/Core/IOS/ES/TitleInformation.cpp | 4 +- Source/Core/Core/IOS/ES/TitleManagement.cpp | 6 +- Source/Core/Core/IOS/ES/Views.cpp | 4 +- Source/Core/UICommon/WiiUtils.cpp | 4 +- Source/UnitTests/Core/IOS/ES/FormatsTest.cpp | 2 +- 10 files changed, 24 insertions(+), 86 deletions(-) diff --git a/Source/Core/Core/IOS/DI/DI.cpp b/Source/Core/Core/IOS/DI/DI.cpp index 0df9861054..4e3f258603 100644 --- a/Source/Core/Core/IOS/DI/DI.cpp +++ b/Source/Core/Core/IOS/DI/DI.cpp @@ -110,7 +110,7 @@ IPCCommandResult DI::IOCtlV(const IOCtlVRequest& request) // Read TMD to the buffer const IOS::ES::TMDReader tmd = DVDThread::GetTMD(partition); - const std::vector raw_tmd = tmd.GetRawTMD(); + const std::vector& raw_tmd = tmd.GetBytes(); Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size()); ES::DIVerify(tmd, DVDThread::GetTicket(partition)); diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp index 8dbe7337ab..629f093f23 100644 --- a/Source/Core/Core/IOS/ES/ES.cpp +++ b/Source/Core/Core/IOS/ES/ES.cpp @@ -610,7 +610,7 @@ s32 ES::DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& tic if (!File::Exists(tmd_path)) { File::IOFile tmd_file(tmd_path, "wb"); - const std::vector& tmd_bytes = tmd.GetRawTMD(); + const std::vector& tmd_bytes = tmd.GetBytes(); if (!tmd_file.WriteBytes(tmd_bytes.data(), tmd_bytes.size())) ERROR_LOG(IOS_ES, "DIVerify failed to write disc TMD to NAND."); } diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index e68d28109f..0fcfa033d3 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -164,26 +164,19 @@ bool IsValidTMDSize(size_t size) return size <= 0x49e4; } -TMDReader::TMDReader(const std::vector& bytes) : m_bytes(bytes) +TMDReader::TMDReader(const std::vector& bytes) : SignedBlobReader(bytes) { } -TMDReader::TMDReader(std::vector&& bytes) : m_bytes(std::move(bytes)) +TMDReader::TMDReader(std::vector&& bytes) : SignedBlobReader(std::move(bytes)) { } -void TMDReader::SetBytes(const std::vector& bytes) -{ - m_bytes = bytes; -} - -void TMDReader::SetBytes(std::vector&& bytes) -{ - m_bytes = std::move(bytes); -} - bool TMDReader::IsValid() const { + if (!IsSignatureValid()) + return false; + if (m_bytes.size() < sizeof(TMDHeader)) { // TMD is too small to contain its base fields. @@ -199,11 +192,6 @@ 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)); @@ -331,37 +319,17 @@ bool TMDReader::FindContentById(u32 id, Content* content) const return false; } -void TMDReader::DoState(PointerWrap& p) -{ - p.Do(m_bytes); -} - -TicketReader::TicketReader(const std::vector& bytes) : m_bytes(bytes) +TicketReader::TicketReader(const std::vector& bytes) : SignedBlobReader(bytes) { } -TicketReader::TicketReader(std::vector&& bytes) : m_bytes(std::move(bytes)) +TicketReader::TicketReader(std::vector&& bytes) : SignedBlobReader(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 { - return !m_bytes.empty() && m_bytes.size() % sizeof(Ticket) == 0; -} - -void TicketReader::DoState(PointerWrap& p) -{ - p.Do(m_bytes); + return IsSignatureValid() && !m_bytes.empty() && m_bytes.size() % sizeof(Ticket) == 0; } size_t TicketReader::GetNumberOfTickets() const @@ -369,11 +337,6 @@ size_t TicketReader::GetNumberOfTickets() const return m_bytes.size() / sizeof(Ticket); } -const std::vector& TicketReader::GetRawTicket() const -{ - return m_bytes; -} - std::vector TicketReader::GetRawTicket(u64 ticket_id_to_find) const { for (size_t i = 0; i < GetNumberOfTickets(); ++i) @@ -404,13 +367,6 @@ std::vector TicketReader::GetRawTicketView(u32 ticket_num) const return view; } -std::string TicketReader::GetIssuer() const -{ - const char* bytes = - reinterpret_cast(m_bytes.data() + offsetof(Ticket, signature.issuer)); - return std::string(bytes, strnlen(bytes, sizeof(Ticket::signature.issuer))); -} - u32 TicketReader::GetDeviceId() const { return Common::swap32(m_bytes.data() + offsetof(Ticket, device_id)); diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h index 83666f726c..dd3c0aad9c 100644 --- a/Source/Core/Core/IOS/ES/Formats.h +++ b/Source/Core/Core/IOS/ES/Formats.h @@ -169,20 +169,16 @@ protected: bool IsValidTMDSize(size_t size); -class TMDReader final +class TMDReader final : public SignedBlobReader { 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; + // Returns parts of the TMD without any kind of parsing. Intended for use by ES. std::vector GetRawHeader() const; std::vector GetRawView() const; @@ -203,27 +199,17 @@ public: 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 +class TicketReader final : public SignedBlobReader { 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; - void DoState(PointerWrap& p); - const std::vector& GetRawTicket() const; std::vector GetRawTicket(u64 ticket_id) const; size_t GetNumberOfTickets() const; @@ -233,7 +219,6 @@ public: // more than just one ticket and generate ticket views for them, so we implement it too. std::vector GetRawTicketView(u32 ticket_num) const; - std::string GetIssuer() const; u32 GetDeviceId() const; u64 GetTitleId() const; std::vector GetTitleKey() const; @@ -244,9 +229,6 @@ public: // Decrypts the title key field for a "personalised" ticket -- one that is device-specific // and has a title key that must be decrypted first. s32 Unpersonalise(); - -private: - std::vector m_bytes; }; class SharedContentMap final diff --git a/Source/Core/Core/IOS/ES/NandUtils.cpp b/Source/Core/Core/IOS/ES/NandUtils.cpp index d0fb59745d..7f5b3a026d 100644 --- a/Source/Core/Core/IOS/ES/NandUtils.cpp +++ b/Source/Core/Core/IOS/ES/NandUtils.cpp @@ -254,7 +254,7 @@ bool ES::WriteImportTMD(const IOS::ES::TMDReader& tmd) { File::IOFile file(tmd_path, "wb"); - if (!file.WriteBytes(tmd.GetRawTMD().data(), tmd.GetRawTMD().size())) + if (!file.WriteBytes(tmd.GetBytes().data(), tmd.GetBytes().size())) return false; } diff --git a/Source/Core/Core/IOS/ES/TitleInformation.cpp b/Source/Core/Core/IOS/ES/TitleInformation.cpp index 51c2dabc18..583c9d5aca 100644 --- a/Source/Core/Core/IOS/ES/TitleInformation.cpp +++ b/Source/Core/Core/IOS/ES/TitleInformation.cpp @@ -150,7 +150,7 @@ IPCCommandResult ES::GetStoredTMDSize(const IOCtlVRequest& request) if (!tmd.IsValid()) return GetDefaultReply(FS_ENOENT); - const u32 tmd_size = static_cast(tmd.GetRawTMD().size()); + const u32 tmd_size = static_cast(tmd.GetBytes().size()); Memory::Write_U32(tmd_size, request.io_vectors[0].address); INFO_LOG(IOS_ES, "GetStoredTMDSize: %u bytes for %016" PRIx64, tmd_size, title_id); @@ -171,7 +171,7 @@ IPCCommandResult ES::GetStoredTMD(const IOCtlVRequest& request) // TODO: actually use this param in when writing to the outbuffer :/ const u32 MaxCount = Memory::Read_U32(request.in_vectors[1].address); - const std::vector raw_tmd = tmd.GetRawTMD(); + const std::vector& raw_tmd = tmd.GetBytes(); if (raw_tmd.size() != request.io_vectors[0].size) return GetDefaultReply(ES_EINVAL); diff --git a/Source/Core/Core/IOS/ES/TitleManagement.cpp b/Source/Core/Core/IOS/ES/TitleManagement.cpp index 01d0627a89..ca81b76333 100644 --- a/Source/Core/Core/IOS/ES/TitleManagement.cpp +++ b/Source/Core/Core/IOS/ES/TitleManagement.cpp @@ -40,7 +40,7 @@ static ReturnCode WriteTicket(const IOS::ES::TicketReader& ticket) if (!ticket_file) return ES_EIO; - const std::vector& raw_ticket = ticket.GetRawTicket(); + const std::vector& raw_ticket = ticket.GetBytes(); return ticket_file.WriteBytes(raw_ticket.data(), raw_ticket.size()) ? IPC_SUCCESS : ES_EIO; } @@ -394,7 +394,7 @@ ReturnCode ES::DeleteTicket(const u8* ticket_view) const u64 ticket_id = Common::swap64(ticket_view + offsetof(IOS::ES::TicketView, ticket_id)); ticket.DeleteTicket(ticket_id); - const std::vector& new_ticket = ticket.GetRawTicket(); + const std::vector& new_ticket = ticket.GetBytes(); const std::string ticket_path = Common::GetTicketFileName(title_id, Common::FROM_SESSION_ROOT); { File::IOFile ticket_file(ticket_path, "wb"); @@ -505,7 +505,7 @@ ReturnCode ES::ExportTitleInit(Context& context, u64 title_id, u8* tmd_bytes, u3 context.title_export.title_key = ticket.GetTitleKey(); - const auto& raw_tmd = context.title_export.tmd.GetRawTMD(); + const std::vector& raw_tmd = context.title_export.tmd.GetBytes(); if (tmd_size != raw_tmd.size()) return ES_EINVAL; diff --git a/Source/Core/Core/IOS/ES/Views.cpp b/Source/Core/Core/IOS/ES/Views.cpp index ead7a5b7e4..5e16aeac34 100644 --- a/Source/Core/Core/IOS/ES/Views.cpp +++ b/Source/Core/Core/IOS/ES/Views.cpp @@ -376,7 +376,7 @@ IPCCommandResult ES::DIGetTMDSize(const IOCtlVRequest& request) if (!GetTitleContext().active) return GetDefaultReply(ES_EINVAL); - Memory::Write_U32(static_cast(GetTitleContext().tmd.GetRawTMD().size()), + Memory::Write_U32(static_cast(GetTitleContext().tmd.GetBytes().size()), request.io_vectors[0].address); return GetDefaultReply(IPC_SUCCESS); } @@ -393,7 +393,7 @@ IPCCommandResult ES::DIGetTMD(const IOCtlVRequest& request) if (!GetTitleContext().active) return GetDefaultReply(ES_EINVAL); - const std::vector& tmd_bytes = GetTitleContext().tmd.GetRawTMD(); + const std::vector& tmd_bytes = GetTitleContext().tmd.GetBytes(); if (static_cast(tmd_bytes.size()) > tmd_size) return GetDefaultReply(ES_EINVAL); diff --git a/Source/Core/UICommon/WiiUtils.cpp b/Source/Core/UICommon/WiiUtils.cpp index e7b3834900..acd4713a33 100644 --- a/Source/Core/UICommon/WiiUtils.cpp +++ b/Source/Core/UICommon/WiiUtils.cpp @@ -27,8 +27,8 @@ bool InstallWAD(const std::string& wad_path) const auto es = ios.GetES(); IOS::HLE::Device::ES::Context context; - if (es->ImportTicket(wad.GetTicket().GetRawTicket()) < 0 || - es->ImportTitleInit(context, tmd.GetRawTMD()) < 0) + if (es->ImportTicket(wad.GetTicket().GetBytes()) < 0 || + es->ImportTitleInit(context, tmd.GetBytes()) < 0) { PanicAlertT("WAD installation failed: Could not initialise title import."); return false; diff --git a/Source/UnitTests/Core/IOS/ES/FormatsTest.cpp b/Source/UnitTests/Core/IOS/ES/FormatsTest.cpp index b249fe8419..4f76bbd51e 100644 --- a/Source/UnitTests/Core/IOS/ES/FormatsTest.cpp +++ b/Source/UnitTests/Core/IOS/ES/FormatsTest.cpp @@ -76,7 +76,7 @@ void TMDReaderTest::TestGeneralInfo() void TMDReaderTest::TestRawTMDAndView() { - const std::vector& dolphin_tmd_bytes = m_tmd.GetRawTMD(); + const std::vector& dolphin_tmd_bytes = m_tmd.GetBytes(); // Separate check because gtest prints neither the size nor the full buffer. EXPECT_EQ(m_raw_tmd.size(), dolphin_tmd_bytes.size()); EXPECT_EQ(m_raw_tmd, dolphin_tmd_bytes); From 90280b3f84057e23195445274c713deee0688860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 11 Jun 2017 13:37:13 +0200 Subject: [PATCH 3/6] IOS/ESFormats: Remove unused TMDReader function The ES function that used to make use of it was changed to be more sane, so GetRawHeader() is now unused and useless. --- Source/Core/Core/IOS/ES/Formats.cpp | 5 ----- Source/Core/Core/IOS/ES/Formats.h | 1 - 2 files changed, 6 deletions(-) diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index 0fcfa033d3..e33b339089 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -192,11 +192,6 @@ bool TMDReader::IsValid() const return true; } -std::vector TMDReader::GetRawHeader() const -{ - return std::vector(m_bytes.begin(), m_bytes.begin() + sizeof(TMDHeader)); -} - std::vector TMDReader::GetRawView() const { // Base fields diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h index dd3c0aad9c..a3adbf7493 100644 --- a/Source/Core/Core/IOS/ES/Formats.h +++ b/Source/Core/Core/IOS/ES/Formats.h @@ -179,7 +179,6 @@ public: bool IsValid() const; // Returns parts of the TMD without any kind of parsing. Intended for use by ES. - std::vector GetRawHeader() const; std::vector GetRawView() const; u16 GetBootIndex() const; From e29f6e383fb95d32710ef22d99e771428c8994a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 10 Jun 2017 19:10:55 +0200 Subject: [PATCH 4/6] IOS/ESFormats: Add CertReader --- Source/Core/Core/IOS/ES/Formats.cpp | 67 +++++++++++++++++++++++++++++ Source/Core/Core/IOS/ES/Formats.h | 20 +++++++++ 2 files changed, 87 insertions(+) diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index e33b339089..377d36eb28 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -614,5 +615,71 @@ u32 UIDSys::GetOrInsertUIDForTitle(const u64 title_id) return uid; } + +CertReader::CertReader(std::vector&& bytes) : SignedBlobReader(std::move(bytes)) +{ + if (!IsSignatureValid()) + return; + + switch (GetSignatureType()) + { + case SignatureType::RSA4096: + if (m_bytes.size() < sizeof(CertRSA4096)) + return; + m_bytes.resize(sizeof(CertRSA4096)); + break; + + case SignatureType::RSA2048: + if (m_bytes.size() < sizeof(CertRSA2048)) + return; + m_bytes.resize(sizeof(CertRSA2048)); + break; + + default: + return; + } + + m_is_valid = true; +} + +bool CertReader::IsValid() const +{ + return m_is_valid; +} + +u32 CertReader::GetId() const +{ + const size_t offset = GetSignatureSize() + offsetof(CertHeader, id); + return Common::swap32(m_bytes.data() + offset); +} + +std::string CertReader::GetName() const +{ + const char* name = reinterpret_cast(m_bytes.data() + GetSignatureSize() + + offsetof(CertHeader, name)); + return std::string(name, strnlen(name, sizeof(CertHeader::name))); +} + +PublicKeyType CertReader::GetPublicKeyType() const +{ + const size_t offset = GetSignatureSize() + offsetof(CertHeader, public_key_type); + return static_cast(Common::swap32(m_bytes.data() + offset)); +} + +std::vector CertReader::GetPublicKey() const +{ + static const std::map> type_to_key_info = {{ + {SignatureType::RSA4096, + {offsetof(CertRSA4096, public_key), + sizeof(CertRSA4096::public_key) + sizeof(CertRSA4096::exponent)}}, + {SignatureType::RSA2048, + {offsetof(CertRSA2048, public_key), + sizeof(CertRSA2048::public_key) + sizeof(CertRSA2048::exponent)}}, + }}; + + const auto info = type_to_key_info.at(GetSignatureType()); + const auto key_begin = m_bytes.begin() + info.first; + return std::vector(key_begin, key_begin + info.second); +} } // namespace ES } // namespace IOS diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h index a3adbf7493..3606b12f49 100644 --- a/Source/Core/Core/IOS/ES/Formats.h +++ b/Source/Core/Core/IOS/ES/Formats.h @@ -16,6 +16,7 @@ #include "Common/CommonTypes.h" #include "Common/NandPaths.h" +#include "Core/IOS/Device.h" #include "Core/IOS/IOSC.h" #include "DiscIO/Enums.h" @@ -264,5 +265,24 @@ private: std::string m_file_path; std::map m_entries; }; + +class CertReader final : public SignedBlobReader +{ +public: + explicit CertReader(std::vector&& bytes); + + bool IsValid() const; + + u32 GetId() const; + // Returns the certificate name. Examples: XS00000003, CA00000001 + std::string GetName() const; + PublicKeyType GetPublicKeyType() const; + // Returns the public key bytes + any other data associated with it. + // For RSA public keys, this includes 4 bytes for the exponent at the end. + std::vector GetPublicKey() const; + +private: + bool m_is_valid = false; +}; } // namespace ES } // namespace IOS From 88652085717f161e94c5bb9828fe23cddfdfbd22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 11 Jun 2017 14:09:57 +0200 Subject: [PATCH 5/6] IOS/ESFormats: Add ParseCertChain This will be used to avoid duplicating cert chain parsing code. --- Source/Core/Core/IOS/ES/Formats.cpp | 18 ++++++++++++++++++ Source/Core/Core/IOS/ES/Formats.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index 377d36eb28..f66e61c07b 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -681,5 +681,23 @@ std::vector CertReader::GetPublicKey() const const auto key_begin = m_bytes.begin() + info.first; return std::vector(key_begin, key_begin + info.second); } + +std::map ParseCertChain(const std::vector& chain) +{ + std::map certs; + + size_t processed = 0; + while (processed != chain.size()) + { + CertReader cert_reader{std::vector(chain.begin() + processed, chain.end())}; + if (!cert_reader.IsValid()) + return certs; + + processed += cert_reader.GetBytes().size(); + const std::string name = cert_reader.GetName(); + certs.emplace(std::move(name), std::move(cert_reader)); + } + return certs; +} } // namespace ES } // namespace IOS diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h index 3606b12f49..943f93d8da 100644 --- a/Source/Core/Core/IOS/ES/Formats.h +++ b/Source/Core/Core/IOS/ES/Formats.h @@ -284,5 +284,7 @@ public: private: bool m_is_valid = false; }; + +std::map ParseCertChain(const std::vector& chain); } // namespace ES } // namespace IOS From a370ad5f6d43ea63496f05e081603ce4300261e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Wed, 14 Jun 2017 23:13:46 +0200 Subject: [PATCH 6/6] IOS/ESFormats: Fix content map writing On Windows, this would fail because the file handle wasn't closed. --- Source/Core/Core/IOS/ES/Formats.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index f66e61c07b..3de8deb312 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -533,10 +533,12 @@ bool SharedContentMap::WriteEntries() const File::CreateFullPath(temp_path); // Atomically write the new content map. - File::IOFile file(temp_path, "w+b"); - if (!file.WriteArray(m_entries.data(), m_entries.size())) - return false; - File::CreateFullPath(m_file_path); + { + File::IOFile file(temp_path, "w+b"); + if (!file.WriteArray(m_entries.data(), m_entries.size())) + return false; + File::CreateFullPath(m_file_path); + } return File::RenameSync(temp_path, m_file_path); }