diff --git a/CMakeLists.txt b/CMakeLists.txt index f785b5d0c..28a9b115c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,6 +137,8 @@ option(ENABLE_SSE42 "Enable SSE4.2 optimizations on x86_64" ON) option(ENABLE_DEVELOPER_OPTIONS "Enable functionality targeted at emulator developers" OFF) +option(ENABLE_BUILTIN_KEYBLOB "Enable the inclusion of the default crypto keys blob" ON) + # Compile options CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF) option(ENABLE_LTO "Enable link time optimization" ${DEFAULT_ENABLE_LTO}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f0da8bbe3..15799c0c4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -183,6 +183,9 @@ endif() if(ENABLE_DEVELOPER_OPTIONS) add_compile_definitions(ENABLE_DEVELOPER_OPTIONS) endif() +if(ENABLE_BUILTIN_KEYBLOB) + add_compile_definitions(ENABLE_BUILTIN_KEYBLOB) +endif() add_subdirectory(common) add_subdirectory(core) diff --git a/src/citra_qt/configuration/configure_system.cpp b/src/citra_qt/configuration/configure_system.cpp index 9ed8bebfa..6b47a28d1 100644 --- a/src/citra_qt/configuration/configure_system.cpp +++ b/src/citra_qt/configuration/configure_system.cpp @@ -670,12 +670,16 @@ void ConfigureSystem::RefreshSecureDataStatus() { return tr("Status: Loaded (Invalid Signature)"); case HW::UniqueData::SecureDataLoadStatus::RegionChanged: return tr("Status: Loaded (Region Changed)"); + case HW::UniqueData::SecureDataLoadStatus::CannotValidateSignature: + return tr("Status: Loaded (Cannot Validate Signature)"); case HW::UniqueData::SecureDataLoadStatus::NotFound: return tr("Status: Not Found"); case HW::UniqueData::SecureDataLoadStatus::Invalid: return tr("Status: Invalid"); case HW::UniqueData::SecureDataLoadStatus::IOError: return tr("Status: IO Error"); + case HW::UniqueData::SecureDataLoadStatus::NoCryptoKeys: + return tr("Status: Missing Crypto Keys"); default: return QString(); } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 79ed4695a..ec7a45bfe 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -464,7 +464,6 @@ add_library(citra_core STATIC hw/aes/key.h hw/ecc.cpp hw/ecc.h - hw/default_keys.h hw/rsa/rsa.cpp hw/rsa/rsa.h hw/unique_data.cpp @@ -502,6 +501,10 @@ add_library(citra_core STATIC tracer/recorder.h ) +if (ENABLE_BUILTIN_KEYBLOB) + target_sources(citra_core PRIVATE hw/default_keys.h) +endif() + create_target_directory_groups(citra_core) target_link_libraries(citra_core PUBLIC citra_common PRIVATE audio_core network video_core) diff --git a/src/core/hw/aes/key.cpp b/src/core/hw/aes/key.cpp index 2e46de838..80aa84d36 100644 --- a/src/core/hw/aes/key.cpp +++ b/src/core/hw/aes/key.cpp @@ -18,7 +18,9 @@ #include "core/hle/service/fs/archive.h" #include "core/hw/aes/arithmetic128.h" #include "core/hw/aes/key.h" +#ifdef ENABLE_BUILTIN_KEYBLOB #include "core/hw/default_keys.h" +#endif // ENABLE_BUILTIN_KEYBLOB #include "core/hw/rsa/rsa.h" #include "core/loader/loader.h" @@ -130,8 +132,8 @@ std::array, NumDlpNfcKeyYs> dlp_nfc_key_y_slots; std::array nfc_secrets; AESIV nfc_iv; -AESKey otp_key; -AESIV otp_iv; +AESKey otp_key{}; +AESIV otp_iv{}; // gets xor'd with the mac address to produce the final iv AESIV dlp_checksum_mod_iv; @@ -297,6 +299,7 @@ std::istringstream GetKeysStream() { if (file.is_open()) { return std::istringstream(std::string(std::istreambuf_iterator(file), {})); } else { +#ifdef ENABLE_BUILTIN_KEYBLOB // The key data is encrypted in the source to prevent easy access to it for unintended // purposes. std::vector kiv(16); @@ -304,6 +307,9 @@ std::istringstream GetKeysStream() { CryptoPP::CBC_Mode::Decryption(kiv.data(), kiv.size(), kiv.data()) .ProcessData(reinterpret_cast(s.data()), default_keys_enc, s.size()); return std::istringstream(s); +#else + return std::istringstream(""); +#endif // ENABLE_BUILTIN_KEYBLOB } } diff --git a/src/core/hw/default_keys.h b/src/core/hw/default_keys.h index 554b74ae1..22fdcdc64 100644 --- a/src/core/hw/default_keys.h +++ b/src/core/hw/default_keys.h @@ -2,7 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -unsigned char default_keys_enc[] = { +#pragma once + +#ifndef ENABLE_BUILTIN_KEYBLOB +#error Attempting to include default_keys.h, but ENABLE_BUILTIN_KEYBLOB is disabled. +#endif + +constexpr unsigned char default_keys_enc[] = { 0x4E, 0x81, 0xE9, 0x54, 0xCC, 0xDE, 0xFD, 0x56, 0x7D, 0xD2, 0x72, 0xE6, 0xD9, 0xCD, 0x8E, 0x11, 0xE1, 0x7F, 0x74, 0xF4, 0xFC, 0x54, 0xA6, 0xA4, 0x27, 0xC2, 0xD7, 0x50, 0xEA, 0xE7, 0xBE, 0xC9, 0xA7, 0x5E, 0xE0, 0x2E, 0x4A, 0xBE, 0xF5, 0xD5, 0x0D, 0x22, 0x76, 0x2E, 0xB6, 0x80, 0xD8, 0x54, @@ -468,4 +474,4 @@ unsigned char default_keys_enc[] = { 0x14, 0x79, 0xD0, 0xA8, 0x3C, 0xB3, 0x46, 0xC3, 0xDA, 0x6C, 0x0C, 0xEC, 0x2A, 0xB2, 0x9B, 0x21, 0xB2, 0xAD, 0x8C, 0x0C, 0x85, 0x9A, 0x8D, 0x7C, 0x10, 0xEA, 0x51, 0x1D, 0x2D, 0xDE, 0x7D, 0x8F}; -const long int default_keys_enc_size = sizeof(default_keys_enc); +constexpr long int default_keys_enc_size = sizeof(default_keys_enc); diff --git a/src/core/hw/rsa/rsa.h b/src/core/hw/rsa/rsa.h index b6dd2c1ca..54e944caa 100644 --- a/src/core/hw/rsa/rsa.h +++ b/src/core/hw/rsa/rsa.h @@ -1,4 +1,4 @@ -// Copyright 2020 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -14,7 +14,8 @@ class RsaSlot { public: RsaSlot() = default; RsaSlot(std::vector exponent, std::vector modulus) - : init(true), exponent(std::move(exponent)), modulus(std::move(modulus)) {} + : init_exponent(true), init_modulus(true), exponent(std::move(exponent)), + modulus(std::move(modulus)) {} std::vector ModularExponentiation(std::span message, int out_size_bytes = -1) const; @@ -25,11 +26,12 @@ public: explicit operator bool() const { // TODO(B3N30): Maybe check if exponent and modulus are vailid - return init; + return init_exponent && init_modulus; } void SetExponent(const std::vector& e) { exponent = e; + init_exponent = true; } const std::vector& GetExponent() const { @@ -38,6 +40,7 @@ public: void SetModulus(const std::vector& m) { modulus = m; + init_modulus = true; } const std::vector& GetModulus() const { @@ -46,6 +49,7 @@ public: void SetPrivateD(const std::vector& d) { private_d = d; + init_private_d = true; } const std::vector& GetPrivateD() const { @@ -53,7 +57,9 @@ public: } private: - bool init = false; + bool init_exponent = false; + bool init_modulus = false; + bool init_private_d = false; std::vector exponent; std::vector modulus; std::vector private_d; diff --git a/src/core/hw/unique_data.cpp b/src/core/hw/unique_data.cpp index 8d7ac9035..4ebc2091a 100644 --- a/src/core/hw/unique_data.cpp +++ b/src/core/hw/unique_data.cpp @@ -27,13 +27,17 @@ static MovableSedFull movable; static bool movable_signature_valid = false; bool SecureInfoA::VerifySignature() const { - return HW::RSA::GetSecureInfoSlot().Verify( - std::span(reinterpret_cast(&body), sizeof(body)), signature); + auto sec_info_slot = HW::RSA::GetSecureInfoSlot(); + return sec_info_slot && + sec_info_slot.Verify( + std::span(reinterpret_cast(&body), sizeof(body)), signature); } bool LocalFriendCodeSeedB::VerifySignature() const { - return HW::RSA::GetLocalFriendCodeSeedSlot().Verify( - std::span(reinterpret_cast(&body), sizeof(body)), signature); + auto lfcs_slot = HW::RSA::GetLocalFriendCodeSeedSlot(); + return lfcs_slot && + HW::RSA::GetLocalFriendCodeSeedSlot().Verify( + std::span(reinterpret_cast(&body), sizeof(body)), signature); } bool MovableSed::VerifySignature() const { @@ -42,6 +46,9 @@ bool MovableSed::VerifySignature() const { SecureDataLoadStatus LoadSecureInfoA() { if (secure_info_a.IsValid()) { + if (!HW::RSA::GetSecureInfoSlot()) { + return SecureDataLoadStatus::CannotValidateSignature; + } return secure_info_a_signature_valid ? SecureDataLoadStatus::Loaded : (secure_info_a_region_changed ? SecureDataLoadStatus::RegionChanged @@ -63,8 +70,11 @@ SecureDataLoadStatus LoadSecureInfoA() { return SecureDataLoadStatus::IOError; } - HW::AES::InitKeys(); secure_info_a_region_changed = false; + HW::AES::InitKeys(); + if (!HW::RSA::GetSecureInfoSlot()) { + return SecureDataLoadStatus::CannotValidateSignature; + } secure_info_a_signature_valid = secure_info_a.VerifySignature(); if (!secure_info_a_signature_valid) { // Check if the file has been region changed @@ -93,6 +103,9 @@ SecureDataLoadStatus LoadSecureInfoA() { SecureDataLoadStatus LoadLocalFriendCodeSeedB() { if (local_friend_code_seed_b.IsValid()) { + if (!HW::RSA::GetLocalFriendCodeSeedSlot()) { + return SecureDataLoadStatus::CannotValidateSignature; + } return local_friend_code_seed_b_signature_valid ? SecureDataLoadStatus::Loaded : SecureDataLoadStatus::InvalidSignature; } @@ -114,6 +127,9 @@ SecureDataLoadStatus LoadLocalFriendCodeSeedB() { } HW::AES::InitKeys(); + if (!HW::RSA::GetLocalFriendCodeSeedSlot()) { + return SecureDataLoadStatus::CannotValidateSignature; + } local_friend_code_seed_b_signature_valid = local_friend_code_seed_b.VerifySignature(); if (!local_friend_code_seed_b_signature_valid) { LOG_WARNING(HW, "LocalFriendCodeSeed_B signature check failed"); @@ -128,10 +144,17 @@ SecureDataLoadStatus LoadOTP() { return SecureDataLoadStatus::Loaded; } + auto is_all_zero = [](const auto& arr) { + return std::all_of(arr.begin(), arr.end(), [](auto x) { return x == 0; }); + }; + const std::string filepath = GetOTPPath(); HW::AES::InitKeys(); auto otp_keyiv = HW::AES::GetOTPKeyIV(); + if (is_all_zero(otp_keyiv.first) || is_all_zero(otp_keyiv.second)) { + return SecureDataLoadStatus::NoCryptoKeys; + } auto loader_status = otp.Load(filepath, otp_keyiv.first, otp_keyiv.second); if (loader_status != Loader::ResultStatus::Success) { @@ -169,6 +192,9 @@ SecureDataLoadStatus LoadOTP() { SecureDataLoadStatus LoadMovable() { if (movable.IsValid()) { + if (!HW::RSA::GetLocalFriendCodeSeedSlot()) { + return SecureDataLoadStatus::CannotValidateSignature; + } return movable_signature_valid ? SecureDataLoadStatus::Loaded : SecureDataLoadStatus::InvalidSignature; } @@ -193,6 +219,9 @@ SecureDataLoadStatus LoadMovable() { } HW::AES::InitKeys(); + if (!HW::RSA::GetLocalFriendCodeSeedSlot()) { + return SecureDataLoadStatus::CannotValidateSignature; + } movable_signature_valid = movable.VerifySignature(); if (!movable_signature_valid) { LOG_WARNING(HW, "movable.sed signature check failed"); diff --git a/src/core/hw/unique_data.h b/src/core/hw/unique_data.h index dd3a4bb8b..3b4f8efbe 100644 --- a/src/core/hw/unique_data.h +++ b/src/core/hw/unique_data.h @@ -136,10 +136,12 @@ enum class SecureDataLoadStatus { Loaded = 0, InvalidSignature = 1, RegionChanged = 2, + CannotValidateSignature = 3, NotFound = -1, Invalid = -2, IOError = -3, + NoCryptoKeys = -4, }; SecureDataLoadStatus LoadSecureInfoA();