From 0708744a1741badb5c48717102e4a779235b79b8 Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Fri, 17 Jan 2020 10:10:58 +0800 Subject: [PATCH] Added encryption scheme display. --- src/core/importer.cpp | 18 ++++++++---- src/core/importer.h | 19 +++++++++++-- src/core/ncch/ncch_container.cpp | 47 ++++++++++++++++++++++++++++++++ src/core/ncch/ncch_container.h | 13 +++++++++ src/frontend/import_dialog.cpp | 35 ++++++++++++++++++++---- src/frontend/import_dialog.ui | 7 ++++- 6 files changed, 125 insertions(+), 14 deletions(-) diff --git a/src/core/importer.cpp b/src/core/importer.cpp index 94e08a0..22f1c5c 100644 --- a/src/core/importer.cpp +++ b/src/core/importer.cpp @@ -296,7 +296,8 @@ std::vector SDMCImporter::ListContent() const { // Regex for half Title IDs static const std::regex title_regex{"[0-9a-f]{8}"}; -std::pair SDMCImporter::LoadTitleData(const std::string& path) const { +std::tuple SDMCImporter::LoadTitleData( + const std::string& path) const { // Remove trailing '/' const auto sdmc_path = config.sdmc_path.substr(0, config.sdmc_path.size() - 1); @@ -356,8 +357,14 @@ std::pair SDMCImporter::LoadTitleData(const std::string& path) u64 extdata_id{}; ncch.ReadExtdataId(extdata_id); - return {Common::UTF16BufferToUTF8(smdh.GetShortTitle(SMDH::TitleLanguage::English)), - extdata_id}; + + EncryptionType encryption = EncryptionType::None; + ncch.ReadEncryptionType(encryption); + + bool seed_crypto{}; + ncch.ReadSeedCrypto(seed_crypto); + return {Common::UTF16BufferToUTF8(smdh.GetShortTitle(SMDH::TitleLanguage::English)), extdata_id, + encryption, seed_crypto}; } void SDMCImporter::ListTitle(std::vector& out) const { @@ -385,11 +392,12 @@ void SDMCImporter::ListTitle(std::vector& out) const { if (FileUtil::Exists(directory + virtual_name + "/content/")) { const auto content_path = fmt::format("/title/{:08x}/{}/content/", high_id, virtual_name); - const auto& [name, extdata_id] = LoadTitleData(content_path); + const auto& [name, extdata_id, encryption, seed_crypto] = + LoadTitleData(content_path); out.push_back( {type, id, FileUtil::Exists(citra_path + "content/"), FileUtil::GetDirectoryTreeSize(directory + virtual_name + "/content/"), - name, extdata_id}); + name, extdata_id, encryption, seed_crypto}); } if (type != ContentType::Application) { diff --git a/src/core/importer.h b/src/core/importer.h index 0bbd176..bb5a5a5 100644 --- a/src/core/importer.h +++ b/src/core/importer.h @@ -27,6 +27,18 @@ enum class ContentType { Sysdata, }; +/** + * Encryption type of an importable content. + */ +enum class EncryptionType { + None, + FixedKey, + NCCHSecure1, + NCCHSecure2, + NCCHSecure3, + NCCHSecure4, +}; + /** * Struct that specifies an importable content. */ @@ -37,6 +49,8 @@ struct ContentSpecifier { u64 maximum_size; ///< The maximum size of the content. May be slightly bigger than real size. std::string name; ///< Optional. The content's preferred display name. u64 extdata_id; ///< Extdata ID for Applications. + EncryptionType encryption = EncryptionType::None; ///< Only for NCCHs. Encryption scheme. + bool seed_crypto = false; ///< Only for NCCHs. Whether seed crypto is used. }; /** @@ -126,11 +140,12 @@ private: void DeleteSysdata(u64 id) const; /** - * Loads the English short title name and extdata id of a title. + * Loads the English short title name, extdata id and encryption of a title. * @param path Path of the 'content' folder relative to the SDMC root folder. * Required to end with '/'. + * @return {name, extdata_id, encryption, seed_crypto} */ - std::pair LoadTitleData(const std::string& path) const; + std::tuple LoadTitleData(const std::string& path) const; bool is_good{}; Config config; diff --git a/src/core/ncch/ncch_container.cpp b/src/core/ncch/ncch_container.cpp index 8afeba5..2b672f9 100644 --- a/src/core/ncch/ncch_container.cpp +++ b/src/core/ncch/ncch_container.cpp @@ -309,6 +309,53 @@ bool NCCHContainer::HasExHeader() { return has_exheader; } +ResultStatus NCCHContainer::ReadEncryptionType(EncryptionType& encryption) { + ResultStatus result = Load(); + if (result != ResultStatus::Success) + return result; + + if (!has_header) + return ResultStatus::ErrorNotUsed; + + if (!is_encrypted) { + encryption = EncryptionType::None; + } else if (ncch_header.fixed_key) { + encryption = EncryptionType::FixedKey; + } else { + switch (ncch_header.secondary_key_slot) { + case 0: + encryption = EncryptionType::NCCHSecure1; + break; + case 1: + encryption = EncryptionType::NCCHSecure2; + break; + case 10: + encryption = EncryptionType::NCCHSecure3; + break; + case 11: + encryption = EncryptionType::NCCHSecure4; + break; + default: + LOG_ERROR(Service_FS, "Unknown encryption type {:X}!", ncch_header.secondary_key_slot); + return ResultStatus::ErrorNotUsed; + } + } + + return ResultStatus::Success; +} + +ResultStatus NCCHContainer::ReadSeedCrypto(bool& used) { + ResultStatus result = Load(); + if (result != ResultStatus::Success) + return result; + + if (!has_header) + return ResultStatus::ErrorNotUsed; + + used = ncch_header.seed_crypto; + return ResultStatus::Success; +} + #pragma pack(push, 1) struct RomFSIVFCHeader { u32_le magic; diff --git a/src/core/ncch/ncch_container.h b/src/core/ncch/ncch_container.h index 37d5012..f6a96e8 100644 --- a/src/core/ncch/ncch_container.h +++ b/src/core/ncch/ncch_container.h @@ -13,6 +13,7 @@ #include "common/file_util.h" #include "common/swap.h" #include "core/decryptor.h" +#include "core/importer.h" #include "core/result_status.h" namespace Core { @@ -252,6 +253,18 @@ public: */ bool HasExHeader(); + /** + * Gets encryption type (which key is used). + * @return ResultStatus result of function. + */ + ResultStatus ReadEncryptionType(EncryptionType& encryption); + + /** + * Gets whether seed crypto is used. + * @return ResultStatus result of function. + */ + ResultStatus ReadSeedCrypto(bool& used); + NCCH_Header ncch_header; ExHeader_Header exheader_header; ExeFs_Header exefs_header; diff --git a/src/frontend/import_dialog.cpp b/src/frontend/import_dialog.cpp index 3cfdf67..d2747a3 100644 --- a/src/frontend/import_dialog.cpp +++ b/src/frontend/import_dialog.cpp @@ -41,6 +41,15 @@ static constexpr std::array, 7> Conten {Core::ContentType::Sysdata, QT_TR_NOOP("System Data")}, }}; +static const std::unordered_map EncryptionTypeMap{{ + {Core::EncryptionType::None, QT_TR_NOOP("None")}, + {Core::EncryptionType::FixedKey, QT_TR_NOOP("FixedKey")}, + {Core::EncryptionType::NCCHSecure1, QT_TR_NOOP("Secure1")}, + {Core::EncryptionType::NCCHSecure2, QT_TR_NOOP("Secure2")}, + {Core::EncryptionType::NCCHSecure3, QT_TR_NOOP("Secure3")}, + {Core::EncryptionType::NCCHSecure4, QT_TR_NOOP("Secure4")}, +}}; + QString GetContentName(const Core::ContentSpecifier& specifier) { return specifier.name.empty() ? QStringLiteral("0x%1").arg(specifier.id, 16, 16, QLatin1Char('0')) @@ -84,11 +93,13 @@ ImportDialog::ImportDialog(QWidget* parent, const Core::Config& config) RelistContent(); UpdateSizeDisplay(); - // Set up column widths - ui->main->setColumnWidth(0, width() / 8); - ui->main->setColumnWidth(1, width() / 2); - ui->main->setColumnWidth(2, width() / 6); - ui->main->setColumnWidth(3, width() / 10); + // Set up column widths. + // These values are tweaked with regard to the default dialog size. + ui->main->setColumnWidth(0, width() * 0.11); + ui->main->setColumnWidth(1, width() * 0.415); + ui->main->setColumnWidth(2, width() * 0.14); + ui->main->setColumnWidth(3, width() * 0.17); + ui->main->setColumnWidth(4, width() * 0.08); } ImportDialog::~ImportDialog() = default; @@ -174,8 +185,20 @@ void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpe name = GetContentName(content); } + QString encryption = tr(EncryptionTypeMap.at(content.encryption)); + if (content.seed_crypto) { + encryption.append(tr(" (Seed)")); + } + + if (content.type != Core::ContentType::Application && + content.type != Core::ContentType::Update && content.type != Core::ContentType::DLC) { + + // Do not display encryption in this case + encryption.clear(); + } + auto* item = new QTreeWidgetItem{ - {QString{}, name, ReadableByteSize(content.maximum_size), + {QString{}, name, ReadableByteSize(content.maximum_size), encryption, content.already_exists ? QStringLiteral("Yes") : QStringLiteral("No")}}; ui->main->invisibleRootItem()->child(row)->addChild(item); diff --git a/src/frontend/import_dialog.ui b/src/frontend/import_dialog.ui index a793d31..2fedaee 100644 --- a/src/frontend/import_dialog.ui +++ b/src/frontend/import_dialog.ui @@ -6,7 +6,7 @@ 0 0 - 600 + 700 400 @@ -56,6 +56,11 @@ Size + + + Encryption + + Exists