diff --git a/src/core/importer.cpp b/src/core/importer.cpp index 54d3293..09e2878 100644 --- a/src/core/importer.cpp +++ b/src/core/importer.cpp @@ -12,6 +12,7 @@ #include "core/extdata.h" #include "core/importer.h" #include "core/key/key.h" +#include "core/ncch/certificate.h" #include "core/ncch/cia_builder.h" #include "core/ncch/ncch_container.h" #include "core/ncch/seed_db.h" @@ -54,6 +55,9 @@ bool SDMCImporter::Init() { if (!config.seed_db_path.empty()) { Seeds::Load(config.seed_db_path); } + if (!config.certs_db_path.empty()) { + Certs::Load(config.certs_db_path); + } decryptor = std::make_unique(config.sdmc_path); cia_builder = std::make_unique(); diff --git a/src/core/ncch/certificate.cpp b/src/core/ncch/certificate.cpp index eba9d90..8466ae6 100644 --- a/src/core/ncch/certificate.cpp +++ b/src/core/ncch/certificate.cpp @@ -3,6 +3,8 @@ // Refer to the license.txt file included. #include +#include +#include #include "common/alignment.h" #include "common/file_util.h" #include "common/logging/log.h" @@ -64,7 +66,42 @@ std::size_t Certificate::Load(std::vector file_data, std::size_t offset) { 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 g_certs; +static bool g_is_loaded = false; + +bool Load(const std::string& path) { + g_certs.clear(); + FileUtil::IOFile file(path, "rb"); DataContainer container(file.GetData()); std::vector> data; @@ -96,12 +133,30 @@ bool LoadCertsDB(CertsMap& out, const std::string& path) { const auto name = Common::StringFromFixedZeroTerminatedBuffer(cert.body.name.data(), cert.body.name.size()); - out.emplace(name, cert); + g_certs.emplace(name, std::move(cert)); 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; } +bool IsLoaded() { + return g_is_loaded; +} + +const Certificate& Get(const std::string& name) { + return g_certs.at(name); +} + +} // namespace Certs + } // namespace Core diff --git a/src/core/ncch/certificate.h b/src/core/ncch/certificate.h index 19a9ee3..b3973be 100644 --- a/src/core/ncch/certificate.h +++ b/src/core/ncch/certificate.h @@ -6,10 +6,14 @@ #include #include -#include #include +#include "common/common_funcs.h" #include "common/swap.h" +namespace FileUtil { +class IOFile; +} + namespace Core { enum PublicKeyType : u32 { @@ -30,6 +34,7 @@ public: // Returns: 0 on failure, size of the cert on success std::size_t Load(std::vector file_data, std::size_t offset = 0); + bool Save(FileUtil::IOFile& file) const; u32_be signature_type; std::vector signature; @@ -45,7 +50,12 @@ struct CertsDBHeader { }; static_assert(sizeof(CertsDBHeader) == 0x10); -using CertsMap = std::unordered_map; -bool LoadCertsDB(CertsMap& out, const std::string& path); +namespace Certs { + +bool Load(const std::string& path); +bool IsLoaded(); +const Certificate& Get(const std::string& name); + +} // namespace Certs } // namespace Core diff --git a/src/core/ncch/cia_builder.cpp b/src/core/ncch/cia_builder.cpp index 4a5de41..5b45190 100644 --- a/src/core/ncch/cia_builder.cpp +++ b/src/core/ncch/cia_builder.cpp @@ -5,7 +5,9 @@ #include #include "common/alignment.h" #include "core/importer.h" +#include "core/ncch/certificate.h" #include "core/ncch/cia_builder.h" +#include "core/ncch/cia_common.h" #include "core/ncch/ticket.h" #include "core/ncch/title_metadata.h" #include "core/title_db.h" @@ -103,39 +105,16 @@ bool CIABuilder::Init(const std::string& destination, TitleMetadata tmd_, const } bool CIABuilder::WriteCert(const std::string& certs_db_path) { - FileUtil::IOFile certs_db(certs_db_path, "rb"); - if (!certs_db) { - LOG_ERROR(Core, "Could not open {}", certs_db_path); + if (!Certs::IsLoaded()) { return false; } - std::array 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); - if (file->WriteBytes(cert.data(), cert.size()) != cert.size()) { - LOG_ERROR(Core, "Could not write cert"); - return false; + for (const auto& cert : CIACertNames) { + if (!Certs::Get(cert).Save(*file)) { + LOG_ERROR(Core, "Failed to write cert"); + return false; + } } 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); } - const auto ticket_data = ticket.GetData(); - header.tik_size = ticket_data.size(); + header.tik_size = ticket.GetSize(); 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"); file.reset(); return false; diff --git a/src/core/ncch/cia_common.h b/src/core/ncch/cia_common.h index 4c77f1e..425ed53 100644 --- a/src/core/ncch/cia_common.h +++ b/src/core/ncch/cia_common.h @@ -4,6 +4,7 @@ #pragma once +#include #include "common/common_types.h" namespace Core { @@ -36,4 +37,10 @@ inline u32 GetSignatureSize(u32 signature_type) { return 0; } +constexpr std::array CIACertNames{{ + "CA00000003", + "XS0000000c", + "CP0000000b", +}}; + } // namespace Core diff --git a/src/core/ncch/ticket.cpp b/src/core/ncch/ticket.cpp index fdd4788..854da53 100644 --- a/src/core/ncch/ticket.cpp +++ b/src/core/ncch/ticket.cpp @@ -6,6 +6,7 @@ #include #include "common/alignment.h" #include "common/assert.h" +#include "common/file_util.h" #include "core/ncch/cia_common.h" #include "core/ncch/ticket.h" @@ -39,18 +40,30 @@ bool Ticket::Load(const std::vector file_data, std::size_t offset) { return true; } -std::vector Ticket::GetData() const { - u32 signature_size = GetSignatureSize(signature_type); - ASSERT(signature_size != 0); +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()) { - const std::size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40); - const std::size_t body_end = body_start + sizeof(Body); + LOG_ERROR(Core, "Failed to write signature"); + return false; + } - std::vector out(body_end); - std::memcpy(out.data(), &signature_type, sizeof(signature_type)); - std::memcpy(out.data() + sizeof(signature_type), signature.data(), signature.size()); - std::memcpy(out.data() + body_start, &body, sizeof(Body)); - return out; + // 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; + } + + 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"; diff --git a/src/core/ncch/ticket.h b/src/core/ncch/ticket.h index 9921c52..4189643 100644 --- a/src/core/ncch/ticket.h +++ b/src/core/ncch/ticket.h @@ -10,6 +10,10 @@ #include "common/common_types.h" #include "common/swap.h" +namespace FileUtil { +class IOFile; +} + namespace Core { class Ticket { @@ -43,7 +47,8 @@ public: #pragma pack(pop) bool Load(const std::vector file_data, std::size_t offset = 0); - std::vector GetData() const; + bool Save(FileUtil::IOFile& file) const; + std::size_t GetSize() const; Body body; u32_be signature_type;