mirror of
https://github.com/Dark98/threeSD.git
synced 2026-07-03 00:38:58 +00:00
Misc changes to ticket, cert and CIA
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
#include "core/extdata.h"
|
#include "core/extdata.h"
|
||||||
#include "core/importer.h"
|
#include "core/importer.h"
|
||||||
#include "core/key/key.h"
|
#include "core/key/key.h"
|
||||||
|
#include "core/ncch/certificate.h"
|
||||||
#include "core/ncch/cia_builder.h"
|
#include "core/ncch/cia_builder.h"
|
||||||
#include "core/ncch/ncch_container.h"
|
#include "core/ncch/ncch_container.h"
|
||||||
#include "core/ncch/seed_db.h"
|
#include "core/ncch/seed_db.h"
|
||||||
@@ -54,6 +55,9 @@ bool SDMCImporter::Init() {
|
|||||||
if (!config.seed_db_path.empty()) {
|
if (!config.seed_db_path.empty()) {
|
||||||
Seeds::Load(config.seed_db_path);
|
Seeds::Load(config.seed_db_path);
|
||||||
}
|
}
|
||||||
|
if (!config.certs_db_path.empty()) {
|
||||||
|
Certs::Load(config.certs_db_path);
|
||||||
|
}
|
||||||
|
|
||||||
decryptor = std::make_unique<SDMCDecryptor>(config.sdmc_path);
|
decryptor = std::make_unique<SDMCDecryptor>(config.sdmc_path);
|
||||||
cia_builder = std::make_unique<CIABuilder>();
|
cia_builder = std::make_unique<CIABuilder>();
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <cryptopp/sha.h>
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
@@ -64,7 +66,42 @@ std::size_t Certificate::Load(std::vector<u8> file_data, std::size_t offset) {
|
|||||||
return body_end + public_key.size();
|
return body_end + public_key.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LoadCertsDB(CertsMap& out, const std::string& path) {
|
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");
|
||||||
|
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)) {
|
||||||
|
|
||||||
|
LOG_ERROR(Core, "Failed to write body");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public key
|
||||||
|
if (file.WriteBytes(public_key.data(), public_key.size()) != public_key.size()) {
|
||||||
|
LOG_ERROR(Core, "Failed to write public key");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Certs {
|
||||||
|
|
||||||
|
static std::unordered_map<std::string, Certificate> g_certs;
|
||||||
|
static bool g_is_loaded = false;
|
||||||
|
|
||||||
|
bool Load(const std::string& path) {
|
||||||
|
g_certs.clear();
|
||||||
|
|
||||||
FileUtil::IOFile file(path, "rb");
|
FileUtil::IOFile file(path, "rb");
|
||||||
DataContainer container(file.GetData());
|
DataContainer container(file.GetData());
|
||||||
std::vector<std::vector<u8>> data;
|
std::vector<std::vector<u8>> data;
|
||||||
@@ -96,12 +133,30 @@ bool LoadCertsDB(CertsMap& out, const std::string& path) {
|
|||||||
|
|
||||||
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());
|
||||||
out.emplace(name, cert);
|
g_certs.emplace(name, std::move(cert));
|
||||||
|
|
||||||
pos += size;
|
pos += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& cert : CIACertNames) {
|
||||||
|
if (!g_certs.count(cert)) {
|
||||||
|
LOG_ERROR(Core, "Cert {} required for CIA building but does not exist", cert);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_is_loaded = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsLoaded() {
|
||||||
|
return g_is_loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Certificate& Get(const std::string& name) {
|
||||||
|
return g_certs.at(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Certs
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|||||||
@@ -6,10 +6,14 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "common/common_funcs.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
|
|
||||||
|
namespace FileUtil {
|
||||||
|
class IOFile;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
enum PublicKeyType : u32 {
|
enum PublicKeyType : u32 {
|
||||||
@@ -30,6 +34,7 @@ public:
|
|||||||
|
|
||||||
// Returns: 0 on failure, size of the cert on success
|
// Returns: 0 on failure, size of the cert on success
|
||||||
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;
|
||||||
|
|
||||||
u32_be signature_type;
|
u32_be signature_type;
|
||||||
std::vector<u8> signature;
|
std::vector<u8> signature;
|
||||||
@@ -45,7 +50,12 @@ struct CertsDBHeader {
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(CertsDBHeader) == 0x10);
|
static_assert(sizeof(CertsDBHeader) == 0x10);
|
||||||
|
|
||||||
using CertsMap = std::unordered_map<std::string, Certificate>;
|
namespace Certs {
|
||||||
bool LoadCertsDB(CertsMap& out, const std::string& path);
|
|
||||||
|
bool Load(const std::string& path);
|
||||||
|
bool IsLoaded();
|
||||||
|
const Certificate& Get(const std::string& name);
|
||||||
|
|
||||||
|
} // namespace Certs
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
#include <cryptopp/sha.h>
|
#include <cryptopp/sha.h>
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "core/importer.h"
|
#include "core/importer.h"
|
||||||
|
#include "core/ncch/certificate.h"
|
||||||
#include "core/ncch/cia_builder.h"
|
#include "core/ncch/cia_builder.h"
|
||||||
|
#include "core/ncch/cia_common.h"
|
||||||
#include "core/ncch/ticket.h"
|
#include "core/ncch/ticket.h"
|
||||||
#include "core/ncch/title_metadata.h"
|
#include "core/ncch/title_metadata.h"
|
||||||
#include "core/title_db.h"
|
#include "core/title_db.h"
|
||||||
@@ -103,40 +105,17 @@ bool CIABuilder::Init(const std::string& destination, TitleMetadata tmd_, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CIABuilder::WriteCert(const std::string& certs_db_path) {
|
bool CIABuilder::WriteCert(const std::string& certs_db_path) {
|
||||||
FileUtil::IOFile certs_db(certs_db_path, "rb");
|
if (!Certs::IsLoaded()) {
|
||||||
if (!certs_db) {
|
|
||||||
LOG_ERROR(Core, "Could not open {}", certs_db_path);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<u8, CIA_CERT_SIZE> cert;
|
|
||||||
// Read CIA cert
|
|
||||||
certs_db.Seek(0x0C10, SEEK_SET);
|
|
||||||
if (certs_db.ReadBytes(cert.data(), 0x1F0) != 0x1F0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
certs_db.Seek(0x3A00, SEEK_SET);
|
|
||||||
if (certs_db.ReadBytes(cert.data() + 0x1F0, 0x210) != 0x210) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
certs_db.Seek(0x3F10, SEEK_SET);
|
|
||||||
if (certs_db.ReadBytes(cert.data() + 0x400, 0x300) != 0x300) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
certs_db.Seek(0x3C10, SEEK_SET);
|
|
||||||
if (certs_db.ReadBytes(cert.data() + 0x700, 0x300) != 0x300) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write CIA cert to file
|
|
||||||
file->Seek(cert_offset, SEEK_SET);
|
file->Seek(cert_offset, SEEK_SET);
|
||||||
if (file->WriteBytes(cert.data(), cert.size()) != cert.size()) {
|
for (const auto& cert : CIACertNames) {
|
||||||
LOG_ERROR(Core, "Could not write cert");
|
if (!Certs::Get(cert).Save(*file)) {
|
||||||
|
LOG_ERROR(Core, "Failed to write cert");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,11 +144,10 @@ bool CIABuilder::WriteTicket(const std::string& ticket_db_path,
|
|||||||
LOG_WARNING(Core, "Could not find title key for {:016x}", title_id);
|
LOG_WARNING(Core, "Could not find title key for {:016x}", title_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto ticket_data = ticket.GetData();
|
header.tik_size = ticket.GetSize();
|
||||||
header.tik_size = ticket_data.size();
|
|
||||||
|
|
||||||
file->Seek(ticket_offset, SEEK_SET);
|
file->Seek(ticket_offset, SEEK_SET);
|
||||||
if (file->WriteBytes(ticket_data.data(), ticket_data.size()) != ticket_data.size()) {
|
if (!ticket.Save(*file)) {
|
||||||
LOG_ERROR(Core, "Could not write ticket");
|
LOG_ERROR(Core, "Could not write ticket");
|
||||||
file.reset();
|
file.reset();
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
@@ -36,4 +37,10 @@ inline u32 GetSignatureSize(u32 signature_type) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr std::array<const char*, 3> CIACertNames{{
|
||||||
|
"CA00000003",
|
||||||
|
"XS0000000c",
|
||||||
|
"CP0000000b",
|
||||||
|
}};
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|||||||
+23
-10
@@ -6,6 +6,7 @@
|
|||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/file_util.h"
|
||||||
#include "core/ncch/cia_common.h"
|
#include "core/ncch/cia_common.h"
|
||||||
#include "core/ncch/ticket.h"
|
#include "core/ncch/ticket.h"
|
||||||
|
|
||||||
@@ -39,18 +40,30 @@ bool Ticket::Load(const std::vector<u8> file_data, std::size_t offset) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> Ticket::GetData() const {
|
bool Ticket::Save(FileUtil::IOFile& file) const {
|
||||||
u32 signature_size = GetSignatureSize(signature_type);
|
// signature
|
||||||
ASSERT(signature_size != 0);
|
if (file.WriteBytes(&signature_type, sizeof(signature_type)) != sizeof(signature_type) ||
|
||||||
|
file.WriteBytes(signature.data(), signature.size()) != signature.size()) {
|
||||||
|
|
||||||
const std::size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40);
|
LOG_ERROR(Core, "Failed to write signature");
|
||||||
const std::size_t body_end = body_start + sizeof(Body);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<u8> out(body_end);
|
// body
|
||||||
std::memcpy(out.data(), &signature_type, sizeof(signature_type));
|
const std::size_t body_start = Common::AlignUp(signature.size() + sizeof(u32), 0x40);
|
||||||
std::memcpy(out.data() + sizeof(signature_type), signature.data(), signature.size());
|
const std::size_t body_end = body_start + sizeof(body);
|
||||||
std::memcpy(out.data() + body_start, &body, sizeof(Body));
|
if (!file.Seek(body_start - signature.size() - sizeof(u32), SEEK_CUR) ||
|
||||||
return out;
|
file.WriteBytes(&body, sizeof(body)) != sizeof(body)) {
|
||||||
|
|
||||||
|
LOG_ERROR(Core, "Failed to write body");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t Ticket::GetSize() const {
|
||||||
|
return Common::AlignUp(signature.size() + sizeof(u32), 0x40) + sizeof(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::string_view TicketIssuer = "Root-CA00000003-XS0000000c";
|
constexpr std::string_view TicketIssuer = "Root-CA00000003-XS0000000c";
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
|
|
||||||
|
namespace FileUtil {
|
||||||
|
class IOFile;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
class Ticket {
|
class Ticket {
|
||||||
@@ -43,7 +47,8 @@ public:
|
|||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
bool Load(const std::vector<u8> file_data, std::size_t offset = 0);
|
bool Load(const std::vector<u8> file_data, std::size_t offset = 0);
|
||||||
std::vector<u8> GetData() const;
|
bool Save(FileUtil::IOFile& file) const;
|
||||||
|
std::size_t GetSize() const;
|
||||||
|
|
||||||
Body body;
|
Body body;
|
||||||
u32_be signature_type;
|
u32_be signature_type;
|
||||||
|
|||||||
Reference in New Issue
Block a user