summaryrefslogtreecommitdiffstats
path: root/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h
diff options
context:
space:
mode:
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.h360
1 files changed, 360 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..d317b35ac
--- /dev/null
+++ b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h
@@ -0,0 +1,360 @@
+// 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;
+
+ const u8* header_1_sign_key_moduli[Header1SignatureKeyGenerationMax + 1];
+ u8 header_1_sign_key_public_exponent[Rsa2048KeyPublicExponentSize];
+ u8 key_area_encryption_key_source[KeyAreaEncryptionKeyIndexCount][Aes128KeySize];
+ u8 header_encryption_key_source[Aes128KeySize];
+ u8 header_encrypted_encryption_keys[HeaderEncryptionKeyCount][Aes128KeySize];
+ 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);
+
+private:
+ NcaHeader m_header;
+ u8 m_decryption_keys[NcaHeader::DecryptionKey_Count][NcaCryptoConfiguration::Aes128KeySize];
+ VirtualFile m_body_storage;
+ VirtualFile m_header_storage;
+ u8 m_external_decryption_key[NcaCryptoConfiguration::Aes128KeySize];
+ 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;
+
+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;
+};
+
+class NcaFsHeaderReader {
+ YUZU_NON_COPYABLE(NcaFsHeaderReader);
+ YUZU_NON_MOVEABLE(NcaFsHeaderReader);
+
+private:
+ NcaFsHeader m_data;
+ s32 m_fs_index;
+
+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;
+};
+
+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,
+ };
+
+private:
+ std::shared_ptr<NcaReader> m_original_reader;
+ std::shared_ptr<NcaReader> m_reader;
+
+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);
+};
+
+} // namespace FileSys