mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 22:00:05 +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
					
				
					 5 changed files with 144 additions and 7 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue