diff options
Diffstat (limited to 'src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h')
-rw-r--r-- | src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h new file mode 100644 index 000000000..5771a21fc --- /dev/null +++ b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h @@ -0,0 +1,364 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/file_sys/fssystem/fssystem_compression_common.h" +#include "core/file_sys/fssystem/fssystem_nca_header.h" +#include "core/file_sys/vfs.h" + +namespace FileSys { + +class CompressedStorage; +class AesCtrCounterExtendedStorage; +class IndirectStorage; +class SparseStorage; + +struct NcaCryptoConfiguration; + +using KeyGenerationFunction = void (*)(void* dst_key, size_t dst_key_size, const void* src_key, + size_t src_key_size, s32 key_type); +using VerifySign1Function = bool (*)(const void* sig, size_t sig_size, const void* data, + size_t data_size, u8 generation); + +struct NcaCryptoConfiguration { + static constexpr size_t Rsa2048KeyModulusSize = 2048 / 8; + static constexpr size_t Rsa2048KeyPublicExponentSize = 3; + static constexpr size_t Rsa2048KeyPrivateExponentSize = Rsa2048KeyModulusSize; + + static constexpr size_t Aes128KeySize = 128 / 8; + + static constexpr size_t Header1SignatureKeyGenerationMax = 1; + + static constexpr s32 KeyAreaEncryptionKeyIndexCount = 3; + static constexpr s32 HeaderEncryptionKeyCount = 2; + + static constexpr u8 KeyAreaEncryptionKeyIndexZeroKey = 0xFF; + + static constexpr size_t KeyGenerationMax = 32; + + std::array<const u8*, Header1SignatureKeyGenerationMax + 1> header_1_sign_key_moduli; + std::array<u8, Rsa2048KeyPublicExponentSize> header_1_sign_key_public_exponent; + std::array<std::array<u8, Aes128KeySize>, KeyAreaEncryptionKeyIndexCount> + key_area_encryption_key_source; + std::array<u8, Aes128KeySize> header_encryption_key_source; + std::array<std::array<u8, Aes128KeySize>, HeaderEncryptionKeyCount> + header_encrypted_encryption_keys; + KeyGenerationFunction generate_key; + VerifySign1Function verify_sign1; + bool is_plaintext_header_available; + bool is_available_sw_key; +}; +static_assert(std::is_trivial_v<NcaCryptoConfiguration>); + +struct NcaCompressionConfiguration { + GetDecompressorFunction get_decompressor; +}; +static_assert(std::is_trivial_v<NcaCompressionConfiguration>); + +constexpr inline s32 KeyAreaEncryptionKeyCount = + NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount * + NcaCryptoConfiguration::KeyGenerationMax; + +enum class KeyType : s32 { + ZeroKey = -2, + InvalidKey = -1, + NcaHeaderKey1 = KeyAreaEncryptionKeyCount + 0, + NcaHeaderKey2 = KeyAreaEncryptionKeyCount + 1, + NcaExternalKey = KeyAreaEncryptionKeyCount + 2, + SaveDataDeviceUniqueMac = KeyAreaEncryptionKeyCount + 3, + SaveDataSeedUniqueMac = KeyAreaEncryptionKeyCount + 4, + SaveDataTransferMac = KeyAreaEncryptionKeyCount + 5, +}; + +constexpr inline bool IsInvalidKeyTypeValue(s32 key_type) { + return key_type < 0; +} + +constexpr inline s32 GetKeyTypeValue(u8 key_index, u8 key_generation) { + if (key_index == NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexZeroKey) { + return static_cast<s32>(KeyType::ZeroKey); + } + + if (key_index >= NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount) { + return static_cast<s32>(KeyType::InvalidKey); + } + + return NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount * key_generation + key_index; +} + +class NcaReader { + YUZU_NON_COPYABLE(NcaReader); + YUZU_NON_MOVEABLE(NcaReader); + +public: + NcaReader(); + ~NcaReader(); + + Result Initialize(VirtualFile base_storage, const NcaCryptoConfiguration& crypto_cfg, + const NcaCompressionConfiguration& compression_cfg); + + VirtualFile GetSharedBodyStorage(); + u32 GetMagic() const; + NcaHeader::DistributionType GetDistributionType() const; + NcaHeader::ContentType GetContentType() const; + u8 GetHeaderSign1KeyGeneration() const; + u8 GetKeyGeneration() const; + u8 GetKeyIndex() const; + u64 GetContentSize() const; + u64 GetProgramId() const; + u32 GetContentIndex() const; + u32 GetSdkAddonVersion() const; + void GetRightsId(u8* dst, size_t dst_size) const; + bool HasFsInfo(s32 index) const; + s32 GetFsCount() const; + const Hash& GetFsHeaderHash(s32 index) const; + void GetFsHeaderHash(Hash* dst, s32 index) const; + void GetFsInfo(NcaHeader::FsInfo* dst, s32 index) const; + u64 GetFsOffset(s32 index) const; + u64 GetFsEndOffset(s32 index) const; + u64 GetFsSize(s32 index) const; + void GetEncryptedKey(void* dst, size_t size) const; + const void* GetDecryptionKey(s32 index) const; + bool HasValidInternalKey() const; + bool HasInternalDecryptionKeyForAesHw() const; + bool IsSoftwareAesPrioritized() const; + void PrioritizeSoftwareAes(); + bool IsAvailableSwKey() const; + bool HasExternalDecryptionKey() const; + const void* GetExternalDecryptionKey() const; + void SetExternalDecryptionKey(const void* src, size_t size); + void GetRawData(void* dst, size_t dst_size) const; + NcaHeader::EncryptionType GetEncryptionType() const; + Result ReadHeader(NcaFsHeader* dst, s32 index) const; + + GetDecompressorFunction GetDecompressor() const; + + bool GetHeaderSign1Valid() const; + + void GetHeaderSign2(void* dst, size_t size) const; + +private: + NcaHeader m_header; + std::array<std::array<u8, NcaCryptoConfiguration::Aes128KeySize>, + NcaHeader::DecryptionKey_Count> + m_decryption_keys; + VirtualFile m_body_storage; + VirtualFile m_header_storage; + std::array<u8, NcaCryptoConfiguration::Aes128KeySize> m_external_decryption_key; + bool m_is_software_aes_prioritized; + bool m_is_available_sw_key; + NcaHeader::EncryptionType m_header_encryption_type; + bool m_is_header_sign1_signature_valid; + GetDecompressorFunction m_get_decompressor; +}; + +class NcaFsHeaderReader { + YUZU_NON_COPYABLE(NcaFsHeaderReader); + YUZU_NON_MOVEABLE(NcaFsHeaderReader); + +public: + NcaFsHeaderReader() : m_fs_index(-1) { + std::memset(std::addressof(m_data), 0, sizeof(m_data)); + } + + Result Initialize(const NcaReader& reader, s32 index); + bool IsInitialized() const { + return m_fs_index >= 0; + } + + void GetRawData(void* dst, size_t dst_size) const; + + NcaFsHeader::HashData& GetHashData(); + const NcaFsHeader::HashData& GetHashData() const; + u16 GetVersion() const; + s32 GetFsIndex() const; + NcaFsHeader::FsType GetFsType() const; + NcaFsHeader::HashType GetHashType() const; + NcaFsHeader::EncryptionType GetEncryptionType() const; + NcaPatchInfo& GetPatchInfo(); + const NcaPatchInfo& GetPatchInfo() const; + const NcaAesCtrUpperIv GetAesCtrUpperIv() const; + + bool IsSkipLayerHashEncryption() const; + Result GetHashTargetOffset(s64* out) const; + + bool ExistsSparseLayer() const; + NcaSparseInfo& GetSparseInfo(); + const NcaSparseInfo& GetSparseInfo() const; + + bool ExistsCompressionLayer() const; + NcaCompressionInfo& GetCompressionInfo(); + const NcaCompressionInfo& GetCompressionInfo() const; + + bool ExistsPatchMetaHashLayer() const; + NcaMetaDataHashDataInfo& GetPatchMetaDataHashDataInfo(); + const NcaMetaDataHashDataInfo& GetPatchMetaDataHashDataInfo() const; + NcaFsHeader::MetaDataHashType GetPatchMetaHashType() const; + + bool ExistsSparseMetaHashLayer() const; + NcaMetaDataHashDataInfo& GetSparseMetaDataHashDataInfo(); + const NcaMetaDataHashDataInfo& GetSparseMetaDataHashDataInfo() const; + NcaFsHeader::MetaDataHashType GetSparseMetaHashType() const; + +private: + NcaFsHeader m_data; + s32 m_fs_index; +}; + +class NcaFileSystemDriver { + YUZU_NON_COPYABLE(NcaFileSystemDriver); + YUZU_NON_MOVEABLE(NcaFileSystemDriver); + +public: + struct StorageContext { + bool open_raw_storage; + VirtualFile body_substorage; + std::shared_ptr<SparseStorage> current_sparse_storage; + VirtualFile sparse_storage_meta_storage; + std::shared_ptr<SparseStorage> original_sparse_storage; + void* external_current_sparse_storage; + void* external_original_sparse_storage; + VirtualFile aes_ctr_ex_storage_meta_storage; + VirtualFile aes_ctr_ex_storage_data_storage; + std::shared_ptr<AesCtrCounterExtendedStorage> aes_ctr_ex_storage; + VirtualFile indirect_storage_meta_storage; + std::shared_ptr<IndirectStorage> indirect_storage; + VirtualFile fs_data_storage; + VirtualFile compressed_storage_meta_storage; + std::shared_ptr<CompressedStorage> compressed_storage; + + VirtualFile patch_layer_info_storage; + VirtualFile sparse_layer_info_storage; + + VirtualFile external_original_storage; + }; + +private: + enum class AlignmentStorageRequirement { + CacheBlockSize = 0, + None = 1, + }; + +public: + static Result SetupFsHeaderReader(NcaFsHeaderReader* out, const NcaReader& reader, + s32 fs_index); + +public: + NcaFileSystemDriver(std::shared_ptr<NcaReader> reader) : m_original_reader(), m_reader(reader) { + ASSERT(m_reader != nullptr); + } + + NcaFileSystemDriver(std::shared_ptr<NcaReader> original_reader, + std::shared_ptr<NcaReader> reader) + : m_original_reader(original_reader), m_reader(reader) { + ASSERT(m_reader != nullptr); + } + + Result OpenStorageWithContext(VirtualFile* out, NcaFsHeaderReader* out_header_reader, + s32 fs_index, StorageContext* ctx); + + Result OpenStorage(VirtualFile* out, NcaFsHeaderReader* out_header_reader, s32 fs_index) { + // Create a storage context. + StorageContext ctx{}; + + // Open the storage. + R_RETURN(OpenStorageWithContext(out, out_header_reader, fs_index, std::addressof(ctx))); + } + +public: + Result CreateStorageByRawStorage(VirtualFile* out, const NcaFsHeaderReader* header_reader, + VirtualFile raw_storage, StorageContext* ctx); + +private: + Result OpenStorageImpl(VirtualFile* out, NcaFsHeaderReader* out_header_reader, s32 fs_index, + StorageContext* ctx); + + Result OpenIndirectableStorageAsOriginal(VirtualFile* out, + const NcaFsHeaderReader* header_reader, + StorageContext* ctx); + + Result CreateBodySubStorage(VirtualFile* out, s64 offset, s64 size); + + Result CreateAesCtrStorage(VirtualFile* out, VirtualFile base_storage, s64 offset, + const NcaAesCtrUpperIv& upper_iv, + AlignmentStorageRequirement alignment_storage_requirement); + Result CreateAesXtsStorage(VirtualFile* out, VirtualFile base_storage, s64 offset); + + Result CreateSparseStorageMetaStorage(VirtualFile* out, VirtualFile base_storage, s64 offset, + const NcaAesCtrUpperIv& upper_iv, + const NcaSparseInfo& sparse_info); + Result CreateSparseStorageCore(std::shared_ptr<SparseStorage>* out, VirtualFile base_storage, + s64 base_size, VirtualFile meta_storage, + const NcaSparseInfo& sparse_info, bool external_info); + Result CreateSparseStorage(VirtualFile* out, s64* out_fs_data_offset, + std::shared_ptr<SparseStorage>* out_sparse_storage, + VirtualFile* out_meta_storage, s32 index, + const NcaAesCtrUpperIv& upper_iv, const NcaSparseInfo& sparse_info); + + Result CreateSparseStorageMetaStorageWithVerification( + VirtualFile* out, VirtualFile* out_verification, VirtualFile base_storage, s64 offset, + const NcaAesCtrUpperIv& upper_iv, const NcaSparseInfo& sparse_info, + const NcaMetaDataHashDataInfo& meta_data_hash_data_info); + Result CreateSparseStorageWithVerification( + VirtualFile* out, s64* out_fs_data_offset, + std::shared_ptr<SparseStorage>* out_sparse_storage, VirtualFile* out_meta_storage, + VirtualFile* out_verification, s32 index, const NcaAesCtrUpperIv& upper_iv, + const NcaSparseInfo& sparse_info, const NcaMetaDataHashDataInfo& meta_data_hash_data_info, + NcaFsHeader::MetaDataHashType meta_data_hash_type); + + Result CreateAesCtrExStorageMetaStorage(VirtualFile* out, VirtualFile base_storage, s64 offset, + NcaFsHeader::EncryptionType encryption_type, + const NcaAesCtrUpperIv& upper_iv, + const NcaPatchInfo& patch_info); + Result CreateAesCtrExStorage(VirtualFile* out, + std::shared_ptr<AesCtrCounterExtendedStorage>* out_ext, + VirtualFile base_storage, VirtualFile meta_storage, + s64 counter_offset, const NcaAesCtrUpperIv& upper_iv, + const NcaPatchInfo& patch_info); + + Result CreateIndirectStorageMetaStorage(VirtualFile* out, VirtualFile base_storage, + const NcaPatchInfo& patch_info); + Result CreateIndirectStorage(VirtualFile* out, std::shared_ptr<IndirectStorage>* out_ind, + VirtualFile base_storage, VirtualFile original_data_storage, + VirtualFile meta_storage, const NcaPatchInfo& patch_info); + + Result CreatePatchMetaStorage(VirtualFile* out_aes_ctr_ex_meta, VirtualFile* out_indirect_meta, + VirtualFile* out_verification, VirtualFile base_storage, + s64 offset, const NcaAesCtrUpperIv& upper_iv, + const NcaPatchInfo& patch_info, + const NcaMetaDataHashDataInfo& meta_data_hash_data_info); + + Result CreateSha256Storage(VirtualFile* out, VirtualFile base_storage, + const NcaFsHeader::HashData::HierarchicalSha256Data& sha256_data); + + Result CreateIntegrityVerificationStorage( + VirtualFile* out, VirtualFile base_storage, + const NcaFsHeader::HashData::IntegrityMetaInfo& meta_info); + Result CreateIntegrityVerificationStorageForMeta( + VirtualFile* out, VirtualFile* out_verification, VirtualFile base_storage, s64 offset, + const NcaMetaDataHashDataInfo& meta_data_hash_data_info); + Result CreateIntegrityVerificationStorageImpl( + VirtualFile* out, VirtualFile base_storage, + const NcaFsHeader::HashData::IntegrityMetaInfo& meta_info, s64 layer_info_offset, + int max_data_cache_entries, int max_hash_cache_entries, s8 buffer_level); + + Result CreateRegionSwitchStorage(VirtualFile* out, const NcaFsHeaderReader* header_reader, + VirtualFile inside_storage, VirtualFile outside_storage); + + Result CreateCompressedStorage(VirtualFile* out, std::shared_ptr<CompressedStorage>* out_cmp, + VirtualFile* out_meta, VirtualFile base_storage, + const NcaCompressionInfo& compression_info); + +public: + Result CreateCompressedStorage(VirtualFile* out, std::shared_ptr<CompressedStorage>* out_cmp, + VirtualFile* out_meta, VirtualFile base_storage, + const NcaCompressionInfo& compression_info, + GetDecompressorFunction get_decompressor); + +private: + std::shared_ptr<NcaReader> m_original_reader; + std::shared_ptr<NcaReader> m_reader; +}; + +} // namespace FileSys |