Service/AM: handle encrypted CIA
This commit is contained in:
parent
df77491938
commit
9668852c0d
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Reference in New Issue