mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-23 11:13:19 +01:00
Edat / Sdata: On the fly decryption for edat, fix EDAT_FLAG_0x20 decryption
This commit is contained in:
parent
596db2c29c
commit
a6f8e4f6b3
@ -142,6 +142,7 @@ std::array<u8, 0x10> get_block_key(int block, NPD_HEADER *npd)
|
||||
}
|
||||
|
||||
// for out data, allocate a buffer the size of 'edat->block_size'
|
||||
// Also, set 'in file' to the beginning of the encrypted data, which may be offset if inside another file, but normally just reset to beginning of file
|
||||
// returns number of bytes written, -1 for error
|
||||
s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *npd, u8* crypt_key, u32 block_num, u32 total_blocks, u64 size_left)
|
||||
{
|
||||
@ -193,7 +194,7 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
|
||||
else if ((edat->flags & EDAT_FLAG_0x20) != 0)
|
||||
{
|
||||
// If FLAG 0x20, the metadata precedes each data block.
|
||||
metadata_sec_offset = metadata_offset + (u64) block_num * (metadata_section_size + length);
|
||||
metadata_sec_offset = metadata_offset + (u64) block_num * (metadata_section_size + edat->block_size);
|
||||
in->seek(file_offset + metadata_sec_offset);
|
||||
|
||||
unsigned char metadata[0x20];
|
||||
@ -217,7 +218,7 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
|
||||
in->seek(file_offset + metadata_sec_offset);
|
||||
|
||||
in->read(hash_result, 0x10);
|
||||
offset = metadata_offset + (u64) block_num * edat->block_size + (u64) block_num * metadata_section_size;
|
||||
offset = metadata_offset + (u64) block_num * edat->block_size + total_blocks * metadata_section_size;
|
||||
length = edat->block_size;
|
||||
|
||||
if ((block_num == (total_blocks - 1)) && (edat->file_size % edat->block_size))
|
||||
@ -280,7 +281,7 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
|
||||
if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), enc_data.get(), dec_data.get(), length, key_result, iv, hash, hash_result))
|
||||
{
|
||||
LOG_ERROR(LOADER, "EDAT: Block at offset 0x%llx has invalid hash!", (u64)offset);
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,7 +297,7 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
|
||||
if (res < 0)
|
||||
{
|
||||
LOG_ERROR(LOADER, "EDAT: Decompression failed!");
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
@ -528,19 +529,10 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int validate_npd_hashes(const char* file_name, unsigned char *klicensee, NPD_HEADER *npd, bool verbose)
|
||||
int validate_dev_klic(const u8* klicensee, NPD_HEADER *npd)
|
||||
{
|
||||
int title_hash_result = 0;
|
||||
int dev_hash_result = 0;
|
||||
|
||||
const int file_name_length = (int) strlen(file_name);
|
||||
std::unique_ptr<u8> buf(new u8[0x30 + file_name_length]);
|
||||
unsigned char dev[0x60] = { 0 };
|
||||
unsigned char key[0x10] = { 0 };
|
||||
|
||||
// Build the title buffer (content_id + file_name).
|
||||
memcpy(buf.get(), npd->content_id, 0x30);
|
||||
memcpy(buf.get() + 0x30, file_name, file_name_length);
|
||||
|
||||
// Build the dev buffer (first 0x60 bytes of NPD header in big-endian).
|
||||
memcpy(dev, npd, 0x60);
|
||||
@ -553,17 +545,6 @@ int validate_npd_hashes(const char* file_name, unsigned char *klicensee, NPD_HEA
|
||||
memcpy(dev + 0x8, &license, 4);
|
||||
memcpy(dev + 0xC, &type, 4);
|
||||
|
||||
// Hash with NPDRM_OMAC_KEY_3 and compare with title_hash.
|
||||
title_hash_result = cmac_hash_compare(NP_OMAC_KEY_3, 0x10, buf.get(), 0x30 + file_name_length, npd->title_hash, 0x10);
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
if (title_hash_result)
|
||||
LOG_NOTICE(LOADER, "EDAT: NPD title hash is valid!");
|
||||
else
|
||||
LOG_WARNING(LOADER, "EDAT: NPD title hash is invalid!");
|
||||
}
|
||||
|
||||
// Check for an empty dev_hash (can't validate if devklic is NULL);
|
||||
bool isDevklicEmpty = true;
|
||||
for (int i = 0; i < 0x10; i++)
|
||||
@ -577,11 +558,8 @@ int validate_npd_hashes(const char* file_name, unsigned char *klicensee, NPD_HEA
|
||||
|
||||
if (isDevklicEmpty)
|
||||
{
|
||||
if (verbose)
|
||||
LOG_WARNING(LOADER, "EDAT: NPD dev hash is empty!");
|
||||
|
||||
// Allow empty dev hash.
|
||||
dev_hash_result = 1;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -589,16 +567,35 @@ int validate_npd_hashes(const char* file_name, unsigned char *klicensee, NPD_HEA
|
||||
xor_key(key, klicensee, NP_OMAC_KEY_2);
|
||||
|
||||
// Hash with generated key and compare with dev_hash.
|
||||
dev_hash_result = cmac_hash_compare(key, 0x10, dev, 0x60, npd->dev_hash, 0x10);
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
if (dev_hash_result)
|
||||
LOG_NOTICE(LOADER, "EDAT: NPD dev hash is valid!");
|
||||
else
|
||||
LOG_WARNING(LOADER, "EDAT: NPD dev hash is invalid!");
|
||||
}
|
||||
return cmac_hash_compare(key, 0x10, dev, 0x60, npd->dev_hash, 0x10);
|
||||
}
|
||||
}
|
||||
|
||||
int validate_npd_hashes(const char* file_name, const u8* klicensee, NPD_HEADER *npd, bool verbose)
|
||||
{
|
||||
int title_hash_result = 0;
|
||||
int dev_hash_result = 0;
|
||||
|
||||
const int file_name_length = (int) strlen(file_name);
|
||||
std::unique_ptr<u8> buf(new u8[0x30 + file_name_length]);
|
||||
|
||||
// Build the title buffer (content_id + file_name).
|
||||
memcpy(buf.get(), npd->content_id, 0x30);
|
||||
memcpy(buf.get() + 0x30, file_name, file_name_length);
|
||||
|
||||
// Hash with NPDRM_OMAC_KEY_3 and compare with title_hash.
|
||||
title_hash_result = cmac_hash_compare(NP_OMAC_KEY_3, 0x10, buf.get(), 0x30 + file_name_length, npd->title_hash, 0x10);
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
if (title_hash_result)
|
||||
LOG_NOTICE(LOADER, "EDAT: NPD title hash is valid!");
|
||||
else
|
||||
LOG_WARNING(LOADER, "EDAT: NPD title hash is invalid!");
|
||||
}
|
||||
|
||||
|
||||
dev_hash_result = validate_dev_klic(klicensee, npd);
|
||||
|
||||
return (title_hash_result && dev_hash_result);
|
||||
}
|
||||
@ -758,6 +755,55 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::array<u8, 0x10> GetEdatRifKeyFromRapFile(const fs::file& rap_file)
|
||||
{
|
||||
std::array<u8, 0x10> rapkey{ 0 };
|
||||
std::array<u8, 0x10> rifkey{ 0 };
|
||||
|
||||
rap_file.read<std::array<u8, 0x10>>(rapkey);
|
||||
|
||||
rap_to_rif(rapkey.data(), rifkey.data());
|
||||
|
||||
return rifkey;
|
||||
}
|
||||
|
||||
bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const std::array<u8, 0x10>& custom_klic)
|
||||
{
|
||||
// Setup NPD and EDAT/SDAT structs.
|
||||
NPD_HEADER NPD;
|
||||
EDAT_HEADER EDAT;
|
||||
|
||||
// Read in the NPD and EDAT/SDAT headers.
|
||||
read_npd_edat_header(&input, NPD, EDAT);
|
||||
|
||||
unsigned char npd_magic[4] = { 0x4E, 0x50, 0x44, 0x00 }; //NPD0
|
||||
if (memcmp(&NPD.magic, npd_magic, 4))
|
||||
{
|
||||
LOG_ERROR(LOADER, "EDAT: %s has invalid NPD header or already decrypted.", input_file_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((EDAT.flags & SDAT_FLAG) == SDAT_FLAG)
|
||||
{
|
||||
LOG_ERROR(LOADER, "EDAT: SDATA file given to edat function");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform header validation (EDAT only).
|
||||
char real_file_name[MAX_PATH];
|
||||
extract_file_name(input_file_name.c_str(), real_file_name);
|
||||
if (!validate_npd_hashes(real_file_name, custom_klic.data(), &NPD, false))
|
||||
{
|
||||
// Ignore header validation in DEBUG data.
|
||||
if ((EDAT.flags & EDAT_DEBUG_DATA_FLAG) != EDAT_DEBUG_DATA_FLAG)
|
||||
{
|
||||
LOG_ERROR(LOADER, "EDAT: NPD hash validation failed!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Decrypts full file
|
||||
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, const std::string& rap_file_name, u8 *custom_klic, bool verbose)
|
||||
{
|
||||
@ -765,7 +811,7 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name,
|
||||
input.seek(0);
|
||||
|
||||
// Set keys (RIF and DEVKLIC).
|
||||
unsigned char rifkey[0x10] = { 0 };
|
||||
std::array<u8, 0x10> rifKey{ 0 };
|
||||
unsigned char devklic[0x10] = { 0 };
|
||||
|
||||
// Select the EDAT key mode.
|
||||
@ -815,16 +861,12 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name,
|
||||
{
|
||||
fs::file rap(rap_file_name);
|
||||
|
||||
unsigned char rapkey[0x10] = { 0 };
|
||||
|
||||
rap.read(rapkey, 0x10);
|
||||
|
||||
rap_to_rif(rapkey, rifkey);
|
||||
rifKey = GetEdatRifKeyFromRapFile(rap);
|
||||
}
|
||||
|
||||
// Delete the bad output file if any errors arise.
|
||||
fs::file output = fs::make_stream<std::vector<u8>>();
|
||||
if (extract_all_data(&input, &output, input_file_name.c_str(), devklic, rifkey, verbose))
|
||||
if (extract_all_data(&input, &output, input_file_name.c_str(), devklic, rifKey.data(), verbose))
|
||||
{
|
||||
output.release();
|
||||
return fs::file{};
|
||||
@ -834,16 +876,11 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name,
|
||||
return output;
|
||||
}
|
||||
|
||||
SDATADecrypter::SDATADecrypter(fs::file&& input, u64 offset)
|
||||
: sdata_file(std::move(input)), file_offset(offset)
|
||||
bool EDATADecrypter::ReadHeader()
|
||||
{
|
||||
}
|
||||
|
||||
bool SDATADecrypter::ReadHeader()
|
||||
{
|
||||
sdata_file.seek(file_offset);
|
||||
edata_file.seek(0);
|
||||
// Read in the NPD and EDAT/SDAT headers.
|
||||
read_npd_edat_header(&sdata_file, npdHeader, edatHeader);
|
||||
read_npd_edat_header(&edata_file, npdHeader, edatHeader);
|
||||
|
||||
unsigned char npd_magic[4] = { 0x4E, 0x50, 0x44, 0x00 }; //NPD0
|
||||
if (memcmp(&npdHeader.magic, npd_magic, 4))
|
||||
@ -852,18 +889,44 @@ bool SDATADecrypter::ReadHeader()
|
||||
}
|
||||
|
||||
// Check for SDAT flag.
|
||||
if ((edatHeader.flags & SDAT_FLAG) != SDAT_FLAG)
|
||||
if ((edatHeader.flags & SDAT_FLAG) == SDAT_FLAG)
|
||||
{
|
||||
fmt::raw_error("Only SDATA decyption supported in sdatadecrypter");
|
||||
return false;
|
||||
// Generate SDAT key.
|
||||
xor_key(dec_key.data(), npdHeader.dev_hash, SDAT_KEY);
|
||||
}
|
||||
// Generate SDAT key.
|
||||
xor_key(dec_key.data(), npdHeader.dev_hash, SDAT_KEY);
|
||||
else
|
||||
{
|
||||
// verify key
|
||||
if (validate_dev_klic(dev_key.data(), &npdHeader) == 0)
|
||||
{
|
||||
LOG_ERROR(LOADER, "EDAT: Failed validating klic");
|
||||
return false;
|
||||
}
|
||||
|
||||
sdata_file.seek(file_offset);
|
||||
// Select EDAT key.
|
||||
if ((npdHeader.license & 0x3) == 0x3) // Type 3: Use supplied devklic.
|
||||
dec_key = std::move(dev_key);
|
||||
else if ((npdHeader.license & 0x2) == 0x2) // Type 2: Use key from RAP file (RIF key).
|
||||
{
|
||||
dec_key = std::move(rif_key);
|
||||
|
||||
if (dec_key == std::array<u8, 0x10>{0})
|
||||
{
|
||||
LOG_ERROR(LOADER, "EDAT: A valid RAP file is needed for this EDAT file!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ((npdHeader.license & 0x1) == 0x1) // Type 1: Use network activation.
|
||||
{
|
||||
LOG_ERROR(LOADER, "EDAT: Network license not supported!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// k the ecdsa_verify function in this check_data function takes a ridiculous amount of time
|
||||
// like it slows down load time by a factor of x20, at least, so its ignored for now
|
||||
edata_file.seek(0);
|
||||
|
||||
// k the ecdsa_verify function in this check_data function takes a ridiculous amount of time
|
||||
// like it slows down load time by a factor of x20, at least, so its ignored for now
|
||||
|
||||
/*if (check_data(dec_key.data(), &edatHeader, &npdHeader, &sdata_file, false))
|
||||
{
|
||||
@ -876,7 +939,7 @@ bool SDATADecrypter::ReadHeader()
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 SDATADecrypter::ReadData(u64 pos, u8* data, u64 size)
|
||||
u64 EDATADecrypter::ReadData(u64 pos, u8* data, u64 size)
|
||||
{
|
||||
if (pos > edatHeader.file_size)
|
||||
return 0;
|
||||
@ -894,11 +957,12 @@ u64 SDATADecrypter::ReadData(u64 pos, u8* data, u64 size)
|
||||
|
||||
// find and decrypt block range covering pos + size
|
||||
const u32 starting_block = static_cast<u32>(pos / edatHeader.block_size);
|
||||
const u32 ending_block = std::min(starting_block + num_blocks, total_blocks);
|
||||
u64 writeOffset = 0;
|
||||
for (u32 i = starting_block; i < (starting_block + num_blocks); ++i)
|
||||
for (u32 i = starting_block; i < ending_block; ++i)
|
||||
{
|
||||
sdata_file.seek(file_offset);
|
||||
u64 res = decrypt_block(&sdata_file, &data_buf[writeOffset], &edatHeader, &npdHeader, dec_key.data(), i, total_blocks, edatHeader.file_size);
|
||||
edata_file.seek(0);
|
||||
u64 res = decrypt_block(&edata_file, &data_buf[writeOffset], &edatHeader, &npdHeader, dec_key.data(), i, total_blocks, edatHeader.file_size);
|
||||
if (res == -1)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Error Decrypting data");
|
||||
@ -909,6 +973,6 @@ u64 SDATADecrypter::ReadData(u64 pos, u8* data, u64 size)
|
||||
|
||||
const u64 bytesWrote = std::min<u64>(writeOffset - startOffset, size);
|
||||
|
||||
memmove(data, &data_buf[startOffset], bytesWrote);
|
||||
memcpy(data, &data_buf[startOffset], bytesWrote);
|
||||
return bytesWrote;
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <array>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
constexpr u32 SDAT_FLAG = 0x01000000;
|
||||
@ -12,6 +14,12 @@ constexpr u32 EDAT_FLAG_0x10 = 0x00000010;
|
||||
constexpr u32 EDAT_FLAG_0x20 = 0x00000020;
|
||||
constexpr u32 EDAT_DEBUG_DATA_FLAG = 0x80000000;
|
||||
|
||||
struct EdatKeys_t
|
||||
{
|
||||
std::array<u8, 0x10> devKlic{};
|
||||
std::array<u8, 0x10> rifKey{};
|
||||
};
|
||||
|
||||
struct NPD_HEADER
|
||||
{
|
||||
u32 magic;
|
||||
@ -36,11 +44,14 @@ struct EDAT_HEADER
|
||||
// Decrypts full file, or null/empty file
|
||||
extern fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, const std::string& rap_file_name, u8 *custom_klic, bool verbose);
|
||||
|
||||
struct SDATADecrypter final : fs::file_base
|
||||
extern bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const std::array<u8,0x10>& custom_klic);
|
||||
|
||||
extern std::array<u8, 0x10> GetEdatRifKeyFromRapFile(const fs::file& rap_file);
|
||||
|
||||
struct EDATADecrypter final : fs::file_base
|
||||
{
|
||||
// file stream
|
||||
const fs::file sdata_file;
|
||||
const u64 file_offset;
|
||||
const fs::file edata_file;
|
||||
u64 file_size{0};
|
||||
u32 total_blocks{0};
|
||||
u64 pos{0};
|
||||
@ -53,9 +64,19 @@ struct SDATADecrypter final : fs::file_base
|
||||
u64 data_buf_size{0};
|
||||
|
||||
std::array<u8, 0x10> dec_key{};
|
||||
|
||||
// edat usage
|
||||
std::array<u8, 0x10> rif_key{};
|
||||
std::array<u8, 0x10> dev_key{};
|
||||
public:
|
||||
SDATADecrypter(fs::file&& input, u64 offset=0);
|
||||
~SDATADecrypter() override {}
|
||||
// SdataByFd usage
|
||||
EDATADecrypter(fs::file&& input)
|
||||
: edata_file(std::move(input)) {}
|
||||
// Edat usage
|
||||
EDATADecrypter(fs::file&& input, const std::array<u8, 0x10>& dev_key, const std::array<u8, 0x10>& rif_key)
|
||||
: edata_file(std::move(input)), rif_key(rif_key), dev_key(dev_key) {}
|
||||
|
||||
~EDATADecrypter() override {}
|
||||
// false if invalid
|
||||
bool ReadHeader();
|
||||
u64 ReadData(u64 pos, u8* data, u64 size);
|
||||
@ -92,7 +113,7 @@ public:
|
||||
whence == fs::seek_set ? pos = offset :
|
||||
whence == fs::seek_cur ? pos = offset + pos :
|
||||
whence == fs::seek_end ? pos = offset + size() :
|
||||
(fmt::raw_error("SDATADecrypter::seek(): invalid whence"), 0);
|
||||
(fmt::raw_error("EDATADecrypter::seek(): invalid whence"), 0);
|
||||
}
|
||||
u64 size() override { return file_size; }
|
||||
};
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
// Auxiliary functions (endian swap, xor and prng).
|
||||
|
||||
void xor_key(unsigned char *dest, unsigned char *src1, unsigned char *src2)
|
||||
void xor_key(unsigned char *dest, const u8* src1, const u8* src2)
|
||||
{
|
||||
for(int i = 0; i < 0x10; i++)
|
||||
{
|
||||
|
@ -42,7 +42,7 @@ inline u64 swap64(u64 i)
|
||||
#endif
|
||||
}
|
||||
|
||||
void xor_key(unsigned char *dest, unsigned char *src1, unsigned char *src2);
|
||||
void xor_key(unsigned char *dest, const u8* src1, const u8* src2);
|
||||
inline void xor_key_sse(u8* dest, const u8* src1, const u8* src2)
|
||||
{
|
||||
_mm_storeu_si128(&(((__m128i*)dest)[0]),
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "Emu/Cell/lv2/sys_process.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Crypto/unedat.h"
|
||||
#include "sceNp.h"
|
||||
|
||||
@ -47,7 +48,7 @@ s32 npDrmIsAvailable(vm::cptr<u8> k_licensee_addr, vm::cptr<char> drm_path)
|
||||
}
|
||||
|
||||
std::string k_licensee_str = "0";
|
||||
u8 k_licensee[0x10];
|
||||
std::array<u8,0x10> k_licensee;
|
||||
|
||||
if (k_licensee_addr)
|
||||
{
|
||||
@ -58,20 +59,30 @@ s32 npDrmIsAvailable(vm::cptr<u8> k_licensee_addr, vm::cptr<char> drm_path)
|
||||
}
|
||||
}
|
||||
|
||||
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<u32>(magic);
|
||||
enc_file.seek(0);
|
||||
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.warning("npDrmIsAvailable(): Found DRM license file at %s", enc_drm_path);
|
||||
sceNp.warning("npDrmIsAvailable(): Using k_licensee 0x%s", k_licensee_str);
|
||||
|
||||
// Set the necessary file paths.
|
||||
const std::string& drm_file_name = enc_drm_path.substr(enc_drm_path.find_last_of('/') + 1);
|
||||
|
||||
// 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);
|
||||
const std::string& title_id = drm_file_dir.substr(0, drm_file_dir.find_first_of('/'));
|
||||
|
||||
const std::string& dec_drm_path = "/dev_hdd1/cache/" + drm_file_name;
|
||||
|
||||
std::string rap_lpath = vfs::get("/dev_hdd0/home/00000001/exdata/"); // TODO: Allow multiple profiles. Use default for now.
|
||||
|
||||
auto edatkeys = fxm::get_always<EdatKeys_t>();
|
||||
|
||||
// Search for a compatible RAP file.
|
||||
for (const auto& entry : fs::dir(rap_lpath))
|
||||
{
|
||||
@ -85,24 +96,23 @@ s32 npDrmIsAvailable(vm::cptr<u8> k_licensee_addr, vm::cptr<char> drm_path)
|
||||
if (rap_lpath.back() == '/')
|
||||
{
|
||||
sceNp.warning("npDrmIsAvailable(): Can't find RAP file for %s", enc_drm_path);
|
||||
rap_lpath.clear();
|
||||
edatkeys->rifKey.fill(0);
|
||||
}
|
||||
else
|
||||
edatkeys->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);
|
||||
|
||||
if (const fs::file dec_file = DecryptEDAT(enc_file, enc_drm_path_local, 8, rap_lpath, k_licensee, false))
|
||||
if (VerifyEDATHeaderWithKLicense(enc_file, enc_drm_path_local, k_licensee))
|
||||
{
|
||||
// If decryption succeeds, replace the encrypted file with it.
|
||||
const std::string& dec_drm_path_local = vfs::get(dec_drm_path);
|
||||
|
||||
fs::file dec_out(dec_drm_path_local, fs::rewrite);
|
||||
dec_out.write(dec_file.to_vector<u8>());
|
||||
|
||||
fs::remove_file(enc_drm_path_local);
|
||||
fs::rename(dec_drm_path_local, enc_drm_path_local);
|
||||
edatkeys->devKlic = std::move(k_licensee);
|
||||
return CELL_OK;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -119,6 +119,25 @@ enum
|
||||
|
||||
// DRM
|
||||
SCE_NP_DRM_ERROR_LICENSE_NOT_FOUND = 0x80029521,
|
||||
SCE_NP_DRM_ERROR_OUT_OF_MEMORY = 0x80029501,
|
||||
SCE_NP_DRM_ERROR_INVALID_PARAM = 0x80029502,
|
||||
SCE_NP_DRM_ERROR_SERVER_RESPONSE = 0x80029509,
|
||||
SCE_NP_DRM_ERROR_NO_ENTITLEMENT = 0x80029513,
|
||||
SCE_NP_DRM_ERROR_BAD_ACT = 0x80029514,
|
||||
SCE_NP_DRM_ERROR_BAD_FORMAT = 0x80029515,
|
||||
SCE_NP_DRM_ERROR_NO_LOGIN = 0x80029516,
|
||||
SCE_NP_DRM_ERROR_INTERNAL = 0x80029517,
|
||||
SCE_NP_DRM_ERROR_BAD_PERM = 0x80029519,
|
||||
SCE_NP_DRM_ERROR_UNKNOWN_VERSION = 0x8002951a,
|
||||
SCE_NP_DRM_ERROR_TIME_LIMIT = 0x8002951b,
|
||||
SCE_NP_DRM_ERROR_DIFFERENT_ACCOUNT_ID = 0x8002951c,
|
||||
SCE_NP_DRM_ERROR_DIFFERENT_DRM_TYPE = 0x8002951d,
|
||||
SCE_NP_DRM_ERROR_SERVICE_NOT_STARTED = 0x8002951e,
|
||||
SCE_NP_DRM_ERROR_BUSY = 0x80029520,
|
||||
SCE_NP_DRM_ERROR_IO = 0x80029525,
|
||||
SCE_NP_DRM_ERROR_FORMAT = 0x80029530,
|
||||
SCE_NP_DRM_ERROR_FILENAME = 0x80029533,
|
||||
SCE_NP_DRM_ERROR_K_LICENSEE = 0x80029534,
|
||||
};
|
||||
|
||||
using SceNpBasicEventHandler = s32(s32 event, s32 retCode, u32 reqId, vm::ptr<void> arg);
|
||||
|
@ -133,7 +133,7 @@ struct lv2_file::file_view : fs::file_base
|
||||
|
||||
u64 size() override
|
||||
{
|
||||
return m_off + m_file->file.size();
|
||||
return m_file->file.size();
|
||||
}
|
||||
};
|
||||
|
||||
@ -255,27 +255,49 @@ error_code sys_fs_open(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, s32 mode
|
||||
if ((flags & CELL_FS_O_MSELF) && (!verify_mself(*fd, file)))
|
||||
return CELL_ENOTMSELF;
|
||||
|
||||
// sdata encryption arg flag
|
||||
const be_t<u32>* casted_args = static_cast<const be_t<u32> *>(arg.get_ptr());
|
||||
if (size == 8 && casted_args[0] == 0x180 && casted_args[1] == 0x10)
|
||||
const auto casted_arg = vm::static_ptr_cast<const u64>(arg);//static_cast<const be_t<u32> *>(arg.get_ptr());
|
||||
if (size == 8)
|
||||
{
|
||||
// check if the file has the NPD header, or else assume its not encrypted
|
||||
u32 magic;
|
||||
file.read<u32>(magic);
|
||||
file.seek(0);
|
||||
if (magic == "NPD\0"_u32)
|
||||
// check for sdata
|
||||
if (*casted_arg == 0x18000000010)
|
||||
{
|
||||
auto sdata_file = std::make_unique<SDATADecrypter>(std::move(file));
|
||||
if (!sdata_file->ReadHeader())
|
||||
// check if the file has the NPD header, or else assume its not encrypted
|
||||
u32 magic;
|
||||
file.read<u32>(magic);
|
||||
file.seek(0);
|
||||
if (magic == "NPD\0"_u32)
|
||||
{
|
||||
sys_fs.error("sys_fs_open(%s): Error reading sdata header!", path);
|
||||
return CELL_EFSSPECIFIC;
|
||||
}
|
||||
auto sdata_file = std::make_unique<EDATADecrypter>(std::move(file));
|
||||
if (!sdata_file->ReadHeader())
|
||||
{
|
||||
sys_fs.error("sys_fs_open(%s): Error reading sdata header!", path);
|
||||
return CELL_EFSSPECIFIC;
|
||||
}
|
||||
|
||||
file.reset(std::move(sdata_file));
|
||||
file.reset(std::move(sdata_file));
|
||||
}
|
||||
}
|
||||
// edata
|
||||
else if (*casted_arg == 0x2)
|
||||
{
|
||||
// check if the file has the NPD header, or else assume its not encrypted
|
||||
u32 magic;
|
||||
file.read<u32>(magic);
|
||||
file.seek(0);
|
||||
if (magic == "NPD\0"_u32)
|
||||
{
|
||||
auto edatkeys = fxm::get_always<EdatKeys_t>();
|
||||
auto sdata_file = std::make_unique<EDATADecrypter>(std::move(file), edatkeys->devKlic, edatkeys->rifKey);
|
||||
if (!sdata_file->ReadHeader())
|
||||
{
|
||||
sys_fs.error("sys_fs_open(%s): Error reading edata header!", path);
|
||||
return CELL_EFSSPECIFIC;
|
||||
}
|
||||
|
||||
file.reset(std::move(sdata_file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const u32 id = idm::make<lv2_fs_object, lv2_file>(path.get_ptr(), std::move(file), mode, flags))
|
||||
{
|
||||
*fd = id;
|
||||
@ -624,7 +646,7 @@ error_code sys_fs_fcntl(u32 fd, u32 op, vm::ptr<void> _arg, u32 _size)
|
||||
return CELL_EBADF;
|
||||
}
|
||||
|
||||
auto sdata_file = std::make_unique<SDATADecrypter>(lv2_file::make_view(file, arg->offset));
|
||||
auto sdata_file = std::make_unique<EDATADecrypter>(lv2_file::make_view(file, arg->offset));
|
||||
|
||||
if (!sdata_file->ReadHeader())
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user