From df5b75694f5abde94ccf05fa6c7a557b1ba9079b Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 27 Jul 2018 23:55:23 -0400 Subject: Remove files that are not used --- src/core/file_sys/card_image.cpp | 150 ++++++++++++++++++++++++++++++ src/core/file_sys/card_image.h | 93 +++++++++++++++++++ src/core/file_sys/content_archive.cpp | 168 ++++++++++++++++++++++++++++------ src/core/file_sys/content_archive.h | 26 ++++-- src/core/file_sys/vfs.cpp | 21 +++++ src/core/file_sys/vfs.h | 3 + 6 files changed, 425 insertions(+), 36 deletions(-) create mode 100644 src/core/file_sys/card_image.cpp create mode 100644 src/core/file_sys/card_image.h (limited to 'src/core/file_sys') diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp new file mode 100644 index 000000000..5ff09a362 --- /dev/null +++ b/src/core/file_sys/card_image.cpp @@ -0,0 +1,150 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include "core/file_sys/card_image.h" +#include "core/file_sys/partition_filesystem.h" +#include "core/file_sys/vfs_offset.h" + +namespace FileSys { + +XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { + if (file->ReadObject(&header) != sizeof(GamecardHeader)) { + status = Loader::ResultStatus::ErrorInvalidFormat; + return; + } + + if (header.magic != Common::MakeMagic('H', 'E', 'A', 'D')) { + status = Loader::ResultStatus::ErrorInvalidFormat; + return; + } + + PartitionFilesystem main_hfs( + std::make_shared(file, header.hfs_size, header.hfs_offset)); + + if (main_hfs.GetStatus() != Loader::ResultStatus::Success) { + status = main_hfs.GetStatus(); + return; + } + + const static std::array partition_names = {"update", "normal", "secure", + "logo"}; + + for (XCIPartition partition : + {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) { + auto raw = main_hfs.GetFile(partition_names[static_cast(partition)]); + if (raw != nullptr) + partitions[static_cast(partition)] = std::make_shared(raw); + } + + auto result = AddNCAFromPartition(XCIPartition::Secure); + if (result != Loader::ResultStatus::Success) { + status = result; + return; + } + result = AddNCAFromPartition(XCIPartition::Update); + if (result != Loader::ResultStatus::Success) { + status = result; + return; + } + + result = AddNCAFromPartition(XCIPartition::Normal); + if (result != Loader::ResultStatus::Success) { + status = result; + return; + } + + if (GetFormatVersion() >= 0x2) { + result = AddNCAFromPartition(XCIPartition::Logo); + if (result != Loader::ResultStatus::Success) { + status = result; + return; + } + } + + status = Loader::ResultStatus::Success; +} + +Loader::ResultStatus XCI::GetStatus() const { + return status; +} + +VirtualDir XCI::GetPartition(XCIPartition partition) const { + return partitions[static_cast(partition)]; +} + +VirtualDir XCI::GetSecurePartition() const { + return GetPartition(XCIPartition::Secure); +} + +VirtualDir XCI::GetNormalPartition() const { + return GetPartition(XCIPartition::Normal); +} + +VirtualDir XCI::GetUpdatePartition() const { + return GetPartition(XCIPartition::Update); +} + +VirtualDir XCI::GetLogoPartition() const { + return GetPartition(XCIPartition::Logo); +} + +std::shared_ptr XCI::GetNCAByType(NCAContentType type) const { + for (const auto& nca : ncas) { + if (nca->GetType() == type) + return nca; + } + + return nullptr; +} + +VirtualFile XCI::GetNCAFileByType(NCAContentType type) const { + auto nca = GetNCAByType(type); + if (nca != nullptr) + return nca->GetBaseFile(); + return nullptr; +} + +std::vector> XCI::GetFiles() const { + return {}; +} + +std::vector> XCI::GetSubdirectories() const { + return std::vector>(); +} + +std::string XCI::GetName() const { + return file->GetName(); +} + +std::shared_ptr XCI::GetParentDirectory() const { + return file->GetContainingDirectory(); +} + +bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { + return false; +} + +Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { + if (partitions[static_cast(part)] == nullptr) { + return Loader::ResultStatus::ErrorInvalidFormat; + } + + for (VirtualFile file : partitions[static_cast(part)]->GetFiles()) { + if (file->GetExtension() != "nca") + continue; + auto nca = std::make_shared(file); + if (nca->GetStatus() == Loader::ResultStatus::Success) + ncas.push_back(std::move(nca)); + } + + return Loader::ResultStatus::Success; +} + +u8 XCI::GetFormatVersion() const { + return GetLogoPartition() == nullptr ? 0x1 : 0x2; +} +} // namespace FileSys diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h new file mode 100644 index 000000000..b765c8bc1 --- /dev/null +++ b/src/core/file_sys/card_image.h @@ -0,0 +1,93 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include "common/swap.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/vfs.h" + +namespace FileSys { + +enum class GamecardSize : u8 { + S_1GB = 0xFA, + S_2GB = 0xF8, + S_4GB = 0xF0, + S_8GB = 0xE0, + S_16GB = 0xE1, + S_32GB = 0xE2, +}; + +struct GamecardInfo { + std::array data; +}; +static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size."); + +struct GamecardHeader { + std::array signature; + u32_le magic; + u32_le secure_area_start; + u32_le backup_area_start; + u8 kek_index; + GamecardSize size; + u8 header_version; + u8 flags; + u64_le package_id; + u64_le valid_data_end; + u128 info_iv; + u64_le hfs_offset; + u64_le hfs_size; + std::array hfs_header_hash; + std::array initial_data_hash; + u32_le secure_mode_flag; + u32_le title_key_flag; + u32_le key_flag; + u32_le normal_area_end; + GamecardInfo info; +}; +static_assert(sizeof(GamecardHeader) == 0x200, "GamecardHeader has incorrect size."); + +enum class XCIPartition : u8 { Update, Normal, Secure, Logo }; + +class XCI : public ReadOnlyVfsDirectory { +public: + explicit XCI(VirtualFile file); + + Loader::ResultStatus GetStatus() const; + + u8 GetFormatVersion() const; + + VirtualDir GetPartition(XCIPartition partition) const; + VirtualDir GetSecurePartition() const; + VirtualDir GetNormalPartition() const; + VirtualDir GetUpdatePartition() const; + VirtualDir GetLogoPartition() const; + + std::shared_ptr GetNCAByType(NCAContentType type) const; + VirtualFile GetNCAFileByType(NCAContentType type) const; + + std::vector> GetFiles() const override; + + std::vector> GetSubdirectories() const override; + + std::string GetName() const override; + + std::shared_ptr GetParentDirectory() const override; + +protected: + bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; + +private: + Loader::ResultStatus AddNCAFromPartition(XCIPartition part); + + VirtualFile file; + GamecardHeader header{}; + + Loader::ResultStatus status; + + std::vector partitions; + std::vector> ncas; +}; +} // namespace FileSys diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 61cb0bbe3..add01974e 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -6,9 +6,12 @@ #include #include "common/logging/log.h" +#include "core/crypto/aes_util.h" +#include "core/crypto/ctr_encryption_layer.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/vfs_offset.h" #include "core/loader/loader.h" +#include "mbedtls/cipher.h" #include "romfs.h" namespace FileSys { @@ -29,11 +32,19 @@ enum class NCASectionFilesystemType : u8 { struct NCASectionHeaderBlock { INSERT_PADDING_BYTES(3); NCASectionFilesystemType filesystem_type; - u8 crypto_type; + NCASectionCryptoType crypto_type; INSERT_PADDING_BYTES(3); }; static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); +struct NCASectionRaw { + NCASectionHeaderBlock header; + std::array block_data; + std::array section_ctr; + INSERT_PADDING_BYTES(0xB8); +}; +static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size."); + struct PFS0Superblock { NCASectionHeaderBlock header_block; std::array hash; @@ -43,62 +54,155 @@ struct PFS0Superblock { u64_le hash_table_size; u64_le pfs0_header_offset; u64_le pfs0_size; - INSERT_PADDING_BYTES(432); + INSERT_PADDING_BYTES(0x1B0); }; static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size."); struct RomFSSuperblock { NCASectionHeaderBlock header_block; IVFCHeader ivfc; + INSERT_PADDING_BYTES(0x118); +}; +static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size."); + +union NCASectionHeader { + NCASectionRaw raw; + PFS0Superblock pfs0; + RomFSSuperblock romfs; }; -static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size."); +static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size."); + +bool IsValidNCA(const NCAHeader& header) { + // TODO(DarkLordZach): Add NCA2/NCA0 support. + return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); +} + +Crypto::Key128 NCA::GetKeyAreaKey(NCASectionCryptoType type) { + u8 master_key_id = header.crypto_type; + if (header.crypto_type_2 > master_key_id) + master_key_id = header.crypto_type_2; + if (master_key_id > 0) + --master_key_id; + + std::vector key_area(header.key_area.begin(), header.key_area.end()); + if (!Crypto::keys.ValidateKey(Crypto::S128KeyType::KEY_AREA, master_key_id, header.key_index)) { + status = Loader::ResultStatus::ErrorEncrypted; + return {}; + } + Crypto::AESCipher cipher( + Crypto::keys.GetKey(Crypto::S128KeyType::KEY_AREA, master_key_id, header.key_index), + Crypto::Mode::ECB); + cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Crypto::Op::DECRYPT); + + Crypto::Key128 out; + if (type == NCASectionCryptoType::XTS) + std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin()); + else if (type == NCASectionCryptoType::CTR) + std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin()); + else + LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}", + static_cast(type)); + + u128 out_128 = *reinterpret_cast(&out); + LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}", + master_key_id, header.key_index, out_128[1], out_128[0]); + + return out; +} + +VirtualFile NCA::Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) { + if (!encrypted) + return in; + + switch (header.raw.header.crypto_type) { + case NCASectionCryptoType::NONE: + LOG_DEBUG(Crypto, "called with mode=NONE"); + return in; + case NCASectionCryptoType::CTR: + LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); + { + auto out = std::make_shared( + std::move(in), GetKeyAreaKey(NCASectionCryptoType::CTR), starting_offset); + std::vector iv(16, 0); + for (u8 i = 0; i < 8; ++i) + iv[i] = header.raw.section_ctr[0x8 - i - 1]; + out->SetIV(iv); + return out; + } + case NCASectionCryptoType::XTS: + // TODO(DarkLordZach): Implement XTSEncryptionLayer and title key encryption. + default: + LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}", + static_cast(header.raw.header.crypto_type)); + return nullptr; + } +} NCA::NCA(VirtualFile file_) : file(std::move(file_)) { if (sizeof(NCAHeader) != file->ReadObject(&header)) - LOG_CRITICAL(Loader, "File reader errored out during header read."); + LOG_ERROR(Loader, "File reader errored out during header read."); + + encrypted = false; if (!IsValidNCA(header)) { - status = Loader::ResultStatus::ErrorInvalidFormat; - return; + NCAHeader dec_header{}; + if (!Crypto::keys.ValidateKey(Crypto::S256KeyType::HEADER)) { + status = Loader::ResultStatus::ErrorEncrypted; + return; + } + Crypto::AESCipher cipher(Crypto::keys.GetKey(Crypto::S256KeyType::HEADER), + Crypto::Mode::XTS); + cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200, Crypto::Op::DECRYPT); + if (IsValidNCA(dec_header)) { + header = dec_header; + encrypted = true; + } else { + status = Loader::ResultStatus::ErrorInvalidFormat; + return; + } } - std::ptrdiff_t number_sections = + const std::ptrdiff_t number_sections = std::count_if(std::begin(header.section_tables), std::end(header.section_tables), [](NCASectionTableEntry entry) { return entry.media_offset > 0; }); + std::vector sections(number_sections); + const auto length_sections = SECTION_HEADER_SIZE * number_sections; + + if (encrypted) { + auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET); + if (!Crypto::keys.ValidateKey(Crypto::S256KeyType::HEADER)) { + status = Loader::ResultStatus::ErrorEncrypted; + return; + } + Crypto::AESCipher cipher(Crypto::keys.GetKey(Crypto::S256KeyType::HEADER), + Crypto::Mode::XTS); + cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE, + Crypto::Op::DECRYPT); + } else { + file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET); + } + for (std::ptrdiff_t i = 0; i < number_sections; ++i) { - // Seek to beginning of this section. - NCASectionHeaderBlock block{}; - if (sizeof(NCASectionHeaderBlock) != - file->ReadObject(&block, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) - LOG_CRITICAL(Loader, "File reader errored out during header read."); - - if (block.filesystem_type == NCASectionFilesystemType::ROMFS) { - RomFSSuperblock sb{}; - if (sizeof(RomFSSuperblock) != - file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) - LOG_CRITICAL(Loader, "File reader errored out during header read."); + auto section = sections[i]; + if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { const size_t romfs_offset = header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + - sb.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; - const size_t romfs_size = sb.ivfc.levels[IVFC_MAX_LEVEL - 1].size; - files.emplace_back(std::make_shared(file, romfs_size, romfs_offset)); + section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; + const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size; + files.emplace_back( + Decrypt(section, std::make_shared(file, romfs_size, romfs_offset), + romfs_offset)); romfs = files.back(); - } else if (block.filesystem_type == NCASectionFilesystemType::PFS0) { - PFS0Superblock sb{}; - // Seek back to beginning of this section. - if (sizeof(PFS0Superblock) != - file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) - LOG_CRITICAL(Loader, "File reader errored out during header read."); - + } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { u64 offset = (static_cast(header.section_tables[i].media_offset) * MEDIA_OFFSET_MULTIPLIER) + - sb.pfs0_header_offset; + section.pfs0.pfs0_header_offset; u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - header.section_tables[i].media_offset); auto npfs = std::make_shared( - std::make_shared(file, size, offset)); + Decrypt(section, std::make_shared(file, size, offset), offset)); if (npfs->GetStatus() == Loader::ResultStatus::Success) { dirs.emplace_back(npfs); @@ -153,6 +257,10 @@ VirtualDir NCA::GetExeFS() const { return exefs; } +VirtualFile NCA::GetBaseFile() const { + return file; +} + bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { return false; } diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 0b8b9db61..1e8d9c8ae 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -12,10 +12,10 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/crypto/key_manager.h" #include "core/file_sys/partition_filesystem.h" namespace FileSys { - enum class NCAContentType : u8 { Program = 0, Meta = 1, @@ -24,6 +24,13 @@ enum class NCAContentType : u8 { Data = 4, }; +enum class NCASectionCryptoType : u8 { + NONE = 1, + XTS = 2, + CTR = 3, + BKTR = 4, +}; + struct NCASectionTableEntry { u32_le media_offset; u32_le media_end_offset; @@ -48,20 +55,19 @@ struct NCAHeader { std::array rights_id; std::array section_tables; std::array, 0x4> hash_tables; - std::array, 0x4> key_area; + std::array key_area; INSERT_PADDING_BYTES(0xC0); }; static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size."); +union NCASectionHeader; + inline bool IsDirectoryExeFS(const std::shared_ptr& pfs) { // According to switchbrew, an exefs must only contain these two files: return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; } -inline bool IsValidNCA(const NCAHeader& header) { - return header.magic == Common::MakeMagic('N', 'C', 'A', '2') || - header.magic == Common::MakeMagic('N', 'C', 'A', '3'); -} +bool IsValidNCA(const NCAHeader& header); // An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner. // After construction, use GetStatus to determine if the file is valid and ready to be used. @@ -81,6 +87,8 @@ public: VirtualFile GetRomFS() const; VirtualDir GetExeFS() const; + VirtualFile GetBaseFile() const; + protected: bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; @@ -95,6 +103,12 @@ private: NCAHeader header{}; Loader::ResultStatus status{}; + + bool encrypted; + + Crypto::Key128 GetKeyAreaKey(NCASectionCryptoType type); + + VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, size_t starting_offset); }; } // namespace FileSys diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index 84a6a7397..57d0aeb85 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp @@ -285,6 +285,27 @@ bool ReadOnlyVfsDirectory::Rename(std::string_view name) { return false; } +bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size) { + if (file1->GetSize() != file2->GetSize()) + return false; + + std::vector f1_v(block_size); + std::vector f2_v(block_size); + for (size_t i = 0; i < file1->GetSize(); i += block_size) { + auto f1_vs = file1->Read(f1_v.data(), block_size, i); + auto f2_vs = file2->Read(f2_v.data(), block_size, i); + + if (f1_vs != f2_vs) + return false; + for (size_t j = 0; j < f1_vs; ++j) { + if (f1_v[j] != f2_v[j]) + return false; + } + } + + return true; +} + bool VfsRawCopy(VirtualFile src, VirtualFile dest) { if (src == nullptr || dest == nullptr) return false; diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index cf871edd6..fab9e2b45 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h @@ -245,6 +245,9 @@ struct ReadOnlyVfsDirectory : public VfsDirectory { bool Rename(std::string_view name) override; }; +// Compare the two files, byte-for-byte, in increments specificed by block_size +bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x200); + // A method that copies the raw data between two different implementations of VirtualFile. If you // are using the same implementation, it is probably better to use the Copy method in the parent // directory of src/dest. -- cgit v1.2.3 From 22342487e8fb851a9837db22408db56240aa6931 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 28 Jul 2018 16:23:00 -0400 Subject: Extract mbedtls to cpp file --- src/core/file_sys/content_archive.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/core/file_sys') diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 1e8d9c8ae..d9ad3bf7e 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -9,6 +9,7 @@ #include #include +#include "core/loader/loader.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" @@ -108,7 +109,7 @@ private: Crypto::Key128 GetKeyAreaKey(NCASectionCryptoType type); - VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, size_t starting_offset); + VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset); }; } // namespace FileSys -- cgit v1.2.3 From 239a3113e4c6a53a2c7b12e67a0f21afae24b0aa Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 28 Jul 2018 21:39:42 -0400 Subject: Make XCI comply to review and style guidelines --- src/core/file_sys/card_image.cpp | 15 +++++------ src/core/file_sys/content_archive.cpp | 48 +++++++++++++---------------------- src/core/file_sys/content_archive.h | 5 ++-- src/core/file_sys/vfs.cpp | 7 +++-- 4 files changed, 30 insertions(+), 45 deletions(-) (limited to 'src/core/file_sys') diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index 5ff09a362..3c1dbf46c 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -30,8 +30,8 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { return; } - const static std::array partition_names = {"update", "normal", "secure", - "logo"}; + static constexpr std::array partition_names = {"update", "normal", "secure", + "logo"}; for (XCIPartition partition : {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) { @@ -93,12 +93,9 @@ VirtualDir XCI::GetLogoPartition() const { } std::shared_ptr XCI::GetNCAByType(NCAContentType type) const { - for (const auto& nca : ncas) { - if (nca->GetType() == type) - return nca; - } - - return nullptr; + auto iter = std::find_if(ncas.begin(), ncas.end(), + [type](std::shared_ptr nca) { return nca->GetType() == type; }); + return iter == ncas.end() ? nullptr : *iter; } VirtualFile XCI::GetNCAFileByType(NCAContentType type) const { @@ -133,7 +130,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { return Loader::ResultStatus::ErrorInvalidFormat; } - for (VirtualFile file : partitions[static_cast(part)]->GetFiles()) { + for (const VirtualFile& file : partitions[static_cast(part)]->GetFiles()) { if (file->GetExtension() != "nca") continue; auto nca = std::make_shared(file); diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index add01974e..952dc7068 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -9,10 +9,9 @@ #include "core/crypto/aes_util.h" #include "core/crypto/ctr_encryption_layer.h" #include "core/file_sys/content_archive.h" +#include "core/file_sys/romfs.h" #include "core/file_sys/vfs_offset.h" #include "core/loader/loader.h" -#include "mbedtls/cipher.h" -#include "romfs.h" namespace FileSys { @@ -77,7 +76,7 @@ bool IsValidNCA(const NCAHeader& header) { return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); } -Crypto::Key128 NCA::GetKeyAreaKey(NCASectionCryptoType type) { +Core::Crypto::Key128 NCA::GetKeyAreaKey(NCASectionCryptoType type) { u8 master_key_id = header.crypto_type; if (header.crypto_type_2 > master_key_id) master_key_id = header.crypto_type_2; @@ -85,16 +84,12 @@ Crypto::Key128 NCA::GetKeyAreaKey(NCASectionCryptoType type) { --master_key_id; std::vector key_area(header.key_area.begin(), header.key_area.end()); - if (!Crypto::keys.ValidateKey(Crypto::S128KeyType::KEY_AREA, master_key_id, header.key_index)) { - status = Loader::ResultStatus::ErrorEncrypted; - return {}; - } - Crypto::AESCipher cipher( - Crypto::keys.GetKey(Crypto::S128KeyType::KEY_AREA, master_key_id, header.key_index), - Crypto::Mode::ECB); - cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Crypto::Op::DECRYPT); + Core::Crypto::AESCipher cipher( + keys.GetKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index), + Core::Crypto::Mode::ECB); + cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt); - Crypto::Key128 out; + Core::Crypto::Key128 out; if (type == NCASectionCryptoType::XTS) std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin()); else if (type == NCASectionCryptoType::CTR) @@ -102,8 +97,8 @@ Crypto::Key128 NCA::GetKeyAreaKey(NCASectionCryptoType type) { else LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}", static_cast(type)); - - u128 out_128 = *reinterpret_cast(&out); + u128 out_128{}; + memcpy(out_128.data(), out.data(), 16); LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}", master_key_id, header.key_index, out_128[1], out_128[0]); @@ -121,9 +116,9 @@ VirtualFile NCA::Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_o case NCASectionCryptoType::CTR: LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); { - auto out = std::make_shared( + auto out = std::make_shared( std::move(in), GetKeyAreaKey(NCASectionCryptoType::CTR), starting_offset); - std::vector iv(16, 0); + std::vector iv(16); for (u8 i = 0; i < 8; ++i) iv[i] = header.raw.section_ctr[0x8 - i - 1]; out->SetIV(iv); @@ -146,13 +141,10 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { if (!IsValidNCA(header)) { NCAHeader dec_header{}; - if (!Crypto::keys.ValidateKey(Crypto::S256KeyType::HEADER)) { - status = Loader::ResultStatus::ErrorEncrypted; - return; - } - Crypto::AESCipher cipher(Crypto::keys.GetKey(Crypto::S256KeyType::HEADER), - Crypto::Mode::XTS); - cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200, Crypto::Op::DECRYPT); + Core::Crypto::AESCipher cipher( + keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); + cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200, + Core::Crypto::Op::Decrypt); if (IsValidNCA(dec_header)) { header = dec_header; encrypted = true; @@ -171,14 +163,10 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { if (encrypted) { auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET); - if (!Crypto::keys.ValidateKey(Crypto::S256KeyType::HEADER)) { - status = Loader::ResultStatus::ErrorEncrypted; - return; - } - Crypto::AESCipher cipher(Crypto::keys.GetKey(Crypto::S256KeyType::HEADER), - Crypto::Mode::XTS); + Core::Crypto::AESCipher cipher( + keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE, - Crypto::Op::DECRYPT); + Core::Crypto::Op::Decrypt); } else { file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET); } diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index d9ad3bf7e..153142b06 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -9,12 +9,12 @@ #include #include -#include "core/loader/loader.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" #include "core/crypto/key_manager.h" #include "core/file_sys/partition_filesystem.h" +#include "core/loader/loader.h" namespace FileSys { enum class NCAContentType : u8 { @@ -107,7 +107,8 @@ private: bool encrypted; - Crypto::Key128 GetKeyAreaKey(NCASectionCryptoType type); + Core::Crypto::KeyManager keys; + Core::Crypto::Key128 GetKeyAreaKey(NCASectionCryptoType type); VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset); }; diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index 57d0aeb85..dae1c16ef 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp @@ -297,10 +297,9 @@ bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block if (f1_vs != f2_vs) return false; - for (size_t j = 0; j < f1_vs; ++j) { - if (f1_v[j] != f2_v[j]) - return false; - } + auto iters = std::mismatch(f1_v.begin(), f1_v.end(), f2_v.begin(), f2_v.end()); + if (iters.first != f1_v.end() && iters.second != f2_v.end()) + return false; } return true; -- cgit v1.2.3 From 03149d3e4a7f8038d9c88cbeb19dee25a39e0042 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 29 Jul 2018 19:00:09 -0400 Subject: Add missing includes and use const where applicable --- src/core/file_sys/card_image.cpp | 5 +++-- src/core/file_sys/card_image.h | 3 +++ src/core/file_sys/content_archive.cpp | 4 ++-- src/core/file_sys/content_archive.h | 11 ++++++----- 4 files changed, 14 insertions(+), 9 deletions(-) (limited to 'src/core/file_sys') diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index 3c1dbf46c..c69812455 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -93,8 +93,9 @@ VirtualDir XCI::GetLogoPartition() const { } std::shared_ptr XCI::GetNCAByType(NCAContentType type) const { - auto iter = std::find_if(ncas.begin(), ncas.end(), - [type](std::shared_ptr nca) { return nca->GetType() == type; }); + const auto iter = + std::find_if(ncas.begin(), ncas.end(), + [type](const std::shared_ptr& nca) { return nca->GetType() == type; }); return iter == ncas.end() ? nullptr : *iter; } diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index b765c8bc1..e089d737c 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h @@ -4,10 +4,13 @@ #pragma once +#include #include +#include "common/common_types.h" #include "common/swap.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/vfs.h" +#include "core/loader/loader.h" namespace FileSys { diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 952dc7068..9eceaa4c4 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -76,7 +76,7 @@ bool IsValidNCA(const NCAHeader& header) { return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); } -Core::Crypto::Key128 NCA::GetKeyAreaKey(NCASectionCryptoType type) { +Core::Crypto::Key128 NCA::GetKeyAreaKey(NCASectionCryptoType type) const { u8 master_key_id = header.crypto_type; if (header.crypto_type_2 > master_key_id) master_key_id = header.crypto_type_2; @@ -105,7 +105,7 @@ Core::Crypto::Key128 NCA::GetKeyAreaKey(NCASectionCryptoType type) { return out; } -VirtualFile NCA::Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) { +VirtualFile NCA::Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const { if (!encrypted) return in; diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 153142b06..e68ab0235 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -17,6 +17,9 @@ #include "core/loader/loader.h" namespace FileSys { + +union NCASectionHeader; + enum class NCAContentType : u8 { Program = 0, Meta = 1, @@ -61,8 +64,6 @@ struct NCAHeader { }; static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size."); -union NCASectionHeader; - inline bool IsDirectoryExeFS(const std::shared_ptr& pfs) { // According to switchbrew, an exefs must only contain these two files: return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; @@ -94,6 +95,9 @@ protected: bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; private: + Core::Crypto::Key128 GetKeyAreaKey(NCASectionCryptoType type) const; + VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const; + std::vector dirs; std::vector files; @@ -108,9 +112,6 @@ private: bool encrypted; Core::Crypto::KeyManager keys; - Core::Crypto::Key128 GetKeyAreaKey(NCASectionCryptoType type); - - VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset); }; } // namespace FileSys -- cgit v1.2.3 From a9c921a41dec63f76f80df1f0d5dc3be40fa80de Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 29 Jul 2018 20:47:33 -0400 Subject: Use ErrorEncrypted where applicable and fix no keys crash --- src/core/file_sys/card_image.cpp | 1 + src/core/file_sys/content_archive.cpp | 45 +++++++++++++++++++++++------------ src/core/file_sys/content_archive.h | 4 ++-- 3 files changed, 33 insertions(+), 17 deletions(-) (limited to 'src/core/file_sys') diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index c69812455..395eea8ae 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -45,6 +45,7 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { status = result; return; } + result = AddNCAFromPartition(XCIPartition::Update); if (result != Loader::ResultStatus::Success) { status = result; diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 9eceaa4c4..cb59c0a2b 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -4,7 +4,7 @@ #include #include - +#include #include "common/logging/log.h" #include "core/crypto/aes_util.h" #include "core/crypto/ctr_encryption_layer.h" @@ -76,13 +76,16 @@ bool IsValidNCA(const NCAHeader& header) { return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); } -Core::Crypto::Key128 NCA::GetKeyAreaKey(NCASectionCryptoType type) const { +boost::optional NCA::GetKeyAreaKey(NCASectionCryptoType type) const { u8 master_key_id = header.crypto_type; if (header.crypto_type_2 > master_key_id) master_key_id = header.crypto_type_2; if (master_key_id > 0) --master_key_id; + if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index)) + return boost::none; + std::vector key_area(header.key_area.begin(), header.key_area.end()); Core::Crypto::AESCipher cipher( keys.GetKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index), @@ -116,13 +119,16 @@ VirtualFile NCA::Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_o case NCASectionCryptoType::CTR: LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); { + const auto key = GetKeyAreaKey(NCASectionCryptoType::CTR); + if (key == boost::none) + return nullptr; auto out = std::make_shared( - std::move(in), GetKeyAreaKey(NCASectionCryptoType::CTR), starting_offset); + std::move(in), key.value(), starting_offset); std::vector iv(16); for (u8 i = 0; i < 8; ++i) iv[i] = header.raw.section_ctr[0x8 - i - 1]; out->SetIV(iv); - return out; + return std::static_pointer_cast(out); } case NCASectionCryptoType::XTS: // TODO(DarkLordZach): Implement XTSEncryptionLayer and title key encryption. @@ -149,7 +155,10 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { header = dec_header; encrypted = true; } else { - status = Loader::ResultStatus::ErrorInvalidFormat; + if (!keys.HasKey(Core::Crypto::S256KeyType::Header)) + status = Loader::ResultStatus::ErrorEncrypted; + else + status = Loader::ResultStatus::ErrorInvalidFormat; return; } } @@ -179,23 +188,29 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size; - files.emplace_back( + const auto dec = Decrypt(section, std::make_shared(file, romfs_size, romfs_offset), - romfs_offset)); - romfs = files.back(); + romfs_offset); + if (dec != nullptr) { + files.emplace_back(); + romfs = files.back(); + } } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { u64 offset = (static_cast(header.section_tables[i].media_offset) * MEDIA_OFFSET_MULTIPLIER) + section.pfs0.pfs0_header_offset; u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - header.section_tables[i].media_offset); - auto npfs = std::make_shared( - Decrypt(section, std::make_shared(file, size, offset), offset)); - - if (npfs->GetStatus() == Loader::ResultStatus::Success) { - dirs.emplace_back(npfs); - if (IsDirectoryExeFS(dirs.back())) - exefs = dirs.back(); + const auto dec = + Decrypt(section, std::make_shared(file, size, offset), offset); + if (dec != nullptr) { + auto npfs = std::make_shared(dec); + + if (npfs->GetStatus() == Loader::ResultStatus::Success) { + dirs.emplace_back(npfs); + if (IsDirectoryExeFS(dirs.back())) + exefs = dirs.back(); + } } } } diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index e68ab0235..6492163b5 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -8,7 +8,7 @@ #include #include #include - +#include #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" @@ -95,7 +95,7 @@ protected: bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; private: - Core::Crypto::Key128 GetKeyAreaKey(NCASectionCryptoType type) const; + boost::optional GetKeyAreaKey(NCASectionCryptoType type) const; VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const; std::vector dirs; -- cgit v1.2.3 From 187d8e215fb157edaa9f3976bebba9a9a7ed103d Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 30 Jul 2018 12:46:23 -0400 Subject: Use more descriptive error codes and messages --- src/core/file_sys/content_archive.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/core/file_sys') diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index cb59c0a2b..2deb727cc 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -156,9 +156,9 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { encrypted = true; } else { if (!keys.HasKey(Core::Crypto::S256KeyType::Header)) - status = Loader::ResultStatus::ErrorEncrypted; + status = Loader::ResultStatus::ErrorMissingKeys; else - status = Loader::ResultStatus::ErrorInvalidFormat; + status = Loader::ResultStatus::ErrorDecrypting; return; } } @@ -194,6 +194,9 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { if (dec != nullptr) { files.emplace_back(); romfs = files.back(); + } else { + status = Loader::ResultStatus::ErrorMissingKeys; + return; } } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { u64 offset = (static_cast(header.section_tables[i].media_offset) * @@ -211,6 +214,9 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { if (IsDirectoryExeFS(dirs.back())) exefs = dirs.back(); } + } else { + status = Loader::ResultStatus::ErrorMissingKeys; + return; } } } -- cgit v1.2.3 From 13cdf1f15952cdce306e8c241b7d156496f72fe5 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 31 Jul 2018 12:27:14 -0400 Subject: Add missing parameter to files.push_back() --- src/core/file_sys/content_archive.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/core/file_sys') diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 2deb727cc..79e70f6ef 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -188,11 +188,11 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size; - const auto dec = + auto dec = Decrypt(section, std::make_shared(file, romfs_size, romfs_offset), romfs_offset); if (dec != nullptr) { - files.emplace_back(); + files.push_back(std::move(dec)); romfs = files.back(); } else { status = Loader::ResultStatus::ErrorMissingKeys; @@ -204,13 +204,13 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { section.pfs0.pfs0_header_offset; u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - header.section_tables[i].media_offset); - const auto dec = + auto dec = Decrypt(section, std::make_shared(file, size, offset), offset); if (dec != nullptr) { - auto npfs = std::make_shared(dec); + auto npfs = std::make_shared(std::move(dec)); if (npfs->GetStatus() == Loader::ResultStatus::Success) { - dirs.emplace_back(npfs); + dirs.push_back(std::move(npfs)); if (IsDirectoryExeFS(dirs.back())) exefs = dirs.back(); } -- cgit v1.2.3