Fix race in Crypto/unedat.cpp, Make NPDRM keys usage atomic

This commit is contained in:
Eladash 2020-06-20 14:12:19 +03:00 committed by Ani
parent 97717defa5
commit 2483cc6f8d
13 changed files with 91 additions and 136 deletions

View File

@ -15,7 +15,8 @@
// 128-bit vector type and also se_storage<> storage type
union alignas(16) v128
{
char _bytes[16];
uchar _bytes[16];
char _chars[16];
template <typename T, std::size_t N, std::size_t M>
struct masked_array_t // array type accessed as (index ^ M)

View File

@ -2,10 +2,15 @@
#include "key_vault.h"
#include "unedat.h"
#include "Utilities/mutex.h"
#include "util/endian.hpp"
#include <cmath>
LOG_CHANNEL(edat_log, "EDAT");
// Static variables are being modified concurrently in ec.cpp, for now use a mutex
static shared_mutex ec_mtx;
void generate_key(int crypto_mode, int version, unsigned char *key_final, unsigned char *iv_final, unsigned char *key, unsigned char *iv)
{
int mode = crypto_mode & 0xF0000000;
@ -131,15 +136,15 @@ std::tuple<u64, s32, s32> dec_section(unsigned char* metadata)
return std::make_tuple(offset, length, compression_end);
}
std::array<u8, 0x10> get_block_key(int block, NPD_HEADER *npd)
v128 get_block_key(int block, NPD_HEADER *npd)
{
unsigned char empty_key[0x10] = {};
unsigned char *src_key = (npd->version <= 1) ? empty_key : npd->dev_hash;
std::array<u8, 0x10> dest_key{};
memcpy(dest_key.data(), src_key, 0xC);
v128 dest_key{};
memcpy(dest_key._bytes, src_key, 0xC);
s32 swappedBlock = swap32(block);
memcpy(&dest_key[0xC], &swappedBlock, sizeof(swappedBlock));
memcpy(&dest_key._bytes[0xC], &swappedBlock, sizeof(swappedBlock));
return dest_key;
}
@ -241,10 +246,10 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
in->read(enc_data.get(), length);
// Generate a key for the current block.
std::array<u8, 0x10> b_key = get_block_key(block_num, npd);
auto b_key = get_block_key(block_num, npd);
// Encrypt the block key with the crypto key.
aesecb128_encrypt(crypt_key, b_key.data(), key_result);
aesecb128_encrypt(crypt_key, b_key._bytes, key_result);
if ((edat->flags & EDAT_FLAG_0x10) != 0)
aesecb128_encrypt(crypt_key, key_result, hash); // If FLAG 0x10 is set, encrypt again to get the final hash.
else
@ -468,6 +473,8 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
unsigned char signature_s[0x15] = { 0 };
unsigned char zero_buf[0x15] = { 0 };
std::lock_guard lock(ec_mtx);
// Setup ECDSA curve and public key.
ecdsa_set_curve(VSH_CURVE_P, VSH_CURVE_A, VSH_CURVE_B, VSH_CURVE_N, VSH_CURVE_GX, VSH_CURVE_GY);
ecdsa_set_pub(VSH_PUB);
@ -518,10 +525,10 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
{
// Setup header signature hash.
memset(signature_hash, 0, 20);
std::unique_ptr<u8[]> header_buf(new u8[0xD8]);
u8 header_buf[0xD8];
f->seek(file_offset);
f->read(header_buf.get(), 0xD8);
sha1(header_buf.get(), 0xD8, signature_hash );
f->read(header_buf, 0xD8);
sha1(header_buf, 0xD8, signature_hash);
if (!ecdsa_verify(signature_hash, signature_r, signature_s))
edat_log.warning("EDAT: Header signature is invalid!");
@ -534,7 +541,6 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
int validate_dev_klic(const u8* klicensee, NPD_HEADER *npd)
{
unsigned char dev[0x60] = { 0 };
unsigned char key[0x10] = { 0 };
// Build the dev buffer (first 0x60 bytes of NPD header in big-endian).
memcpy(dev, npd, 0x60);
@ -548,17 +554,9 @@ int validate_dev_klic(const u8* klicensee, NPD_HEADER *npd)
memcpy(dev + 0xC, &type, 4);
// Check for an empty dev_hash (can't validate if devklic is NULL);
bool isDevklicEmpty = true;
for (int i = 0; i < 0x10; i++)
{
if (klicensee[i] != 0)
{
isDevklicEmpty = false;
break;
}
}
auto klic = v128::loadu(klicensee);
if (isDevklicEmpty)
if (klic == v128{})
{
// Allow empty dev hash.
return 1;
@ -566,10 +564,10 @@ int validate_dev_klic(const u8* klicensee, NPD_HEADER *npd)
else
{
// Generate klicensee xor key.
xor_key(key, klicensee, NP_OMAC_KEY_2);
auto key = klic ^ std::bit_cast<v128>(NP_OMAC_KEY_2);
// Hash with generated key and compare with dev_hash.
return cmac_hash_compare(key, 0x10, dev, 0x60, npd->dev_hash, 0x10);
return cmac_hash_compare(key._bytes, 0x10, dev, 0x60, npd->dev_hash, 0x10);
}
}
@ -650,7 +648,7 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
}
// Set decryption key.
u8 key[0x10] = { 0 };
v128 key{};
// Check EDAT/SDAT flag.
if ((EDAT.flags & SDAT_FLAG) == SDAT_FLAG)
@ -664,7 +662,7 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
}
// Generate SDAT key.
xor_key(key, NPD.dev_hash, SDAT_KEY);
key = std::bit_cast<v128>(NPD.dev_hash) ^ std::bit_cast<v128>(SDAT_KEY);
}
else
{
@ -691,23 +689,13 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
// Select EDAT key.
if ((NPD.license & 0x3) == 0x3) // Type 3: Use supplied devklic.
memcpy(key, devklic, 0x10);
memcpy(&key, devklic, 0x10);
else if ((NPD.license & 0x2) == 0x2) // Type 2: Use key from RAP file (RIF key).
{
memcpy(key, rifkey, 0x10);
memcpy(&key, rifkey, 0x10);
// Make sure we don't have an empty RIF key.
int i, test = 0;
for (i = 0; i < 0x10; i++)
{
if (key[i] != 0)
{
test = 1;
break;
}
}
if (!test)
if (key == v128{})
{
edat_log.error("EDAT: A valid RAP file is needed for this EDAT file!");
return 1;
@ -721,34 +709,29 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
if (verbose)
{
int i;
edat_log.notice("DEVKLIC: ");
for (i = 0; i < 0x10; i++)
edat_log.notice("%02X", devklic[i]);
be_t<v128> data;
edat_log.notice("RIF KEY: ");
for (i = 0; i < 0x10; i++)
edat_log.notice("%02X", rifkey[i]);
std::memcpy(&data, devklic, sizeof(data));
edat_log.notice("DEVKLIC: %s", data);
std::memcpy(&data, rifkey, sizeof(data));
edat_log.notice("RIF KEY: %s", data);
}
}
if (verbose)
{
int i;
edat_log.notice("DECRYPTION KEY: ");
for (i = 0; i < 0x10; i++)
edat_log.notice("%02X", key[i]);
edat_log.notice("DECRYPTION KEY: %s", std::bit_cast<be_t<v128>>(key));
}
input->seek(0);
if (check_data(key, &EDAT, &NPD, input, verbose))
if (check_data(key._bytes, &EDAT, &NPD, input, verbose))
{
edat_log.error("EDAT: Data parsing failed!");
return 1;
}
input->seek(0);
if (decrypt_data(input, output, &EDAT, &NPD, key, verbose))
if (decrypt_data(input, output, &EDAT, &NPD, key._bytes, verbose))
{
edat_log.error("EDAT: Data decryption failed!");
return 1;
@ -757,19 +740,19 @@ 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)
v128 GetEdatRifKeyFromRapFile(const fs::file& rap_file)
{
std::array<u8, 0x10> rapkey{ 0 };
std::array<u8, 0x10> rifkey{ 0 };
v128 rapkey{};
v128 rifkey{};
rap_file.read<std::array<u8, 0x10>>(rapkey);
rap_file.read<v128>(rapkey);
rap_to_rif(rapkey.data(), rifkey.data());
rap_to_rif(rapkey._bytes, rifkey._bytes);
return rifkey;
}
bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const std::array<u8, 0x10>& custom_klic, std::string* contentID)
bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const u8* custom_klic, std::string* contentID)
{
// Setup NPD and EDAT/SDAT structs.
NPD_HEADER NPD;
@ -794,7 +777,7 @@ bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& inpu
// 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))
if (!validate_npd_hashes(real_file_name, custom_klic, &NPD, false))
{
// Ignore header validation in DEBUG data.
if ((EDAT.flags & EDAT_DEBUG_DATA_FLAG) != EDAT_DEBUG_DATA_FLAG)
@ -815,8 +798,8 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name,
input.seek(0);
// Set keys (RIF and DEVKLIC).
std::array<u8, 0x10> rifKey{ 0 };
unsigned char devklic[0x10] = { 0 };
v128 rifKey{};
v128 devklic{};
// Select the EDAT key mode.
switch (mode)
@ -824,30 +807,30 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name,
case 0:
break;
case 1:
memcpy(devklic, NP_KLIC_FREE, 0x10);
memcpy(&devklic, NP_KLIC_FREE, 0x10);
break;
case 2:
memcpy(devklic, NP_OMAC_KEY_2, 0x10);
memcpy(&devklic, NP_OMAC_KEY_2, 0x10);
break;
case 3:
memcpy(devklic, NP_OMAC_KEY_3, 0x10);
memcpy(&devklic, NP_OMAC_KEY_3, 0x10);
break;
case 4:
memcpy(devklic, NP_KLIC_KEY, 0x10);
memcpy(&devklic, NP_KLIC_KEY, 0x10);
break;
case 5:
memcpy(devklic, NP_PSX_KEY, 0x10);
memcpy(&devklic, NP_PSX_KEY, 0x10);
break;
case 6:
memcpy(devklic, NP_PSP_KEY_1, 0x10);
memcpy(&devklic, NP_PSP_KEY_1, 0x10);
break;
case 7:
memcpy(devklic, NP_PSP_KEY_2, 0x10);
memcpy(&devklic, NP_PSP_KEY_2, 0x10);
break;
case 8:
{
if (custom_klic != NULL)
memcpy(devklic, custom_klic, 0x10);
memcpy(&devklic, custom_klic, 0x10);
else
{
edat_log.error("EDAT: Invalid custom klic!");
@ -870,7 +853,7 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name,
// 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.data(), verbose))
if (extract_all_data(&input, &output, input_file_name.c_str(), devklic._bytes, rifKey._bytes, verbose))
{
output.release();
return fs::file{};
@ -896,12 +879,12 @@ bool EDATADecrypter::ReadHeader()
if ((edatHeader.flags & SDAT_FLAG) == SDAT_FLAG)
{
// Generate SDAT key.
xor_key(dec_key.data(), npdHeader.dev_hash, SDAT_KEY);
dec_key = std::bit_cast<v128>(npdHeader.dev_hash) ^ std::bit_cast<v128>(SDAT_KEY);
}
else
{
// verify key
if (validate_dev_klic(dev_key.data(), &npdHeader) == 0)
if (validate_dev_klic(dev_key._bytes, &npdHeader) == 0)
{
edat_log.error("EDAT: Failed validating klic");
return false;
@ -914,7 +897,7 @@ bool EDATADecrypter::ReadHeader()
{
dec_key = std::move(rif_key);
if (dec_key == std::array<u8, 0x10>{0})
if (dec_key == v128{})
{
edat_log.warning("EDAT: Empty Dec key!");
}
@ -931,13 +914,13 @@ bool EDATADecrypter::ReadHeader()
// 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))
/*if (check_data(dec_key._bytes, &edatHeader, &npdHeader, &sdata_file, false))
{
return false;
}*/
file_size = edatHeader.file_size;
total_blocks = static_cast<u32>((edatHeader.file_size + edatHeader.block_size - 1) / edatHeader.block_size);
total_blocks = ::aligned_div(edatHeader.file_size, edatHeader.block_size);
return true;
}
@ -950,7 +933,7 @@ u64 EDATADecrypter::ReadData(u64 pos, u8* data, u64 size)
// now we need to offset things to account for the actual 'range' requested
const u64 startOffset = pos % edatHeader.block_size;
const u32 num_blocks = static_cast<u32>(std::ceil((startOffset + size) / (0. + edatHeader.block_size)));
const u32 num_blocks = static_cast<u32>(::aligned_div(startOffset + size, edatHeader.block_size));
const u64 bufSize = num_blocks*edatHeader.block_size;
if (data_buf_size < (bufSize))
{
@ -965,7 +948,7 @@ u64 EDATADecrypter::ReadData(u64 pos, u8* data, u64 size)
for (u32 i = starting_block; i < ending_block; ++i)
{
edata_file.seek(0);
u64 res = decrypt_block(&edata_file, &data_buf[writeOffset], &edatHeader, &npdHeader, dec_key.data(), i, total_blocks, edatHeader.file_size);
u64 res = decrypt_block(&edata_file, &data_buf[writeOffset], &edatHeader, &npdHeader, dec_key._bytes, i, total_blocks, edatHeader.file_size);
if (res == umax)
{
edat_log.error("Error Decrypting data");

View File

@ -4,6 +4,7 @@
#include "utils.h"
#include "Utilities/BEType.h"
#include "Utilities/File.h"
constexpr u32 SDAT_FLAG = 0x01000000;
@ -16,8 +17,8 @@ constexpr u32 EDAT_DEBUG_DATA_FLAG = 0x80000000;
struct loaded_npdrm_keys
{
std::array<u8, 0x10> devKlic{};
std::array<u8, 0x10> rifKey{};
atomic_t<v128> devKlic{};
atomic_t<v128> rifKey{};
atomic_t<u32> npdrm_fds{0};
};
@ -45,9 +46,9 @@ 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);
extern bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const std::array<u8,0x10>& custom_klic, std::string* contentID);
extern bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const u8* custom_klic, std::string* contentID);
extern std::array<u8, 0x10> GetEdatRifKeyFromRapFile(const fs::file& rap_file);
v128 GetEdatRifKeyFromRapFile(const fs::file& rap_file);
struct EDATADecrypter final : fs::file_base
{
@ -64,17 +65,17 @@ struct EDATADecrypter final : fs::file_base
std::unique_ptr<u8[]> data_buf;
u64 data_buf_size{0};
std::array<u8, 0x10> dec_key{};
v128 dec_key{};
// edat usage
std::array<u8, 0x10> rif_key{};
std::array<u8, 0x10> dev_key{};
v128 rif_key{};
v128 dev_key{};
public:
// 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)
EDATADecrypter(fs::file&& input, const v128& dev_key, const v128& rif_key)
: edata_file(std::move(input)), rif_key(rif_key), dev_key(dev_key) {}
~EDATADecrypter() override {}

View File

@ -3,6 +3,7 @@
#include "sha1.h"
#include "utils.h"
#include "unself.h"
#include "Utilities/BEType.h"
#include "Emu/VFS.h"
#include "Emu/System.h"
@ -1483,9 +1484,7 @@ bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key)
return true;
}
std::array<u8, 0x10> get_default_self_klic()
v128 get_default_self_klic()
{
std::array<u8, 0x10> key;
std::copy(std::begin(NP_KLIC_FREE), std::end(NP_KLIC_FREE), std::begin(key));
return key;
return std::bit_cast<v128>(NP_KLIC_FREE);
}

View File

@ -508,4 +508,6 @@ private:
fs::file decrypt_self(fs::file elf_or_self, u8* klic_key = nullptr, SelfAdditionalInfo* additional_info = nullptr);
bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key = nullptr);
std::array<u8, 0x10> get_default_self_klic();
union v128;
v128 get_default_self_klic();

View File

@ -15,14 +15,6 @@
// Auxiliary functions (endian swap, xor).
void xor_key(unsigned char *dest, const u8* src1, const u8* src2)
{
for(int i = 0; i < 0x10; i++)
{
dest[i] = src1[i] ^ src2[i];
}
}
// Hex string conversion auxiliary functions.
u64 hex_to_u64(const char* hex_str)
{
@ -67,22 +59,6 @@ void hex_to_bytes(unsigned char* data, const char* hex_str, unsigned int str_len
}
}
bool is_hex(const char* hex_str, unsigned int str_length)
{
static const char hex_chars[] = "0123456789abcdefABCDEF";
if (hex_str == NULL)
return false;
unsigned int i;
for (i = 0; i < str_length; i++)
{
if (strchr(hex_chars, hex_str[i]) == 0)
return false;
}
return true;
}
// Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC).
void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, int len)

View File

@ -42,19 +42,11 @@ inline u64 swap64(u64 i)
#endif
}
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(reinterpret_cast<__m128i*>(dest),
_mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i*>(src1)), _mm_loadu_si128(reinterpret_cast<const __m128i*>(src2))));
}
char* extract_file_name(const char* file_path, char real_file_name[MAX_PATH]);
// Hex string conversion auxiliary functions.
u64 hex_to_u64(const char* hex_str);
void hex_to_bytes(unsigned char *data, const char *hex_str, unsigned int str_length);
bool is_hex(const char* hex_str, unsigned int str_length);
// Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC).
void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, int len);

View File

@ -428,17 +428,17 @@ error_code sceNpTerm()
error_code npDrmIsAvailable(vm::cptr<u8> k_licensee_addr, vm::cptr<char> drm_path)
{
std::array<u8, 0x10> k_licensee{};
v128 k_licensee{};
if (k_licensee_addr)
{
std::copy_n(k_licensee_addr.get_ptr(), k_licensee.size(), k_licensee.begin());
sceNp.notice("npDrmIsAvailable(): KLicense key %s", *reinterpret_cast<be_t<v128, 1>*>(k_licensee.data()));
std::memcpy(&k_licensee, k_licensee_addr.get_ptr(), sizeof(k_licensee));
sceNp.notice("npDrmIsAvailable(): KLicense key %s", std::bit_cast<be_t<v128>>(k_licensee));
}
if (Emu.GetCat() == "PE")
{
std::copy_n(NP_PSP_KEY_2, std::size(NP_PSP_KEY_2), k_licensee.begin());
std::memcpy(&k_licensee, NP_PSP_KEY_2, sizeof(k_licensee));
sceNp.success("npDrmIsAvailable(): PSP remaster KLicense key applied.");
}
@ -469,9 +469,9 @@ error_code npDrmIsAvailable(vm::cptr<u8> k_licensee_addr, vm::cptr<char> drm_pat
if (!k_licensee_addr)
k_licensee = get_default_self_klic();
if (verify_npdrm_self_headers(enc_file, k_licensee.data()))
if (verify_npdrm_self_headers(enc_file, k_licensee._bytes))
{
npdrmkeys->devKlic = std::move(k_licensee);
npdrmkeys->devKlic = k_licensee;
}
else
{
@ -485,10 +485,10 @@ error_code npDrmIsAvailable(vm::cptr<u8> k_licensee_addr, vm::cptr<char> drm_pat
std::string contentID;
if (VerifyEDATHeaderWithKLicense(enc_file, enc_drm_path_local, k_licensee, &contentID))
if (VerifyEDATHeaderWithKLicense(enc_file, enc_drm_path_local, k_licensee._bytes, &contentID))
{
const std::string rap_file = rap_dir_path + contentID + ".rap";
npdrmkeys->devKlic = std::move(k_licensee);
npdrmkeys->devKlic = k_licensee;
if (fs::is_file(vfs::get(rap_file)))
npdrmkeys->rifKey = GetEdatRifKeyFromRapFile(fs::file{vfs::get(rap_file)});

View File

@ -437,7 +437,7 @@ lv2_file::open_result_t lv2_file::open(std::string_view vpath, s32 flags, s32 mo
if (magic == "NPD\0"_u32)
{
auto edatkeys = g_fxo->get<loaded_npdrm_keys>();
auto sdata_file = std::make_unique<EDATADecrypter>(std::move(file), edatkeys->devKlic, edatkeys->rifKey);
auto sdata_file = std::make_unique<EDATADecrypter>(std::move(file), edatkeys->devKlic.load(), edatkeys->rifKey.load());
if (!sdata_file->ReadHeader())
{
return {CELL_EFSSPECIFIC, path};

View File

@ -31,7 +31,7 @@ static error_code overlay_load_module(vm::ptr<u32> ovlmid, const std::string& vp
src = std::move(lv2_file);
}
const ppu_exec_object obj = decrypt_self(std::move(src), g_fxo->get<loaded_npdrm_keys>()->devKlic.data());
const ppu_exec_object obj = decrypt_self(std::move(src), g_fxo->get<loaded_npdrm_keys>()->devKlic.load()._bytes);
if (obj != elf_error::ok)
{

View File

@ -341,7 +341,7 @@ void _sys_process_exit2(ppu_thread& ppu, s32 status, vm::ptr<sys_exit2_param> ar
vm::temporary_unlock(ppu);
Emu.CallAfter([path = std::move(path), argv = std::move(argv), envp = std::move(envp), data = std::move(data), disc = std::move(disc), hdd1 = std::move(hdd1), klic = g_fxo->get<loaded_npdrm_keys>()->devKlic]() mutable
Emu.CallAfter([path = std::move(path), argv = std::move(argv), envp = std::move(envp), data = std::move(data), disc = std::move(disc), hdd1 = std::move(hdd1), klic = g_fxo->get<loaded_npdrm_keys>()->devKlic.load()]() mutable
{
sys_process.success("Process finished -> %s", argv[0]);
Emu.SetForceBoot(true);
@ -352,9 +352,10 @@ void _sys_process_exit2(ppu_thread& ppu, s32 status, vm::ptr<sys_exit2_param> ar
Emu.disc = std::move(disc);
Emu.hdd1 = std::move(hdd1);
if (klic != std::array<u8, 16>{})
if (klic != v128{})
{
Emu.klic.assign(klic.begin(), klic.end());
// TODO: Use std::optional
Emu.klic.assign(std::begin(klic._bytes), std::end(klic._bytes));
}
Emu.SetForceBoot(true);

View File

@ -166,7 +166,7 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<s
src = std::move(lv2_file);
}
const ppu_prx_object obj = decrypt_self(std::move(src), g_fxo->get<loaded_npdrm_keys>()->devKlic.data());
const ppu_prx_object obj = decrypt_self(std::move(src), g_fxo->get<loaded_npdrm_keys>()->devKlic.load()._bytes);
if (obj != elf_error::ok)
{

View File

@ -249,7 +249,7 @@ error_code sys_spu_image_open(ppu_thread& ppu, vm::ptr<sys_spu_image> img, vm::c
return {fs_error, path};
}
const fs::file elf_file = decrypt_self(std::move(file), g_fxo->get<loaded_npdrm_keys>()->devKlic.data());
const fs::file elf_file = decrypt_self(std::move(file), g_fxo->get<loaded_npdrm_keys>()->devKlic.load()._bytes);
if (!elf_file)
{