From 9668852c0d7bfd2949385313881e51a76e173f21 Mon Sep 17 00:00:00 2001 From: Weiyi Wang Date: Tue, 4 Sep 2018 12:29:59 -0400 Subject: [PATCH] Service/AM: handle encrypted CIA --- src/core/hle/service/am/am.cpp | 65 +++++++++++++++++++++++++++++----- src/core/hle/service/am/am.h | 13 ++++--- 2 files changed, 64 insertions(+), 14 deletions(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 984f5fb83..7a2462632 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include "common/file_util.h" #include "common/logging/log.h" @@ -74,13 +76,31 @@ struct TicketInfo { static_assert(sizeof(TicketInfo) == 0x18, "Ticket info structure size is wrong"); +class CIAFile::DecryptionState { +public: + std::vector::Decryption> content; +}; + +CIAFile::CIAFile(Service::FS::MediaType media_type) + : media_type(media_type), decryption_state(std::make_unique()) {} + +CIAFile::~CIAFile() { + Close(); +} + ResultVal CIAFile::Read(u64 offset, std::size_t length, u8* buffer) const { UNIMPLEMENTED(); return MakeResult(length); } -ResultVal CIAFile::WriteTitleMetadata(u64 offset, std::size_t length, - const u8* buffer) { +ResultCode CIAFile::WriteTicket() { + container.LoadTicket(data, container.GetTicketOffset()); + + install_state = CIAInstallState::TicketLoaded; + return RESULT_SUCCESS; +} + +ResultCode CIAFile::WriteTitleMetadata() { container.LoadTitleMetadata(data, container.GetTitleMetadataOffset()); FileSys::TitleMetadata tmd = container.GetTitleMetadata(); tmd.Print(); @@ -109,10 +129,22 @@ ResultVal CIAFile::WriteTitleMetadata(u64 offset, std::size_t lengt &app_folder, nullptr, nullptr); FileUtil::CreateFullPath(app_folder); - content_written.resize(container.GetTitleMetadata().GetContentCount()); + auto content_count = container.GetTitleMetadata().GetContentCount(); + content_written.resize(content_count); + + auto title_key = container.GetTicket().GetTitleKey(); + if (title_key) { + decryption_state->content.resize(content_count); + for (std::size_t i = 0; i < content_count; ++i) { + auto ctr = tmd.GetContentCTRByIndex(i); + decryption_state->content[i].SetKeyWithIV(title_key->data(), title_key->size(), + ctr.data()); + } + } + install_state = CIAInstallState::TMDLoaded; - return MakeResult(length); + return RESULT_SUCCESS; } ResultVal CIAFile::WriteContentData(u64 offset, std::size_t length, const u8* buffer) { @@ -144,7 +176,15 @@ ResultVal CIAFile::WriteContentData(u64 offset, std::size_t length, if (!file.IsOpen()) return FileSys::ERROR_INSUFFICIENT_SPACE; - file.WriteBytes(buffer + (range_min - offset), available_to_write); + std::vector temp(buffer + (range_min - offset), + buffer + (range_min - offset) + available_to_write); + + if (tmd.GetContentTypeByIndex(static_cast(i)) & + FileSys::TMDContentTypeFlag::Encrypted) { + decryption_state->content[i].ProcessData(temp.data(), temp.data(), temp.size()); + } + + file.WriteBytes(temp.data(), temp.size()); // Keep tabs on how much of this content ID has been written so new range_min // values can be calculated. @@ -207,8 +247,12 @@ ResultVal CIAFile::Write(u64 offset, std::size_t length, bool flush // The end of our TMD is at the beginning of Content data, so ensure we have that much // buffered before trying to parse. if (written >= container.GetContentOffset() && install_state != CIAInstallState::TMDLoaded) { - auto result = WriteTitleMetadata(offset, length, buffer); - if (result.Failed()) + auto result = WriteTicket(); + if (result.IsError()) + return result; + + result = WriteTitleMetadata(); + if (result.IsError()) return result; } @@ -296,9 +340,12 @@ InstallStatus InstallCIA(const std::string& path, Service::AM::CIAFile installFile( Service::AM::GetTitleMediaType(container.GetTitleMetadata().GetTitleID())); + bool title_key_available = container.GetTicket().GetTitleKey().is_initialized(); + for (std::size_t i = 0; i < container.GetTitleMetadata().GetContentCount(); i++) { - if (container.GetTitleMetadata().GetContentTypeByIndex(static_cast(i)) & - FileSys::TMDContentTypeFlag::Encrypted) { + if ((container.GetTitleMetadata().GetContentTypeByIndex(static_cast(i)) & + FileSys::TMDContentTypeFlag::Encrypted) && + !title_key_available) { LOG_ERROR(Service_AM, "File {} is encrypted! Aborting...", path); return InstallStatus::ErrorEncrypted; } diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 6729a6b36..7d12b72fc 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include "common/common_types.h" @@ -61,13 +62,12 @@ using ProgressCallback = void(std::size_t, std::size_t); // A file handled returned for CIAs to be written into and subsequently installed. class CIAFile final : public FileSys::FileBackend { public: - explicit CIAFile(Service::FS::MediaType media_type) : media_type(media_type) {} - ~CIAFile() { - Close(); - } + explicit CIAFile(Service::FS::MediaType media_type); + ~CIAFile(); ResultVal Read(u64 offset, std::size_t length, u8* buffer) const override; - ResultVal WriteTitleMetadata(u64 offset, std::size_t length, const u8* buffer); + ResultCode WriteTicket(); + ResultCode WriteTitleMetadata(); ResultVal WriteContentData(u64 offset, std::size_t length, const u8* buffer); ResultVal Write(u64 offset, std::size_t length, bool flush, const u8* buffer) override; @@ -89,6 +89,9 @@ private: std::vector data; std::vector content_written; Service::FS::MediaType media_type; + + class DecryptionState; + std::unique_ptr decryption_state; }; /**