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 {
|
namespace FileSys {
|
||||||
|
|
||||||
enum class NCCHFilePathType : u32 {
|
|
||||||
RomFS = 0,
|
|
||||||
Code = 1,
|
|
||||||
ExeFS = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct NCCHArchivePath {
|
struct NCCHArchivePath {
|
||||||
u64_le tid;
|
u64_le tid;
|
||||||
u32_le media_type;
|
u32_le media_type;
|
||||||
|
@ -48,6 +42,28 @@ struct NCCHFilePath {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(NCCHFilePath) == 0x14, "NCCHFilePath has wrong size!");
|
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,
|
ResultVal<std::unique_ptr<FileBackend>> NCCHArchive::OpenFile(const Path& path,
|
||||||
const Mode& mode) const {
|
const Mode& mode) const {
|
||||||
if (path.GetType() != LowPathType::Binary) {
|
if (path.GetType() != LowPathType::Binary) {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "core/file_sys/archive_backend.h"
|
#include "core/file_sys/archive_backend.h"
|
||||||
|
@ -21,6 +22,24 @@ enum class MediaType : u32;
|
||||||
|
|
||||||
namespace FileSys {
|
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)
|
/// Archive backend for NCCH Archives (RomFS, ExeFS)
|
||||||
class NCCHArchive : public ArchiveBackend {
|
class NCCHArchive : public ArchiveBackend {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -2,9 +2,16 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// 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/ipc_helpers.h"
|
||||||
#include "core/hle/kernel/ipc.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/hle/service/http_c.h"
|
||||||
|
#include "core/hw/aes/key.h"
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
namespace HTTP {
|
namespace HTTP {
|
||||||
|
@ -279,6 +286,87 @@ void HTTP_C::AddRequestHeader(Kernel::HLERequestContext& ctx) {
|
||||||
context_handle);
|
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) {
|
HTTP_C::HTTP_C() : ServiceFramework("http:C", 32) {
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0x00010044, &HTTP_C::Initialize, "Initialize"},
|
{0x00010044, &HTTP_C::Initialize, "Initialize"},
|
||||||
|
@ -339,6 +427,8 @@ HTTP_C::HTTP_C() : ServiceFramework("http:C", 32) {
|
||||||
{0x00390000, nullptr, "Finalize"},
|
{0x00390000, nullptr, "Finalize"},
|
||||||
};
|
};
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
|
||||||
|
DecryptClCertA();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||||
|
|
|
@ -197,6 +197,8 @@ private:
|
||||||
*/
|
*/
|
||||||
void AddRequestHeader(Kernel::HLERequestContext& ctx);
|
void AddRequestHeader(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
void DecryptClCertA();
|
||||||
|
|
||||||
Kernel::SharedPtr<Kernel::SharedMemory> shared_memory = nullptr;
|
Kernel::SharedPtr<Kernel::SharedMemory> shared_memory = nullptr;
|
||||||
|
|
||||||
/// The next handle number to use when a new HTTP context is created.
|
/// 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.
|
/// Global list of ClientCert contexts currently opened.
|
||||||
std::unordered_map<ClientCertContext::Handle, ClientCertContext> client_certs;
|
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);
|
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||||
|
|
|
@ -12,13 +12,17 @@ namespace HW {
|
||||||
namespace AES {
|
namespace AES {
|
||||||
|
|
||||||
enum KeySlotID : size_t {
|
enum KeySlotID : size_t {
|
||||||
|
|
||||||
|
// Used to decrypt the SSL client cert/private-key stored in ClCertA.
|
||||||
|
SSLKey = 0x0D,
|
||||||
|
|
||||||
// AES keyslots used to decrypt NCCH
|
// AES keyslots used to decrypt NCCH
|
||||||
NCCHSecure1 = 0x2C,
|
NCCHSecure1 = 0x2C,
|
||||||
NCCHSecure2 = 0x25,
|
NCCHSecure2 = 0x25,
|
||||||
NCCHSecure3 = 0x18,
|
NCCHSecure3 = 0x18,
|
||||||
NCCHSecure4 = 0x1B,
|
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,
|
UDSDataKey = 0x2D,
|
||||||
|
|
||||||
// AES keyslot used for APT:Wrap/Unwrap functions
|
// AES keyslot used for APT:Wrap/Unwrap functions
|
||||||
|
|
Reference in New Issue