#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 certData; RSA* pkey; std::vector 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> 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 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::string& additionalErrorInfo_filePath) { const auto path = ActiveSettings::GetMlcPath(mlcSubpath); additionalErrorInfo_filePath = _pathToUtf8(path); sint32 fileDataSize = 0; auto fileData = FileStream::LoadIntoMemory(path); if (!fileData) return false; return true; } sint32 iosuCrypt_checkRequirementsForOnlineMode(std::string& 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 iosuCrypt_getCertificateKeys() { std::vector 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 iosuCrypt_getCertificateNames() { std::vector result; result.reserve(std::size(g_certificates)); for (const auto& c : g_certificates) { result.emplace_back(c.name); } return result; }