mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2026-04-16 23:21:35 -06:00
- Allow downloading titles from eshop and system settings - Remove encrypted game support
228 lines
7.5 KiB
C++
228 lines
7.5 KiB
C++
// Copyright 2024 Azahar Emulator Project
|
|
// Licensed under GPLv2 or any later version
|
|
// Refer to the license.txt file included.
|
|
|
|
#include "common/common_paths.h"
|
|
#include "core/file_sys/certificate.h"
|
|
#include "core/file_sys/otp.h"
|
|
#include "core/hw/aes/key.h"
|
|
#include "core/hw/ecc.h"
|
|
#include "core/hw/rsa/rsa.h"
|
|
#include "core/hw/unique_data.h"
|
|
#include "core/loader/loader.h"
|
|
|
|
namespace HW::UniqueData {
|
|
|
|
static SecureInfoA secure_info_a;
|
|
static bool secure_info_a_signature_valid = false;
|
|
static LocalFriendCodeSeedB local_friend_code_seed_b;
|
|
static bool local_friend_code_seed_b_signature_valid = false;
|
|
static FileSys::OTP otp;
|
|
static FileSys::Certificate ct_cert;
|
|
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);
|
|
}
|
|
|
|
bool LocalFriendCodeSeedB::VerifySignature() const {
|
|
return HW::RSA::GetLocalFriendCodeSeedSlot().Verify(
|
|
std::span<const u8>(reinterpret_cast<const u8*>(&body), sizeof(body)), signature);
|
|
}
|
|
|
|
bool MovableSed::VerifySignature() const {
|
|
return lfcs.VerifySignature();
|
|
}
|
|
|
|
SecureDataLoadStatus LoadSecureInfoA() {
|
|
if (secure_info_a.IsValid()) {
|
|
return secure_info_a_signature_valid ? SecureDataLoadStatus::Loaded
|
|
: SecureDataLoadStatus::InvalidSignature;
|
|
}
|
|
std::string file_path = GetSecureInfoAPath();
|
|
if (!FileUtil::Exists(file_path)) {
|
|
return SecureDataLoadStatus::NotFound;
|
|
}
|
|
FileUtil::IOFile file(file_path, "rb");
|
|
if (!file.IsOpen()) {
|
|
return SecureDataLoadStatus::IOError;
|
|
}
|
|
if (file.GetSize() != sizeof(SecureInfoA)) {
|
|
return SecureDataLoadStatus::Invalid;
|
|
}
|
|
if (file.ReadBytes(&secure_info_a, sizeof(SecureInfoA)) != sizeof(SecureInfoA)) {
|
|
secure_info_a.Invalidate();
|
|
return SecureDataLoadStatus::IOError;
|
|
}
|
|
|
|
secure_info_a_signature_valid = secure_info_a.VerifySignature();
|
|
if (!secure_info_a_signature_valid) {
|
|
LOG_WARNING(HW, "SecureInfo_A signature check failed");
|
|
}
|
|
|
|
return secure_info_a_signature_valid ? SecureDataLoadStatus::Loaded
|
|
: SecureDataLoadStatus::InvalidSignature;
|
|
}
|
|
|
|
SecureDataLoadStatus LoadLocalFriendCodeSeedB() {
|
|
if (local_friend_code_seed_b.IsValid()) {
|
|
return local_friend_code_seed_b_signature_valid ? SecureDataLoadStatus::Loaded
|
|
: SecureDataLoadStatus::InvalidSignature;
|
|
}
|
|
std::string file_path = GetLocalFriendCodeSeedBPath();
|
|
if (!FileUtil::Exists(file_path)) {
|
|
return SecureDataLoadStatus::NotFound;
|
|
}
|
|
FileUtil::IOFile file(file_path, "rb");
|
|
if (!file.IsOpen()) {
|
|
return SecureDataLoadStatus::IOError;
|
|
}
|
|
if (file.GetSize() != sizeof(LocalFriendCodeSeedB)) {
|
|
return SecureDataLoadStatus::Invalid;
|
|
}
|
|
if (file.ReadBytes(&local_friend_code_seed_b, sizeof(LocalFriendCodeSeedB)) !=
|
|
sizeof(LocalFriendCodeSeedB)) {
|
|
local_friend_code_seed_b.Invalidate();
|
|
return SecureDataLoadStatus::IOError;
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
return local_friend_code_seed_b_signature_valid ? SecureDataLoadStatus::Loaded
|
|
: SecureDataLoadStatus::InvalidSignature;
|
|
}
|
|
|
|
SecureDataLoadStatus LoadOTP() {
|
|
if (otp.Valid()) {
|
|
return SecureDataLoadStatus::Loaded;
|
|
}
|
|
|
|
const std::string filepath = GetOTPPath();
|
|
|
|
auto otp_keyiv = HW::AES::GetOTPKeyIV();
|
|
|
|
auto loader_status = otp.Load(filepath, otp_keyiv.first, otp_keyiv.second);
|
|
if (loader_status != Loader::ResultStatus::Success) {
|
|
otp.Invalidate();
|
|
ct_cert.Invalidate();
|
|
return loader_status == Loader::ResultStatus::ErrorNotFound ? SecureDataLoadStatus::NotFound
|
|
: SecureDataLoadStatus::Invalid;
|
|
}
|
|
|
|
constexpr const char* issuer_ret = "Nintendo CA - G3_NintendoCTR2prod";
|
|
constexpr const char* issuer_dev = "Nintendo CA - G3_NintendoCTR2dev";
|
|
std::array<u8, 0x40> issuer = {0};
|
|
if (otp.IsDev()) {
|
|
memcpy(issuer.data(), issuer_dev, strlen(issuer_dev));
|
|
} else {
|
|
memcpy(issuer.data(), issuer_ret, strlen(issuer_ret));
|
|
}
|
|
std::string name_str = fmt::format("CT{:08X}-{:02X}", otp.GetDeviceID(), otp.GetSystemType());
|
|
std::array<u8, 0x40> name = {0};
|
|
memcpy(name.data(), name_str.data(), name_str.size());
|
|
|
|
ct_cert.BuildECC(issuer, name, otp.GetCTCertExpiration(),
|
|
HW::ECC::CreateECCPrivateKey(otp.GetCTCertPrivateKey(), true),
|
|
HW::ECC::CreateECCSignature(otp.GetCTCertSignature()));
|
|
|
|
if (!ct_cert.VerifyMyself(HW::ECC::GetRootPublicKey())) {
|
|
LOG_ERROR(HW, "CTCert failed verification");
|
|
otp.Invalidate();
|
|
ct_cert.Invalidate();
|
|
return SecureDataLoadStatus::IOError;
|
|
}
|
|
|
|
return SecureDataLoadStatus::Loaded;
|
|
}
|
|
|
|
SecureDataLoadStatus LoadMovable() {
|
|
if (movable.IsValid()) {
|
|
return movable_signature_valid ? SecureDataLoadStatus::Loaded
|
|
: SecureDataLoadStatus::InvalidSignature;
|
|
}
|
|
std::string file_path = GetMovablePath();
|
|
if (!FileUtil::Exists(file_path)) {
|
|
return SecureDataLoadStatus::NotFound;
|
|
}
|
|
FileUtil::IOFile file(file_path, "rb");
|
|
if (!file.IsOpen()) {
|
|
return SecureDataLoadStatus::IOError;
|
|
}
|
|
if (file.GetSize() != sizeof(MovableSedFull)) {
|
|
if (file.GetSize() == sizeof(MovableSed)) {
|
|
LOG_WARNING(HW, "Uninitialized movable.sed files are not supported");
|
|
}
|
|
return SecureDataLoadStatus::Invalid;
|
|
}
|
|
if (file.ReadBytes(&movable, sizeof(MovableSedFull)) != sizeof(MovableSedFull)) {
|
|
movable.Invalidate();
|
|
return SecureDataLoadStatus::IOError;
|
|
}
|
|
|
|
movable_signature_valid = movable.VerifySignature();
|
|
if (!movable_signature_valid) {
|
|
LOG_WARNING(HW, "LocalFriendCodeSeed_B signature check failed");
|
|
}
|
|
|
|
return movable_signature_valid ? SecureDataLoadStatus::Loaded
|
|
: SecureDataLoadStatus::InvalidSignature;
|
|
}
|
|
|
|
std::string GetSecureInfoAPath() {
|
|
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "rw/sys/SecureInfo_A";
|
|
}
|
|
|
|
std::string GetLocalFriendCodeSeedBPath() {
|
|
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "rw/sys/LocalFriendCodeSeed_B";
|
|
}
|
|
|
|
std::string GetOTPPath() {
|
|
return FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + "otp.bin";
|
|
}
|
|
|
|
std::string GetMovablePath() {
|
|
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "private/movable.sed";
|
|
}
|
|
|
|
SecureInfoA& GetSecureInfoA() {
|
|
LoadSecureInfoA();
|
|
|
|
return secure_info_a;
|
|
}
|
|
|
|
LocalFriendCodeSeedB& GetLocalFriendCodeSeedB() {
|
|
LoadLocalFriendCodeSeedB();
|
|
|
|
return local_friend_code_seed_b;
|
|
}
|
|
|
|
FileSys::Certificate& HW::UniqueData::GetCTCert() {
|
|
LoadOTP();
|
|
|
|
return ct_cert;
|
|
}
|
|
|
|
FileSys::OTP& GetOTP() {
|
|
LoadOTP();
|
|
|
|
return otp;
|
|
}
|
|
MovableSedFull& GetMovableSed() {
|
|
LoadMovable();
|
|
|
|
return movable;
|
|
}
|
|
void InvalidateSecureData() {
|
|
secure_info_a.Invalidate();
|
|
local_friend_code_seed_b.Invalidate();
|
|
otp.Invalidate();
|
|
ct_cert.Invalidate();
|
|
movable.Invalidate();
|
|
}
|
|
|
|
} // namespace HW::UniqueData
|