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:
Ben 2018-08-09 23:02:53 +02:00 committed by GitHub
parent d09646ab9d
commit 5e658efdb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 144 additions and 7 deletions

View File

@ -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) {

View File

@ -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:

View File

@ -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) {

View File

@ -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);

View File

@ -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