mirror of
https://github.com/ARMSX2/ARMSX3.git
synced 2026-08-24 16:58:52 -07:00
Fully implements cellSysmodule and sets it to HLE by default. There is an undocumented function that decrypts the OpenPSID. There doesn't seem to be any information about this on the PS3 dev wiki or anywhere else, so I thought I mention this separately.
254 lines
7.5 KiB
C++
254 lines
7.5 KiB
C++
// Copyright (C) 2014 Hykem <hykem@hotmail.com>
|
||
// Licensed under the terms of the GNU GPL, version 2.0 or later versions.
|
||
// http://www.gnu.org/licenses/gpl-2.0.txt
|
||
|
||
#include "utils.h"
|
||
#include "aes.h"
|
||
#include "sha1.h"
|
||
#include "sha256.h"
|
||
#include "key_vault.h"
|
||
#include <charconv>
|
||
#include <cstdlib>
|
||
#include <cstring>
|
||
#include <cstdio>
|
||
#include <ctime>
|
||
#include "Utilities/StrFmt.h"
|
||
#include "Utilities/StrUtil.h"
|
||
#include "Utilities/File.h"
|
||
|
||
#include <memory>
|
||
#include <string>
|
||
#include <string_view>
|
||
#include <span>
|
||
|
||
// Auxiliary functions (endian swap, xor).
|
||
|
||
// Bytes conversion auxiliary function.
|
||
void bytes_to_hex(std::string& hex_str, const unsigned char* data, usz data_length)
|
||
{
|
||
const usz str_length = data_length * 2;
|
||
|
||
hex_str.resize(str_length);
|
||
|
||
for (usz i = 0; i < str_length; i += 2)
|
||
{
|
||
const auto [ptr, err] = std::to_chars(hex_str.data() + i, hex_str.data() + i + 2, *data++, 16);
|
||
if (err != std::errc())
|
||
{
|
||
fmt::throw_exception("Failed to read bytes: %s", std::make_error_code(err).message());
|
||
}
|
||
|
||
// Padding handling for values < 0x10 (e.g. 0x05 becomes "5" instead of "05")
|
||
// If to_chars only writes 1 character, we move to the right and put '0'
|
||
if (ptr == &hex_str[i] + 1)
|
||
{
|
||
hex_str[i + 1] = hex_str[i];
|
||
hex_str[i] = '0';
|
||
}
|
||
}
|
||
}
|
||
|
||
// Hex string conversion auxiliary function.
|
||
void hex_to_bytes(unsigned char* data, std::string_view hex_str, usz str_length, std::string* error)
|
||
{
|
||
const usz strn_length = (str_length > 0) ? str_length : hex_str.size();
|
||
|
||
// Don't convert if the string length is odd.
|
||
if ((strn_length % 2) == 0)
|
||
{
|
||
for (usz i = 0; i < strn_length; i += 2)
|
||
{
|
||
const auto [ptr, err] = std::from_chars(hex_str.data() + i, hex_str.data() + i + 2, *data++, 16);
|
||
if (err != std::errc())
|
||
{
|
||
std::string msg = fmt::format("Failed to read hex string: %s (hex='%s')", std::make_error_code(err).message(), hex_str);
|
||
|
||
if (error)
|
||
{
|
||
*error = std::move(msg);
|
||
}
|
||
else
|
||
{
|
||
fmt::throw_exception("%s", msg);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC).
|
||
void aescbc128_decrypt(const unsigned char *key, unsigned char *iv, const unsigned char *in, unsigned char *out, usz len)
|
||
{
|
||
aes_context ctx;
|
||
aes_setkey_dec(&ctx, key, 128);
|
||
aes_crypt_cbc(&ctx, AES_DECRYPT, len, iv, in, out);
|
||
|
||
// Reset the IV.
|
||
memset(iv, 0, 0x10);
|
||
}
|
||
|
||
void aescbc128_encrypt(const unsigned char *key, unsigned char *iv, const unsigned char *in, unsigned char *out, usz len)
|
||
{
|
||
aes_context ctx;
|
||
aes_setkey_enc(&ctx, key, 128);
|
||
aes_crypt_cbc(&ctx, AES_ENCRYPT, len, iv, in, out);
|
||
|
||
// Reset the IV.
|
||
memset(iv, 0, 0x10);
|
||
}
|
||
|
||
void aesecb128_encrypt(const unsigned char *key, const unsigned char *in, unsigned char *out)
|
||
{
|
||
aes_context ctx;
|
||
aes_setkey_enc(&ctx, key, 128);
|
||
aes_crypt_ecb(&ctx, AES_ENCRYPT, in, out);
|
||
}
|
||
|
||
bool hmac_hash_compare(const unsigned char *key, int key_len, const unsigned char *in, usz in_len, const unsigned char *hash, usz hash_len)
|
||
{
|
||
const std::unique_ptr<u8[]> out(new u8[key_len]);
|
||
|
||
sha1_hmac(key, key_len, in, in_len, out.get());
|
||
|
||
return std::memcmp(out.get(), hash, hash_len) == 0;
|
||
}
|
||
|
||
void hmac_hash_forge(const unsigned char *key, int key_len, const unsigned char *in, usz in_len, unsigned char *hash)
|
||
{
|
||
sha1_hmac(key, key_len, in, in_len, hash);
|
||
}
|
||
|
||
bool cmac_hash_compare(const unsigned char *key, int key_len, const unsigned char *in, usz in_len, const unsigned char *hash, usz hash_len)
|
||
{
|
||
const std::unique_ptr<u8[]> out(new u8[key_len]);
|
||
|
||
aes_context ctx;
|
||
aes_setkey_enc(&ctx, key, 128);
|
||
aes_cmac(&ctx, in_len, in, out.get());
|
||
|
||
return std::memcmp(out.get(), hash, hash_len) == 0;
|
||
}
|
||
|
||
void cmac_hash_forge(const unsigned char *key, int /*key_len*/, const unsigned char *in, usz in_len, unsigned char *hash)
|
||
{
|
||
aes_context ctx;
|
||
aes_setkey_enc(&ctx, key, 128);
|
||
aes_cmac(&ctx, in_len, in, hash);
|
||
}
|
||
|
||
char* extract_file_name(std::string_view file_path, char real_file_name[CRYPTO_MAX_PATH])
|
||
{
|
||
if (const auto pos = file_path.find_last_of(fs::delim); pos != umax)
|
||
{
|
||
file_path.remove_prefix(pos + 1);
|
||
}
|
||
|
||
std::span r(real_file_name, CRYPTO_MAX_PATH);
|
||
strcpy_trunc(r, file_path);
|
||
return real_file_name;
|
||
}
|
||
|
||
std::string sha256_get_hash(const char* data, usz size, bool lower_case)
|
||
{
|
||
u8 res_hash[32];
|
||
mbedtls_sha256_context ctx;
|
||
mbedtls_sha256_init(&ctx);
|
||
mbedtls_sha256_starts_ret(&ctx, 0);
|
||
mbedtls_sha256_update_ret(&ctx, reinterpret_cast<const unsigned char*>(data), size);
|
||
mbedtls_sha256_finish_ret(&ctx, res_hash);
|
||
|
||
std::string res_hash_string("0000000000000000000000000000000000000000000000000000000000000000");
|
||
|
||
for (usz index = 0; index < 32; index++)
|
||
{
|
||
const auto pal = lower_case ? "0123456789abcdef" : "0123456789ABCDEF";
|
||
res_hash_string[index * 2] = pal[res_hash[index] >> 4];
|
||
res_hash_string[(index * 2) + 1] = pal[res_hash[index] & 15];
|
||
}
|
||
|
||
return res_hash_string;
|
||
}
|
||
|
||
void mbedtls_zeroize(void *v, size_t n)
|
||
{
|
||
static void *(*const volatile unop_memset)(void *, int, size_t) = &memset;
|
||
(void)unop_memset(v, 0, n);
|
||
}
|
||
|
||
|
||
// SC passphrase crypto
|
||
|
||
void sc_form_key(const u8* sc_key, const std::array<u8, PASSPHRASE_KEY_LEN>& laid_paid, u8* key)
|
||
{
|
||
for (u32 i = 0; i < PASSPHRASE_KEY_LEN; i++)
|
||
{
|
||
key[i] = static_cast<u8>(sc_key[i] ^ laid_paid[i]);
|
||
}
|
||
}
|
||
|
||
std::array<u8, PASSPHRASE_KEY_LEN> sc_combine_laid_paid(s64 laid, s64 paid)
|
||
{
|
||
const std::string paid_laid = fmt::format("%016llx%016llx", laid, paid);
|
||
std::array<u8, PASSPHRASE_KEY_LEN> out{};
|
||
hex_to_bytes(out.data(), paid_laid, PASSPHRASE_KEY_LEN * 2);
|
||
return out;
|
||
}
|
||
|
||
std::array<u8, PASSPHRASE_KEY_LEN> vtrm_get_laid_paid_from_type(int type)
|
||
{
|
||
// No idea what this type stands for
|
||
switch (type)
|
||
{
|
||
case 0: return sc_combine_laid_paid(0xFFFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL);
|
||
case 1: return sc_combine_laid_paid(LAID_2, 0x1070000000000001L);
|
||
case 2: return sc_combine_laid_paid(LAID_2, 0x0000000000000000L);
|
||
case 3: return sc_combine_laid_paid(LAID_2, PAID_69);
|
||
default:
|
||
fmt::throw_exception("vtrm_get_laid_paid_from_type: Wrong type specified (type=%d)", type);
|
||
}
|
||
}
|
||
|
||
std::array<u8, PASSPHRASE_KEY_LEN> vtrm_portability_laid_paid()
|
||
{
|
||
// 107000002A000001
|
||
return sc_combine_laid_paid(0x0000000000000000L, 0x0000000000000000L);
|
||
}
|
||
|
||
int sc_decrypt(const u8* sc_key, const std::array<u8, PASSPHRASE_KEY_LEN>& laid_paid, u8* iv, const u8* input, u8* output)
|
||
{
|
||
aes_context ctx;
|
||
u8 key[PASSPHRASE_KEY_LEN];
|
||
sc_form_key(sc_key, laid_paid, key);
|
||
aes_setkey_dec(&ctx, key, 128);
|
||
return aes_crypt_cbc(&ctx, AES_DECRYPT, PASSPHRASE_OUT_LEN, iv, input, output);
|
||
}
|
||
|
||
int vtrm_decrypt(int type, u8* iv, const u8* input, u8* output)
|
||
{
|
||
return sc_decrypt(SC_ISO_SERIES_KEY_2, vtrm_get_laid_paid_from_type(type), iv, input, output);
|
||
}
|
||
|
||
int vtrm_decrypt_master(s64 laid, s64 paid, u8* iv, const u8* input, u8* output)
|
||
{
|
||
return sc_decrypt(SC_ISO_SERIES_INTERNAL_KEY_3, sc_combine_laid_paid(laid, paid), iv, input, output);
|
||
}
|
||
|
||
const u8* vtrm_portability_type_mapper(int type)
|
||
{
|
||
// No idea what this type stands for
|
||
switch (type)
|
||
{
|
||
//case 0: return key_for_type_1;
|
||
case 1: return SC_ISO_SERIES_KEY_2;
|
||
case 2: return SC_ISO_SERIES_KEY_1;
|
||
case 3: return SC_KEY_FOR_MASTER_2;
|
||
default:
|
||
fmt::throw_exception("vtrm_portability_type_mapper: Wrong type specified (type=%d)", type);
|
||
}
|
||
}
|
||
|
||
int vtrm_decrypt_with_portability(int type, u8* iv, const u8* input, u8* output)
|
||
{
|
||
return sc_decrypt(vtrm_portability_type_mapper(type), vtrm_portability_laid_paid(), iv, input, output);
|
||
}
|