From bda989dc6c7dd459751041777882594cf0e4ef98 Mon Sep 17 00:00:00 2001 From: ownedbywuigi Date: Tue, 8 Jul 2025 06:22:37 +0100 Subject: [PATCH] more import progress --- common/logging/log.h | 6 +- core/crypto/aes_util.h | 62 ++++++ core/crypto/ctr_encryption_layer.h | 35 +++ core/crypto/encryption_layer.h | 32 +++ core/crypto/key_manager.h | 344 +++++++++++++++++++++++++++++ core/fs/bis_factory.h | 4 +- core/fs/card_image.h | 6 +- core/fs/common_funcs.h | 2 +- core/fs/content_archive.cpp | 24 +- 9 files changed, 494 insertions(+), 21 deletions(-) create mode 100755 core/crypto/aes_util.h create mode 100755 core/crypto/ctr_encryption_layer.h create mode 100755 core/crypto/encryption_layer.h create mode 100755 core/crypto/key_manager.h diff --git a/common/logging/log.h b/common/logging/log.h index 0517b18..574f559 100755 --- a/common/logging/log.h +++ b/common/logging/log.h @@ -6,10 +6,10 @@ #include #include -#include +#include -#include "common/logging/formatter.h" -#include "common/logging/types.h" +#include "formatter.h" +#include "types.h" namespace Common::Log { diff --git a/core/crypto/aes_util.h b/core/crypto/aes_util.h new file mode 100755 index 0000000..12e3de4 --- /dev/null +++ b/core/crypto/aes_util.h @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include "../common/common_types.h" +#include "../core/file_sys/vfs/vfs.h" + +namespace Core::Crypto { + +struct CipherContext; + +enum class Mode { + CTR = 11, + ECB = 2, + XTS = 70, +}; + +enum class Op { + Encrypt, + Decrypt, +}; + +template +class AESCipher { + static_assert(std::is_same_v>, "Key must be std::array of u8."); + static_assert(KeySize == 0x10 || KeySize == 0x20, "KeySize must be 128 or 256."); + +public: + AESCipher(Key key, Mode mode); + ~AESCipher(); + + void SetIV(std::span data); + + template + void Transcode(const Source* src, std::size_t size, Dest* dest, Op op) const { + static_assert(std::is_trivially_copyable_v && std::is_trivially_copyable_v, + "Transcode source and destination types must be trivially copyable."); + Transcode(reinterpret_cast(src), size, reinterpret_cast(dest), op); + } + + void Transcode(const u8* src, std::size_t size, u8* dest, Op op) const; + + template + void XTSTranscode(const Source* src, std::size_t size, Dest* dest, std::size_t sector_id, + std::size_t sector_size, Op op) { + static_assert(std::is_trivially_copyable_v && std::is_trivially_copyable_v, + "XTSTranscode source and destination types must be trivially copyable."); + XTSTranscode(reinterpret_cast(src), size, reinterpret_cast(dest), sector_id, + sector_size, op); + } + + void XTSTranscode(const u8* src, std::size_t size, u8* dest, std::size_t sector_id, + std::size_t sector_size, Op op); + +private: + std::unique_ptr ctx; +}; +} // namespace Core::Crypto diff --git a/core/crypto/ctr_encryption_layer.h b/core/crypto/ctr_encryption_layer.h new file mode 100755 index 0000000..aa8e470 --- /dev/null +++ b/core/crypto/ctr_encryption_layer.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "aes_util.h" +#include "encryption_layer.h" +#include "key_manager.h" + +namespace Core::Crypto { + +// Sits on top of a VirtualFile and provides CTR-mode AES description. +class CTREncryptionLayer : public EncryptionLayer { +public: + using IVData = std::array; + + CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, std::size_t base_offset_); + + std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; + + void SetIV(const IVData& iv); + +private: + std::size_t base_offset; + + // Must be mutable as operations modify cipher contexts. + mutable AESCipher cipher; + mutable IVData iv{}; + + void UpdateIV(std::size_t offset) const; +}; + +} // namespace Core::Crypto diff --git a/core/crypto/encryption_layer.h b/core/crypto/encryption_layer.h new file mode 100755 index 0000000..159f11c --- /dev/null +++ b/core/crypto/encryption_layer.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "../common/common_types.h" +#include "../core/file_sys/vfs/vfs.h" + +namespace Core::Crypto { + +// Basically non-functional class that implements all of the methods that are irrelevant to an +// EncryptionLayer. Reduces duplicate code. +class EncryptionLayer : public FileSys::VfsFile { +public: + explicit EncryptionLayer(FileSys::VirtualFile base); + + std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override = 0; + + std::string GetName() const override; + std::size_t GetSize() const override; + bool Resize(std::size_t new_size) override; + std::shared_ptr GetContainingDirectory() const override; + bool IsWritable() const override; + bool IsReadable() const override; + std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; + bool Rename(std::string_view name) override; + +protected: + FileSys::VirtualFile base; +}; + +} // namespace Core::Crypto diff --git a/core/crypto/key_manager.h b/core/crypto/key_manager.h new file mode 100755 index 0000000..bbd66f7 --- /dev/null +++ b/core/crypto/key_manager.h @@ -0,0 +1,344 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../common/common_funcs.h" +#include "../common/common_types.h" +#include "partition_data_manager.h" + +namespace Common::FS { +class IOFile; +} + +namespace FileSys { +class ContentProvider; +} + +namespace Loader { +enum class ResultStatus : u16; +} + +namespace Core::Crypto { + +using Key128 = std::array; +using Key256 = std::array; +using SHA256Hash = std::array; + +enum class SignatureType { + RSA_4096_SHA1 = 0x10000, + RSA_2048_SHA1 = 0x10001, + ECDSA_SHA1 = 0x10002, + RSA_4096_SHA256 = 0x10003, + RSA_2048_SHA256 = 0x10004, + ECDSA_SHA256 = 0x10005, +}; + +u64 GetSignatureTypeDataSize(SignatureType type); +u64 GetSignatureTypePaddingSize(SignatureType type); + +enum class TitleKeyType : u8 { + Common = 0, + Personalized = 1, +}; + +struct TicketData { + std::array issuer; + union { + std::array title_key_block; + + struct { + Key128 title_key_common; + std::array title_key_common_pad; + }; + }; + + INSERT_PADDING_BYTES(0x1); + TitleKeyType type; + INSERT_PADDING_BYTES(0x3); + u8 revision; + INSERT_PADDING_BYTES(0xA); + u64 ticket_id; + u64 device_id; + std::array rights_id; + u32 account_id; + INSERT_PADDING_BYTES(0x14C); +}; +static_assert(sizeof(TicketData) == 0x2C0, "TicketData has incorrect size."); + +struct RSA4096Ticket { + SignatureType sig_type; + std::array sig_data; + INSERT_PADDING_BYTES(0x3C); + TicketData data; +}; +static_assert(sizeof(RSA4096Ticket) == 0x500, "RSA4096Ticket has incorrect size."); + +struct RSA2048Ticket { + SignatureType sig_type; + std::array sig_data; + INSERT_PADDING_BYTES(0x3C); + TicketData data; +}; +static_assert(sizeof(RSA2048Ticket) == 0x400, "RSA2048Ticket has incorrect size."); + +struct ECDSATicket { + SignatureType sig_type; + std::array sig_data; + INSERT_PADDING_BYTES(0x40); + TicketData data; +}; +static_assert(sizeof(ECDSATicket) == 0x340, "ECDSATicket has incorrect size."); + +struct Ticket { + std::variant data; + + [[nodiscard]] bool IsValid() const; + [[nodiscard]] SignatureType GetSignatureType() const; + [[nodiscard]] TicketData& GetData(); + [[nodiscard]] const TicketData& GetData() const; + [[nodiscard]] u64 GetSize() const; + + /** + * Synthesizes a common ticket given a title key and rights ID. + * + * @param title_key Title key to store in the ticket. + * @param rights_id Rights ID the ticket is for. + * @return The synthesized common ticket. + */ + static Ticket SynthesizeCommon(Key128 title_key, const std::array& rights_id); + + /** + * Reads a ticket from a file. + * + * @param file File to read the ticket from. + * @return The read ticket. If the ticket data is invalid, Ticket::IsValid() will be false. + */ + static Ticket Read(const FileSys::VirtualFile& file); + + /** + * Reads a ticket from a memory buffer. + * + * @param raw_data Buffer to read the ticket from. + * @return The read ticket. If the ticket data is invalid, Ticket::IsValid() will be false. + */ + static Ticket Read(std::span raw_data); +}; + +static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big."); +static_assert(sizeof(Key256) == 32, "Key256 must be 256 bytes big."); + +template > 3)> +struct RSAKeyPair { + std::array encryption_key; + std::array decryption_key; + std::array modulus; + std::array exponent; +}; + +template +bool operator==(const RSAKeyPair& lhs, + const RSAKeyPair& rhs) { + return std::tie(lhs.encryption_key, lhs.decryption_key, lhs.modulus, lhs.exponent) == + std::tie(rhs.encryption_key, rhs.decryption_key, rhs.modulus, rhs.exponent); +} + +template +bool operator!=(const RSAKeyPair& lhs, + const RSAKeyPair& rhs) { + return !(lhs == rhs); +} + +enum class KeyCategory : u8 { + Standard, + Title, + Console, +}; + +enum class S256KeyType : u64 { + SDKey, // f1=SDKeyType + Header, // + SDKeySource, // f1=SDKeyType + HeaderSource, // +}; + +enum class S128KeyType : u64 { + Master, // f1=crypto revision + Package1, // f1=crypto revision + Package2, // f1=crypto revision + Titlekek, // f1=crypto revision + ETicketRSAKek, // + KeyArea, // f1=crypto revision f2=type {app, ocean, system} + SDSeed, // + Titlekey, // f1=rights id LSB f2=rights id MSB + Source, // f1=source type, f2= sub id + Keyblob, // f1=crypto revision + KeyblobMAC, // f1=crypto revision + TSEC, // + SecureBoot, // + BIS, // f1=partition (0-3), f2=type {crypt, tweak} + HeaderKek, // + SDKek, // + RSAKek, // +}; + +enum class KeyAreaKeyType : u8 { + Application, + Ocean, + System, +}; + +enum class SourceKeyType : u8 { + SDKek, // + AESKekGeneration, // + AESKeyGeneration, // + RSAOaepKekGeneration, // + Master, // + Keyblob, // f2=crypto revision + KeyAreaKey, // f2=KeyAreaKeyType + Titlekek, // + Package2, // + HeaderKek, // + KeyblobMAC, // + ETicketKek, // + ETicketKekek, // +}; + +enum class SDKeyType : u8 { + Save, + NCA, +}; + +enum class BISKeyType : u8 { + Crypto, + Tweak, +}; + +enum class RSAKekType : u8 { + Mask0, + Seed3, +}; + +template +struct KeyIndex { + KeyType type; + u64 field1; + u64 field2; + + std::string DebugInfo() const { + u8 key_size = 16; + if constexpr (std::is_same_v) + key_size = 32; + return fmt::format("key_size={:02X}, key={:02X}, field1={:016X}, field2={:016X}", key_size, + static_cast(type), field1, field2); + } +}; + +// boost flat_map requires operator< for O(log(n)) lookups. +template +bool operator<(const KeyIndex& lhs, const KeyIndex& rhs) { + return std::tie(lhs.type, lhs.field1, lhs.field2) < std::tie(rhs.type, rhs.field1, rhs.field2); +} + +class KeyManager { +public: + static KeyManager& Instance() { + static KeyManager instance; + return instance; + } + + KeyManager(const KeyManager&) = delete; + KeyManager& operator=(const KeyManager&) = delete; + + KeyManager(KeyManager&&) = delete; + KeyManager& operator=(KeyManager&&) = delete; + + bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; + bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; + + Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; + Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; + + Key256 GetBISKey(u8 partition_id) const; + + void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); + void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); + + static bool KeyFileExists(bool title); + + // Call before using the sd seed to attempt to derive it if it doesn't exist. Needs system + // save 8*43 and the private file to exist. + void DeriveSDSeedLazy(); + + bool BaseDeriveNecessary() const; + void DeriveBase(); + void DeriveETicket(PartitionDataManager& data, const FileSys::ContentProvider& provider); + void PopulateTickets(); + void SynthesizeTickets(); + + void PopulateFromPartitionData(PartitionDataManager& data); + + const std::map& GetCommonTickets() const; + const std::map& GetPersonalizedTickets() const; + + bool AddTicket(const Ticket& ticket); + + void ReloadKeys(); + bool AreKeysLoaded() const; + +private: + KeyManager(); + + std::map, Key128> s128_keys; + std::map, Key256> s256_keys; + + // Map from rights ID to ticket + std::map common_tickets; + std::map personal_tickets; + bool ticket_databases_loaded = false; + + std::array, 0x20> encrypted_keyblobs{}; + std::array, 0x20> keyblobs{}; + std::array eticket_extended_kek{}; + RSAKeyPair<2048> eticket_rsa_keypair{}; + + bool dev_mode; + void LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys); + + template + void WriteKeyToFile(KeyCategory category, std::string_view keyname, + const std::array& key); + + void DeriveGeneralPurposeKeys(std::size_t crypto_revision); + + void DeriveETicketRSAKey(); + + void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); + void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); + + /// Parses the title key section of a ticket. + std::optional ParseTicketTitleKey(const Ticket& ticket); +}; + +Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed); +Key128 DeriveKeyblobKey(const Key128& sbk, const Key128& tsec, Key128 source); +Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source); +Key128 DeriveMasterKey(const std::array& keyblob, const Key128& master_source); +std::array DecryptKeyblob(const std::array& encrypted_keyblob, + const Key128& key); + +std::optional DeriveSDSeed(); +Loader::ResultStatus DeriveSDKeys(std::array& sd_keys, KeyManager& keys); + +std::vector GetTicketblob(const Common::FS::IOFile& ticket_save); + +} // namespace Core::Crypto diff --git a/core/fs/bis_factory.h b/core/fs/bis_factory.h index df4fa2b..f3e0326 100644 --- a/core/fs/bis_factory.h +++ b/core/fs/bis_factory.h @@ -7,8 +7,8 @@ #include -#include "import/common/common_types.h" -#include "core/fs/vfs/vfs_types.h" +#include "..common/common_types.h" +#include "vfs/vfs_types.h" namespace FileSys { diff --git a/core/fs/card_image.h b/core/fs/card_image.h index 6fc3ad7..2c12744 100644 --- a/core/fs/card_image.h +++ b/core/fs/card_image.h @@ -8,9 +8,9 @@ #include #include #include -#include "import/common/common_types.h" -#include "import/common/swap.h" -#include "core/fs/vfs/vfs.h" +#include "../common/common_types.h" +#include "../common/swap.h" +#include "vfs/vfs.h" namespace Core::Crypto { class KeyManager; diff --git a/core/fs/common_funcs.h b/core/fs/common_funcs.h index 3dd8f12..8e700b5 100644 --- a/core/fs/common_funcs.h +++ b/core/fs/common_funcs.h @@ -3,7 +3,7 @@ #pragma once -#include "common/common_types.h" +#include "../common/common_types.h" namespace FileSys { diff --git a/core/fs/content_archive.cpp b/core/fs/content_archive.cpp index 6652523..d561413 100644 --- a/core/fs/content_archive.cpp +++ b/core/fs/content_archive.cpp @@ -6,19 +6,19 @@ #include #include -#include "common/logging/log.h" -#include "common/polyfill_ranges.h" -#include "core/crypto/aes_util.h" -#include "core/crypto/ctr_encryption_layer.h" -#include "core/crypto/key_manager.h" -#include "core/file_sys/content_archive.h" -#include "core/file_sys/partition_filesystem.h" -#include "core/file_sys/vfs/vfs_offset.h" -#include "core/loader/loader.h" +#include "../common/logging/log.h" +#include "../common/polyfill_ranges.h" +#include "../core/crypto/aes_util.h" +#include "../core/crypto/ctr_encryption_layer.h" +#include "../core/crypto/key_manager.h" +#include "content_archive.h" +#include "partition_filesystem.h" +#include "vfs/vfs_offset.h" +#include "../core/loader/loader.h" -#include "core/file_sys/fssystem/fssystem_compression_configuration.h" -#include "core/file_sys/fssystem/fssystem_crypto_configuration.h" -#include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h" +#include "fssystem/fssystem_compression_configuration.h" +#include "fssystem/fssystem_crypto_configuration.h" +#include "fssystem/fssystem_nca_file_system_driver.h" namespace FileSys {