mirror of
https://github.com/Dark98/threeSD.git
synced 2026-07-02 16:49:04 +00:00
Refactor code - Add Signature class
This commit is contained in:
@@ -19,6 +19,8 @@ add_library(core STATIC
|
||||
ncch/cia_common.h
|
||||
ncch/ncch_container.cpp
|
||||
ncch/ncch_container.h
|
||||
ncch/signature.cpp
|
||||
ncch/signature.h
|
||||
ncch/seed_db.cpp
|
||||
ncch/seed_db.h
|
||||
ncch/smdh.cpp
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <cryptopp/integer.h>
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -31,57 +32,36 @@ inline std::size_t GetPublicKeySize(u32 public_key_type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t Certificate::Load(std::vector<u8> file_data, std::size_t offset) {
|
||||
std::size_t total_size = static_cast<std::size_t>(file_data.size() - offset);
|
||||
if (total_size < sizeof(u32))
|
||||
return 0;
|
||||
bool Certificate::Load(std::vector<u8> file_data, std::size_t offset) {
|
||||
const auto total_size = static_cast<std::size_t>(file_data.size() - offset);
|
||||
|
||||
std::memcpy(&signature_type, &file_data[offset], sizeof(u32));
|
||||
|
||||
// Signature lengths are variable, and the body follows the signature
|
||||
u32 signature_size = GetSignatureSize(signature_type);
|
||||
if (signature_size == 0) {
|
||||
return 0;
|
||||
if (!signature.Load(file_data, offset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The certificate body start position is rounded to the nearest 0x40 after the signature
|
||||
std::size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40);
|
||||
std::size_t body_end = body_start + sizeof(Body);
|
||||
|
||||
if (total_size < body_end)
|
||||
return 0;
|
||||
|
||||
// Read signature + certificate body
|
||||
signature.resize(signature_size);
|
||||
std::memcpy(signature.data(), &file_data[offset + sizeof(u32)], signature_size);
|
||||
std::memcpy(&body, &file_data[offset + body_start], sizeof(Body));
|
||||
// certificate body
|
||||
const auto signature_size = signature.GetSize();
|
||||
TRY_MEMCPY(&body, file_data, offset + signature_size, sizeof(Body));
|
||||
|
||||
// Public key lengths are variable
|
||||
std::size_t public_key_size = GetPublicKeySize(body.key_type);
|
||||
const auto public_key_size = GetPublicKeySize(body.key_type);
|
||||
if (public_key_size == 0) {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
public_key.resize(public_key_size);
|
||||
std::memcpy(public_key.data(), &file_data[offset + body_end], public_key.size());
|
||||
|
||||
return body_end + public_key.size();
|
||||
const auto public_key_offset = offset + signature_size + sizeof(Body);
|
||||
TRY_MEMCPY(public_key.data(), file_data, public_key_offset, public_key.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Certificate::Save(FileUtil::IOFile& file) const {
|
||||
// signature
|
||||
if (file.WriteBytes(&signature_type, sizeof(signature_type)) != sizeof(signature_type) ||
|
||||
file.WriteBytes(signature.data(), signature.size()) != signature.size()) {
|
||||
|
||||
LOG_ERROR(Core, "Failed to write signature");
|
||||
if (!signature.Save(file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// body
|
||||
const std::size_t body_start = Common::AlignUp(signature.size() + sizeof(u32), 0x40);
|
||||
const std::size_t body_end = body_start + sizeof(body);
|
||||
if (!file.Seek(body_start - signature.size() - sizeof(u32), SEEK_CUR) ||
|
||||
file.WriteBytes(&body, sizeof(body)) != sizeof(body)) {
|
||||
|
||||
if (file.WriteBytes(&body, sizeof(body)) != sizeof(body)) {
|
||||
LOG_ERROR(Core, "Failed to write body");
|
||||
return false;
|
||||
}
|
||||
@@ -95,6 +75,10 @@ bool Certificate::Save(FileUtil::IOFile& file) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t Certificate::GetSize() const {
|
||||
return signature.GetSize() + sizeof(Body) + public_key.size();
|
||||
}
|
||||
|
||||
std::pair<CryptoPP::Integer, CryptoPP::Integer> Certificate::GetRSAPublicKey() const {
|
||||
if (body.key_type == PublicKeyType::RSA_2048) {
|
||||
return {CryptoPP::Integer(public_key.data(), 0x100),
|
||||
@@ -139,10 +123,10 @@ bool Load(const std::string& path) {
|
||||
std::size_t pos = sizeof(header);
|
||||
while (pos < total_size) {
|
||||
Certificate cert;
|
||||
const auto size = cert.Load(data[0], pos);
|
||||
if (!size) { // Failed to load
|
||||
if (!cert.Load(data[0], pos)) { // Failed to load
|
||||
return false;
|
||||
}
|
||||
pos += cert.GetSize();
|
||||
|
||||
const auto issuer = Common::StringFromFixedZeroTerminatedBuffer(cert.body.issuer.data(),
|
||||
cert.body.issuer.size());
|
||||
@@ -150,8 +134,6 @@ bool Load(const std::string& path) {
|
||||
cert.body.name.size());
|
||||
const auto full_name = issuer + "-" + name;
|
||||
g_certs.emplace(full_name, std::move(cert));
|
||||
|
||||
pos += size;
|
||||
}
|
||||
|
||||
for (const auto& cert : CIACertNames) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <vector>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/ncch/signature.h"
|
||||
|
||||
namespace CryptoPP {
|
||||
class Integer;
|
||||
@@ -36,15 +37,14 @@ public:
|
||||
};
|
||||
static_assert(sizeof(Body) == 0x88);
|
||||
|
||||
// Returns: 0 on failure, size of the cert on success
|
||||
std::size_t Load(std::vector<u8> file_data, std::size_t offset = 0);
|
||||
bool Load(std::vector<u8> file_data, std::size_t offset = 0);
|
||||
bool Save(FileUtil::IOFile& file) const;
|
||||
std::size_t GetSize() const;
|
||||
|
||||
/// (modulus, exponent)
|
||||
std::pair<CryptoPP::Integer, CryptoPP::Integer> GetRSAPublicKey() const;
|
||||
|
||||
u32_be signature_type;
|
||||
std::vector<u8> signature;
|
||||
Signature signature;
|
||||
Body body;
|
||||
std::vector<u8> public_key;
|
||||
};
|
||||
|
||||
@@ -64,9 +64,11 @@ bool CIABuilder::Init(CIABuildType type_, const std::string& destination, TitleM
|
||||
}
|
||||
|
||||
tmd = std::move(tmd_);
|
||||
// Remove encrypted flag from TMD chunks
|
||||
for (auto& chunk : tmd.tmd_chunks) {
|
||||
chunk.type &= ~0x01;
|
||||
if (type == CIABuildType::Standard) {
|
||||
// Remove encrypted flag from TMD chunks
|
||||
for (auto& chunk : tmd.tmd_chunks) {
|
||||
chunk.type &= ~0x01;
|
||||
}
|
||||
}
|
||||
|
||||
header.header_size = sizeof(header);
|
||||
@@ -114,7 +116,7 @@ bool CIABuilder::WriteCert(const std::string& certs_db_path) {
|
||||
file->Seek(cert_offset, SEEK_SET);
|
||||
for (const auto& cert : CIACertNames) {
|
||||
if (!Certs::Get(cert).Save(*file)) {
|
||||
LOG_ERROR(Core, "Failed to write cert");
|
||||
LOG_ERROR(Core, "Failed to write cert {}", cert);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,34 +9,6 @@
|
||||
|
||||
namespace Core {
|
||||
|
||||
enum TMDSignatureType : u32 {
|
||||
Rsa4096Sha1 = 0x10000,
|
||||
Rsa2048Sha1 = 0x10001,
|
||||
EllipticSha1 = 0x10002,
|
||||
Rsa4096Sha256 = 0x10003,
|
||||
Rsa2048Sha256 = 0x10004,
|
||||
EcdsaSha256 = 0x10005
|
||||
};
|
||||
|
||||
inline u32 GetSignatureSize(u32 signature_type) {
|
||||
switch (signature_type) {
|
||||
case Rsa4096Sha1:
|
||||
case Rsa4096Sha256:
|
||||
return 0x200;
|
||||
|
||||
case Rsa2048Sha1:
|
||||
case Rsa2048Sha256:
|
||||
return 0x100;
|
||||
|
||||
case EllipticSha1:
|
||||
case EcdsaSha256:
|
||||
return 0x3C;
|
||||
}
|
||||
|
||||
LOG_ERROR(Common_Filesystem, "Tried to read ticket with bad signature {}", signature_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Full names of the certificates contained in a CIA.
|
||||
constexpr std::array<const char*, 3> CIACertNames{{
|
||||
"Root-CA00000003",
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright 2021 threeSD Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cryptopp/rsa.h>
|
||||
#include "common/alignment.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/ncch/certificate.h"
|
||||
#include "core/ncch/signature.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
enum SignatureType : u32 {
|
||||
Rsa4096Sha1 = 0x10000,
|
||||
Rsa2048Sha1 = 0x10001,
|
||||
EllipticSha1 = 0x10002,
|
||||
Rsa4096Sha256 = 0x10003,
|
||||
Rsa2048Sha256 = 0x10004,
|
||||
EcdsaSha256 = 0x10005
|
||||
};
|
||||
|
||||
static u32 GetSignatureSize(u32 type) {
|
||||
switch (type) {
|
||||
case Rsa4096Sha1:
|
||||
case Rsa4096Sha256:
|
||||
return 0x200;
|
||||
|
||||
case Rsa2048Sha1:
|
||||
case Rsa2048Sha256:
|
||||
return 0x100;
|
||||
|
||||
case EllipticSha1:
|
||||
case EcdsaSha256:
|
||||
return 0x3C;
|
||||
}
|
||||
|
||||
LOG_ERROR(Common_Filesystem, "Invalid signature type {}", type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Signature::Load(const std::vector<u8>& file_data, std::size_t offset) {
|
||||
TRY_MEMCPY(&type, file_data, offset, sizeof(type));
|
||||
|
||||
const auto data_size = GetSignatureSize(type);
|
||||
if (data_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data.resize(data_size);
|
||||
TRY_MEMCPY(data.data(), file_data, offset + sizeof(u32), data_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Signature::Save(FileUtil::IOFile& file) const {
|
||||
if (file.WriteBytes(&type, sizeof(type)) != sizeof(type)) {
|
||||
LOG_ERROR(Core, "Could not write to file");
|
||||
return false;
|
||||
}
|
||||
if (file.WriteBytes(data.data(), data.size()) != data.size()) {
|
||||
LOG_ERROR(Core, "Could not write to file");
|
||||
return false;
|
||||
}
|
||||
return file.Seek(GetSize() - data.size() - sizeof(type), SEEK_CUR);
|
||||
}
|
||||
|
||||
std::size_t Signature::GetSize() const {
|
||||
return Common::AlignUp(data.size() + sizeof(type), 0x40);
|
||||
}
|
||||
|
||||
bool Signature::Verify(const std::string& issuer,
|
||||
const std::function<void(CryptoPP::PK_MessageAccumulator*)>& func) const {
|
||||
|
||||
const auto& cert = Certs::Get(issuer);
|
||||
if (type != SignatureType::Rsa2048Sha256 || cert.body.key_type != PublicKeyType::RSA_2048) {
|
||||
|
||||
LOG_ERROR(Core, "Unsupported signature type or cert public key type");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto [modulus, exponent] = cert.GetRSAPublicKey();
|
||||
CryptoPP::RSASS<CryptoPP::PKCS1v15, CryptoPP::SHA256>::Verifier verifier(modulus, exponent);
|
||||
|
||||
auto* message = verifier.NewVerificationAccumulator();
|
||||
func(message);
|
||||
verifier.InputSignature(*message, data.data(), data.size());
|
||||
return verifier.Verify(message);
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2021 threeSD Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
namespace CryptoPP {
|
||||
class PK_MessageAccumulator;
|
||||
}
|
||||
|
||||
namespace FileUtil {
|
||||
class IOFile;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
/// Consists of a signature type, a signature, and alignment to 0x40.
|
||||
class Signature {
|
||||
public:
|
||||
bool Load(const std::vector<u8>& file_data, std::size_t offset = 0);
|
||||
|
||||
/// Writes signature to file. Includes the alignment
|
||||
bool Save(FileUtil::IOFile& file) const;
|
||||
|
||||
std::size_t GetSize() const;
|
||||
|
||||
/// Verifies the signature. Accepts a functor which should add the message to the accumulator
|
||||
bool Verify(const std::string& issuer,
|
||||
const std::function<void(CryptoPP::PK_MessageAccumulator*)>& func) const;
|
||||
|
||||
u32_be type;
|
||||
std::vector<u8> data;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <string_view>
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/file_util.h"
|
||||
#include "core/ncch/cia_common.h"
|
||||
#include "core/ncch/ticket.h"
|
||||
@@ -13,48 +14,21 @@
|
||||
namespace Core {
|
||||
|
||||
bool Ticket::Load(const std::vector<u8> file_data, std::size_t offset) {
|
||||
std::size_t total_size = static_cast<std::size_t>(file_data.size() - offset);
|
||||
if (total_size < sizeof(u32))
|
||||
return false;
|
||||
|
||||
std::memcpy(&signature_type, &file_data[offset], sizeof(u32));
|
||||
|
||||
// Signature lengths are variable, and the body follows the signature
|
||||
u32 signature_size = GetSignatureSize(signature_type);
|
||||
if (signature_size == 0) {
|
||||
if (!signature.Load(file_data, offset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The ticket body start position is rounded to the nearest 0x40 after the signature
|
||||
std::size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40);
|
||||
std::size_t body_end = body_start + sizeof(Body);
|
||||
|
||||
if (total_size < body_end)
|
||||
return false;
|
||||
|
||||
// Read signature + ticket body
|
||||
signature.resize(signature_size);
|
||||
memcpy(signature.data(), &file_data[offset + sizeof(u32)], signature_size);
|
||||
memcpy(&body, &file_data[offset + body_start], sizeof(Body));
|
||||
|
||||
TRY_MEMCPY(&body, file_data, offset + signature.GetSize(), sizeof(Body));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Ticket::Save(FileUtil::IOFile& file) const {
|
||||
// signature
|
||||
if (file.WriteBytes(&signature_type, sizeof(signature_type)) != sizeof(signature_type) ||
|
||||
file.WriteBytes(signature.data(), signature.size()) != signature.size()) {
|
||||
|
||||
LOG_ERROR(Core, "Failed to write signature");
|
||||
if (!signature.Save(file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// body
|
||||
const std::size_t body_start = Common::AlignUp(signature.size() + sizeof(u32), 0x40);
|
||||
const std::size_t body_end = body_start + sizeof(body);
|
||||
if (!file.Seek(body_start - signature.size() - sizeof(u32), SEEK_CUR) ||
|
||||
file.WriteBytes(&body, sizeof(body)) != sizeof(body)) {
|
||||
|
||||
if (file.WriteBytes(&body, sizeof(body)) != sizeof(body)) {
|
||||
LOG_ERROR(Core, "Failed to write body");
|
||||
return false;
|
||||
}
|
||||
@@ -63,7 +37,7 @@ bool Ticket::Save(FileUtil::IOFile& file) const {
|
||||
}
|
||||
|
||||
std::size_t Ticket::GetSize() const {
|
||||
return Common::AlignUp(signature.size() + sizeof(u32), 0x40) + sizeof(body);
|
||||
return signature.GetSize() + sizeof(body);
|
||||
}
|
||||
|
||||
constexpr std::string_view TicketIssuer = "Root-CA00000003-XS0000000c";
|
||||
@@ -80,10 +54,10 @@ constexpr std::array<u8, 44> TicketContentIndex{
|
||||
// Values taken from GodMode9
|
||||
Ticket BuildFakeTicket(u64 title_id) {
|
||||
Ticket ticket{};
|
||||
ticket.signature_type = 0x10004; // RSA_2048 SHA256
|
||||
|
||||
ticket.signature.resize(GetSignatureSize(ticket.signature_type));
|
||||
std::memset(ticket.signature.data(), 0xFF, ticket.signature.size());
|
||||
ticket.signature.type = 0x10004; // RSA_2048 SHA256
|
||||
ticket.signature.data.resize(0x100); // Size of RSA_2048 signature
|
||||
std::memset(ticket.signature.data.data(), 0xFF, ticket.signature.data.size());
|
||||
|
||||
auto& body = ticket.body;
|
||||
std::memcpy(body.issuer.data(), TicketIssuer.data(), TicketIssuer.size());
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/ncch/signature.h"
|
||||
|
||||
namespace FileUtil {
|
||||
class IOFile;
|
||||
@@ -50,9 +51,8 @@ public:
|
||||
bool Save(FileUtil::IOFile& file) const;
|
||||
std::size_t GetSize() const;
|
||||
|
||||
Signature signature;
|
||||
Body body;
|
||||
u32_be signature_type;
|
||||
std::vector<u8> signature;
|
||||
};
|
||||
|
||||
Ticket BuildFakeTicket(u64 title_id);
|
||||
|
||||
@@ -22,28 +22,19 @@ ResultStatus TitleMetadata::Load(const std::vector<u8> file_data, std::size_t of
|
||||
if (total_size < sizeof(u32_be))
|
||||
return ResultStatus::Error;
|
||||
|
||||
memcpy(&signature_type, &file_data[offset], sizeof(u32_be));
|
||||
|
||||
// Signature lengths are variable, and the body follows the signature
|
||||
u32 signature_size = GetSignatureSize(signature_type);
|
||||
if (signature_size == 0) {
|
||||
if (!signature.Load(file_data, offset)) {
|
||||
return ResultStatus::Error;
|
||||
}
|
||||
|
||||
// The TMD body start position is rounded to the nearest 0x40 after the signature
|
||||
std::size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40);
|
||||
std::size_t body_end = body_start + sizeof(Body);
|
||||
|
||||
const auto signature_size = signature.GetSize();
|
||||
std::size_t body_end = signature_size + sizeof(Body);
|
||||
if (total_size < body_end)
|
||||
return ResultStatus::Error;
|
||||
|
||||
// Read signature + TMD body, then load the amount of ContentChunks specified
|
||||
tmd_signature.resize(signature_size);
|
||||
memcpy(tmd_signature.data(), &file_data[offset + sizeof(u32_be)], signature_size);
|
||||
memcpy(&tmd_body, &file_data[offset + body_start], sizeof(TitleMetadata::Body));
|
||||
// Read TMD body, then load the amount of ContentChunks specified
|
||||
std::memcpy(&tmd_body, &file_data[offset + signature_size], sizeof(TitleMetadata::Body));
|
||||
|
||||
std::size_t expected_size =
|
||||
body_start + sizeof(Body) + static_cast<u16>(tmd_body.content_count) * sizeof(ContentChunk);
|
||||
std::size_t expected_size = signature_size + sizeof(Body) +
|
||||
static_cast<u16>(tmd_body.content_count) * sizeof(ContentChunk);
|
||||
if (total_size < expected_size) {
|
||||
LOG_ERROR(Service_FS, "Malformed TMD, expected size 0x{:x}, got 0x{:x}!", expected_size,
|
||||
total_size);
|
||||
@@ -53,8 +44,8 @@ ResultStatus TitleMetadata::Load(const std::vector<u8> file_data, std::size_t of
|
||||
for (u16 i = 0; i < tmd_body.content_count; i++) {
|
||||
ContentChunk chunk;
|
||||
|
||||
memcpy(&chunk, &file_data[offset + body_end + (i * sizeof(ContentChunk))],
|
||||
sizeof(ContentChunk));
|
||||
std::memcpy(&chunk, &file_data[offset + body_end + (i * sizeof(ContentChunk))],
|
||||
sizeof(ContentChunk));
|
||||
tmd_chunks.push_back(chunk);
|
||||
}
|
||||
|
||||
@@ -67,22 +58,10 @@ ResultStatus TitleMetadata::Save(FileUtil::IOFile& file) {
|
||||
if (!file.IsOpen())
|
||||
return ResultStatus::Error;
|
||||
|
||||
if (!file.WriteBytes(&signature_type, sizeof(u32_be)))
|
||||
return ResultStatus::Error;
|
||||
|
||||
// Signature lengths are variable, and the body follows the signature
|
||||
u32 signature_size = GetSignatureSize(signature_type);
|
||||
if (signature_size == 0) {
|
||||
if (!signature.Save(file)) {
|
||||
return ResultStatus::Error;
|
||||
}
|
||||
|
||||
if (!file.WriteBytes(tmd_signature.data(), signature_size))
|
||||
return ResultStatus::Error;
|
||||
|
||||
// The TMD body start position is rounded to the nearest 0x40 after the signature
|
||||
std::size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40);
|
||||
file.Seek(offset + body_start, SEEK_SET);
|
||||
|
||||
// Update our TMD body values and hashes
|
||||
tmd_body.content_count = static_cast<u16>(tmd_chunks.size());
|
||||
|
||||
@@ -124,42 +103,18 @@ ResultStatus TitleMetadata::Save(const std::string& file_path) {
|
||||
}
|
||||
|
||||
bool TitleMetadata::ValidateSignature() const {
|
||||
if (!Certs::IsLoaded()) {
|
||||
LOG_ERROR(Core, "Certificates not available");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto cert_name =
|
||||
const auto issuer =
|
||||
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);
|
||||
return signature.Verify(issuer, [this](auto* message) {
|
||||
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));
|
||||
});
|
||||
}
|
||||
|
||||
std::size_t TitleMetadata::GetSize() const {
|
||||
const std::size_t body_start =
|
||||
Common::AlignUp(GetSignatureSize(signature_type) + sizeof(u32), 0x40);
|
||||
return body_start + sizeof(TitleMetadata::Body) + sizeof(ContentChunk) * tmd_chunks.size();
|
||||
return signature.GetSize() + sizeof(TitleMetadata::Body) +
|
||||
sizeof(ContentChunk) * tmd_chunks.size();
|
||||
}
|
||||
|
||||
u64 TitleMetadata::GetTitleID() const {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/ncch/signature.h"
|
||||
#include "core/result_status.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -110,9 +111,8 @@ public:
|
||||
|
||||
void Print() const;
|
||||
|
||||
Signature signature;
|
||||
Body tmd_body;
|
||||
u32_be signature_type;
|
||||
std::vector<u8> tmd_signature;
|
||||
std::vector<ContentChunk> tmd_chunks;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user