Service/AM: handle encrypted CIA
This commit is contained in:
parent
df77491938
commit
9668852c0d
@ -6,6 +6,8 @@
|
||||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <cryptopp/aes.h>
|
||||
#include <cryptopp/modes.h>
|
||||
#include <fmt/format.h>
|
||||
#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<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 {
|
||||
UNIMPLEMENTED();
|
||||
return MakeResult<std::size_t>(length);
|
||||
}
|
||||
|
||||
ResultVal<std::size_t> 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<std::size_t> 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<std::size_t>(length);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
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())
|
||||
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
|
||||
// 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
|
||||
// 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<u16>(i)) &
|
||||
FileSys::TMDContentTypeFlag::Encrypted) {
|
||||
if ((container.GetTitleMetadata().GetContentTypeByIndex(static_cast<u16>(i)) &
|
||||
FileSys::TMDContentTypeFlag::Encrypted) &&
|
||||
!title_key_available) {
|
||||
LOG_ERROR(Service_AM, "File {} is encrypted! Aborting...", path);
|
||||
return InstallStatus::ErrorEncrypted;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#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<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> Write(u64 offset, std::size_t length, bool flush,
|
||||
const u8* buffer) override;
|
||||
@ -89,6 +89,9 @@ private:
|
||||
std::vector<u8> data;
|
||||
std::vector<u64> content_written;
|
||||
Service::FS::MediaType media_type;
|
||||
|
||||
class DecryptionState;
|
||||
std::unique_ptr<DecryptionState> decryption_state;
|
||||
};
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user