mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-04-29 14:59:26 -04:00
690 lines
21 KiB
C++
690 lines
21 KiB
C++
#include "iosu_crypto.h"
|
|
|
|
#include "config/ActiveSettings.h"
|
|
#include "openssl/bn.h"
|
|
#include "openssl/obj_mac.h"
|
|
#include "openssl/ec.h"
|
|
#include "openssl/x509.h"
|
|
#include "openssl/ssl.h"
|
|
#include "openssl/ecdsa.h"
|
|
|
|
#include "util/crypto/aes128.h"
|
|
#include "Common/FileStream.h"
|
|
|
|
uint8 otpMem[1024];
|
|
bool hasOtpMem = false;
|
|
|
|
uint8 seepromMem[512];
|
|
bool hasSeepromMem = false;
|
|
|
|
struct
|
|
{
|
|
bool hasCertificates;
|
|
struct
|
|
{
|
|
bool isValid;
|
|
sint32 id;
|
|
X509* cert;
|
|
std::vector<uint8> certData;
|
|
RSA* pkey;
|
|
std::vector<uint8> pkeyDERData;
|
|
}certList[256];
|
|
sint32 certListCount;
|
|
}iosuCryptoCertificates = { 0 };
|
|
|
|
CertECC_t g_wiiuDeviceCert;
|
|
|
|
void iosuCrypto_readOtpData(void* output, sint32 wordIndex, sint32 size)
|
|
{
|
|
memcpy(output, otpMem + wordIndex * 4, size);
|
|
}
|
|
|
|
void iosuCrypto_readOtpData(uint32be& output, sint32 wordIndex)
|
|
{
|
|
memcpy(&output, otpMem + wordIndex * 4, sizeof(uint32be));
|
|
}
|
|
|
|
void iosuCrypto_readSeepromData(void* output, sint32 wordIndex, sint32 size)
|
|
{
|
|
memcpy(output, seepromMem + wordIndex * 2, size);
|
|
}
|
|
|
|
bool iosuCrypto_getDeviceId(uint32* deviceId)
|
|
{
|
|
uint32be deviceIdBE;
|
|
*deviceId = 0;
|
|
if (!hasOtpMem)
|
|
return false;
|
|
iosuCrypto_readOtpData(&deviceIdBE, 0x87, sizeof(uint32));
|
|
*deviceId = (uint32)deviceIdBE;
|
|
return true;
|
|
}
|
|
|
|
void iosuCrypto_getDeviceSerialString(char* serialString)
|
|
{
|
|
char serialStringPart0[32]; // code
|
|
char serialStringPart1[32]; // serial
|
|
if (hasSeepromMem == false)
|
|
{
|
|
strcpy(serialString, "FEH000000000");
|
|
return;
|
|
}
|
|
memset(serialStringPart0, 0, sizeof(serialStringPart0));
|
|
memset(serialStringPart1, 0, sizeof(serialStringPart1));
|
|
iosuCrypto_readSeepromData(serialStringPart0, 0xAC, 8);
|
|
iosuCrypto_readSeepromData(serialStringPart1, 0xB0, 0x10);
|
|
sprintf(serialString, "%s%s", serialStringPart0, serialStringPart1);
|
|
}
|
|
|
|
static const char* base64_charset =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
"abcdefghijklmnopqrstuvwxyz"
|
|
"0123456789+/";
|
|
|
|
int iosuCrypto_base64Encode(unsigned char const* bytes_to_encode, unsigned int inputLen, char* output)
|
|
{
|
|
int i = 0;
|
|
int j = 0;
|
|
unsigned char charArray_3[3];
|
|
unsigned char charArray_4[4];
|
|
sint32 outputLength = 0;
|
|
while (inputLen--)
|
|
{
|
|
charArray_3[i++] = *(bytes_to_encode++);
|
|
if (i == 3)
|
|
{
|
|
charArray_4[0] = (charArray_3[0] & 0xfc) >> 2;
|
|
charArray_4[1] = ((charArray_3[0] & 0x03) << 4) + ((charArray_3[1] & 0xf0) >> 4);
|
|
charArray_4[2] = ((charArray_3[1] & 0x0f) << 2) + ((charArray_3[2] & 0xc0) >> 6);
|
|
charArray_4[3] = charArray_3[2] & 0x3f;
|
|
for (i = 0; (i < 4); i++)
|
|
{
|
|
output[outputLength] = base64_charset[charArray_4[i]];
|
|
outputLength++;
|
|
}
|
|
i = 0;
|
|
}
|
|
}
|
|
if (i)
|
|
{
|
|
for (j = i; j < 3; j++)
|
|
charArray_3[j] = '\0';
|
|
|
|
charArray_4[0] = (charArray_3[0] & 0xfc) >> 2;
|
|
charArray_4[1] = ((charArray_3[0] & 0x03) << 4) + ((charArray_3[1] & 0xf0) >> 4);
|
|
charArray_4[2] = ((charArray_3[1] & 0x0f) << 2) + ((charArray_3[2] & 0xc0) >> 6);
|
|
charArray_4[3] = charArray_3[2] & 0x3f;
|
|
|
|
for (j = 0; j < (i + 1); j++)
|
|
{
|
|
output[outputLength] = base64_charset[charArray_4[j]];
|
|
outputLength++;
|
|
}
|
|
while (i++ < 3)
|
|
{
|
|
output[outputLength] = '=';
|
|
outputLength++;
|
|
}
|
|
}
|
|
return outputLength;
|
|
}
|
|
|
|
std::string iosuCrypto_base64Encode(unsigned char const* bytes_to_encode, unsigned int inputLen)
|
|
{
|
|
int encodedLen = inputLen / 3 * 4 + 16;
|
|
std::string strB64;
|
|
strB64.resize(encodedLen);
|
|
int outputLen = iosuCrypto_base64Encode(bytes_to_encode, inputLen, strB64.data());
|
|
cemu_assert_debug(outputLen < strB64.size());
|
|
strB64.resize(outputLen);
|
|
return strB64;
|
|
}
|
|
static_assert(sizeof(CertECC_t) == sizeof(CertECC_t));
|
|
|
|
EC_KEY* ECCPubKey_getPublicKey(ECCPubKey& pubKey) // verified and works
|
|
{
|
|
BIGNUM* bn_r = BN_new();
|
|
BIGNUM* bn_s = BN_new();
|
|
BN_bin2bn(pubKey.keyData + 0, 30, bn_r);
|
|
BN_bin2bn(pubKey.keyData + 30, 30, bn_s);
|
|
|
|
EC_KEY* ec_pubKey = EC_KEY_new_by_curve_name(NID_sect233r1);
|
|
int r = EC_KEY_set_public_key_affine_coordinates(ec_pubKey, bn_r, bn_s);
|
|
|
|
BN_free(bn_r);
|
|
BN_free(bn_s);
|
|
|
|
return ec_pubKey;
|
|
}
|
|
|
|
ECDSA_SIG* ECCPubKey_getSignature(CertECC_t& cert)
|
|
{
|
|
BIGNUM* bn_r = BN_new();
|
|
BIGNUM* bn_s = BN_new();
|
|
BN_bin2bn(cert.signature + 0, 30, bn_r);
|
|
BN_bin2bn(cert.signature + 30, 30, bn_s);
|
|
|
|
//EC_KEY* ec_pubKey = EC_KEY_new_by_curve_name(NID_sect233r1);
|
|
//int r = EC_KEY_set_public_key_affine_coordinates(ec_pubKey, bn_r, bn_s);
|
|
ECDSA_SIG* ec_sig = ECDSA_SIG_new();
|
|
//ECDSA_do_sign_ex
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
ECDSA_SIG_set0(ec_sig, bn_r, bn_s);
|
|
#else
|
|
BN_copy(ec_sig->r, bn_r);
|
|
BN_copy(ec_sig->s, bn_s);
|
|
#endif
|
|
|
|
BN_free(bn_r);
|
|
BN_free(bn_s);
|
|
|
|
return ec_sig;
|
|
}
|
|
|
|
void ECCPubKey_setSignature(CertECC_t& cert, ECDSA_SIG* sig)
|
|
{
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
const BIGNUM* sig_r = nullptr, * sig_s = nullptr;
|
|
ECDSA_SIG_get0(sig, &sig_r, &sig_s);
|
|
|
|
sint32 lenR = BN_num_bytes(sig_r);
|
|
sint32 lenS = BN_num_bytes(sig_s);
|
|
|
|
cemu_assert_debug(lenR <= 30);
|
|
cemu_assert_debug(lenS <= 30);
|
|
|
|
memset(cert.signature, 0, sizeof(cert.signature));
|
|
BN_bn2bin(sig_r, cert.signature + (30 - lenR));
|
|
BN_bn2bin(sig_s, cert.signature + 60 / 2 + (30 - lenS));
|
|
#else
|
|
sint32 lenR = BN_num_bytes(sig->r);
|
|
sint32 lenS = BN_num_bytes(sig->s);
|
|
|
|
cemu_assert_debug(lenR <= 30);
|
|
cemu_assert_debug(lenS <= 30);
|
|
|
|
memset(cert.signature, 0, sizeof(cert.signature));
|
|
BN_bn2bin(sig->r, cert.signature + (30 - lenR));
|
|
BN_bn2bin(sig->s, cert.signature + 60 / 2 + (30 - lenS));
|
|
#endif
|
|
}
|
|
|
|
ECCPrivKey g_consoleCertPrivKey{};
|
|
|
|
void iosuCrypto_getDeviceCertPrivateKey(void* privKeyOut, sint32 len)
|
|
{
|
|
cemu_assert(len == 30);
|
|
memcpy(privKeyOut, g_consoleCertPrivKey.keyData, 30);
|
|
}
|
|
|
|
void iosuCrypto_getDeviceCertificate(void* certOut, sint32 len)
|
|
{
|
|
cemu_assert(len == 0x180);
|
|
memcpy(certOut, &g_wiiuDeviceCert, 0x180);
|
|
}
|
|
|
|
void iosuCrypto_generateDeviceCertificate()
|
|
{
|
|
static_assert(sizeof(g_wiiuDeviceCert) == 0x180);
|
|
memset(&g_wiiuDeviceCert, 0, sizeof(g_wiiuDeviceCert));
|
|
if (!hasOtpMem)
|
|
return; // cant generate certificate without OPT
|
|
|
|
// set header based on otp security mode
|
|
g_wiiuDeviceCert.sigType = CertECC_t::SIGTYPE::ECC_SHA256;
|
|
|
|
g_wiiuDeviceCert.ukn0C0[0] = 0x00;
|
|
g_wiiuDeviceCert.ukn0C0[1] = 0x00;
|
|
g_wiiuDeviceCert.ukn0C0[2] = 0x00;
|
|
g_wiiuDeviceCert.ukn0C0[3] = 0x02;
|
|
|
|
|
|
iosuCrypto_readOtpData(g_wiiuDeviceCert.signature, 0xA3, 0x3C);
|
|
|
|
uint32be caValue;
|
|
iosuCrypto_readOtpData(caValue, 0xA1);
|
|
uint32be msValue;
|
|
iosuCrypto_readOtpData(msValue, 0xA0);
|
|
|
|
sprintf(g_wiiuDeviceCert.certificateSubject, "Root-CA%08x-MS%08x", (uint32)caValue, (uint32)msValue);
|
|
|
|
uint32be ngNameValue;
|
|
iosuCrypto_readOtpData(ngNameValue, 0x87);
|
|
sprintf(g_wiiuDeviceCert.ngName, "NG%08x", (uint32)ngNameValue);
|
|
|
|
iosuCrypto_readOtpData(&g_wiiuDeviceCert.ngKeyId, 0xA2, sizeof(uint32));
|
|
|
|
|
|
uint8 privateKey[0x20];
|
|
memset(privateKey, 0, sizeof(privateKey));
|
|
iosuCrypto_readOtpData(privateKey, 0x88, 0x1E);
|
|
memcpy(g_consoleCertPrivKey.keyData, privateKey, 30);
|
|
|
|
auto context = BN_CTX_new();
|
|
BN_CTX_start(context);
|
|
BIGNUM* bn_privKey = BN_CTX_get(context);
|
|
BN_bin2bn(privateKey, 0x1E, bn_privKey);
|
|
|
|
EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_sect233r1);
|
|
EC_POINT *pubkey = EC_POINT_new(group);
|
|
|
|
EC_POINT_mul(group, pubkey, bn_privKey, NULL, NULL, NULL);
|
|
|
|
BIGNUM* bn_x = BN_CTX_get(context);
|
|
BIGNUM* bn_y = BN_CTX_get(context);
|
|
|
|
EC_POINT_get_affine_coordinates(group, pubkey, bn_x, bn_y, NULL);
|
|
|
|
uint8 publicKeyOutput[0x3C];
|
|
memset(publicKeyOutput, 0, sizeof(publicKeyOutput));
|
|
|
|
sint32 lenX = BN_num_bytes(bn_x);
|
|
sint32 lenY = BN_num_bytes(bn_y);
|
|
|
|
BN_bn2bin(bn_x, publicKeyOutput + (0x1E - lenX)); // todo - verify if the bias is correct
|
|
BN_bn2bin(bn_y, publicKeyOutput + 0x3C / 2 + (0x1E - lenY));
|
|
|
|
memcpy(g_wiiuDeviceCert.publicKey, publicKeyOutput, 0x3C);
|
|
|
|
// clean up
|
|
EC_POINT_free(pubkey);
|
|
BN_CTX_end(context); // clears all BN variables
|
|
BN_CTX_free(context);
|
|
}
|
|
|
|
bool iosuCrypto_hasAllDataForLogin()
|
|
{
|
|
if (hasOtpMem == false)
|
|
return false;
|
|
if (hasSeepromMem == false)
|
|
return false;
|
|
// todo - check if certificates are available
|
|
return true;
|
|
}
|
|
|
|
sint32 iosuCrypto_getDeviceCertificateBase64Encoded(char* output)
|
|
{
|
|
iosuCrypto_base64Encode((uint8*)&g_wiiuDeviceCert, sizeof(g_wiiuDeviceCert), output);
|
|
sint32 len = sizeof(g_wiiuDeviceCert) / 3 * 4;
|
|
output[len] = '\0';
|
|
return len;
|
|
}
|
|
|
|
bool iosuCrypto_loadCertificate(uint32 id, std::wstring_view mlcSubpath, std::wstring_view pkeyMlcSubpath)
|
|
{
|
|
X509* cert = nullptr;
|
|
// load cert data
|
|
const auto certPath = ActiveSettings::GetMlcPath(mlcSubpath);
|
|
auto certData = FileStream::LoadIntoMemory(certPath);
|
|
if (!certData)
|
|
return false; // file missing
|
|
// get optional aes encrypted private key data
|
|
std::optional<std::vector<uint8>> pkeyData;
|
|
if (!pkeyMlcSubpath.empty())
|
|
{
|
|
const auto pkeyPath = ActiveSettings::GetMlcPath(pkeyMlcSubpath);
|
|
pkeyData = FileStream::LoadIntoMemory(pkeyPath);
|
|
if (!pkeyData || pkeyData->empty())
|
|
{
|
|
cemuLog_log(LogType::Force, "Unable to load private key file {}", pkeyPath.generic_string());
|
|
return false;
|
|
}
|
|
else if ((pkeyData->size() % 16) != 0)
|
|
{
|
|
cemuLog_log(LogType::Force, "Private key file has invalid length. Possibly corrupted? File: {}", pkeyPath.generic_string());
|
|
return false;
|
|
}
|
|
}
|
|
// load certificate
|
|
unsigned char* tempPtr = (unsigned char*)certData->data();
|
|
cert = d2i_X509(nullptr, (const unsigned char**)&tempPtr, certData->size());
|
|
if (cert == nullptr)
|
|
{
|
|
cemuLog_log(LogType::Force, "IOSU_CRYPTO: Unable to load certificate \"{}\"", boost::nowide::narrow(std::wstring(mlcSubpath)));
|
|
return false;
|
|
}
|
|
// load optional rsa key
|
|
RSA* pkeyRSA = nullptr;
|
|
if (pkeyData)
|
|
{
|
|
cemu_assert((pkeyData->size() & 15) == 0);
|
|
uint8 aesKey[16];
|
|
uint8 iv[16] = { 0 };
|
|
uint8 pkeyDecryptedData[4096];
|
|
// decrypt pkey
|
|
iosuCrypto_readOtpData(aesKey, 0x120 / 4, 16);
|
|
AES128_CBC_decrypt(pkeyDecryptedData, pkeyData->data(), pkeyData->size(), aesKey, iv);
|
|
// convert to OpenSSL RSA pkey
|
|
unsigned char* pkeyTempPtr = pkeyDecryptedData;
|
|
pkeyRSA = d2i_RSAPrivateKey(nullptr, (const unsigned char **)&pkeyTempPtr, pkeyData->size());
|
|
if (pkeyRSA == nullptr)
|
|
{
|
|
cemuLog_log(LogType::Force, "IOSU_CRYPTO: Unable to decrypt private key \"{}\"", boost::nowide::narrow(std::wstring(pkeyMlcSubpath)));
|
|
return false;
|
|
}
|
|
// encode private key as DER
|
|
EVP_PKEY *evpPkey = EVP_PKEY_new();
|
|
EVP_PKEY_assign_RSA(evpPkey, pkeyRSA);
|
|
std::vector<uint8> derPKeyData(1024 * 32);
|
|
unsigned char* derPkeyTemp = derPKeyData.data();
|
|
sint32 derPkeySize = i2d_PrivateKey(evpPkey, &derPkeyTemp);
|
|
derPKeyData.resize(derPkeySize);
|
|
derPKeyData.shrink_to_fit();
|
|
iosuCryptoCertificates.certList[iosuCryptoCertificates.certListCount].pkeyDERData = derPKeyData;
|
|
}
|
|
|
|
// register certificate and optional pkey
|
|
iosuCryptoCertificates.certList[iosuCryptoCertificates.certListCount].cert = cert;
|
|
iosuCryptoCertificates.certList[iosuCryptoCertificates.certListCount].certData = *certData;
|
|
iosuCryptoCertificates.certList[iosuCryptoCertificates.certListCount].pkey = pkeyRSA;
|
|
iosuCryptoCertificates.certList[iosuCryptoCertificates.certListCount].id = id;
|
|
iosuCryptoCertificates.certList[iosuCryptoCertificates.certListCount].isValid = true;
|
|
iosuCryptoCertificates.certListCount++;
|
|
return true;
|
|
}
|
|
|
|
bool iosuCrypto_addClientCertificate(void* sslctx, sint32 certificateId)
|
|
{
|
|
SSL_CTX* ctx = (SSL_CTX*)sslctx;
|
|
// find entry
|
|
for (sint32 i = 0; i < iosuCryptoCertificates.certListCount; i++)
|
|
{
|
|
if (iosuCryptoCertificates.certList[i].isValid && iosuCryptoCertificates.certList[i].id == certificateId)
|
|
{
|
|
if (SSL_CTX_use_certificate(ctx, iosuCryptoCertificates.certList[i].cert) != 1)
|
|
{
|
|
cemuLog_log(LogType::Force, "Unable to setup certificate {}", certificateId);
|
|
return false;
|
|
}
|
|
if (SSL_CTX_use_RSAPrivateKey(ctx, iosuCryptoCertificates.certList[i].pkey) != 1)
|
|
{
|
|
cemuLog_log(LogType::Force, "Unable to setup certificate {} RSA private key", certificateId);
|
|
return false;
|
|
}
|
|
|
|
if (SSL_CTX_check_private_key(ctx) == false)
|
|
{
|
|
cemuLog_log(LogType::Force, "Certificate private key could not be validated (verify required files for online mode or disable online mode)");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
cemuLog_log(LogType::Force, "Certificate not found (verify required files for online mode or disable online mode)");
|
|
return false;
|
|
}
|
|
|
|
bool iosuCrypto_addCACertificate(void* sslctx, sint32 certificateId)
|
|
{
|
|
SSL_CTX* ctx = (SSL_CTX*)sslctx;
|
|
// find entry
|
|
for (sint32 i = 0; i < iosuCryptoCertificates.certListCount; i++)
|
|
{
|
|
if (iosuCryptoCertificates.certList[i].isValid && iosuCryptoCertificates.certList[i].id == certificateId)
|
|
{
|
|
X509_STORE* store = SSL_CTX_get_cert_store((SSL_CTX*)sslctx);
|
|
X509_STORE_add_cert(store, iosuCryptoCertificates.certList[i].cert);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool iosuCrypto_addCustomCACertificate(void* sslctx, uint8* certData, sint32 certLength)
|
|
{
|
|
SSL_CTX* ctx = (SSL_CTX*)sslctx;
|
|
X509_STORE* store = SSL_CTX_get_cert_store((SSL_CTX*)sslctx);
|
|
unsigned char* tempPtr = (unsigned char*)certData;
|
|
X509* cert = d2i_X509(NULL, (const unsigned char**)&tempPtr, certLength);
|
|
if (cert == nullptr)
|
|
{
|
|
cemuLog_log(LogType::Force, "Invalid custom server PKI certificate");
|
|
return false;
|
|
}
|
|
X509_STORE_add_cert(store, cert);
|
|
return true;
|
|
}
|
|
|
|
uint8* iosuCrypto_getCertificateDataById(sint32 certificateId, sint32* certificateSize)
|
|
{
|
|
for (sint32 i = 0; i < iosuCryptoCertificates.certListCount; i++)
|
|
{
|
|
if (iosuCryptoCertificates.certList[i].isValid && iosuCryptoCertificates.certList[i].id == certificateId)
|
|
{
|
|
*certificateSize = iosuCryptoCertificates.certList[i].certData.size();
|
|
return iosuCryptoCertificates.certList[i].certData.data();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
uint8* iosuCrypto_getCertificatePrivateKeyById(sint32 certificateId, sint32* certificateSize)
|
|
{
|
|
for (sint32 i = 0; i < iosuCryptoCertificates.certListCount; i++)
|
|
{
|
|
if (iosuCryptoCertificates.certList[i].isValid && iosuCryptoCertificates.certList[i].id == certificateId)
|
|
{
|
|
*certificateSize = iosuCryptoCertificates.certList[i].pkeyDERData.size();
|
|
return iosuCryptoCertificates.certList[i].pkeyDERData.data();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
struct
|
|
{
|
|
const int id;
|
|
const wchar_t name[256];
|
|
const wchar_t key[256];
|
|
} const g_certificates[] = {
|
|
// NINTENDO CLIENT CERTS
|
|
{ 1, L"ccerts/WIIU_COMMON_1_CERT.der", L"ccerts/WIIU_COMMON_1_RSA_KEY.aes" },
|
|
{ 3, L"ccerts/WIIU_ACCOUNT_1_CERT.der", L"ccerts/WIIU_ACCOUNT_1_RSA_KEY.aes" },
|
|
{ 4, L"ccerts/WIIU_OLIVE_1_CERT.der", L"ccerts/WIIU_OLIVE_1_RSA_KEY.aes" },
|
|
{ 5, L"ccerts/WIIU_VINO_1_CERT.der", L"ccerts/WIIU_VINO_1_RSA_KEY.aes" },
|
|
{ 6, L"ccerts/WIIU_WOOD_1_CERT.der", L"ccerts/WIIU_WOOD_1_RSA_KEY.aes" },
|
|
{ 7, L"ccerts/WIIU_OLIVE_1_CERT.der", L"ccerts/WIIU_OLIVE_1_RSA_KEY.aes" },
|
|
{ 8, L"ccerts/WIIU_WOOD_1_CERT.der", L"ccerts/WIIU_WOOD_1_RSA_KEY.aes" },
|
|
|
|
// NINTENDO CA CERTS
|
|
{ 100, L"scerts/CACERT_NINTENDO_CA.der", L"" },
|
|
{ 101, L"scerts/CACERT_NINTENDO_CA_G2.der", L"" },
|
|
{ 102, L"scerts/CACERT_NINTENDO_CA_G3.der", L"" },
|
|
{ 103, L"scerts/CACERT_NINTENDO_CLASS2_CA.der", L"" },
|
|
{ 104, L"scerts/CACERT_NINTENDO_CLASS2_CA_G2.der", L"" },
|
|
{ 105, L"scerts/CACERT_NINTENDO_CLASS2_CA_G3.der", L"" },
|
|
|
|
// COMMERCIAL CA CERTS
|
|
{ 1001, L"scerts/BALTIMORE_CYBERTRUST_ROOT_CA.der", L"" },
|
|
{ 1002, L"scerts/CYBERTRUST_GLOBAL_ROOT_CA.der", L"" },
|
|
{ 1003, L"scerts/VERIZON_GLOBAL_ROOT_CA.der", L"" },
|
|
{ 1004, L"scerts/GLOBALSIGN_ROOT_CA.der", L"" },
|
|
{ 1005, L"scerts/GLOBALSIGN_ROOT_CA_R2.der", L"" },
|
|
{ 1006, L"scerts/GLOBALSIGN_ROOT_CA_R3.der", L"" },
|
|
{ 1007, L"scerts/VERISIGN_CLASS3_PUBLIC_PRIMARY_CA_G3.der", L"" },
|
|
{ 1008, L"scerts/VERISIGN_UNIVERSAL_ROOT_CA.der", L"" },
|
|
{ 1009, L"scerts/VERISIGN_CLASS3_PUBLIC_PRIMARY_CA_G5.der", L"" },
|
|
{ 1010, L"scerts/THAWTE_PRIMARY_ROOT_CA_G3.der", L"" },
|
|
{ 1011, L"scerts/THAWTE_PRIMARY_ROOT_CA.der", L"" },
|
|
{ 1012, L"scerts/GEOTRUST_GLOBAL_CA.der", L"" },
|
|
{ 1013, L"scerts/GEOTRUST_GLOBAL_CA2.der", L"" },
|
|
{ 1014, L"scerts/GEOTRUST_PRIMARY_CA.der", L"" },
|
|
{ 1015, L"scerts/GEOTRUST_PRIMARY_CA_G3.der", L"" },
|
|
{ 1016, L"scerts/ADDTRUST_EXT_CA_ROOT.der", L"" },
|
|
{ 1017, L"scerts/COMODO_CA.der", L"" },
|
|
{ 1018, L"scerts/UTN_DATACORP_SGC_CA.der", L"" },
|
|
{ 1019, L"scerts/UTN_USERFIRST_HARDWARE_CA.der" , L"" },
|
|
{ 1020, L"scerts/DIGICERT_HIGH_ASSURANCE_EV_ROOT_CA.der", L"" },
|
|
{ 1021, L"scerts/DIGICERT_ASSURED_ID_ROOT_CA.der", L"" },
|
|
{ 1022, L"scerts/DIGICERT_GLOBAL_ROOT_CA.der", L"" },
|
|
{ 1023, L"scerts/GTE_CYBERTRUST_GLOBAL_ROOT.der", L"" },
|
|
{ 1024, L"scerts/VERISIGN_CLASS3_PUBLIC_PRIMARY_CA.der", L"" },
|
|
{ 1025, L"scerts/THAWTE_PREMIUM_SERVER_CA.der", L"" },
|
|
{ 1026, L"scerts/EQUIFAX_SECURE_CA.der", L"" },
|
|
{ 1027, L"scerts/ENTRUST_SECURE_SERVER_CA.der", L"" },
|
|
{ 1028, L"scerts/VERISIGN_CLASS3_PUBLIC_PRIMARY_CA_G2.der", L"" },
|
|
{ 1029, L"scerts/ENTRUST_CA_2048.der", L"" },
|
|
{ 1030, L"scerts/ENTRUST_ROOT_CA.der", L"" },
|
|
{ 1031, L"scerts/ENTRUST_ROOT_CA_G2.der", L"" },
|
|
{ 1032, L"scerts/DIGICERT_ASSURED_ID_ROOT_CA_G2.der", L"" },
|
|
{ 1033, L"scerts/DIGICERT_GLOBAL_ROOT_CA_G2.der", L"" },
|
|
};
|
|
|
|
void iosuCrypto_loadSSLCertificates()
|
|
{
|
|
if (iosuCryptoCertificates.hasCertificates)
|
|
return;
|
|
|
|
if (!hasOtpMem)
|
|
return; // cant load certificates without OTP keys
|
|
|
|
// load CA certificate
|
|
bool hasAllCertificates = true;
|
|
for( const auto& c : g_certificates )
|
|
{
|
|
std::wstring certDir = L"sys/title/0005001b/10054000/content/";
|
|
std::wstring certFilePath = certDir + c.name;
|
|
std::wstring keyFilePath;
|
|
|
|
if( *c.key )
|
|
keyFilePath = certDir + c.key;
|
|
else
|
|
keyFilePath.clear();
|
|
|
|
if (iosuCrypto_loadCertificate(c.id, certFilePath, keyFilePath) == false)
|
|
{
|
|
cemuLog_log(LogType::Force, "Unable to load certificate \"{}\"", boost::nowide::narrow(certFilePath));
|
|
hasAllCertificates = false;
|
|
}
|
|
}
|
|
iosuCryptoCertificates.hasCertificates = hasAllCertificates; // true
|
|
}
|
|
|
|
void iosuCrypto_init()
|
|
{
|
|
// load OTP dump
|
|
if (std::ifstream otp_file(ActiveSettings::GetUserDataPath("otp.bin"), std::ifstream::in | std::ios::binary); otp_file.is_open())
|
|
{
|
|
otp_file.seekg(0, std::ifstream::end);
|
|
const auto length = otp_file.tellg();
|
|
otp_file.seekg(0, std::ifstream::beg);
|
|
// verify if OTP is ok
|
|
if (length != 1024) // todo - should also check some fixed values to verify integrity of otp dump
|
|
{
|
|
cemuLog_log(LogType::Force, "IOSU_CRYPTO: otp.bin has wrong size (must be 1024 bytes)");
|
|
hasOtpMem = false;
|
|
}
|
|
else
|
|
{
|
|
otp_file.read((char*)otpMem, 1024);
|
|
hasOtpMem = (bool)otp_file;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cemuLog_log(LogType::Force, "IOSU_CRYPTO: No otp.bin found. Online mode cannot be used");
|
|
hasOtpMem = false;
|
|
}
|
|
|
|
if (std::ifstream seeprom_file(ActiveSettings::GetUserDataPath("seeprom.bin"), std::ifstream::in | std::ios::binary); seeprom_file.is_open())
|
|
{
|
|
seeprom_file.seekg(0, std::ifstream::end);
|
|
const auto length = seeprom_file.tellg();
|
|
seeprom_file.seekg(0, std::ifstream::beg);
|
|
// verify if seeprom is ok
|
|
if (length != 512) // todo - maybe check some known values to verify integrity of seeprom
|
|
{
|
|
cemuLog_log(LogType::Force, "IOSU_CRYPTO: seeprom.bin has wrong size (must be 512 bytes)");
|
|
hasSeepromMem = false;
|
|
}
|
|
else
|
|
{
|
|
seeprom_file.read((char*)seepromMem, 512);
|
|
hasSeepromMem = (bool)seeprom_file;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cemuLog_log(LogType::Force, "IOSU_CRYPTO: No Seeprom.bin found. Online mode cannot be used");
|
|
hasSeepromMem = false;
|
|
}
|
|
|
|
// generate device certificate
|
|
iosuCrypto_generateDeviceCertificate();
|
|
// load SSL certificates
|
|
iosuCrypto_loadSSLCertificates();
|
|
}
|
|
|
|
bool iosuCrypto_checkRequirementMLCFile(std::string_view mlcSubpath, std::wstring& additionalErrorInfo_filePath)
|
|
{
|
|
const auto path = ActiveSettings::GetMlcPath(mlcSubpath);
|
|
additionalErrorInfo_filePath = path.generic_wstring();
|
|
sint32 fileDataSize = 0;
|
|
auto fileData = FileStream::LoadIntoMemory(path);
|
|
if (!fileData)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
sint32 iosuCrypt_checkRequirementsForOnlineMode(std::wstring& additionalErrorInfo)
|
|
{
|
|
std::error_code ec;
|
|
// check if otp.bin is present
|
|
const auto otp_file = ActiveSettings::GetUserDataPath("otp.bin");
|
|
if(!fs::exists(otp_file, ec))
|
|
return IOS_CRYPTO_ONLINE_REQ_OTP_MISSING;
|
|
if(fs::file_size(otp_file, ec) != 1024)
|
|
return IOS_CRYPTO_ONLINE_REQ_OTP_CORRUPTED;
|
|
// check if seeprom.bin is present
|
|
const auto seeprom_file = ActiveSettings::GetUserDataPath("seeprom.bin");
|
|
if (!fs::exists(seeprom_file, ec))
|
|
return IOS_CRYPTO_ONLINE_REQ_SEEPROM_MISSING;
|
|
if (fs::file_size(seeprom_file, ec) != 512)
|
|
return IOS_CRYPTO_ONLINE_REQ_SEEPROM_CORRUPTED;
|
|
|
|
for (const auto& c : g_certificates)
|
|
{
|
|
std::string subPath = fmt::format("sys/title/0005001b/10054000/content/{}", boost::nowide::narrow(c.name));
|
|
if (iosuCrypto_checkRequirementMLCFile(subPath, additionalErrorInfo) == false)
|
|
{
|
|
cemuLog_log(LogType::Force, "Missing dumped file for online mode: {}", subPath);
|
|
return IOS_CRYPTO_ONLINE_REQ_MISSING_FILE;
|
|
}
|
|
|
|
if (*c.key)
|
|
{
|
|
std::string subPath = fmt::format("sys/title/0005001b/10054000/content/{}", boost::nowide::narrow(c.key));
|
|
if (iosuCrypto_checkRequirementMLCFile(subPath, additionalErrorInfo) == false)
|
|
{
|
|
cemuLog_log(LogType::Force, "Missing dumped file for online mode: {}", subPath);
|
|
return IOS_CRYPTO_ONLINE_REQ_MISSING_FILE;
|
|
}
|
|
}
|
|
}
|
|
return IOS_CRYPTO_ONLINE_REQ_OK;
|
|
}
|
|
|
|
std::vector<const wchar_t*> iosuCrypt_getCertificateKeys()
|
|
{
|
|
std::vector<const wchar_t*> result;
|
|
result.reserve(std::size(g_certificates));
|
|
for (const auto& c : g_certificates)
|
|
{
|
|
if (c.key[0] == '\0')
|
|
continue;
|
|
|
|
result.emplace_back(c.key);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::vector<const wchar_t*> iosuCrypt_getCertificateNames()
|
|
{
|
|
std::vector<const wchar_t*> result;
|
|
result.reserve(std::size(g_certificates));
|
|
for (const auto& c : g_certificates)
|
|
{
|
|
result.emplace_back(c.name);
|
|
}
|
|
return result;
|
|
}
|