From 5e658efdb89b7ab95ea90ba7f7fc434d30b82005 Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 9 Aug 2018 23:02:53 +0200 Subject: [PATCH] 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 --- src/core/file_sys/archive_ncch.cpp | 28 ++++++++-- src/core/file_sys/archive_ncch.h | 19 +++++++ src/core/hle/service/http_c.cpp | 90 ++++++++++++++++++++++++++++++ src/core/hle/service/http_c.h | 8 +++ src/core/hw/aes/key.h | 6 +- 5 files changed, 144 insertions(+), 7 deletions(-) diff --git a/src/core/file_sys/archive_ncch.cpp b/src/core/file_sys/archive_ncch.cpp index d096f9c17..ff86d0a4e 100644 --- a/src/core/file_sys/archive_ncch.cpp +++ b/src/core/file_sys/archive_ncch.cpp @@ -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(tid); + path.media_type = static_cast(media_type); + path.unknown = 0; + std::vector 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& exefs_filepath) { + NCCHFilePath path; + path.open_type = static_cast(open_type); + path.content_index = static_cast(content_index); + path.filepath_type = static_cast(filepath_type); + path.exefs_filepath = exefs_filepath; + std::vector file(sizeof(path)); + std::memcpy(&file[0], &path, sizeof(path)); + return FileSys::Path(file); +} + ResultVal> NCCHArchive::OpenFile(const Path& path, const Mode& mode) const { if (path.GetType() != LowPathType::Binary) { diff --git a/src/core/file_sys/archive_ncch.h b/src/core/file_sys/archive_ncch.h index c62d27f7c..31cdc2f5d 100644 --- a/src/core/file_sys/archive_ncch.h +++ b/src/core/file_sys/archive_ncch.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #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& exefs_filepath); + /// Archive backend for NCCH Archives (RomFS, ExeFS) class NCCHArchive : public ArchiveBackend { public: diff --git a/src/core/hle/service/http_c.cpp b/src/core/hle/service/http_c.cpp index a4ef456c5..54b72437a 100644 --- a/src/core/hle/service/http_c.cpp +++ b/src/core/hle/service/http_c.cpp @@ -2,9 +2,16 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include +#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 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 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 cert_data(cert_file.Length() - iv_length); + + using CryptoPP::AES; + CryptoPP::CBC_Mode::Decryption aes_cert; + std::array 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 key_data(key_file.Length() - iv_length); + + CryptoPP::CBC_Mode::Decryption aes_key; + std::array 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) { diff --git a/src/core/hle/service/http_c.h b/src/core/hle/service/http_c.h index 59f5e973f..1e089d5f0 100644 --- a/src/core/hle/service/http_c.h +++ b/src/core/hle/service/http_c.h @@ -197,6 +197,8 @@ private: */ void AddRequestHeader(Kernel::HLERequestContext& ctx); + void DecryptClCertA(); + Kernel::SharedPtr 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 client_certs; + + struct { + std::vector certificate; + std::vector private_key; + bool init = false; + } ClCertA; }; void InstallInterfaces(SM::ServiceManager& service_manager); diff --git a/src/core/hw/aes/key.h b/src/core/hw/aes/key.h index e827771d7..915560359 100644 --- a/src/core/hw/aes/key.h +++ b/src/core/hw/aes/key.h @@ -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