From df7749193835e7bb7b590bb2030dcf5069dc15d3 Mon Sep 17 00:00:00 2001 From: Weiyi Wang Date: Tue, 4 Sep 2018 12:28:38 -0400 Subject: [PATCH] FileSys/cia: add ticket parsing --- src/core/CMakeLists.txt | 3 ++ src/core/file_sys/cia_common.h | 40 +++++++++++++++++++ src/core/file_sys/cia_container.cpp | 9 +++++ src/core/file_sys/cia_container.h | 4 ++ src/core/file_sys/ticket.cpp | 56 +++++++++++++++++++++++++++ src/core/file_sys/ticket.h | 58 ++++++++++++++++++++++++++++ src/core/file_sys/title_metadata.cpp | 25 ++++-------- src/core/file_sys/title_metadata.h | 10 +---- 8 files changed, 178 insertions(+), 27 deletions(-) create mode 100644 src/core/file_sys/cia_common.h create mode 100644 src/core/file_sys/ticket.cpp create mode 100644 src/core/file_sys/ticket.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b1a14c782..1ce61ad40 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -50,6 +50,7 @@ add_library(core STATIC file_sys/archive_source_sd_savedata.h file_sys/archive_systemsavedata.cpp file_sys/archive_systemsavedata.h + file_sys/cia_common.h file_sys/cia_container.cpp file_sys/cia_container.h file_sys/directory_backend.h @@ -68,6 +69,8 @@ add_library(core STATIC file_sys/romfs_reader.h file_sys/savedata_archive.cpp file_sys/savedata_archive.h + file_sys/ticket.cpp + file_sys/ticket.h file_sys/title_metadata.cpp file_sys/title_metadata.h frontend/applets/default_applets.cpp diff --git a/src/core/file_sys/cia_common.h b/src/core/file_sys/cia_common.h new file mode 100644 index 000000000..900c82af6 --- /dev/null +++ b/src/core/file_sys/cia_common.h @@ -0,0 +1,40 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/assert.h" +#include "common/common_types.h" + +namespace FileSys { + +enum TMDSignatureType : u32 { + Rsa4096Sha1 = 0x10000, + Rsa2048Sha1 = 0x10001, + EllipticSha1 = 0x10002, + Rsa4096Sha256 = 0x10003, + Rsa2048Sha256 = 0x10004, + EcdsaSha256 = 0x10005 +}; + +inline u32 GetSignatureSize(u32 signature_type) { + switch (signature_type) { + case Rsa4096Sha1: + case Rsa4096Sha256: + return 0x200; + + case Rsa2048Sha1: + case Rsa2048Sha256: + return 0x100; + + case EllipticSha1: + case EcdsaSha256: + return 0x3C; + } + + UNREACHABLE(); + return 0; +} + +} // namespace FileSys diff --git a/src/core/file_sys/cia_container.cpp b/src/core/file_sys/cia_container.cpp index 84aee77c7..a53e04d4b 100644 --- a/src/core/file_sys/cia_container.cpp +++ b/src/core/file_sys/cia_container.cpp @@ -124,6 +124,11 @@ Loader::ResultStatus CIAContainer::LoadHeader(const std::vector& header_data return Loader::ResultStatus::Success; } +Loader::ResultStatus CIAContainer::LoadTicket(const std::vector& ticket_data, + std::size_t offset) { + return cia_ticket.Load(ticket_data, offset); +} + Loader::ResultStatus CIAContainer::LoadTitleMetadata(const std::vector& tmd_data, std::size_t offset) { return cia_tmd.Load(tmd_data, offset); @@ -139,6 +144,10 @@ Loader::ResultStatus CIAContainer::LoadMetadata(const std::vector& meta_data return Loader::ResultStatus::Success; } +const Ticket& CIAContainer::GetTicket() const { + return cia_ticket; +} + const TitleMetadata& CIAContainer::GetTitleMetadata() const { return cia_tmd; } diff --git a/src/core/file_sys/cia_container.h b/src/core/file_sys/cia_container.h index 9ebe0aa34..09bb1fcf5 100644 --- a/src/core/file_sys/cia_container.h +++ b/src/core/file_sys/cia_container.h @@ -10,6 +10,7 @@ #include #include "common/common_types.h" #include "common/swap.h" +#include "core/file_sys/ticket.h" #include "core/file_sys/title_metadata.h" namespace Loader { @@ -44,9 +45,11 @@ public: // Load parts of CIAs (for CIAs streamed in) Loader::ResultStatus LoadHeader(const std::vector& header_data, std::size_t offset = 0); + Loader::ResultStatus LoadTicket(const std::vector& ticket_data, std::size_t offset = 0); Loader::ResultStatus LoadTitleMetadata(const std::vector& tmd_data, std::size_t offset = 0); Loader::ResultStatus LoadMetadata(const std::vector& meta_data, std::size_t offset = 0); + const Ticket& GetTicket() const; const TitleMetadata& GetTitleMetadata() const; std::array& GetDependencies(); u32 GetCoreVersion() const; @@ -99,6 +102,7 @@ private: Header cia_header; Metadata cia_metadata; + Ticket cia_ticket; TitleMetadata cia_tmd; }; diff --git a/src/core/file_sys/ticket.cpp b/src/core/file_sys/ticket.cpp new file mode 100644 index 000000000..15a72116b --- /dev/null +++ b/src/core/file_sys/ticket.cpp @@ -0,0 +1,56 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include "common/alignment.h" +#include "core/file_sys/cia_common.h" +#include "core/file_sys/ticket.h" +#include "core/hw/aes/key.h" +#include "core/loader/loader.h" + +namespace FileSys { + +Loader::ResultStatus Ticket::Load(const std::vector file_data, std::size_t offset) { + std::size_t total_size = static_cast(file_data.size() - offset); + if (total_size < sizeof(u32)) + return Loader::ResultStatus::Error; + + std::memcpy(&signature_type, &file_data[offset], sizeof(u32)); + + // Signature lengths are variable, and the body follows the signature + u32 signature_size = GetSignatureSize(signature_type); + + // The ticket body start position is rounded to the nearest 0x40 after the signature + std::size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40); + std::size_t body_end = body_start + sizeof(Body); + + if (total_size < body_end) + return Loader::ResultStatus::Error; + + // Read signature + ticket body + ticket_signature.resize(signature_size); + memcpy(ticket_signature.data(), &file_data[offset + sizeof(u32)], signature_size); + memcpy(&ticket_body, &file_data[offset + body_start], sizeof(Body)); + + return Loader::ResultStatus::Success; +} + +boost::optional> Ticket::GetTitleKey() const { + HW::AES::InitKeys(); + std::array ctr{}; + std::memcpy(ctr.data(), &ticket_body.title_id, sizeof(u64)); + HW::AES::SelectCommonKeyIndex(ticket_body.common_key_index); + if (!HW::AES::IsNormalKeyAvailable(HW::AES::KeySlotID::TicketCommonKey)) { + return boost::none; + } + auto key = HW::AES::GetNormalKey(HW::AES::KeySlotID::TicketCommonKey); + auto title_key = ticket_body.title_key; + CryptoPP::CBC_Mode::Decryption{key.data(), key.size(), ctr.data()}.ProcessData( + title_key.data(), title_key.data(), title_key.size()); + return title_key; +} + +} // namespace FileSys diff --git a/src/core/file_sys/ticket.h b/src/core/file_sys/ticket.h new file mode 100644 index 000000000..f8700ae87 --- /dev/null +++ b/src/core/file_sys/ticket.h @@ -0,0 +1,58 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/swap.h" + +namespace Loader { +enum class ResultStatus; +} + +namespace FileSys { +class Ticket { +public: +#pragma pack(push, 1) + struct Body { + std::array issuer; + std::array ecc_public_key; + u8 version; + u8 ca_crl_version; + u8 signer_crl_version; + std::array title_key; + INSERT_PADDING_BYTES(1); + u64_be ticket_id; + u32_be console_id; + u64_be title_id; + INSERT_PADDING_BYTES(2); + u16_be ticket_title_version; + INSERT_PADDING_BYTES(8); + u8 license_type; + u8 common_key_index; + INSERT_PADDING_BYTES(0x2A); + u32_be eshop_account_id; + INSERT_PADDING_BYTES(1); + u8 audit; + INSERT_PADDING_BYTES(0x42); + std::array limits; + std::array content_index; + }; + static_assert(sizeof(Body) == 0x210, "Ticket body structure size is wrong"); +#pragma pack(pop) + + Loader::ResultStatus Load(const std::vector file_data, std::size_t offset = 0); + boost::optional> GetTitleKey() const; + +private: + Body ticket_body; + u32_be signature_type; + std::vector ticket_signature; +}; +} // namespace FileSys diff --git a/src/core/file_sys/title_metadata.cpp b/src/core/file_sys/title_metadata.cpp index 97f0a78bd..21d973328 100644 --- a/src/core/file_sys/title_metadata.cpp +++ b/src/core/file_sys/title_metadata.cpp @@ -7,6 +7,7 @@ #include "common/alignment.h" #include "common/file_util.h" #include "common/logging/log.h" +#include "core/file_sys/cia_common.h" #include "core/file_sys/title_metadata.h" #include "core/loader/loader.h" @@ -15,24 +16,6 @@ namespace FileSys { -static u32 GetSignatureSize(u32 signature_type) { - switch (signature_type) { - case Rsa4096Sha1: - case Rsa4096Sha256: - return 0x200; - - case Rsa2048Sha1: - case Rsa2048Sha256: - return 0x100; - - case EllipticSha1: - case EcdsaSha256: - return 0x3C; - } - - return 0; -} - Loader::ResultStatus TitleMetadata::Load(const std::string& file_path) { FileUtil::IOFile file(file_path, "rb"); if (!file.IsOpen()) @@ -188,6 +171,12 @@ u64 TitleMetadata::GetContentSizeByIndex(u16 index) const { return tmd_chunks[index].size; } +std::array TitleMetadata::GetContentCTRByIndex(u16 index) const { + std::array ctr{}; + std::memcpy(ctr.data(), &tmd_chunks[index].index, sizeof(u16)); + return ctr; +} + void TitleMetadata::SetTitleID(u64 title_id) { tmd_body.title_id = title_id; } diff --git a/src/core/file_sys/title_metadata.h b/src/core/file_sys/title_metadata.h index 6099c6a3a..a5c926ef1 100644 --- a/src/core/file_sys/title_metadata.h +++ b/src/core/file_sys/title_metadata.h @@ -19,15 +19,6 @@ enum class ResultStatus; namespace FileSys { -enum TMDSignatureType : u32 { - Rsa4096Sha1 = 0x10000, - Rsa2048Sha1 = 0x10001, - EllipticSha1 = 0x10002, - Rsa4096Sha256 = 0x10003, - Rsa2048Sha256 = 0x10004, - EcdsaSha256 = 0x10005 -}; - enum TMDContentTypeFlag : u16 { Encrypted = 1 << 0, Disc = 1 << 2, @@ -108,6 +99,7 @@ public: u32 GetContentIDByIndex(u16 index) const; u16 GetContentTypeByIndex(u16 index) const; u64 GetContentSizeByIndex(u16 index) const; + std::array GetContentCTRByIndex(u16 index) const; void SetTitleID(u64 title_id); void SetTitleType(u32 type);