mirror of
https://github.com/Dark98/threeSD.git
synced 2026-07-03 00:38:58 +00:00
Add signature validation to TMD
This commit is contained in:
Vendored
+2
@@ -158,6 +158,7 @@ set(cryptopp_SOURCES
|
|||||||
cryptopp/neon-simd.cpp
|
cryptopp/neon-simd.cpp
|
||||||
cryptopp/oaep.cpp
|
cryptopp/oaep.cpp
|
||||||
cryptopp/osrng.cpp
|
cryptopp/osrng.cpp
|
||||||
|
cryptopp/pkcspad.cpp
|
||||||
cryptopp/pubkey.cpp
|
cryptopp/pubkey.cpp
|
||||||
cryptopp/queue.cpp
|
cryptopp/queue.cpp
|
||||||
cryptopp/randpool.cpp
|
cryptopp/randpool.cpp
|
||||||
@@ -165,6 +166,7 @@ set(cryptopp_SOURCES
|
|||||||
cryptopp/rijndael-simd.cpp
|
cryptopp/rijndael-simd.cpp
|
||||||
cryptopp/rijndael.cpp
|
cryptopp/rijndael.cpp
|
||||||
cryptopp/rng.cpp
|
cryptopp/rng.cpp
|
||||||
|
cryptopp/rsa.cpp
|
||||||
cryptopp/sha-simd.cpp
|
cryptopp/sha-simd.cpp
|
||||||
cryptopp/sha.cpp
|
cryptopp/sha.cpp
|
||||||
cryptopp/sse-simd.cpp
|
cryptopp/sse-simd.cpp
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <cryptopp/sha.h>
|
#include <cryptopp/integer.h>
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
|
#include "common/assert.h"
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
@@ -94,6 +95,18 @@ bool Certificate::Save(FileUtil::IOFile& file) const {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<CryptoPP::Integer, CryptoPP::Integer> Certificate::GetRSAPublicKey() const {
|
||||||
|
if (body.key_type == PublicKeyType::RSA_2048) {
|
||||||
|
return {CryptoPP::Integer(public_key.data(), 0x100),
|
||||||
|
CryptoPP::Integer(public_key.data() + 0x100, 0x4)};
|
||||||
|
} else if (body.key_type == PublicKeyType::RSA_4096) {
|
||||||
|
return {CryptoPP::Integer(public_key.data(), 0x200),
|
||||||
|
CryptoPP::Integer(public_key.data() + 0x200, 0x4)};
|
||||||
|
} else {
|
||||||
|
UNREACHABLE_MSG("Certificate is not RSA");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace Certs {
|
namespace Certs {
|
||||||
|
|
||||||
static std::unordered_map<std::string, Certificate> g_certs;
|
static std::unordered_map<std::string, Certificate> g_certs;
|
||||||
@@ -131,9 +144,12 @@ bool Load(const std::string& path) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto issuer = Common::StringFromFixedZeroTerminatedBuffer(cert.body.issuer.data(),
|
||||||
|
cert.body.issuer.size());
|
||||||
const auto name = Common::StringFromFixedZeroTerminatedBuffer(cert.body.name.data(),
|
const auto name = Common::StringFromFixedZeroTerminatedBuffer(cert.body.name.data(),
|
||||||
cert.body.name.size());
|
cert.body.name.size());
|
||||||
g_certs.emplace(name, std::move(cert));
|
const auto full_name = issuer + "-" + name;
|
||||||
|
g_certs.emplace(full_name, std::move(cert));
|
||||||
|
|
||||||
pos += size;
|
pos += size;
|
||||||
}
|
}
|
||||||
@@ -157,6 +173,10 @@ const Certificate& Get(const std::string& name) {
|
|||||||
return g_certs.at(name);
|
return g_certs.at(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Exists(const std::string& name) {
|
||||||
|
return g_certs.count(name);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Certs
|
} // namespace Certs
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
|
|
||||||
|
namespace CryptoPP {
|
||||||
|
class Integer;
|
||||||
|
}
|
||||||
|
|
||||||
namespace FileUtil {
|
namespace FileUtil {
|
||||||
class IOFile;
|
class IOFile;
|
||||||
}
|
}
|
||||||
@@ -36,6 +40,9 @@ public:
|
|||||||
std::size_t Load(std::vector<u8> file_data, std::size_t offset = 0);
|
std::size_t Load(std::vector<u8> file_data, std::size_t offset = 0);
|
||||||
bool Save(FileUtil::IOFile& file) const;
|
bool Save(FileUtil::IOFile& file) const;
|
||||||
|
|
||||||
|
/// (modulus, exponent)
|
||||||
|
std::pair<CryptoPP::Integer, CryptoPP::Integer> GetRSAPublicKey() const;
|
||||||
|
|
||||||
u32_be signature_type;
|
u32_be signature_type;
|
||||||
std::vector<u8> signature;
|
std::vector<u8> signature;
|
||||||
Body body;
|
Body body;
|
||||||
@@ -55,6 +62,7 @@ namespace Certs {
|
|||||||
bool Load(const std::string& path);
|
bool Load(const std::string& path);
|
||||||
bool IsLoaded();
|
bool IsLoaded();
|
||||||
const Certificate& Get(const std::string& name);
|
const Certificate& Get(const std::string& name);
|
||||||
|
bool Exists(const std::string& name);
|
||||||
|
|
||||||
} // namespace Certs
|
} // namespace Certs
|
||||||
|
|
||||||
|
|||||||
@@ -37,10 +37,11 @@ inline u32 GetSignatureSize(u32 signature_type) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Full names of the certificates contained in a CIA.
|
||||||
constexpr std::array<const char*, 3> CIACertNames{{
|
constexpr std::array<const char*, 3> CIACertNames{{
|
||||||
"CA00000003",
|
"Root-CA00000003",
|
||||||
"XS0000000c",
|
"Root-CA00000003-XS0000000c",
|
||||||
"CP0000000b",
|
"Root-CA00000003-CP0000000b",
|
||||||
}};
|
}};
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|||||||
@@ -4,11 +4,14 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
#include <cryptopp/rsa.h>
|
||||||
#include <cryptopp/sha.h>
|
#include <cryptopp/sha.h>
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
#include "core/ncch/certificate.h"
|
||||||
#include "core/ncch/cia_common.h"
|
#include "core/ncch/cia_common.h"
|
||||||
#include "core/ncch/title_metadata.h"
|
#include "core/ncch/title_metadata.h"
|
||||||
|
|
||||||
@@ -120,6 +123,39 @@ ResultStatus TitleMetadata::Save(const std::string& file_path) {
|
|||||||
return Save(file);
|
return Save(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TitleMetadata::ValidateSignature() const {
|
||||||
|
if (!Certs::IsLoaded()) {
|
||||||
|
LOG_ERROR(Core, "Certificates not available");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto cert_name =
|
||||||
|
Common::StringFromFixedZeroTerminatedBuffer(tmd_body.issuer.data(), tmd_body.issuer.size());
|
||||||
|
if (!Certs::Exists(cert_name)) {
|
||||||
|
LOG_ERROR(Core, "Cert {} does not exist", cert_name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& cert = Certs::Get(cert_name);
|
||||||
|
if (signature_type != TMDSignatureType::Rsa2048Sha256 ||
|
||||||
|
cert.body.key_type != PublicKeyType::RSA_2048) {
|
||||||
|
|
||||||
|
LOG_ERROR(Core, "Unsupported TMD signature type or cert public key type");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto [modulus, exponent] = Certs::Get(cert_name).GetRSAPublicKey();
|
||||||
|
CryptoPP::RSASS<CryptoPP::PKCS1v15, CryptoPP::SHA256>::Verifier verifier(modulus, exponent);
|
||||||
|
|
||||||
|
auto* message = verifier.NewVerificationAccumulator();
|
||||||
|
message->Update(reinterpret_cast<const u8*>(&tmd_body), sizeof(tmd_body));
|
||||||
|
message->Update(reinterpret_cast<const u8*>(tmd_chunks.data()),
|
||||||
|
tmd_chunks.size() * sizeof(ContentChunk));
|
||||||
|
verifier.InputSignature(*message, tmd_signature.data(), tmd_signature.size());
|
||||||
|
|
||||||
|
return verifier.Verify(message);
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t TitleMetadata::GetSize() const {
|
std::size_t TitleMetadata::GetSize() const {
|
||||||
const std::size_t body_start =
|
const std::size_t body_start =
|
||||||
Common::AlignUp(GetSignatureSize(signature_type) + sizeof(u32), 0x40);
|
Common::AlignUp(GetSignatureSize(signature_type) + sizeof(u32), 0x40);
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public:
|
|||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
struct Body {
|
struct Body {
|
||||||
std::array<u8, 0x40> issuer;
|
std::array<char, 0x40> issuer;
|
||||||
u8 version;
|
u8 version;
|
||||||
u8 ca_crl_version;
|
u8 ca_crl_version;
|
||||||
u8 signer_crl_version;
|
u8 signer_crl_version;
|
||||||
@@ -82,8 +82,9 @@ public:
|
|||||||
ResultStatus Save(FileUtil::IOFile& file);
|
ResultStatus Save(FileUtil::IOFile& file);
|
||||||
ResultStatus Save(const std::string& file_path);
|
ResultStatus Save(const std::string& file_path);
|
||||||
|
|
||||||
std::size_t GetSize() const;
|
bool ValidateSignature() const;
|
||||||
|
|
||||||
|
std::size_t GetSize() const;
|
||||||
u64 GetTitleID() const;
|
u64 GetTitleID() const;
|
||||||
u32 GetTitleType() const;
|
u32 GetTitleType() const;
|
||||||
u16 GetTitleVersion() const;
|
u16 GetTitleVersion() const;
|
||||||
|
|||||||
Reference in New Issue
Block a user