Service/AM: handle encrypted CIA

This commit is contained in:
Weiyi Wang 2018-09-04 12:29:59 -04:00
parent df77491938
commit 9668852c0d
2 changed files with 64 additions and 14 deletions

View file

@ -6,6 +6,8 @@
#include <cinttypes> #include <cinttypes>
#include <cstddef> #include <cstddef>
#include <cstring> #include <cstring>
#include <cryptopp/aes.h>
#include <cryptopp/modes.h>
#include <fmt/format.h> #include <fmt/format.h>
#include "common/file_util.h" #include "common/file_util.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -74,13 +76,31 @@ struct TicketInfo {
static_assert(sizeof(TicketInfo) == 0x18, "Ticket info structure size is wrong"); static_assert(sizeof(TicketInfo) == 0x18, "Ticket info structure size is wrong");
class CIAFile::DecryptionState {
public:
std::vector<CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption> content;
};
CIAFile::CIAFile(Service::FS::MediaType media_type)
: media_type(media_type), decryption_state(std::make_unique<DecryptionState>()) {}
CIAFile::~CIAFile() {
Close();
}
ResultVal<std::size_t> CIAFile::Read(u64 offset, std::size_t length, u8* buffer) const { ResultVal<std::size_t> CIAFile::Read(u64 offset, std::size_t length, u8* buffer) const {
UNIMPLEMENTED(); UNIMPLEMENTED();
return MakeResult<std::size_t>(length); return MakeResult<std::size_t>(length);
} }
ResultVal<std::size_t> CIAFile::WriteTitleMetadata(u64 offset, std::size_t length, ResultCode CIAFile::WriteTicket() {
const u8* buffer) { container.LoadTicket(data, container.GetTicketOffset());
install_state = CIAInstallState::TicketLoaded;
return RESULT_SUCCESS;
}
ResultCode CIAFile::WriteTitleMetadata() {
container.LoadTitleMetadata(data, container.GetTitleMetadataOffset()); container.LoadTitleMetadata(data, container.GetTitleMetadataOffset());
FileSys::TitleMetadata tmd = container.GetTitleMetadata(); FileSys::TitleMetadata tmd = container.GetTitleMetadata();
tmd.Print(); tmd.Print();
@ -109,10 +129,22 @@ ResultVal<std::size_t> CIAFile::WriteTitleMetadata(u64 offset, std::size_t lengt
&app_folder, nullptr, nullptr); &app_folder, nullptr, nullptr);
FileUtil::CreateFullPath(app_folder); 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; install_state = CIAInstallState::TMDLoaded;
return MakeResult<std::size_t>(length); return RESULT_SUCCESS;
} }
ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length, const u8* buffer) { ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length, const u8* buffer) {
@ -144,7 +176,15 @@ ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length,
if (!file.IsOpen()) if (!file.IsOpen())
return FileSys::ERROR_INSUFFICIENT_SPACE; return FileSys::ERROR_INSUFFICIENT_SPACE;
file.WriteBytes(buffer + (range_min - offset), available_to_write); std::vector<u8> temp(buffer + (range_min - offset),
buffer + (range_min - offset) + available_to_write);
if (tmd.GetContentTypeByIndex(static_cast<u16>(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 // Keep tabs on how much of this content ID has been written so new range_min
// values can be calculated. // values can be calculated.
@ -207,8 +247,12 @@ ResultVal<std::size_t> 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 // The end of our TMD is at the beginning of Content data, so ensure we have that much
// buffered before trying to parse. // buffered before trying to parse.
if (written >= container.GetContentOffset() && install_state != CIAInstallState::TMDLoaded) { if (written >= container.GetContentOffset() && install_state != CIAInstallState::TMDLoaded) {
auto result = WriteTitleMetadata(offset, length, buffer); auto result = WriteTicket();
if (result.Failed()) if (result.IsError())
return result;
result = WriteTitleMetadata();
if (result.IsError())
return result; return result;
} }
@ -296,9 +340,12 @@ InstallStatus InstallCIA(const std::string& path,
Service::AM::CIAFile installFile( Service::AM::CIAFile installFile(
Service::AM::GetTitleMediaType(container.GetTitleMetadata().GetTitleID())); 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++) { for (std::size_t i = 0; i < container.GetTitleMetadata().GetContentCount(); i++) {
if (container.GetTitleMetadata().GetContentTypeByIndex(static_cast<u16>(i)) & if ((container.GetTitleMetadata().GetContentTypeByIndex(static_cast<u16>(i)) &
FileSys::TMDContentTypeFlag::Encrypted) { FileSys::TMDContentTypeFlag::Encrypted) &&
!title_key_available) {
LOG_ERROR(Service_AM, "File {} is encrypted! Aborting...", path); LOG_ERROR(Service_AM, "File {} is encrypted! Aborting...", path);
return InstallStatus::ErrorEncrypted; return InstallStatus::ErrorEncrypted;
} }

View file

@ -6,6 +6,7 @@
#include <array> #include <array>
#include <functional> #include <functional>
#include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include "common/common_types.h" #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. // A file handled returned for CIAs to be written into and subsequently installed.
class CIAFile final : public FileSys::FileBackend { class CIAFile final : public FileSys::FileBackend {
public: public:
explicit CIAFile(Service::FS::MediaType media_type) : media_type(media_type) {} explicit CIAFile(Service::FS::MediaType media_type);
~CIAFile() { ~CIAFile();
Close();
}
ResultVal<std::size_t> Read(u64 offset, std::size_t length, u8* buffer) const override; ResultVal<std::size_t> Read(u64 offset, std::size_t length, u8* buffer) const override;
ResultVal<std::size_t> WriteTitleMetadata(u64 offset, std::size_t length, const u8* buffer); ResultCode WriteTicket();
ResultCode WriteTitleMetadata();
ResultVal<std::size_t> WriteContentData(u64 offset, std::size_t length, const u8* buffer); ResultVal<std::size_t> WriteContentData(u64 offset, std::size_t length, const u8* buffer);
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush, ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush,
const u8* buffer) override; const u8* buffer) override;
@ -89,6 +89,9 @@ private:
std::vector<u8> data; std::vector<u8> data;
std::vector<u64> content_written; std::vector<u64> content_written;
Service::FS::MediaType media_type; Service::FS::MediaType media_type;
class DecryptionState;
std::unique_ptr<DecryptionState> decryption_state;
}; };
/** /**