core: Add CMAKE option to disable built-in keyblob (#2024)

* core: Add CMAKE option to disable built-in keyblob

* Additional assurance against accidental inclusion of default_keys.h

* default_keys.h: Make default_keys_enc constexpr

* default_keys.h: Make default_keys_enc_size constexpr

---------

Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
This commit is contained in:
PabloMK7 2026-04-12 19:57:55 +02:00 committed by GitHub
parent e8c75b4107
commit 336d871a7f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 75 additions and 14 deletions

View File

@ -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})

View File

@ -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)

View File

@ -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();
}

View File

@ -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)

View File

@ -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<std::optional<AESKey>, NumDlpNfcKeyYs> dlp_nfc_key_y_slots;
std::array<NfcSecret, NumNfcSecrets> 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<char>(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<u8> kiv(16);
@ -304,6 +307,9 @@ std::istringstream GetKeysStream() {
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption(kiv.data(), kiv.size(), kiv.data())
.ProcessData(reinterpret_cast<u8*>(s.data()), default_keys_enc, s.size());
return std::istringstream(s);
#else
return std::istringstream("");
#endif // ENABLE_BUILTIN_KEYBLOB
}
}

View File

@ -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);

View File

@ -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<u8> exponent, std::vector<u8> 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<u8> ModularExponentiation(std::span<const u8> 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<u8>& e) {
exponent = e;
init_exponent = true;
}
const std::vector<u8>& GetExponent() const {
@ -38,6 +40,7 @@ public:
void SetModulus(const std::vector<u8>& m) {
modulus = m;
init_modulus = true;
}
const std::vector<u8>& GetModulus() const {
@ -46,6 +49,7 @@ public:
void SetPrivateD(const std::vector<u8>& d) {
private_d = d;
init_private_d = true;
}
const std::vector<u8>& 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<u8> exponent;
std::vector<u8> modulus;
std::vector<u8> private_d;

View File

@ -27,13 +27,17 @@ static MovableSedFull movable;
static bool movable_signature_valid = false;
bool SecureInfoA::VerifySignature() const {
return HW::RSA::GetSecureInfoSlot().Verify(
std::span<const u8>(reinterpret_cast<const u8*>(&body), sizeof(body)), signature);
auto sec_info_slot = HW::RSA::GetSecureInfoSlot();
return sec_info_slot &&
sec_info_slot.Verify(
std::span<const u8>(reinterpret_cast<const u8*>(&body), sizeof(body)), signature);
}
bool LocalFriendCodeSeedB::VerifySignature() const {
return HW::RSA::GetLocalFriendCodeSeedSlot().Verify(
std::span<const u8>(reinterpret_cast<const u8*>(&body), sizeof(body)), signature);
auto lfcs_slot = HW::RSA::GetLocalFriendCodeSeedSlot();
return lfcs_slot &&
HW::RSA::GetLocalFriendCodeSeedSlot().Verify(
std::span<const u8>(reinterpret_cast<const u8*>(&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");

View File

@ -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();