From 65696bf6a362f724576418432d8cd01a4d02aec3 Mon Sep 17 00:00:00 2001 From: Jake Date: Wed, 22 Mar 2017 06:13:20 -0500 Subject: [PATCH] Crypto: fix sprx/self check in npDrm and edata fixes --- rpcs3/Crypto/key_vault.h | 2 +- rpcs3/Crypto/unedat.cpp | 3 +- rpcs3/Crypto/unedat.h | 2 +- rpcs3/Crypto/unself.cpp | 49 ++++++++++++++-- rpcs3/Crypto/unself.h | 5 +- rpcs3/Emu/Cell/Modules/sceNp.cpp | 99 ++++++++++++++++++-------------- rpcs3/Emu/Cell/lv2/sys_fs.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_prx.cpp | 5 +- 8 files changed, 109 insertions(+), 58 deletions(-) diff --git a/rpcs3/Crypto/key_vault.h b/rpcs3/Crypto/key_vault.h index 057c79e843..634fd97c2a 100644 --- a/rpcs3/Crypto/key_vault.h +++ b/rpcs3/Crypto/key_vault.h @@ -159,7 +159,7 @@ class KeyVault std::vector sk_LDR_arr; std::vector sk_UNK7_arr; std::vector sk_NPDRM_arr; - u8 klicensee_key[0x10]; + u8 klicensee_key[0x10] = {}; public: KeyVault(); diff --git a/rpcs3/Crypto/unedat.cpp b/rpcs3/Crypto/unedat.cpp index 70ce7506a6..b400895ee6 100644 --- a/rpcs3/Crypto/unedat.cpp +++ b/rpcs3/Crypto/unedat.cpp @@ -912,8 +912,7 @@ bool EDATADecrypter::ReadHeader() if (dec_key == std::array{0}) { - LOG_ERROR(LOADER, "EDAT: A valid RAP file is needed for this EDAT file!"); - return false; + LOG_WARNING(LOADER, "EDAT: Empty Dec key!"); } } else if ((npdHeader.license & 0x1) == 0x1) // Type 1: Use network activation. diff --git a/rpcs3/Crypto/unedat.h b/rpcs3/Crypto/unedat.h index 405171fd9f..01b2b4b379 100644 --- a/rpcs3/Crypto/unedat.h +++ b/rpcs3/Crypto/unedat.h @@ -14,7 +14,7 @@ constexpr u32 EDAT_FLAG_0x10 = 0x00000010; constexpr u32 EDAT_FLAG_0x20 = 0x00000020; constexpr u32 EDAT_DEBUG_DATA_FLAG = 0x80000000; -struct EdatKeys_t +struct LoadedNpdrmKeys_t { std::array devKlic{}; std::array rifKey{}; diff --git a/rpcs3/Crypto/unself.cpp b/rpcs3/Crypto/unself.cpp index d989190b2e..1114812d4a 100644 --- a/rpcs3/Crypto/unself.cpp +++ b/rpcs3/Crypto/unself.cpp @@ -1089,10 +1089,11 @@ bool SELFDecrypter::DecryptNPDRM(u8 *metadata, u32 metadata_size) memcpy(klicensee_key, key_v.GetKlicenseeKey(), 0x10); // Use klicensee if available. - if (klicensee_key != NULL) + if (memcmp(klicensee_key, std::array{0}.data(), 0x10)) + { memcpy(npdrm_key, klicensee_key, 0x10); - - if (ctrl->npdrm.license == 1) // Network license. + } + else if (ctrl->npdrm.license == 1) // Network license. { LOG_ERROR(LOADER, "SELF: Can't decrypt network NPDRM!"); return false; @@ -1131,7 +1132,7 @@ bool SELFDecrypter::DecryptNPDRM(u8 *metadata, u32 metadata_size) return true; } -bool SELFDecrypter::LoadMetadata() +bool SELFDecrypter::LoadMetadata(u8* klic_key) { aes_context aes; u32 metadata_info_size = SIZE_32(meta_info); @@ -1150,6 +1151,10 @@ bool SELFDecrypter::LoadMetadata() // Find the right keyset from the key vault. SELF_KEY keyset = key_v.FindSelfKey(app_info.self_type, sce_hdr.se_flags, app_info.version); + // Set klic if given + if (klic_key) + key_v.SetKlicenseeKey(klic_key); + // Copy the necessary parameters. u8 metadata_key[0x20]; u8 metadata_iv[0x10]; @@ -1480,7 +1485,7 @@ static bool CheckDebugSelf(fs::file& s) return false; } -extern fs::file decrypt_self(fs::file elf_or_self) +extern fs::file decrypt_self(fs::file elf_or_self, u8* klic_key) { if (!elf_or_self) { @@ -1506,7 +1511,7 @@ extern fs::file decrypt_self(fs::file elf_or_self) } // Load and decrypt the SELF file metadata. - if (!self_dec.LoadMetadata()) + if (!self_dec.LoadMetadata(klic_key)) { LOG_ERROR(LOADER, "SELF: Failed to load SELF file metadata!"); return fs::file{}; @@ -1525,3 +1530,35 @@ extern fs::file decrypt_self(fs::file elf_or_self) return elf_or_self; } + +extern bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key) +{ + if (!self) + return false; + + self.seek(0); + + if (self.size() >= 4 && self.read() == "SCE\0"_u32) + { + // Check the ELF file class (32 or 64 bit). + bool isElf32 = IsSelfElf32(self); + + // Start the decrypter on this SELF file. + SELFDecrypter self_dec(self); + + // Load the SELF file headers. + if (!self_dec.LoadHeaders(isElf32)) + { + LOG_ERROR(LOADER, "SELF: Failed to load SELF file headers!"); + return false; + } + + // Load and decrypt the SELF file metadata. + if (!self_dec.LoadMetadata(klic_key)) + { + LOG_ERROR(LOADER, "SELF: Failed to load SELF file metadata!"); + return false; + } + } + return true; +} \ No newline at end of file diff --git a/rpcs3/Crypto/unself.h b/rpcs3/Crypto/unself.h index a219b3c6ef..3336b41435 100644 --- a/rpcs3/Crypto/unself.h +++ b/rpcs3/Crypto/unself.h @@ -410,10 +410,11 @@ public: fs::file MakeElf(bool isElf32); bool LoadHeaders(bool isElf32); void ShowHeaders(bool isElf32); - bool LoadMetadata(); + bool LoadMetadata(u8* klic_key); bool DecryptData(); bool DecryptNPDRM(u8 *metadata, u32 metadata_size); bool GetKeyFromRap(u8 *content_id, u8 *npdrm_key); }; -extern fs::file decrypt_self(fs::file elf_or_self); +extern fs::file decrypt_self(fs::file elf_or_self, u8* klic_key = nullptr); +extern bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key = nullptr); diff --git a/rpcs3/Emu/Cell/Modules/sceNp.cpp b/rpcs3/Emu/Cell/Modules/sceNp.cpp index d8e2ddc150..3305505e79 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp.cpp @@ -5,6 +5,7 @@ #include "Emu/Cell/lv2/sys_process.h" #include "Emu/IdManager.h" #include "Crypto/unedat.h" +#include "Crypto/unself.h" #include "sceNp.h" logs::channel sceNp("sceNp", logs::level::notice); @@ -48,7 +49,7 @@ s32 npDrmIsAvailable(vm::cptr k_licensee_addr, vm::cptr drm_path) } std::string k_licensee_str = ""; - std::array k_licensee; + std::array k_licensee{0}; if (k_licensee_addr) { @@ -57,29 +58,11 @@ s32 npDrmIsAvailable(vm::cptr k_licensee_addr, vm::cptr drm_path) k_licensee[i] = *(k_licensee_addr + i); k_licensee_str += fmt::format("%02x", k_licensee[i]); } - } - const std::string& enc_drm_path_local = vfs::get(enc_drm_path); - const fs::file enc_file(vfs::get(enc_drm_path)); - - u32 magic; - enc_file.read(magic); - enc_file.seek(0); - if (magic == "SCE\0"_u32) { - sceNp.notice("npDrmIsAvailable(): Assuming file is encrypted at %s", enc_drm_path); - //sprx - } - else if (magic != "NPD\0"_u32) - { - // for now assume its just unencrypted - sceNp.notice("npDrmIsAvailable(): Assuming edat file is unencrypted at %s", enc_drm_path); - return CELL_OK; + sceNp.notice("npDrmIsAvailable(): KLicense key %s", k_licensee_str); } sceNp.warning("npDrmIsAvailable(): Found DRM license file at %s", enc_drm_path); - if (k_licensee_addr) { - sceNp.notice("npDrmIsAvailable(): KLicense key %s", k_licensee_str); - } // TODO: Make more explicit what this actually does (currently it copies "XXXXXXXX" from drm_path (== "/dev_hdd0/game/XXXXXXXXX/*" assumed) const std::string& drm_file_dir = enc_drm_path.substr(15); @@ -87,39 +70,67 @@ s32 npDrmIsAvailable(vm::cptr k_licensee_addr, vm::cptr drm_path) std::string rap_lpath = vfs::get("/dev_hdd0/home/00000001/exdata/"); // TODO: Allow multiple profiles. Use default for now. - auto edatkeys = fxm::get_always(); - // Search for a compatible RAP file. - if (!k_licensee_addr) { - for (const auto& entry : fs::dir(rap_lpath)) + for (const auto& entry : fs::dir(rap_lpath)) + { + if (entry.name.find(title_id) != -1) { - if (entry.name.find(title_id) != -1) - { - rap_lpath += entry.name; - break; - } + rap_lpath += entry.name; + break; } - - if (rap_lpath.back() == '/') - { - sceNp.warning("npDrmIsAvailable(): Can't find RAP file for %s", enc_drm_path); - edatkeys->rifKey.fill(0); - } - else - edatkeys->rifKey = GetEdatRifKeyFromRapFile(fs::file{ rap_lpath }); } - if (VerifyEDATHeaderWithKLicense(enc_file, enc_drm_path_local, k_licensee)) + auto npdrmkeys = fxm::get_always(); + + npdrmkeys->devKlic.fill(0); + npdrmkeys->rifKey.fill(0); + + if (rap_lpath.back() == '/') { - edatkeys->devKlic = std::move(k_licensee); - return CELL_OK; + sceNp.warning("npDrmIsAvailable(): Can't find RAP file for %s", enc_drm_path); + } + else + npdrmkeys->rifKey = GetEdatRifKeyFromRapFile(fs::file{ rap_lpath }); + + const std::string& enc_drm_path_local = vfs::get(enc_drm_path); + const fs::file enc_file(enc_drm_path_local); + + u32 magic; + + enc_file.read(magic); + enc_file.seek(0); + + if (magic == "SCE\0"_u32) + { + if (verify_npdrm_self_headers(enc_file, k_licensee.data())) + { + npdrmkeys->devKlic = std::move(k_licensee); + } + else + { + sceNp.error("npDrmIsAvailable(): Failed to verify sce file %s", enc_drm_path); + return SCE_NP_DRM_ERROR_NO_ENTITLEMENT; + } + + } + else if (magic == "NPD\0"_u32) + { + // edata / sdata files + + if (VerifyEDATHeaderWithKLicense(enc_file, enc_drm_path_local, k_licensee)) + { + npdrmkeys->devKlic = std::move(k_licensee); + } + else + { + sceNp.error("npDrmIsAvailable(): Failed to verify npd file %s", enc_drm_path); + return SCE_NP_DRM_ERROR_NO_ENTITLEMENT; + } } else { - sceNp.error("npDrmIsAvailable(): Failed to verify edat file %s", enc_drm_path); - edatkeys->devKlic.fill(0); - edatkeys->rifKey.fill(0); - return SCE_NP_DRM_ERROR_FORMAT; + // for now assume its just unencrypted + sceNp.notice("npDrmIsAvailable(): Assuming npdrm file is unencrypted at %s", enc_drm_path); } return CELL_OK; } diff --git a/rpcs3/Emu/Cell/lv2/sys_fs.cpp b/rpcs3/Emu/Cell/lv2/sys_fs.cpp index 5e55861829..9c7290436e 100644 --- a/rpcs3/Emu/Cell/lv2/sys_fs.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_fs.cpp @@ -286,7 +286,7 @@ error_code sys_fs_open(vm::cptr path, s32 flags, vm::ptr fd, s32 mode file.seek(0); if (magic == "NPD\0"_u32) { - auto edatkeys = fxm::get_always(); + auto edatkeys = fxm::get_always(); auto sdata_file = std::make_unique(std::move(file), edatkeys->devKlic, edatkeys->rifKey); if (!sdata_file->ReadHeader()) { diff --git a/rpcs3/Emu/Cell/lv2/sys_prx.cpp b/rpcs3/Emu/Cell/lv2/sys_prx.cpp index 83698df989..bacccaeb80 100644 --- a/rpcs3/Emu/Cell/lv2/sys_prx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_prx.cpp @@ -6,6 +6,7 @@ #include "Loader/ELF.h" #include "Emu/Cell/ErrorCodes.h" +#include "Crypto/unedat.h" #include "sys_prx.h" namespace vm { using namespace ps3; } @@ -19,7 +20,9 @@ s32 prx_load_module(std::string path, u64 flags, vm::ptr(); + + const ppu_prx_object obj = decrypt_self(fs::file(vfs::get(path)), loadedkeys->devKlic.data()); if (obj != elf_error::ok) {