mirror of
https://github.com/citra-emu/citra.git
synced 2024-12-18 16:40:04 +00:00
Service::HTTP_C: Add decryption of the ClCertA (#4045)
* Service::HTTP_C: Add decryption of the ClCertA * fixup! Service::HTTP_C: Add decryption of the ClCertA * fixup! Service::HTTP_C: Add decryption of the ClCertA * FileSys:: Add MakeNCCHArchivePath and MakeNCCHFilePath; Small fixes in HTTP_C::DecryptDefaultClientCert * fixup! fixup! Service::HTTP_C: Add decryption of the ClCertA * fixup! fixup! fixup! Service::HTTP_C: Add decryption of the ClCertA
This commit is contained in:
parent
d09646ab9d
commit
5e658efdb8
@ -27,12 +27,6 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
enum class NCCHFilePathType : u32 {
|
||||
RomFS = 0,
|
||||
Code = 1,
|
||||
ExeFS = 2,
|
||||
};
|
||||
|
||||
struct NCCHArchivePath {
|
||||
u64_le tid;
|
||||
u32_le media_type;
|
||||
@ -48,6 +42,28 @@ struct NCCHFilePath {
|
||||
};
|
||||
static_assert(sizeof(NCCHFilePath) == 0x14, "NCCHFilePath has wrong size!");
|
||||
|
||||
Path MakeNCCHArchivePath(u64 tid, Service::FS::MediaType media_type) {
|
||||
NCCHArchivePath path;
|
||||
path.tid = static_cast<u64_le>(tid);
|
||||
path.media_type = static_cast<u32_le>(media_type);
|
||||
path.unknown = 0;
|
||||
std::vector<u8> archive(sizeof(path));
|
||||
std::memcpy(&archive[0], &path, sizeof(path));
|
||||
return FileSys::Path(archive);
|
||||
}
|
||||
|
||||
Path MakeNCCHFilePath(NCCHFileOpenType open_type, u32 content_index, NCCHFilePathType filepath_type,
|
||||
std::array<char, 8>& exefs_filepath) {
|
||||
NCCHFilePath path;
|
||||
path.open_type = static_cast<u32_le>(open_type);
|
||||
path.content_index = static_cast<u32_le>(content_index);
|
||||
path.filepath_type = static_cast<u32_le>(filepath_type);
|
||||
path.exefs_filepath = exefs_filepath;
|
||||
std::vector<u8> file(sizeof(path));
|
||||
std::memcpy(&file[0], &path, sizeof(path));
|
||||
return FileSys::Path(file);
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> NCCHArchive::OpenFile(const Path& path,
|
||||
const Mode& mode) const {
|
||||
if (path.GetType() != LowPathType::Binary) {
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
@ -21,6 +22,24 @@ enum class MediaType : u32;
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
enum class NCCHFilePathType : u32 {
|
||||
RomFS = 0,
|
||||
Code = 1,
|
||||
ExeFS = 2,
|
||||
};
|
||||
|
||||
enum class NCCHFileOpenType : u32 {
|
||||
NCCHData = 0,
|
||||
SaveData = 1,
|
||||
};
|
||||
|
||||
/// Helper function to generate a Path for NCCH archives
|
||||
Path MakeNCCHArchivePath(u64 tid, Service::FS::MediaType media_type);
|
||||
|
||||
/// Helper function to generate a Path for NCCH files
|
||||
Path MakeNCCHFilePath(NCCHFileOpenType open_type, u32 content_index, NCCHFilePathType filepath_type,
|
||||
std::array<char, 8>& exefs_filepath);
|
||||
|
||||
/// Archive backend for NCCH Archives (RomFS, ExeFS)
|
||||
class NCCHArchive : public ArchiveBackend {
|
||||
public:
|
||||
|
@ -2,9 +2,16 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cryptopp/aes.h>
|
||||
#include <cryptopp/modes.h>
|
||||
#include "core/file_sys/archive_ncch.h"
|
||||
#include "core/file_sys/file_backend.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/ipc.h"
|
||||
#include "core/hle/romfs.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/hle/service/http_c.h"
|
||||
#include "core/hw/aes/key.h"
|
||||
|
||||
namespace Service {
|
||||
namespace HTTP {
|
||||
@ -279,6 +286,87 @@ void HTTP_C::AddRequestHeader(Kernel::HLERequestContext& ctx) {
|
||||
context_handle);
|
||||
}
|
||||
|
||||
void HTTP_C::DecryptClCertA() {
|
||||
static constexpr u32 iv_length = 16;
|
||||
|
||||
FileSys::Path archive_path =
|
||||
FileSys::MakeNCCHArchivePath(0x0004001b00010002, Service::FS::MediaType::NAND);
|
||||
auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path);
|
||||
if (archive_result.Failed()) {
|
||||
LOG_ERROR(Service_HTTP, "ClCertA archive missing");
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<char, 8> exefs_filepath;
|
||||
FileSys::Path file_path = FileSys::MakeNCCHFilePath(
|
||||
FileSys::NCCHFileOpenType::NCCHData, 0, FileSys::NCCHFilePathType::RomFS, exefs_filepath);
|
||||
FileSys::Mode open_mode = {};
|
||||
open_mode.read_flag.Assign(1);
|
||||
auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode);
|
||||
if (file_result.Failed()) {
|
||||
LOG_ERROR(Service_HTTP, "ClCertA file missing");
|
||||
return;
|
||||
}
|
||||
|
||||
auto romfs = std::move(file_result).Unwrap();
|
||||
std::vector<u8> romfs_buffer(romfs->backend->GetSize());
|
||||
romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data());
|
||||
romfs->backend->Close();
|
||||
|
||||
if (!HW::AES::IsNormalKeyAvailable(HW::AES::KeySlotID::SSLKey)) {
|
||||
LOG_ERROR(Service_HTTP, "NormalKey in KeySlot 0x0D missing");
|
||||
return;
|
||||
}
|
||||
HW::AES::AESKey key = HW::AES::GetNormalKey(HW::AES::KeySlotID::SSLKey);
|
||||
|
||||
const RomFS::RomFSFile cert_file =
|
||||
RomFS::GetFile(romfs_buffer.data(), {u"ctr-common-1-cert.bin"});
|
||||
if (cert_file.Length() == 0) {
|
||||
LOG_ERROR(Service_HTTP, "ctr-common-1-cert.bin missing");
|
||||
return;
|
||||
}
|
||||
if (cert_file.Length() <= iv_length) {
|
||||
LOG_ERROR(Service_HTTP, "ctr-common-1-cert.bin size is too small. Size: {}",
|
||||
cert_file.Length());
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<u8> cert_data(cert_file.Length() - iv_length);
|
||||
|
||||
using CryptoPP::AES;
|
||||
CryptoPP::CBC_Mode<AES>::Decryption aes_cert;
|
||||
std::array<u8, iv_length> cert_iv;
|
||||
std::memcpy(cert_iv.data(), cert_file.Data(), iv_length);
|
||||
aes_cert.SetKeyWithIV(key.data(), AES::BLOCKSIZE, cert_iv.data());
|
||||
aes_cert.ProcessData(cert_data.data(), cert_file.Data() + iv_length,
|
||||
cert_file.Length() - iv_length);
|
||||
|
||||
const RomFS::RomFSFile key_file =
|
||||
RomFS::GetFile(romfs_buffer.data(), {u"ctr-common-1-key.bin"});
|
||||
if (key_file.Length() == 0) {
|
||||
LOG_ERROR(Service_HTTP, "ctr-common-1-key.bin missing");
|
||||
return;
|
||||
}
|
||||
if (key_file.Length() <= iv_length) {
|
||||
LOG_ERROR(Service_HTTP, "ctr-common-1-key.bin size is too small. Size: {}",
|
||||
key_file.Length());
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<u8> key_data(key_file.Length() - iv_length);
|
||||
|
||||
CryptoPP::CBC_Mode<AES>::Decryption aes_key;
|
||||
std::array<u8, iv_length> key_iv;
|
||||
std::memcpy(key_iv.data(), key_file.Data(), iv_length);
|
||||
aes_key.SetKeyWithIV(key.data(), AES::BLOCKSIZE, key_iv.data());
|
||||
aes_key.ProcessData(key_data.data(), key_file.Data() + iv_length,
|
||||
key_file.Length() - iv_length);
|
||||
|
||||
ClCertA.certificate = std::move(cert_data);
|
||||
ClCertA.private_key = std::move(key_data);
|
||||
ClCertA.init = true;
|
||||
}
|
||||
|
||||
HTTP_C::HTTP_C() : ServiceFramework("http:C", 32) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x00010044, &HTTP_C::Initialize, "Initialize"},
|
||||
@ -339,6 +427,8 @@ HTTP_C::HTTP_C() : ServiceFramework("http:C", 32) {
|
||||
{0x00390000, nullptr, "Finalize"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
DecryptClCertA();
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
|
@ -197,6 +197,8 @@ private:
|
||||
*/
|
||||
void AddRequestHeader(Kernel::HLERequestContext& ctx);
|
||||
|
||||
void DecryptClCertA();
|
||||
|
||||
Kernel::SharedPtr<Kernel::SharedMemory> shared_memory = nullptr;
|
||||
|
||||
/// The next handle number to use when a new HTTP context is created.
|
||||
@ -210,6 +212,12 @@ private:
|
||||
|
||||
/// Global list of ClientCert contexts currently opened.
|
||||
std::unordered_map<ClientCertContext::Handle, ClientCertContext> client_certs;
|
||||
|
||||
struct {
|
||||
std::vector<u8> certificate;
|
||||
std::vector<u8> private_key;
|
||||
bool init = false;
|
||||
} ClCertA;
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
|
@ -12,13 +12,17 @@ namespace HW {
|
||||
namespace AES {
|
||||
|
||||
enum KeySlotID : size_t {
|
||||
|
||||
// Used to decrypt the SSL client cert/private-key stored in ClCertA.
|
||||
SSLKey = 0x0D,
|
||||
|
||||
// AES keyslots used to decrypt NCCH
|
||||
NCCHSecure1 = 0x2C,
|
||||
NCCHSecure2 = 0x25,
|
||||
NCCHSecure3 = 0x18,
|
||||
NCCHSecure4 = 0x1B,
|
||||
|
||||
// AES keyslot used to generate the UDS data frame CCMP key.
|
||||
// AES Keyslot used to generate the UDS data frame CCMP key.
|
||||
UDSDataKey = 0x2D,
|
||||
|
||||
// AES keyslot used for APT:Wrap/Unwrap functions
|
||||
|
Loading…
Reference in New Issue
Block a user