mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
fs: update nca drivers (and dependents/callees) for 14.0.0 changes
This commit is contained in:
parent
20e53fcd82
commit
ec44eaa263
24 changed files with 1491 additions and 457 deletions
|
@ -39,6 +39,9 @@
|
|||
#include <stratosphere/fssystem/fssystem_crypto_configuration.hpp>
|
||||
#include <stratosphere/fssystem/fssystem_compression_configuration.hpp>
|
||||
#include <stratosphere/fssystem/fssystem_aes_ctr_counter_extended_storage.hpp>
|
||||
#include <stratosphere/fssystem/fssystem_aes_ctr_storage_external.hpp>
|
||||
#include <stratosphere/fssystem/fssystem_aes_xts_storage_external.hpp>
|
||||
#include <stratosphere/fssystem/fssystem_switch_storage.hpp>
|
||||
#include <stratosphere/fssystem/buffers/fssystem_buffer_manager_utils.hpp>
|
||||
#include <stratosphere/fssystem/buffers/fssystem_file_system_buffer_manager.hpp>
|
||||
#include <stratosphere/fssystem/fssystem_pooled_buffer.hpp>
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace ams::fssystem {
|
|||
|
||||
using IAllocator = BucketTree::IAllocator;
|
||||
|
||||
using DecryptFunction = void(*)(void *dst, size_t dst_size, s32 index, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
|
||||
using DecryptFunction = void(*)(void *dst, size_t dst_size, u8 index, u8 gen, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
|
||||
|
||||
class IDecryptor {
|
||||
public:
|
||||
|
@ -72,7 +72,7 @@ namespace ams::fssystem {
|
|||
return BucketTree::QueryEntryStorageSize(NodeSize, sizeof(Entry), entry_count);
|
||||
}
|
||||
|
||||
static Result CreateExternalDecryptor(std::unique_ptr<IDecryptor> *out, DecryptFunction func, s32 key_index);
|
||||
static Result CreateExternalDecryptor(std::unique_ptr<IDecryptor> *out, DecryptFunction func, s32 key_index, s32 key_generation);
|
||||
static Result CreateSoftwareDecryptor(std::unique_ptr<IDecryptor> *out);
|
||||
private:
|
||||
BucketTree m_table;
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/fs/fs_istorage.hpp>
|
||||
#include <stratosphere/fs/impl/fs_newable.hpp>
|
||||
#include <stratosphere/fssystem/fssystem_nca_file_system_driver.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
/* ACCURATE_TO_VERSION: 14.3.0.0 */
|
||||
class AesCtrStorageExternal : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
||||
NON_COPYABLE(AesCtrStorageExternal);
|
||||
NON_MOVEABLE(AesCtrStorageExternal);
|
||||
public:
|
||||
static constexpr size_t BlockSize = crypto::Aes128CtrEncryptor::BlockSize;
|
||||
static constexpr size_t KeySize = crypto::Aes128CtrEncryptor::KeySize;
|
||||
static constexpr size_t IvSize = crypto::Aes128CtrEncryptor::IvSize;
|
||||
private:
|
||||
std::shared_ptr<fs::IStorage> m_base_storage;
|
||||
u8 m_iv[IvSize];
|
||||
DecryptAesCtrFunction m_decrypt_function;
|
||||
s32 m_key_index;
|
||||
s32 m_key_generation;
|
||||
u8 m_encrypted_key[KeySize];
|
||||
public:
|
||||
AesCtrStorageExternal(std::shared_ptr<fs::IStorage> bs, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, DecryptAesCtrFunction df, s32 kidx, s32 kgen);
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
|
||||
virtual Result GetSize(s64 *out) override;
|
||||
virtual Result Flush() override;
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override;
|
||||
virtual Result SetSize(s64 size) override;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/fs/fs_istorage.hpp>
|
||||
#include <stratosphere/fs/impl/fs_newable.hpp>
|
||||
#include <stratosphere/fssystem/fssystem_nca_file_system_driver.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
/* ACCURATE_TO_VERSION: 14.3.0.0 */
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
class AesXtsStorageExternal : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
||||
NON_COPYABLE(AesXtsStorageExternal);
|
||||
NON_MOVEABLE(AesXtsStorageExternal);
|
||||
public:
|
||||
static constexpr size_t AesBlockSize = crypto::Aes128XtsEncryptor::BlockSize;
|
||||
static constexpr size_t KeySize = crypto::Aes128XtsEncryptor::KeySize;
|
||||
static constexpr size_t IvSize = crypto::Aes128XtsEncryptor::IvSize;
|
||||
private:
|
||||
BasePointer m_base_storage;
|
||||
char m_key[2][KeySize];
|
||||
char m_iv[IvSize];
|
||||
const size_t m_block_size;
|
||||
CryptAesXtsFunction m_encrypt_function;
|
||||
CryptAesXtsFunction m_decrypt_function;
|
||||
public:
|
||||
AesXtsStorageExternal(BasePointer bs, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size, CryptAesXtsFunction ef, CryptAesXtsFunction df);
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
|
||||
virtual Result GetSize(s64 *out) override;
|
||||
virtual Result Flush() override;
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override;
|
||||
virtual Result SetSize(s64 size) override;
|
||||
};
|
||||
|
||||
using AesXtsStorageExternalByPointer = AesXtsStorageExternal<fs::IStorage *>;
|
||||
using AesXtsStorageExternalBySharedPointer = AesXtsStorageExternal<std::shared_ptr<fs::IStorage>>;
|
||||
|
||||
}
|
|
@ -99,13 +99,13 @@ namespace ams::fssystem {
|
|||
size_t m_verification_block_shift;
|
||||
s32 m_flags;
|
||||
s32 m_buffer_level;
|
||||
fs::StorageType m_storage_type;
|
||||
BlockCacheManager m_block_cache_manager;
|
||||
bool m_is_writable;
|
||||
public:
|
||||
BlockCacheBufferedStorage();
|
||||
virtual ~BlockCacheBufferedStorage() override;
|
||||
|
||||
Result Initialize(fs::IBufferManager *bm, os::SdkRecursiveMutex *mtx, IStorage *data, s64 data_size, size_t verif_block_size, s32 max_cache_entries, bool is_real_data, s8 buffer_level, bool is_keep_burst_mode, fs::StorageType storage_type);
|
||||
Result Initialize(fs::IBufferManager *bm, os::SdkRecursiveMutex *mtx, IStorage *data, s64 data_size, size_t verif_block_size, s32 max_cache_entries, bool is_real_data, s8 buffer_level, bool is_keep_burst_mode, bool is_writable);
|
||||
void Finalize();
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
namespace ams::fssystem {
|
||||
|
||||
/* ACCURATE_TO_VERSION: Unknown */
|
||||
/* ACCURATE_TO_VERSION: 14.3.0.0 */
|
||||
|
||||
struct HierarchicalIntegrityVerificationLevelInformation {
|
||||
fs::Int64 offset;
|
||||
|
@ -151,20 +151,25 @@ namespace ams::fssystem {
|
|||
os::SdkRecursiveMutex *m_mutex;
|
||||
IntegrityVerificationStorage m_verify_storages[MaxLayers - 1];
|
||||
BlockCacheBufferedStorage m_buffer_storages[MaxLayers - 1];
|
||||
os::Semaphore *m_read_semaphore;
|
||||
os::Semaphore *m_write_semaphore;
|
||||
s64 m_data_size;
|
||||
s32 m_max_layers;
|
||||
bool m_is_written_for_rollback;
|
||||
public:
|
||||
HierarchicalIntegrityVerificationStorage() : m_buffers(nullptr), m_mutex(nullptr), m_data_size(-1), m_is_written_for_rollback(false) { /* ... */ }
|
||||
HierarchicalIntegrityVerificationStorage() : m_buffers(nullptr), m_mutex(nullptr), m_data_size(-1) { /* ... */ }
|
||||
virtual ~HierarchicalIntegrityVerificationStorage() override { this->Finalize(); }
|
||||
|
||||
Result Initialize(const HierarchicalIntegrityVerificationInformation &info, HierarchicalStorageInformation storage, FileSystemBufferManagerSet *bufs, IHash256GeneratorFactory *hgf, os::SdkRecursiveMutex *mtx, fs::StorageType storage_type);
|
||||
Result Initialize(const HierarchicalIntegrityVerificationInformation &info, HierarchicalStorageInformation storage, FileSystemBufferManagerSet *bufs, IHash256GeneratorFactory *hgf, bool hash_salt_enabled, os::SdkRecursiveMutex *mtx, os::Semaphore *read_sema, os::Semaphore *write_sema, int max_data_cache_entries, int max_hash_cache_entries, s8 buffer_level, bool is_writable, bool allow_cleared_blocks);
|
||||
Result Initialize(const HierarchicalIntegrityVerificationInformation &info, HierarchicalStorageInformation storage, FileSystemBufferManagerSet *bufs, IHash256GeneratorFactory *hgf, bool hash_salt_enabled, os::SdkRecursiveMutex *mtx, int max_data_cache_entries, int max_hash_cache_entries, s8 buffer_level, bool is_writable, bool allow_cleared_blocks) {
|
||||
R_RETURN(this->Initialize(info, storage, bufs, hgf, hash_salt_enabled, mtx, nullptr, nullptr, max_data_cache_entries, max_hash_cache_entries, buffer_level, is_writable, allow_cleared_blocks));
|
||||
}
|
||||
|
||||
void Finalize();
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override;
|
||||
|
||||
virtual Result SetSize(s64 size) override { AMS_UNUSED(size); return fs::ResultUnsupportedSetSizeForHierarchicalIntegrityVerificationStorage(); }
|
||||
virtual Result SetSize(s64 size) override { AMS_UNUSED(size); R_THROW(fs::ResultUnsupportedSetSizeForHierarchicalIntegrityVerificationStorage()); }
|
||||
virtual Result GetSize(s64 *out) override;
|
||||
|
||||
virtual Result Flush() override;
|
||||
|
@ -179,10 +184,6 @@ namespace ams::fssystem {
|
|||
return m_data_size >= 0;
|
||||
}
|
||||
|
||||
bool IsWrittenForRollback() const {
|
||||
return m_is_written_for_rollback;
|
||||
}
|
||||
|
||||
FileSystemBufferManagerSet *GetBuffers() {
|
||||
return m_buffers;
|
||||
}
|
||||
|
@ -201,6 +202,10 @@ namespace ams::fssystem {
|
|||
fs::SubStorage GetL1HashStorage() {
|
||||
return fs::SubStorage(std::addressof(m_buffer_storages[m_max_layers - 3]), 0, util::DivideUp(m_data_size, this->GetL1HashVerificationBlockSize()));
|
||||
}
|
||||
public:
|
||||
static constexpr s8 GetDefaultDataCacheBufferLevel(u32 max_layers) {
|
||||
return 16 + max_layers - 2;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -39,33 +39,33 @@ namespace ams::fssystem {
|
|||
IntegrityRomFsStorage() : m_mutex() { /* ... */ }
|
||||
virtual ~IntegrityRomFsStorage() override { this->Finalize(); }
|
||||
|
||||
Result Initialize(HierarchicalIntegrityVerificationInformation level_hash_info, Hash master_hash, HierarchicalIntegrityVerificationStorage::HierarchicalStorageInformation storage_info, fs::IBufferManager *bm, IHash256GeneratorFactory *hgf);
|
||||
Result Initialize(HierarchicalIntegrityVerificationInformation level_hash_info, Hash master_hash, HierarchicalIntegrityVerificationStorage::HierarchicalStorageInformation storage_info, fs::IBufferManager *bm, int max_data_cache_entries, int max_hash_cache_entries, s8 buffer_level, IHash256GeneratorFactory *hgf);
|
||||
void Finalize();
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
return m_integrity_storage.Read(offset, buffer, size);
|
||||
R_RETURN(m_integrity_storage.Read(offset, buffer, size));
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
return m_integrity_storage.Write(offset, buffer, size);
|
||||
R_RETURN(m_integrity_storage.Write(offset, buffer, size));
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override { AMS_UNUSED(size); return fs::ResultUnsupportedSetSizeForIntegrityRomFsStorage(); }
|
||||
virtual Result SetSize(s64 size) override { AMS_UNUSED(size); R_THROW(fs::ResultUnsupportedSetSizeForIntegrityRomFsStorage()); }
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
return m_integrity_storage.GetSize(out);
|
||||
R_RETURN(m_integrity_storage.GetSize(out));
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
return m_integrity_storage.Flush();
|
||||
R_RETURN(m_integrity_storage.Flush());
|
||||
}
|
||||
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
|
||||
return m_integrity_storage.OperateRange(dst, dst_size, op_id, offset, size, src, src_size);
|
||||
R_RETURN(m_integrity_storage.OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
}
|
||||
|
||||
Result Commit() {
|
||||
return m_integrity_storage.Commit();
|
||||
R_RETURN(m_integrity_storage.Commit());
|
||||
}
|
||||
|
||||
FileSystemBufferManagerSet *GetBuffers() {
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
namespace ams::fssystem {
|
||||
|
||||
/* ACCURATE_TO_VERSION: Unknown */
|
||||
/* ACCURATE_TO_VERSION: 14.3.0.0 */
|
||||
|
||||
class IntegrityVerificationStorage : public ::ams::fs::IStorage {
|
||||
NON_COPYABLE(IntegrityVerificationStorage);
|
||||
|
@ -43,15 +43,16 @@ namespace ams::fssystem {
|
|||
s64 m_upper_layer_verification_block_size;
|
||||
s64 m_upper_layer_verification_block_order;
|
||||
fs::IBufferManager *m_buffer_manager;
|
||||
fs::HashSalt m_salt;
|
||||
util::optional<fs::HashSalt> m_salt;
|
||||
bool m_is_real_data;
|
||||
fs::StorageType m_storage_type;
|
||||
fssystem::IHash256GeneratorFactory *m_hash_generator_factory;
|
||||
bool m_is_writable;
|
||||
bool m_allow_cleared_blocks;
|
||||
public:
|
||||
IntegrityVerificationStorage() : m_verification_block_size(0), m_verification_block_order(0), m_upper_layer_verification_block_size(0), m_upper_layer_verification_block_order(0), m_buffer_manager(nullptr) { /* ... */ }
|
||||
IntegrityVerificationStorage() : m_verification_block_size(0), m_verification_block_order(0), m_upper_layer_verification_block_size(0), m_upper_layer_verification_block_order(0), m_buffer_manager(nullptr), m_salt(util::nullopt) { /* ... */ }
|
||||
virtual ~IntegrityVerificationStorage() override { this->Finalize(); }
|
||||
|
||||
Result Initialize(fs::SubStorage hs, fs::SubStorage ds, s64 verif_block_size, s64 upper_layer_verif_block_size, fs::IBufferManager *bm, fssystem::IHash256GeneratorFactory *hgf, const fs::HashSalt &salt, bool is_real_data, fs::StorageType storage_type);
|
||||
Result Initialize(fs::SubStorage hs, fs::SubStorage ds, s64 verif_block_size, s64 upper_layer_verif_block_size, fs::IBufferManager *bm, fssystem::IHash256GeneratorFactory *hgf, const util::optional<fs::HashSalt> &salt, bool is_real_data, bool is_writable, bool allow_cleared_blocks);
|
||||
void Finalize();
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
namespace ams::fssystem {
|
||||
|
||||
/* ACCURATE_TO_VERSION: 13.4.0.0 */
|
||||
/* ACCURATE_TO_VERSION: 14.3.0.0 */
|
||||
|
||||
class CompressedStorage;
|
||||
class AesCtrCounterExtendedStorage;
|
||||
|
@ -34,8 +34,11 @@ namespace ams::fssystem {
|
|||
|
||||
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, const NcaCryptoConfiguration &cfg);
|
||||
using DecryptAesCtrFunction = void (*)(void *dst, size_t dst_size, s32 key_type, const void *src_key, size_t src_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
|
||||
using KeyGenerationFunction = void (*)(void *dst_key, size_t dst_key_size, const void *src_key, size_t src_key_size, s32 key_type);
|
||||
using DecryptAesCtrFunction = void (*)(void *dst, size_t dst_size, u8 key_index, u8 key_generation, const void *src_key, size_t src_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
|
||||
|
||||
using CryptAesXtsFunction = Result (*)(void *dst, size_t dst_size, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
|
||||
using VerifySign1Function = bool (*)(const void *sig, size_t sig_size, const void *data, size_t data_size, u8 generation, const NcaCryptoConfiguration &cfg);
|
||||
|
||||
struct NcaCryptoConfiguration {
|
||||
static constexpr size_t Rsa2048KeyModulusSize = crypto::Rsa2048PssSha256Verifier::ModulusSize;
|
||||
|
@ -49,6 +52,8 @@ namespace ams::fssystem {
|
|||
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];
|
||||
|
@ -57,9 +62,13 @@ namespace ams::fssystem {
|
|||
u8 header_encryption_key_source[Aes128KeySize];
|
||||
u8 header_encrypted_encryption_keys[HeaderEncryptionKeyCount][Aes128KeySize];
|
||||
KeyGenerationFunction generate_key;
|
||||
CryptAesXtsFunction decrypt_aes_xts_external;
|
||||
CryptAesXtsFunction encrypt_aes_xts_external;
|
||||
DecryptAesCtrFunction decrypt_aes_ctr;
|
||||
DecryptAesCtrFunction decrypt_aes_ctr_external;
|
||||
VerifySign1Function verify_sign1;
|
||||
bool is_plaintext_header_available;
|
||||
bool is_available_sw_key;
|
||||
|
||||
#if !defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
bool is_unsigned_header_available_for_host_tool;
|
||||
|
@ -72,30 +81,35 @@ namespace ams::fssystem {
|
|||
};
|
||||
static_assert(util::is_pod<NcaCompressionConfiguration>::value);
|
||||
|
||||
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) {
|
||||
constexpr s32 InvalidKeyTypeValue = -1;
|
||||
static_assert(IsInvalidKeyTypeValue(InvalidKeyTypeValue));
|
||||
if (key_index == NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexZeroKey) {
|
||||
return util::ToUnderlying(KeyType::ZeroKey);
|
||||
}
|
||||
|
||||
if (key_index >= NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount) {
|
||||
return InvalidKeyTypeValue;
|
||||
return util::ToUnderlying(KeyType::InvalidKey);
|
||||
}
|
||||
|
||||
return NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount * key_generation + key_index;
|
||||
}
|
||||
|
||||
constexpr inline s32 KeyAreaEncryptionKeyCount = NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount * NcaCryptoConfiguration::KeyGenerationMax;
|
||||
|
||||
enum class KeyType : s32 {
|
||||
NcaHeaderKey = KeyAreaEncryptionKeyCount + 0,
|
||||
NcaExternalKey = KeyAreaEncryptionKeyCount + 1,
|
||||
SaveDataDeviceUniqueMac = KeyAreaEncryptionKeyCount + 2,
|
||||
SaveDataSeedUniqueMac = KeyAreaEncryptionKeyCount + 3,
|
||||
};
|
||||
|
||||
class NcaReader : public ::ams::fs::impl::Newable {
|
||||
NON_COPYABLE(NcaReader);
|
||||
NON_MOVEABLE(NcaReader);
|
||||
|
@ -108,6 +122,7 @@ namespace ams::fssystem {
|
|||
DecryptAesCtrFunction m_decrypt_aes_ctr;
|
||||
DecryptAesCtrFunction m_decrypt_aes_ctr_external;
|
||||
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;
|
||||
|
@ -144,6 +159,7 @@ namespace ams::fssystem {
|
|||
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);
|
||||
|
@ -191,12 +207,27 @@ namespace ams::fssystem {
|
|||
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 : public ::ams::fs::impl::Newable {
|
||||
|
@ -224,6 +255,9 @@ namespace ams::fssystem {
|
|||
std::shared_ptr<fs::IStorage> compressed_storage_meta_storage;
|
||||
std::shared_ptr<fssystem::CompressedStorage> compressed_storage;
|
||||
|
||||
std::shared_ptr<fs::IStorage> patch_layer_info_storage;
|
||||
std::shared_ptr<fs::IStorage> sparse_layer_info_storage;
|
||||
|
||||
/* For tools. */
|
||||
std::shared_ptr<fs::IStorage> external_original_storage;
|
||||
};
|
||||
|
@ -282,15 +316,24 @@ namespace ams::fssystem {
|
|||
Result CreateSparseStorageCore(std::shared_ptr<fssystem::SparseStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 base_size, std::shared_ptr<fs::IStorage> meta_storage, const NcaSparseInfo &sparse_info, bool external_info);
|
||||
Result CreateSparseStorage(std::shared_ptr<fs::IStorage> *out, s64 *out_fs_data_offset, std::shared_ptr<fssystem::SparseStorage> *out_sparse_storage, std::shared_ptr<fs::IStorage> *out_meta_storage, s32 index, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info);
|
||||
|
||||
Result CreateAesCtrExMetaStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info);
|
||||
Result CreateSparseStorageMetaStorageWithVerification(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> *out_verification, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info, const NcaMetaDataHashDataInfo &meta_data_hash_data_info, IHash256GeneratorFactory *hgf);
|
||||
Result CreateSparseStorageWithVerification(std::shared_ptr<fs::IStorage> *out, s64 *out_fs_data_offset, std::shared_ptr<fssystem::SparseStorage> *out_sparse_storage, std::shared_ptr<fs::IStorage> *out_meta_storage, std::shared_ptr<fs::IStorage> *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(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 offset, NcaFsHeader::EncryptionType encryption_type, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info);
|
||||
Result CreateAesCtrExStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::AesCtrCounterExtendedStorage> *out_ext, std::shared_ptr<fs::IStorage> base_storage, std::shared_ptr<fs::IStorage> meta_storage, s64 counter_offset, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info);
|
||||
|
||||
Result CreateIndirectStorageMetaStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, const NcaPatchInfo &patch_info);
|
||||
Result CreateIndirectStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::IndirectStorage> *out_ind, std::shared_ptr<fs::IStorage> base_storage, std::shared_ptr<fs::IStorage> original_data_storage, std::shared_ptr<fs::IStorage> meta_storage, const NcaPatchInfo &patch_info);
|
||||
|
||||
Result CreatePatchMetaStorage(std::shared_ptr<fs::IStorage> *out_aes_ctr_ex_meta, std::shared_ptr<fs::IStorage> *out_indirect_meta, std::shared_ptr<fs::IStorage> *out_verification, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info, const NcaMetaDataHashDataInfo &meta_data_hash_data_info, IHash256GeneratorFactory *hgf);
|
||||
|
||||
Result CreateSha256Storage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, const NcaFsHeader::HashData::HierarchicalSha256Data &sha256_data, IHash256GeneratorFactory *hgf);
|
||||
|
||||
Result CreateIntegrityVerificationStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, const NcaFsHeader::HashData::IntegrityMetaInfo &meta_info, IHash256GeneratorFactory *hgf);
|
||||
Result CreateIntegrityVerificationStorageForMeta(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> *out_verification, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaMetaDataHashDataInfo &meta_data_hash_data_info, IHash256GeneratorFactory *hgf);
|
||||
Result CreateIntegrityVerificationStorageImpl(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> 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, IHash256GeneratorFactory *hgf);
|
||||
|
||||
Result CreateRegionSwitchStorage(std::shared_ptr<fs::IStorage> *out, const NcaFsHeaderReader *header_reader, std::shared_ptr<fs::IStorage> inside_storage, std::shared_ptr<fs::IStorage> outside_storage);
|
||||
|
||||
Result CreateCompressedStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::CompressedStorage> *out_cmp, std::shared_ptr<fs::IStorage> *out_meta, std::shared_ptr<fs::IStorage> base_storage, const NcaCompressionInfo &compression_info);
|
||||
public:
|
||||
|
|
|
@ -15,13 +15,14 @@
|
|||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/fssystem/fssystem_i_hash_256_generator.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
/* ACCURATE_TO_VERSION: 13.4.0.0 */
|
||||
/* ACCURATE_TO_VERSION: 14.3.0.0 */
|
||||
|
||||
struct Hash {
|
||||
static constexpr size_t Size = crypto::Sha256Generator::HashSize;
|
||||
static constexpr size_t Size = IHash256Generator::HashSize;
|
||||
u8 value[Size];
|
||||
};
|
||||
static_assert(sizeof(Hash) == Hash::Size);
|
||||
|
@ -185,9 +186,17 @@ namespace ams::fssystem {
|
|||
|
||||
struct NcaCompressionInfo {
|
||||
NcaBucketInfo bucket;
|
||||
u8 reserved[8];
|
||||
};
|
||||
static_assert(util::is_pod<NcaCompressionInfo>::value);
|
||||
|
||||
struct NcaMetaDataHashDataInfo {
|
||||
fs::Int64 offset;
|
||||
fs::Int64 size;
|
||||
Hash hash;
|
||||
};
|
||||
static_assert(util::is_pod<NcaMetaDataHashDataInfo>::value);
|
||||
|
||||
struct NcaFsHeader {
|
||||
static constexpr size_t Size = 0x200;
|
||||
static constexpr size_t HashDataOffset = 0x8;
|
||||
|
@ -204,18 +213,28 @@ namespace ams::fssystem {
|
|||
};
|
||||
|
||||
enum class EncryptionType : u8 {
|
||||
Auto = 0,
|
||||
None = 1,
|
||||
AesXts = 2,
|
||||
AesCtr = 3,
|
||||
AesCtrEx = 4,
|
||||
Auto = 0,
|
||||
None = 1,
|
||||
AesXts = 2,
|
||||
AesCtr = 3,
|
||||
AesCtrEx = 4,
|
||||
AesCtrSkipLayerHash = 5,
|
||||
AesCtrExSkipLayerHash = 6,
|
||||
};
|
||||
|
||||
enum class HashType : u8 {
|
||||
Auto = 0,
|
||||
None = 1,
|
||||
HierarchicalSha256Hash = 2,
|
||||
HierarchicalIntegrityHash = 3,
|
||||
Auto = 0,
|
||||
None = 1,
|
||||
HierarchicalSha256Hash = 2,
|
||||
HierarchicalIntegrityHash = 3,
|
||||
AutoSha3 = 4,
|
||||
HierarchicalSha3256Hash = 5,
|
||||
HierarchicalIntegritySha3Hash = 6,
|
||||
};
|
||||
|
||||
enum class MetaDataHashType : u8 {
|
||||
None = 0,
|
||||
HierarchicalIntegrity = 1,
|
||||
};
|
||||
|
||||
union HashData {
|
||||
|
@ -265,13 +284,34 @@ namespace ams::fssystem {
|
|||
FsType fs_type;
|
||||
HashType hash_type;
|
||||
EncryptionType encryption_type;
|
||||
u8 reserved[3];
|
||||
MetaDataHashType meta_data_hash_type;
|
||||
u8 reserved[2];
|
||||
HashData hash_data;
|
||||
NcaPatchInfo patch_info;
|
||||
NcaAesCtrUpperIv aes_ctr_upper_iv;
|
||||
NcaSparseInfo sparse_info;
|
||||
NcaCompressionInfo compression_info;
|
||||
u8 pad[0x68];
|
||||
NcaMetaDataHashDataInfo meta_data_hash_data_info;
|
||||
u8 pad[0x30];
|
||||
|
||||
bool IsSkipLayerHashEncryption() const {
|
||||
return this->encryption_type == EncryptionType::AesCtrSkipLayerHash || this->encryption_type == EncryptionType::AesCtrExSkipLayerHash;
|
||||
}
|
||||
|
||||
Result GetHashTargetOffset(s64 *out) const {
|
||||
switch (this->hash_type) {
|
||||
case HashType::HierarchicalIntegrityHash:
|
||||
case HashType::HierarchicalIntegritySha3Hash:
|
||||
*out = this->hash_data.integrity_meta_info.level_hash_info.info[this->hash_data.integrity_meta_info.level_hash_info.max_layers - 2].offset;
|
||||
R_SUCCEED();
|
||||
case HashType::HierarchicalSha256Hash:
|
||||
case HashType::HierarchicalSha3256Hash:
|
||||
*out = this->hash_data.hierarchical_sha256_data.hash_layer_region[this->hash_data.hierarchical_sha256_data.hash_layer_count - 1].offset;
|
||||
R_SUCCEED();
|
||||
default:
|
||||
R_THROW(fs::ResultInvalidNcaFsHeader());
|
||||
}
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(NcaFsHeader) == NcaFsHeader::Size);
|
||||
static_assert(util::is_pod<NcaFsHeader>::value);
|
||||
|
@ -280,4 +320,11 @@ namespace ams::fssystem {
|
|||
inline constexpr const size_t NcaFsHeader::HashData::HierarchicalSha256Data::MasterHashOffset = AMS_OFFSETOF(NcaFsHeader, hash_data.hierarchical_sha256_data.fs_data_master_hash);
|
||||
inline constexpr const size_t NcaFsHeader::HashData::IntegrityMetaInfo::MasterHashOffset = AMS_OFFSETOF(NcaFsHeader, hash_data.integrity_meta_info.master_hash);
|
||||
|
||||
struct NcaMetaDataHashData {
|
||||
s64 layer_info_offset;
|
||||
NcaFsHeader::HashData::IntegrityMetaInfo integrity_meta_info;
|
||||
};
|
||||
static_assert(sizeof(NcaMetaDataHashData) == sizeof(NcaFsHeader::HashData::IntegrityMetaInfo) + sizeof(s64));
|
||||
static_assert(util::is_pod<NcaMetaDataHashData>::value);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/fs/fs_istorage.hpp>
|
||||
#include <stratosphere/fs/impl/fs_newable.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
/* ACCURATE_TO_VERSION: 14.3.0.0 */
|
||||
template<typename F>
|
||||
class SwitchStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
||||
NON_COPYABLE(SwitchStorage);
|
||||
NON_MOVEABLE(SwitchStorage);
|
||||
private:
|
||||
std::shared_ptr<fs::IStorage> m_true_storage;
|
||||
std::shared_ptr<fs::IStorage> m_false_storage;
|
||||
F m_truth_function;
|
||||
private:
|
||||
ALWAYS_INLINE std::shared_ptr<fs::IStorage> &SelectStorage() {
|
||||
return m_truth_function() ? m_true_storage : m_false_storage;
|
||||
}
|
||||
public:
|
||||
SwitchStorage(std::shared_ptr<fs::IStorage> t, std::shared_ptr<fs::IStorage> f, F func) : m_true_storage(std::move(t)), m_false_storage(std::move(f)), m_truth_function(func) { /* ... */ }
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
R_RETURN(this->SelectStorage()->Read(offset, buffer, size));
|
||||
}
|
||||
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
|
||||
switch (op_id) {
|
||||
case fs::OperationId::Invalidate:
|
||||
{
|
||||
R_TRY(m_true_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
R_TRY(m_false_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
R_SUCCEED();
|
||||
}
|
||||
case fs::OperationId::QueryRange:
|
||||
{
|
||||
R_TRY(this->SelectStorage()->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
R_SUCCEED();
|
||||
}
|
||||
default:
|
||||
R_THROW(fs::ResultUnsupportedOperateRangeForSwitchStorage());
|
||||
}
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
R_RETURN(this->SelectStorage()->GetSize(out));
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
R_RETURN(this->SelectStorage()->Flush());
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
R_RETURN(this->SelectStorage()->Write(offset, buffer, size));
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
R_RETURN(this->SelectStorage()->SetSize(size));
|
||||
}
|
||||
};
|
||||
|
||||
class RegionSwitchStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
||||
NON_COPYABLE(RegionSwitchStorage);
|
||||
NON_MOVEABLE(RegionSwitchStorage);
|
||||
public:
|
||||
struct Region {
|
||||
s64 offset;
|
||||
s64 size;
|
||||
};
|
||||
private:
|
||||
std::shared_ptr<fs::IStorage> m_inside_region_storage;
|
||||
std::shared_ptr<fs::IStorage> m_outside_region_storage;
|
||||
Region m_region;
|
||||
public:
|
||||
RegionSwitchStorage(std::shared_ptr<fs::IStorage> &&i, std::shared_ptr<fs::IStorage> &&o, Region r) : m_inside_region_storage(std::move(i)), m_outside_region_storage(std::move(o)), m_region(r) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
/* Process until we're done. */
|
||||
size_t processed = 0;
|
||||
while (processed < size) {
|
||||
/* Process on the appropriate storage. */
|
||||
s64 cur_size = 0;
|
||||
if (this->CheckRegions(std::addressof(cur_size), offset + processed, size - processed)) {
|
||||
R_TRY(m_inside_region_storage->Read(offset + processed, static_cast<u8 *>(buffer) + processed, cur_size));
|
||||
} else {
|
||||
R_TRY(m_outside_region_storage->Read(offset + processed, static_cast<u8 *>(buffer) + processed, cur_size));
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
processed += cur_size;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
/* Process until we're done. */
|
||||
size_t processed = 0;
|
||||
while (processed < size) {
|
||||
/* Process on the appropriate storage. */
|
||||
s64 cur_size = 0;
|
||||
if (this->CheckRegions(std::addressof(cur_size), offset + processed, size - processed)) {
|
||||
R_TRY(m_inside_region_storage->Write(offset + processed, static_cast<const u8 *>(buffer) + processed, cur_size));
|
||||
} else {
|
||||
R_TRY(m_outside_region_storage->Write(offset + processed, static_cast<const u8 *>(buffer) + processed, cur_size));
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
processed += cur_size;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
|
||||
switch (op_id) {
|
||||
case fs::OperationId::Invalidate:
|
||||
{
|
||||
R_TRY(m_inside_region_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
R_TRY(m_outside_region_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
R_SUCCEED();
|
||||
}
|
||||
case fs::OperationId::QueryRange:
|
||||
{
|
||||
/* Create a new info. */
|
||||
fs::QueryRangeInfo merged_info;
|
||||
merged_info.Clear();
|
||||
|
||||
/* Process until we're done. */
|
||||
s64 processed = 0;
|
||||
while (processed < size) {
|
||||
/* Process on the appropriate storage. */
|
||||
s64 cur_size = 0;
|
||||
fs::QueryRangeInfo cur_info;
|
||||
if (this->CheckRegions(std::addressof(cur_size), offset + processed, size - processed)) {
|
||||
R_TRY(m_inside_region_storage->OperateRange(std::addressof(cur_info), sizeof(cur_info), op_id, offset + processed, cur_size, src, src_size));
|
||||
} else {
|
||||
R_TRY(m_outside_region_storage->OperateRange(std::addressof(cur_info), sizeof(cur_info), op_id, offset + processed, cur_size, src, src_size));
|
||||
}
|
||||
|
||||
/* Merge the current info. */
|
||||
merged_info.Merge(cur_info);
|
||||
|
||||
/* Advance. */
|
||||
processed += cur_size;
|
||||
}
|
||||
|
||||
/* Write the merged info. */
|
||||
*reinterpret_cast<fs::QueryRangeInfo *>(dst) = merged_info;
|
||||
R_SUCCEED();
|
||||
}
|
||||
default:
|
||||
R_THROW(fs::ResultUnsupportedOperateRangeForRegionSwitchStorage());
|
||||
}
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
R_RETURN(m_inside_region_storage->GetSize(out));
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
/* Flush both storages. */
|
||||
R_TRY(m_inside_region_storage->Flush());
|
||||
R_TRY(m_outside_region_storage->Flush());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
/* Set size for both storages. */
|
||||
R_TRY(m_inside_region_storage->SetSize(size));
|
||||
R_TRY(m_outside_region_storage->SetSize(size));
|
||||
R_SUCCEED();
|
||||
}
|
||||
private:
|
||||
bool CheckRegions(s64 *out_current_size, s64 offset, s64 size) const {
|
||||
/* Check if our region contains the access. */
|
||||
if (m_region.offset <= offset) {
|
||||
if (offset < m_region.offset + m_region.size) {
|
||||
if (m_region.offset + m_region.size <= offset + size) {
|
||||
*out_current_size = m_region.offset + m_region.size - offset;
|
||||
} else {
|
||||
*out_current_size = size;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
*out_current_size = size;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (m_region.offset <= offset + size) {
|
||||
*out_current_size = m_region.offset - offset;
|
||||
} else {
|
||||
*out_current_size = size;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -128,14 +128,26 @@ namespace ams::fssrv {
|
|||
/* Key Generation Function */
|
||||
nullptr,
|
||||
|
||||
/* Decrypt Aes Xts Eternal Function */
|
||||
nullptr,
|
||||
|
||||
/* Encrypt Aes Xts Eternal Function */
|
||||
nullptr,
|
||||
|
||||
/* Decrypt Aes Ctr Function */
|
||||
nullptr,
|
||||
|
||||
/* Decrypt Aes Ctr External Function */
|
||||
nullptr,
|
||||
|
||||
/* Verify Sign1 Function */
|
||||
nullptr,
|
||||
|
||||
/* Plaintext Header Available */
|
||||
false,
|
||||
|
||||
/* Software Key Available */
|
||||
true,
|
||||
};
|
||||
|
||||
constexpr inline const ::ams::fssystem::NcaCryptoConfiguration DefaultNcaCryptoConfigurationProd = {
|
||||
|
@ -169,14 +181,26 @@ namespace ams::fssrv {
|
|||
/* Key Generation Function */
|
||||
nullptr,
|
||||
|
||||
/* Decrypt Aes Xts Eternal Function */
|
||||
nullptr,
|
||||
|
||||
/* Encrypt Aes Xts Eternal Function */
|
||||
nullptr,
|
||||
|
||||
/* Decrypt Aes Ctr Function */
|
||||
nullptr,
|
||||
|
||||
/* Decrypt Aes Ctr External Function */
|
||||
nullptr,
|
||||
|
||||
/* Verify Sign1 Function */
|
||||
nullptr,
|
||||
|
||||
/* Plaintext Header Available */
|
||||
false,
|
||||
|
||||
/* Software Key Available */
|
||||
true,
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -33,8 +33,9 @@ namespace ams::fssystem {
|
|||
private:
|
||||
AesCtrCounterExtendedStorage::DecryptFunction m_decrypt_function;
|
||||
s32 m_key_index;
|
||||
s32 m_key_generation;
|
||||
public:
|
||||
ExternalDecryptor(AesCtrCounterExtendedStorage::DecryptFunction df, s32 key_idx) : m_decrypt_function(df), m_key_index(key_idx) {
|
||||
ExternalDecryptor(AesCtrCounterExtendedStorage::DecryptFunction df, s32 key_idx, s32 key_gen) : m_decrypt_function(df), m_key_index(key_idx), m_key_generation(key_gen) {
|
||||
AMS_ASSERT(m_decrypt_function != nullptr);
|
||||
}
|
||||
public:
|
||||
|
@ -44,8 +45,8 @@ namespace ams::fssystem {
|
|||
|
||||
}
|
||||
|
||||
Result AesCtrCounterExtendedStorage::CreateExternalDecryptor(std::unique_ptr<IDecryptor> *out, DecryptFunction func, s32 key_index) {
|
||||
std::unique_ptr<IDecryptor> decryptor = std::make_unique<ExternalDecryptor>(func, key_index);
|
||||
Result AesCtrCounterExtendedStorage::CreateExternalDecryptor(std::unique_ptr<IDecryptor> *out, DecryptFunction func, s32 key_index, s32 key_generation) {
|
||||
std::unique_ptr<IDecryptor> decryptor = std::make_unique<ExternalDecryptor>(func, key_index, key_generation);
|
||||
R_UNLESS(decryptor != nullptr, fs::ResultAllocationMemoryFailedInAesCtrCounterExtendedStorageA());
|
||||
*out = std::move(decryptor);
|
||||
return ResultSuccess();
|
||||
|
@ -280,7 +281,7 @@ namespace ams::fssystem {
|
|||
size_t cur_size = std::min(pooled_buffer.GetSize(), remaining_size);
|
||||
u8 *dst = static_cast<u8 *>(buf) + cur_offset;
|
||||
|
||||
m_decrypt_function(pooled_buffer.GetBuffer(), cur_size, m_key_index, enc_key, enc_key_size, ctr, IvSize, dst, cur_size);
|
||||
m_decrypt_function(pooled_buffer.GetBuffer(), cur_size, m_key_index, m_key_generation, enc_key, enc_key_size, ctr, IvSize, dst, cur_size);
|
||||
|
||||
std::memcpy(dst, pooled_buffer.GetBuffer(), cur_size);
|
||||
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
AesCtrStorageExternal::AesCtrStorageExternal(std::shared_ptr<fs::IStorage> bs, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, DecryptAesCtrFunction df, s32 kidx, s32 kgen) : m_base_storage(std::move(bs)), m_decrypt_function(df), m_key_index(kidx), m_key_generation(kgen) {
|
||||
AMS_ASSERT(m_base_storage != nullptr);
|
||||
AMS_ASSERT(enc_key_size == KeySize);
|
||||
AMS_ASSERT(iv != nullptr);
|
||||
AMS_ASSERT(iv_size == IvSize);
|
||||
AMS_UNUSED(iv_size);
|
||||
|
||||
std::memcpy(m_iv, iv, IvSize);
|
||||
std::memcpy(m_encrypted_key, enc_key, enc_key_size);
|
||||
}
|
||||
|
||||
Result AesCtrStorageExternal::Read(s64 offset, void *buffer, size_t size) {
|
||||
/* Allow zero size. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Validate arguments. */
|
||||
/* NOTE: For some reason, Nintendo uses InvalidArgument instead of InvalidOffset/InvalidSize here. */
|
||||
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
|
||||
R_UNLESS(util::IsAligned(offset, BlockSize), fs::ResultInvalidArgument());
|
||||
R_UNLESS(util::IsAligned(size, BlockSize), fs::ResultInvalidArgument());
|
||||
|
||||
/* Read the data. */
|
||||
R_TRY(m_base_storage->Read(offset, buffer, size));
|
||||
|
||||
/* Temporarily increase our thread priority. */
|
||||
ScopedThreadPriorityChanger cp(+1, ScopedThreadPriorityChanger::Mode::Relative);
|
||||
|
||||
/* Allocate a pooled buffer for decryption. */
|
||||
PooledBuffer pooled_buffer;
|
||||
pooled_buffer.AllocateParticularlyLarge(size, BlockSize);
|
||||
AMS_ASSERT(pooled_buffer.GetSize() >= BlockSize);
|
||||
|
||||
/* Setup the counter. */
|
||||
u8 ctr[IvSize];
|
||||
std::memcpy(ctr, m_iv, IvSize);
|
||||
AddCounter(ctr, IvSize, offset / BlockSize);
|
||||
|
||||
/* Setup tracking. */
|
||||
size_t remaining_size = size;
|
||||
s64 cur_offset = 0;
|
||||
|
||||
while (remaining_size > 0) {
|
||||
/* Get the current size to process. */
|
||||
size_t cur_size = std::min(pooled_buffer.GetSize(), remaining_size);
|
||||
char *dst = static_cast<char *>(buffer) + cur_offset;
|
||||
|
||||
/* Decrypt into the temporary buffer */
|
||||
m_decrypt_function(pooled_buffer.GetBuffer(), cur_size, m_key_index, m_key_generation, m_encrypted_key, KeySize, ctr, IvSize, dst, cur_size);
|
||||
|
||||
/* Copy to the destination. */
|
||||
std::memcpy(dst, pooled_buffer.GetBuffer(), cur_size);
|
||||
|
||||
/* Update tracking. */
|
||||
cur_offset += cur_size;
|
||||
remaining_size -= cur_size;
|
||||
|
||||
if (remaining_size > 0) {
|
||||
AddCounter(ctr, IvSize, cur_size / BlockSize);
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result AesCtrStorageExternal::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
|
||||
switch (op_id) {
|
||||
case fs::OperationId::QueryRange:
|
||||
{
|
||||
/* Validate that we have an output range info. */
|
||||
R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
|
||||
R_UNLESS(dst_size == sizeof(fs::QueryRangeInfo), fs::ResultInvalidSize());
|
||||
|
||||
/* Operate on our base storage. */
|
||||
R_TRY(m_base_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
|
||||
/* Add in new flags. */
|
||||
fs::QueryRangeInfo new_info;
|
||||
new_info.Clear();
|
||||
new_info.aes_ctr_key_type = static_cast<s32>(m_key_index >= 0 ? fs::AesCtrKeyTypeFlag::InternalKeyForHardwareAes : fs::AesCtrKeyTypeFlag::ExternalKeyForHardwareAes);
|
||||
|
||||
/* Merge the new info in. */
|
||||
reinterpret_cast<fs::QueryRangeInfo *>(dst)->Merge(new_info);
|
||||
R_SUCCEED();
|
||||
}
|
||||
default:
|
||||
{
|
||||
/* Operate on our base storage. */
|
||||
R_RETURN(m_base_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result AesCtrStorageExternal::GetSize(s64 *out) {
|
||||
R_RETURN(m_base_storage->GetSize(out));
|
||||
}
|
||||
|
||||
Result AesCtrStorageExternal::Flush() {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result AesCtrStorageExternal::Write(s64 offset, const void *buffer, size_t size) {
|
||||
AMS_UNUSED(offset, buffer, size);
|
||||
R_THROW(fs::ResultUnsupportedWriteForAesCtrStorageExternal());
|
||||
}
|
||||
|
||||
Result AesCtrStorageExternal::SetSize(s64 size) {
|
||||
AMS_UNUSED(size);
|
||||
R_THROW(fs::ResultUnsupportedSetSizeForAesCtrStorageExternal());
|
||||
}
|
||||
|
||||
}
|
|
@ -108,7 +108,7 @@ namespace ams::fssystem {
|
|||
AddCounter(ctr, IvSize, 1);
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
|
@ -206,36 +206,39 @@ namespace ams::fssystem {
|
|||
remaining -= write_size;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
Result AesXtsStorage<BasePointer>::Flush() {
|
||||
return m_base_storage->Flush();
|
||||
R_RETURN(m_base_storage->Flush());
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
Result AesXtsStorage<BasePointer>::SetSize(s64 size) {
|
||||
R_UNLESS(util::IsAligned(size, AesBlockSize), fs::ResultUnexpectedInAesXtsStorageA());
|
||||
|
||||
return m_base_storage->SetSize(size);
|
||||
R_RETURN(m_base_storage->SetSize(size));
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
Result AesXtsStorage<BasePointer>::GetSize(s64 *out) {
|
||||
return m_base_storage->GetSize(out);
|
||||
R_RETURN(m_base_storage->GetSize(out));
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
Result AesXtsStorage<BasePointer>::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
|
||||
/* Handle the zero size case. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
/* Unless invalidating cache, check the arguments. */
|
||||
if (op_id != fs::OperationId::Invalidate) {
|
||||
/* Handle the zero size case. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Ensure alignment. */
|
||||
R_UNLESS(util::IsAligned(offset, AesBlockSize), fs::ResultInvalidArgument());
|
||||
R_UNLESS(util::IsAligned(size, AesBlockSize), fs::ResultInvalidArgument());
|
||||
/* Ensure alignment. */
|
||||
R_UNLESS(util::IsAligned(offset, AesBlockSize), fs::ResultInvalidArgument());
|
||||
R_UNLESS(util::IsAligned(size, AesBlockSize), fs::ResultInvalidArgument());
|
||||
}
|
||||
|
||||
return m_base_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size);
|
||||
R_RETURN(m_base_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
}
|
||||
|
||||
template class AesXtsStorage<fs::IStorage *>;
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
template<typename BasePointer>
|
||||
AesXtsStorageExternal<BasePointer>::AesXtsStorageExternal(BasePointer bs, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size, CryptAesXtsFunction ef, CryptAesXtsFunction df) : m_base_storage(std::move(bs)), m_block_size(block_size), m_encrypt_function(ef), m_decrypt_function(df) {
|
||||
AMS_ASSERT(key_size == KeySize);
|
||||
AMS_ASSERT(iv_size == IvSize);
|
||||
AMS_UNUSED(key_size, iv_size);
|
||||
|
||||
if (key1 != nullptr) {
|
||||
std::memcpy(m_key[0], key1, KeySize);
|
||||
}
|
||||
|
||||
if (key2 != nullptr) {
|
||||
std::memcpy(m_key[1], key2, KeySize);
|
||||
}
|
||||
|
||||
std::memcpy(m_iv, iv, IvSize);
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
Result AesXtsStorageExternal<BasePointer>::Read(s64 offset, void *buffer, size_t size) {
|
||||
/* Allow zero size. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Ensure buffer is valid. */
|
||||
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* Ensure we can decrypt. */
|
||||
R_UNLESS(m_decrypt_function != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* We can only read at block aligned offsets. */
|
||||
R_UNLESS(util::IsAligned(offset, AesBlockSize), fs::ResultInvalidArgument());
|
||||
R_UNLESS(util::IsAligned(size, AesBlockSize), fs::ResultInvalidArgument());
|
||||
|
||||
/* Read the data. */
|
||||
R_TRY(m_base_storage->Read(offset, buffer, size));
|
||||
|
||||
/* Temporarily increase our thread priority. */
|
||||
ScopedThreadPriorityChanger cp(+1, ScopedThreadPriorityChanger::Mode::Relative);
|
||||
|
||||
/* Setup the counter. */
|
||||
char ctr[IvSize];
|
||||
std::memcpy(ctr, m_iv, IvSize);
|
||||
AddCounter(ctr, IvSize, offset / m_block_size);
|
||||
|
||||
/* Handle any unaligned data before the start. */
|
||||
size_t processed_size = 0;
|
||||
if ((offset % m_block_size) != 0) {
|
||||
/* Determine the size of the pre-data read. */
|
||||
const size_t skip_size = static_cast<size_t>(offset - util::AlignDown(offset, m_block_size));
|
||||
const size_t data_size = std::min(size, m_block_size - skip_size);
|
||||
|
||||
/* Decrypt into a pooled buffer. */
|
||||
{
|
||||
PooledBuffer tmp_buf(m_block_size, m_block_size);
|
||||
AMS_ASSERT(tmp_buf.GetSize() >= m_block_size);
|
||||
|
||||
std::memset(tmp_buf.GetBuffer(), 0, skip_size);
|
||||
std::memcpy(tmp_buf.GetBuffer() + skip_size, buffer, data_size);
|
||||
|
||||
/* Decrypt. */
|
||||
R_TRY(m_decrypt_function(tmp_buf.GetBuffer(), m_block_size, m_key[0], m_key[1], KeySize, ctr, IvSize, tmp_buf.GetBuffer(), m_block_size));
|
||||
|
||||
std::memcpy(buffer, tmp_buf.GetBuffer() + skip_size, data_size);
|
||||
}
|
||||
|
||||
AddCounter(ctr, IvSize, 1);
|
||||
processed_size += data_size;
|
||||
AMS_ASSERT(processed_size == std::min(size, m_block_size - skip_size));
|
||||
}
|
||||
|
||||
/* Decrypt aligned chunks. */
|
||||
char *cur = static_cast<char *>(buffer) + processed_size;
|
||||
size_t remaining = size - processed_size;
|
||||
while (remaining > 0) {
|
||||
const size_t cur_size = std::min(m_block_size, remaining);
|
||||
R_TRY(m_decrypt_function(cur, cur_size, m_key[0], m_key[1], KeySize, ctr, IvSize, cur, cur_size));
|
||||
|
||||
remaining -= cur_size;
|
||||
cur += cur_size;
|
||||
|
||||
AddCounter(ctr, IvSize, 1);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
Result AesXtsStorageExternal<BasePointer>::Write(s64 offset, const void *buffer, size_t size) {
|
||||
/* Allow zero-size writes. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Ensure buffer is valid. */
|
||||
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* Ensure we can encrypt. */
|
||||
R_UNLESS(m_decrypt_function != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* We can only write at block aligned offsets. */
|
||||
R_UNLESS(util::IsAligned(offset, AesBlockSize), fs::ResultInvalidArgument());
|
||||
R_UNLESS(util::IsAligned(size, AesBlockSize), fs::ResultInvalidArgument());
|
||||
|
||||
/* Get a pooled buffer. */
|
||||
PooledBuffer pooled_buffer;
|
||||
const bool use_work_buffer = !IsDeviceAddress(buffer);
|
||||
if (use_work_buffer) {
|
||||
pooled_buffer.Allocate(size, m_block_size);
|
||||
}
|
||||
|
||||
/* Setup the counter. */
|
||||
char ctr[IvSize];
|
||||
std::memcpy(ctr, m_iv, IvSize);
|
||||
AddCounter(ctr, IvSize, offset / m_block_size);
|
||||
|
||||
/* Handle any unaligned data before the start. */
|
||||
size_t processed_size = 0;
|
||||
if ((offset % m_block_size) != 0) {
|
||||
/* Determine the size of the pre-data read. */
|
||||
const size_t skip_size = static_cast<size_t>(offset - util::AlignDown(offset, m_block_size));
|
||||
const size_t data_size = std::min(size, m_block_size - skip_size);
|
||||
|
||||
/* Encrypt into a pooled buffer. */
|
||||
{
|
||||
/* NOTE: Nintendo allocates a second pooled buffer here despite having one already allocated above. */
|
||||
PooledBuffer tmp_buf(m_block_size, m_block_size);
|
||||
AMS_ASSERT(tmp_buf.GetSize() >= m_block_size);
|
||||
|
||||
std::memset(tmp_buf.GetBuffer(), 0, skip_size);
|
||||
std::memcpy(tmp_buf.GetBuffer() + skip_size, buffer, data_size);
|
||||
|
||||
R_TRY(m_encrypt_function(tmp_buf.GetBuffer(), m_block_size, m_key[0], m_key[1], KeySize, ctr, IvSize, tmp_buf.GetBuffer(), m_block_size));
|
||||
|
||||
R_TRY(m_base_storage->Write(offset, tmp_buf.GetBuffer() + skip_size, data_size));
|
||||
}
|
||||
|
||||
AddCounter(ctr, IvSize, 1);
|
||||
processed_size += data_size;
|
||||
AMS_ASSERT(processed_size == std::min(size, m_block_size - skip_size));
|
||||
}
|
||||
|
||||
/* Encrypt aligned chunks. */
|
||||
size_t remaining = size - processed_size;
|
||||
s64 cur_offset = offset + processed_size;
|
||||
while (remaining > 0) {
|
||||
/* Determine data we're writing and where. */
|
||||
const size_t write_size = use_work_buffer ? std::min(pooled_buffer.GetSize(), remaining) : remaining;
|
||||
|
||||
/* Encrypt the data, with temporarily increased priority. */
|
||||
{
|
||||
ScopedThreadPriorityChanger cp(+1, ScopedThreadPriorityChanger::Mode::Relative);
|
||||
|
||||
size_t remaining_write = write_size;
|
||||
size_t encrypt_offset = 0;
|
||||
while (remaining_write > 0) {
|
||||
const size_t cur_size = std::min(remaining_write, m_block_size);
|
||||
const void *src = static_cast<const char *>(buffer) + processed_size + encrypt_offset;
|
||||
void *dst = use_work_buffer ? pooled_buffer.GetBuffer() + encrypt_offset : const_cast<void *>(src);
|
||||
|
||||
R_TRY(m_encrypt_function(dst, cur_size, m_key[0], m_key[1], KeySize, ctr, IvSize, src, cur_size));
|
||||
|
||||
AddCounter(ctr, IvSize, 1);
|
||||
|
||||
encrypt_offset += cur_size;
|
||||
remaining_write -= cur_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the encrypted data. */
|
||||
const void *write_buf = use_work_buffer ? pooled_buffer.GetBuffer() : static_cast<const char *>(buffer) + processed_size;
|
||||
R_TRY(m_base_storage->Write(cur_offset, write_buf, write_size));
|
||||
|
||||
/* Advance. */
|
||||
cur_offset += write_size;
|
||||
processed_size += write_size;
|
||||
remaining -= write_size;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
Result AesXtsStorageExternal<BasePointer>::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
|
||||
/* Unless invalidating cache, check the arguments. */
|
||||
if (op_id != fs::OperationId::Invalidate) {
|
||||
/* Handle the zero size case. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Ensure alignment. */
|
||||
R_UNLESS(util::IsAligned(offset, AesBlockSize), fs::ResultInvalidArgument());
|
||||
R_UNLESS(util::IsAligned(size, AesBlockSize), fs::ResultInvalidArgument());
|
||||
}
|
||||
|
||||
R_RETURN(m_base_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
Result AesXtsStorageExternal<BasePointer>::GetSize(s64 *out) {
|
||||
R_RETURN(m_base_storage->GetSize(out));
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
Result AesXtsStorageExternal<BasePointer>::Flush() {
|
||||
R_RETURN(m_base_storage->Flush());
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
Result AesXtsStorageExternal<BasePointer>::SetSize(s64 size) {
|
||||
R_UNLESS(util::IsAligned(size, AesBlockSize), fs::ResultUnexpectedInAesXtsStorageA());
|
||||
|
||||
R_RETURN(m_base_storage->SetSize(size));
|
||||
}
|
||||
|
||||
template class AesXtsStorageExternal<fs::IStorage *>;
|
||||
template class AesXtsStorageExternal<std::shared_ptr<fs::IStorage>>;
|
||||
|
||||
}
|
|
@ -17,8 +17,7 @@
|
|||
|
||||
namespace ams::fssystem {
|
||||
|
||||
BlockCacheBufferedStorage::BlockCacheBufferedStorage() : m_mutex(), m_data_storage(), m_last_result(ResultSuccess()), m_data_size(), m_verification_block_size(), m_verification_block_shift(), m_flags(), m_buffer_level(-1), m_block_cache_manager()
|
||||
{
|
||||
BlockCacheBufferedStorage::BlockCacheBufferedStorage() : m_mutex(), m_data_storage(), m_last_result(ResultSuccess()), m_data_size(), m_verification_block_size(), m_verification_block_shift(), m_flags(), m_buffer_level(-1), m_block_cache_manager() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
|
@ -26,7 +25,7 @@ namespace ams::fssystem {
|
|||
this->Finalize();
|
||||
}
|
||||
|
||||
Result BlockCacheBufferedStorage::Initialize(fs::IBufferManager *bm, os::SdkRecursiveMutex *mtx, IStorage *data, s64 data_size, size_t verif_block_size, s32 max_cache_entries, bool is_real_data, s8 buffer_level, bool is_keep_burst_mode, fs::StorageType storage_type) {
|
||||
Result BlockCacheBufferedStorage::Initialize(fs::IBufferManager *bm, os::SdkRecursiveMutex *mtx, IStorage *data, s64 data_size, size_t verif_block_size, s32 max_cache_entries, bool is_real_data, s8 buffer_level, bool is_keep_burst_mode, bool is_writable) {
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(data != nullptr);
|
||||
AMS_ASSERT(bm != nullptr);
|
||||
|
@ -46,7 +45,7 @@ namespace ams::fssystem {
|
|||
m_last_result = ResultSuccess();
|
||||
m_flags = 0;
|
||||
m_buffer_level = buffer_level;
|
||||
m_storage_type = storage_type;
|
||||
m_is_writable = is_writable;
|
||||
|
||||
/* Calculate block shift. */
|
||||
m_verification_block_shift = ILog2(static_cast<u32>(verif_block_size));
|
||||
|
@ -385,24 +384,20 @@ namespace ams::fssystem {
|
|||
switch (op_id) {
|
||||
case fs::OperationId::FillZero:
|
||||
{
|
||||
R_TRY(this->FillZeroImpl(offset, size));
|
||||
R_SUCCEED();
|
||||
R_RETURN(this->FillZeroImpl(offset, size));
|
||||
}
|
||||
case fs::OperationId::DestroySignature:
|
||||
{
|
||||
R_TRY(this->DestroySignatureImpl(offset, size));
|
||||
R_SUCCEED();
|
||||
R_RETURN(this->DestroySignatureImpl(offset, size));
|
||||
}
|
||||
case fs::OperationId::Invalidate:
|
||||
{
|
||||
R_UNLESS(m_storage_type != fs::StorageType_SaveData, fs::ResultUnsupportedOperateRangeForNonSaveDataBlockCacheBufferedStorage());
|
||||
R_TRY(this->InvalidateImpl());
|
||||
R_SUCCEED();
|
||||
R_UNLESS(!m_is_writable, fs::ResultUnsupportedOperateRangeForWritableBlockCacheBufferedStorage());
|
||||
R_RETURN(this->InvalidateImpl());
|
||||
}
|
||||
case fs::OperationId::QueryRange:
|
||||
{
|
||||
R_TRY(this->QueryRangeImpl(dst, dst_size, offset, size));
|
||||
R_SUCCEED();
|
||||
R_RETURN(this->QueryRangeImpl(dst, dst_size, offset, size));
|
||||
}
|
||||
default:
|
||||
R_THROW(fs::ResultUnsupportedOperateRangeForBlockCacheBufferedStorage());
|
||||
|
|
|
@ -120,24 +120,24 @@ namespace ams::fssystem {
|
|||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(spl::AccessKey, s_nca_header_kek_access_key);
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(spl::AccessKey, s_invalid_nca_kek_access_key);
|
||||
|
||||
if (key_type > static_cast<s32>(KeyType::NcaHeaderKey) || IsInvalidKeyTypeValue(key_type)) {
|
||||
if (key_type > static_cast<s32>(KeyType::NcaHeaderKey2) || IsInvalidKeyTypeValue(key_type)) {
|
||||
return s_invalid_nca_kek_access_key;
|
||||
} else if (key_type == static_cast<s32>(KeyType::NcaHeaderKey)) {
|
||||
} else if (key_type == static_cast<s32>(KeyType::NcaHeaderKey1) || key_type == static_cast<s32>(KeyType::NcaHeaderKey2)) {
|
||||
return s_nca_header_kek_access_key;
|
||||
} else {
|
||||
return s_nca_kek_access_key_array[key_type];
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateNcaKey(void *dst, size_t dst_size, const void *src, size_t src_size, s32 key_type, const NcaCryptoConfiguration &cfg) {
|
||||
AMS_UNUSED(cfg);
|
||||
|
||||
void GenerateNcaKey(void *dst, size_t dst_size, const void *src, size_t src_size, s32 key_type) {
|
||||
R_ABORT_UNLESS(spl::GenerateAesKey(dst, dst_size, GetNcaKekAccessKey(key_type), src, src_size));
|
||||
}
|
||||
|
||||
void DecryptAesCtr(void *dst, size_t dst_size, s32 key_type, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||
void DecryptAesCtr(void *dst, size_t dst_size, u8 key_index, u8 key_generation, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||
std::unique_ptr<KeySlotCacheAccessor> accessor;
|
||||
|
||||
const s32 key_type = GetKeyTypeValue(key_index, key_generation);
|
||||
|
||||
R_TRY_CATCH(g_key_slot_cache.Find(std::addressof(accessor), enc_key, enc_key_size, key_type)) {
|
||||
R_CATCH(fs::ResultTargetNotFound) {
|
||||
R_ABORT_UNLESS(g_key_slot_cache.AllocateHighPriority(std::addressof(accessor), enc_key, enc_key_size, key_type));
|
||||
|
@ -148,10 +148,11 @@ namespace ams::fssystem {
|
|||
R_ABORT_UNLESS(spl::ComputeCtr(dst, dst_size, accessor->GetKeySlotIndex(), src, src_size, iv, iv_size));
|
||||
}
|
||||
|
||||
void DecryptAesCtrForPreparedKey(void *dst, size_t dst_size, s32 key_type, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||
void DecryptAesCtrForPreparedKey(void *dst, size_t dst_size, u8 key_index, u8 key_generation, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||
std::unique_ptr<KeySlotCacheAccessor> accessor;
|
||||
|
||||
key_type = static_cast<s32>(KeyType::NcaExternalKey);
|
||||
AMS_UNUSED(key_index, key_generation);
|
||||
const s32 key_type = static_cast<s32>(KeyType::NcaExternalKey);
|
||||
|
||||
R_TRY_CATCH(g_key_slot_cache.Find(std::addressof(accessor), enc_key, enc_key_size, key_type)) {
|
||||
R_CATCH(fs::ResultTargetNotFound) {
|
||||
|
@ -167,6 +168,15 @@ namespace ams::fssystem {
|
|||
R_ABORT_UNLESS(spl::ComputeCtr(dst, dst_size, accessor->GetKeySlotIndex(), src, src_size, iv, iv_size));
|
||||
}
|
||||
|
||||
bool VerifySign1(const void *sig, size_t sig_size, const void *data, size_t data_size, u8 generation, const NcaCryptoConfiguration &cfg) {
|
||||
const u8 *mod = cfg.header_1_sign_key_moduli[generation];
|
||||
const size_t mod_size = NcaCryptoConfiguration::Rsa2048KeyModulusSize;
|
||||
const u8 *exp = cfg.header_1_sign_key_public_exponent;
|
||||
const size_t exp_size = NcaCryptoConfiguration::Rsa2048KeyPublicExponentSize;
|
||||
|
||||
return crypto::VerifyRsa2048PssSha256(sig, sig_size, mod, mod_size, exp, exp_size, data, data_size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const ::ams::fssystem::NcaCryptoConfiguration *GetNcaCryptoConfiguration(bool prod) {
|
||||
|
@ -176,9 +186,13 @@ namespace ams::fssystem {
|
|||
|
||||
/* Set the key generation functions. */
|
||||
cfg->generate_key = GenerateNcaKey;
|
||||
cfg->decrypt_aes_xts_external = nullptr;
|
||||
cfg->encrypt_aes_xts_external = nullptr;
|
||||
cfg->decrypt_aes_ctr = DecryptAesCtr;
|
||||
cfg->decrypt_aes_ctr_external = DecryptAesCtrForPreparedKey;
|
||||
cfg->verify_sign1 = VerifySign1;
|
||||
cfg->is_plaintext_header_available = !prod;
|
||||
cfg->is_available_sw_key = true;
|
||||
|
||||
/* TODO: Should this default to false for host tools with api to set explicitly? */
|
||||
#if !defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
|
@ -204,7 +218,7 @@ namespace ams::fssystem {
|
|||
}
|
||||
|
||||
/* Setup the header encryption key. */
|
||||
R_ABORT_UNLESS(spl::GenerateAesKek(std::addressof(GetNcaKekAccessKey(static_cast<s32>(KeyType::NcaHeaderKey))), nca_crypto_cfg->header_encryption_key_source, KeySize, 0, Option));
|
||||
R_ABORT_UNLESS(spl::GenerateAesKek(std::addressof(GetNcaKekAccessKey(static_cast<s32>(KeyType::NcaHeaderKey1))), nca_crypto_cfg->header_encryption_key_source, KeySize, 0, Option));
|
||||
}
|
||||
|
||||
/* TODO FS-REIMPL: Save stuff. */
|
||||
|
|
|
@ -23,11 +23,6 @@ namespace ams::fssystem {
|
|||
constexpr inline u32 IntegrityVerificationStorageVersion = 0x00020000;
|
||||
constexpr inline u32 IntegrityVerificationStorageVersionMask = 0xFFFF0000;
|
||||
|
||||
constexpr inline auto MaxSaveDataFsDataCacheEntryCount = 32;
|
||||
constexpr inline auto MaxSaveDataFsHashCacheEntryCount = 4;
|
||||
constexpr inline auto MaxRomFsDataCacheEntryCount = 24;
|
||||
constexpr inline auto MaxRomFsHashCacheEntryCount = 8;
|
||||
|
||||
constexpr inline auto AccessCountMax = 5;
|
||||
constexpr inline auto AccessTimeout = TimeSpan::FromMilliSeconds(10);
|
||||
|
||||
|
@ -92,7 +87,7 @@ namespace ams::fssystem {
|
|||
out->layered_hash_sizes[level - 1] = level_size[level];
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result HierarchicalIntegrityVerificationStorageControlArea::Expand(fs::SubStorage meta_storage, const HierarchicalIntegrityVerificationMetaInformation &meta) {
|
||||
|
@ -122,7 +117,7 @@ namespace ams::fssystem {
|
|||
R_TRY(meta_storage.Write(0, std::addressof(meta), sizeof(meta)));
|
||||
R_TRY(meta_storage.Flush());
|
||||
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result HierarchicalIntegrityVerificationStorageControlArea::Initialize(fs::SubStorage meta_storage) {
|
||||
|
@ -143,36 +138,37 @@ namespace ams::fssystem {
|
|||
/* Validate the meta version. */
|
||||
R_UNLESS((m_meta.version & IntegrityVerificationStorageVersionMask) == (IntegrityVerificationStorageVersion & IntegrityVerificationStorageVersionMask), fs::ResultUnsupportedVersion());
|
||||
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void HierarchicalIntegrityVerificationStorageControlArea::Finalize() {
|
||||
m_storage = fs::SubStorage();
|
||||
}
|
||||
|
||||
Result HierarchicalIntegrityVerificationStorage::Initialize(const HierarchicalIntegrityVerificationInformation &info, HierarchicalStorageInformation storage, FileSystemBufferManagerSet *bufs, IHash256GeneratorFactory *hgf, os::SdkRecursiveMutex *mtx, fs::StorageType storage_type) {
|
||||
Result HierarchicalIntegrityVerificationStorage::Initialize(const HierarchicalIntegrityVerificationInformation &info, HierarchicalStorageInformation storage, FileSystemBufferManagerSet *bufs, IHash256GeneratorFactory *hgf, bool hash_salt_enabled, os::SdkRecursiveMutex *mtx, os::Semaphore *read_sema, os::Semaphore *write_sema, int max_data_cache_entries, int max_hash_cache_entries, s8 buffer_level, bool is_writable, bool allow_cleared_blocks) {
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(bufs != nullptr);
|
||||
AMS_ASSERT(IntegrityMinLayerCount <= info.max_layers && info.max_layers <= IntegrityMaxLayerCount);
|
||||
|
||||
/* Set member variables. */
|
||||
m_max_layers = info.max_layers;
|
||||
m_buffers = bufs;
|
||||
m_mutex = mtx;
|
||||
m_max_layers = info.max_layers;
|
||||
m_buffers = bufs;
|
||||
m_mutex = mtx;
|
||||
m_read_semaphore = read_sema;
|
||||
m_write_semaphore = write_sema;
|
||||
|
||||
/* Determine our cache counts. */
|
||||
const auto max_data_cache_entry_count = (storage_type == fs::StorageType_SaveData) ? MaxSaveDataFsDataCacheEntryCount : MaxRomFsDataCacheEntryCount;
|
||||
const auto max_hash_cache_entry_count = (storage_type == fs::StorageType_SaveData) ? MaxSaveDataFsHashCacheEntryCount : MaxRomFsHashCacheEntryCount;
|
||||
|
||||
/* Initialize the top level verification storage. */
|
||||
{
|
||||
fs::HashSalt mac;
|
||||
crypto::GenerateHmacSha256(mac.value, sizeof(mac), info.seed.value, sizeof(info.seed), KeyArray[0].key, KeyArray[0].size);
|
||||
m_verify_storages[0].Initialize(storage[HierarchicalStorageInformation::MasterStorage], storage[HierarchicalStorageInformation::Layer1Storage], static_cast<s64>(1) << info.info[0].block_order, HashSize, m_buffers->buffers[m_max_layers - 2], hgf, mac, false, storage_type);
|
||||
/* If hash salt is enabled, generate it. */
|
||||
util::optional<fs::HashSalt> hash_salt = util::nullopt;
|
||||
if (hash_salt_enabled) {
|
||||
hash_salt.emplace();
|
||||
crypto::GenerateHmacSha256(hash_salt->value, sizeof(hash_salt->value), info.seed.value, sizeof(info.seed), KeyArray[0].key, KeyArray[0].size);
|
||||
}
|
||||
|
||||
/* Initialize the top level verification storage. */
|
||||
m_verify_storages[0].Initialize(storage[HierarchicalStorageInformation::MasterStorage], storage[HierarchicalStorageInformation::Layer1Storage], static_cast<s64>(1) << info.info[0].block_order, HashSize, m_buffers->buffers[m_max_layers - 2], hgf, hash_salt, false, is_writable, allow_cleared_blocks);
|
||||
|
||||
/* Ensure we don't leak state if further initialization goes wrong. */
|
||||
auto top_verif_guard = SCOPE_GUARD {
|
||||
ON_RESULT_FAILURE {
|
||||
m_verify_storages[0].Finalize();
|
||||
|
||||
m_data_size = -1;
|
||||
|
@ -181,14 +177,14 @@ namespace ams::fssystem {
|
|||
};
|
||||
|
||||
/* Initialize the top level buffer storage. */
|
||||
R_TRY(m_buffer_storages[0].Initialize(m_buffers->buffers[0], m_mutex, std::addressof(m_verify_storages[0]), info.info[0].size, static_cast<s64>(1) << info.info[0].block_order, max_hash_cache_entry_count, false, 0x10, false, storage_type));
|
||||
auto top_buffer_guard = SCOPE_GUARD { m_buffer_storages[0].Finalize(); };
|
||||
R_TRY(m_buffer_storages[0].Initialize(m_buffers->buffers[0], m_mutex, std::addressof(m_verify_storages[0]), info.info[0].size, static_cast<s64>(1) << info.info[0].block_order, max_hash_cache_entries, false, 0x10, false, is_writable));
|
||||
ON_RESULT_FAILURE_2 { m_buffer_storages[0].Finalize(); };
|
||||
|
||||
/* Prepare to initialize the level storages. */
|
||||
s32 level = 0;
|
||||
|
||||
/* Ensure we don't leak state if further initialization goes wrong. */
|
||||
auto level_guard = SCOPE_GUARD {
|
||||
ON_RESULT_FAILURE_2 {
|
||||
m_verify_storages[level + 1].Finalize();
|
||||
for (/* ... */; level > 0; --level) {
|
||||
m_buffer_storages[level].Finalize();
|
||||
|
@ -198,40 +194,43 @@ namespace ams::fssystem {
|
|||
|
||||
/* Initialize the level storages. */
|
||||
for (/* ... */; level < m_max_layers - 3; ++level) {
|
||||
/* Initialize the verification storage. */
|
||||
{
|
||||
fs::SubStorage buffer_storage(std::addressof(m_buffer_storages[level]), 0, info.info[level].size);
|
||||
fs::HashSalt mac;
|
||||
crypto::GenerateHmacSha256(mac.value, sizeof(mac), info.seed.value, sizeof(info.seed), KeyArray[level + 1].key, KeyArray[level + 1].size);
|
||||
m_verify_storages[level + 1].Initialize(buffer_storage, storage[level + 2], static_cast<s64>(1) << info.info[level + 1].block_order, static_cast<s64>(1) << info.info[level].block_order, m_buffers->buffers[m_max_layers - 2], hgf, mac, false, storage_type);
|
||||
/* If hash salt is enabled, generate it. */
|
||||
util::optional<fs::HashSalt> hash_salt = util::nullopt;
|
||||
if (hash_salt_enabled) {
|
||||
hash_salt.emplace();
|
||||
crypto::GenerateHmacSha256(hash_salt->value, sizeof(hash_salt->value), info.seed.value, sizeof(info.seed), KeyArray[level + 1].key, KeyArray[level + 1].size);
|
||||
}
|
||||
|
||||
/* Initialize the verification storage. */
|
||||
fs::SubStorage buffer_storage(std::addressof(m_buffer_storages[level]), 0, info.info[level].size);
|
||||
m_verify_storages[level + 1].Initialize(buffer_storage, storage[level + 2], static_cast<s64>(1) << info.info[level + 1].block_order, static_cast<s64>(1) << info.info[level].block_order, m_buffers->buffers[m_max_layers - 2], hgf, hash_salt, false, is_writable, allow_cleared_blocks);
|
||||
|
||||
/* Initialize the buffer storage. */
|
||||
R_TRY(m_buffer_storages[level + 1].Initialize(m_buffers->buffers[level + 1], m_mutex, std::addressof(m_verify_storages[level + 1]), info.info[level + 1].size, static_cast<s64>(1) << info.info[level + 1].block_order, max_hash_cache_entry_count, false, 0x11 + static_cast<s8>(level), false, storage_type));
|
||||
R_TRY(m_buffer_storages[level + 1].Initialize(m_buffers->buffers[level + 1], m_mutex, std::addressof(m_verify_storages[level + 1]), info.info[level + 1].size, static_cast<s64>(1) << info.info[level + 1].block_order, max_hash_cache_entries, false, 0x11 + static_cast<s8>(level), false, is_writable));
|
||||
}
|
||||
|
||||
/* Initialize the final level storage. */
|
||||
{
|
||||
/* Initialize the verification storage. */
|
||||
{
|
||||
fs::SubStorage buffer_storage(std::addressof(m_buffer_storages[level]), 0, info.info[level].size);
|
||||
fs::HashSalt mac;
|
||||
crypto::GenerateHmacSha256(mac.value, sizeof(mac), info.seed.value, sizeof(info.seed), KeyArray[level + 1].key, KeyArray[level + 1].size);
|
||||
m_verify_storages[level + 1].Initialize(buffer_storage, storage[level + 2], static_cast<s64>(1) << info.info[level + 1].block_order, static_cast<s64>(1) << info.info[level].block_order, m_buffers->buffers[m_max_layers - 2], hgf, mac, true, storage_type);
|
||||
/* If hash salt is enabled, generate it. */
|
||||
util::optional<fs::HashSalt> hash_salt = util::nullopt;
|
||||
if (hash_salt_enabled) {
|
||||
hash_salt.emplace();
|
||||
crypto::GenerateHmacSha256(hash_salt->value, sizeof(hash_salt->value), info.seed.value, sizeof(info.seed), KeyArray[level + 1].key, KeyArray[level + 1].size);
|
||||
}
|
||||
|
||||
/* Initialize the verification storage. */
|
||||
fs::SubStorage buffer_storage(std::addressof(m_buffer_storages[level]), 0, info.info[level].size);
|
||||
m_verify_storages[level + 1].Initialize(buffer_storage, storage[level + 2], static_cast<s64>(1) << info.info[level + 1].block_order, static_cast<s64>(1) << info.info[level].block_order, m_buffers->buffers[m_max_layers - 2], hgf, hash_salt, true, is_writable, allow_cleared_blocks);
|
||||
|
||||
/* Initialize the buffer storage. */
|
||||
R_TRY(m_buffer_storages[level + 1].Initialize(m_buffers->buffers[level + 1], m_mutex, std::addressof(m_verify_storages[level + 1]), info.info[level + 1].size, static_cast<s64>(1) << info.info[level + 1].block_order, max_data_cache_entry_count, true, 0x11 + static_cast<s8>(level), true, storage_type));
|
||||
R_TRY(m_buffer_storages[level + 1].Initialize(m_buffers->buffers[level + 1], m_mutex, std::addressof(m_verify_storages[level + 1]), info.info[level + 1].size, static_cast<s64>(1) << info.info[level + 1].block_order, max_data_cache_entries, true, buffer_level, true, is_writable));
|
||||
}
|
||||
|
||||
/* Set the data size. */
|
||||
m_data_size = info.info[level + 1].size;
|
||||
|
||||
/* We succeeded. */
|
||||
level_guard.Cancel();
|
||||
top_buffer_guard.Cancel();
|
||||
top_verif_guard.Cancel();
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void HierarchicalIntegrityVerificationStorage::Finalize() {
|
||||
|
@ -260,7 +259,11 @@ namespace ams::fssystem {
|
|||
/* Validate arguments. */
|
||||
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* Acquire access to the read semaphore. */
|
||||
/* If we have a read semaphore, acquire it. */
|
||||
if (m_read_semaphore != nullptr) { m_read_semaphore->Acquire(); }
|
||||
ON_SCOPE_EXIT { if (m_read_semaphore != nullptr) { m_read_semaphore->Release(); } };
|
||||
|
||||
/* Acquire access to the global read semaphore. */
|
||||
if (!g_read_semaphore.TimedAcquire(AccessTimeout)) {
|
||||
for (auto level = m_max_layers - 2; level >= 0; --level) {
|
||||
R_TRY(m_buffer_storages[level].Flush());
|
||||
|
@ -272,8 +275,7 @@ namespace ams::fssystem {
|
|||
ON_SCOPE_EXIT { g_read_semaphore.Release(); };
|
||||
|
||||
/* Read the data. */
|
||||
R_TRY(m_buffer_storages[m_max_layers - 2].Read(offset, buffer, size));
|
||||
return ResultSuccess();
|
||||
R_RETURN(m_buffer_storages[m_max_layers - 2].Read(offset, buffer, size));
|
||||
}
|
||||
|
||||
Result HierarchicalIntegrityVerificationStorage::Write(s64 offset, const void *buffer, size_t size) {
|
||||
|
@ -286,6 +288,10 @@ namespace ams::fssystem {
|
|||
/* Validate arguments. */
|
||||
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* If we have a write semaphore, acquire it. */
|
||||
if (m_write_semaphore != nullptr) { m_write_semaphore->Acquire(); }
|
||||
ON_SCOPE_EXIT { if (m_write_semaphore != nullptr) { m_write_semaphore->Release(); } };
|
||||
|
||||
/* Acquire access to the write semaphore. */
|
||||
if (!g_write_semaphore.TimedAcquire(AccessTimeout)) {
|
||||
for (auto level = m_max_layers - 2; level >= 0; --level) {
|
||||
|
@ -298,20 +304,18 @@ namespace ams::fssystem {
|
|||
ON_SCOPE_EXIT { g_write_semaphore.Release(); };
|
||||
|
||||
/* Write the data. */
|
||||
R_TRY(m_buffer_storages[m_max_layers - 2].Write(offset, buffer, size));
|
||||
m_is_written_for_rollback = true;
|
||||
return ResultSuccess();
|
||||
R_RETURN(m_buffer_storages[m_max_layers - 2].Write(offset, buffer, size));
|
||||
}
|
||||
|
||||
Result HierarchicalIntegrityVerificationStorage::GetSize(s64 *out) {
|
||||
AMS_ASSERT(out != nullptr);
|
||||
AMS_ASSERT(m_data_size >= 0);
|
||||
*out = m_data_size;
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result HierarchicalIntegrityVerificationStorage::Flush() {
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result HierarchicalIntegrityVerificationStorage::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
|
||||
|
@ -320,17 +324,16 @@ namespace ams::fssystem {
|
|||
case fs::OperationId::DestroySignature:
|
||||
{
|
||||
R_TRY(m_buffer_storages[m_max_layers - 2].OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
m_is_written_for_rollback = true;
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
case fs::OperationId::Invalidate:
|
||||
case fs::OperationId::QueryRange:
|
||||
{
|
||||
R_TRY(m_buffer_storages[m_max_layers - 2].OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
default:
|
||||
return fs::ResultUnsupportedOperateRangeForHierarchicalIntegrityVerificationStorage();
|
||||
R_THROW(fs::ResultUnsupportedOperateRangeForHierarchicalIntegrityVerificationStorage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -338,15 +341,14 @@ namespace ams::fssystem {
|
|||
for (s32 level = m_max_layers - 2; level >= 0; --level) {
|
||||
R_TRY(m_buffer_storages[level].Commit());
|
||||
}
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result HierarchicalIntegrityVerificationStorage::OnRollback() {
|
||||
for (s32 level = m_max_layers - 2; level >= 0; --level) {
|
||||
R_TRY(m_buffer_storages[level].OnRollback());
|
||||
}
|
||||
m_is_written_for_rollback = false;
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
namespace ams::fssystem {
|
||||
|
||||
Result IntegrityRomFsStorage::Initialize(HierarchicalIntegrityVerificationInformation level_hash_info, Hash master_hash, HierarchicalIntegrityVerificationStorage::HierarchicalStorageInformation storage_info, fs::IBufferManager *bm, IHash256GeneratorFactory *hgf) {
|
||||
Result IntegrityRomFsStorage::Initialize(HierarchicalIntegrityVerificationInformation level_hash_info, Hash master_hash, HierarchicalIntegrityVerificationStorage::HierarchicalStorageInformation storage_info, fs::IBufferManager *bm, int max_data_cache_entries, int max_hash_cache_entries, s8 buffer_level, IHash256GeneratorFactory *hgf) {
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(bm != nullptr);
|
||||
|
||||
|
@ -35,7 +35,7 @@ namespace ams::fssystem {
|
|||
}
|
||||
|
||||
/* Initialize our integrity storage. */
|
||||
return m_integrity_storage.Initialize(level_hash_info, storage_info, std::addressof(m_buffers), hgf, std::addressof(m_mutex), fs::StorageType_RomFs);
|
||||
R_RETURN(m_integrity_storage.Initialize(level_hash_info, storage_info, std::addressof(m_buffers), hgf, false, std::addressof(m_mutex), max_data_cache_entries, max_hash_cache_entries, buffer_level, false, false));
|
||||
}
|
||||
|
||||
void IntegrityRomFsStorage::Finalize() {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
namespace ams::fssystem {
|
||||
|
||||
Result IntegrityVerificationStorage::Initialize(fs::SubStorage hs, fs::SubStorage ds, s64 verif_block_size, s64 upper_layer_verif_block_size, fs::IBufferManager *bm, fssystem::IHash256GeneratorFactory *hgf, const fs::HashSalt &salt, bool is_real_data, fs::StorageType storage_type) {
|
||||
Result IntegrityVerificationStorage::Initialize(fs::SubStorage hs, fs::SubStorage ds, s64 verif_block_size, s64 upper_layer_verif_block_size, fs::IBufferManager *bm, fssystem::IHash256GeneratorFactory *hgf, const util::optional<fs::HashSalt> &salt, bool is_real_data, bool is_writable, bool allow_cleared_blocks) {
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(verif_block_size >= HashSize);
|
||||
AMS_ASSERT(bm != nullptr);
|
||||
|
@ -49,18 +49,19 @@ namespace ams::fssystem {
|
|||
s64 hash_size = 0;
|
||||
s64 data_size = 0;
|
||||
AMS_ASSERT(R_SUCCEEDED(m_hash_storage.GetSize(std::addressof(hash_size))));
|
||||
AMS_ASSERT(R_SUCCEEDED(m_data_storage.GetSize(std::addressof(hash_size))));
|
||||
AMS_ASSERT(R_SUCCEEDED(m_data_storage.GetSize(std::addressof(data_size))));
|
||||
AMS_ASSERT(((hash_size / HashSize) * m_verification_block_size) >= data_size);
|
||||
AMS_UNUSED(hash_size, data_size);
|
||||
}
|
||||
|
||||
/* Set salt. */
|
||||
std::memcpy(m_salt.value, salt.value, fs::HashSalt::Size);
|
||||
m_salt = salt;
|
||||
|
||||
/* Set data and storage type. */
|
||||
m_is_real_data = is_real_data;
|
||||
m_storage_type = storage_type;
|
||||
return ResultSuccess();
|
||||
/* Set data, writable, and allow cleared. */
|
||||
m_is_real_data = is_real_data;
|
||||
m_is_writable = is_writable;
|
||||
m_allow_cleared_blocks = allow_cleared_blocks;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void IntegrityVerificationStorage::Finalize() {
|
||||
|
@ -146,7 +147,7 @@ namespace ams::fssystem {
|
|||
std::memset(cur_buf, 0, m_verification_block_size);
|
||||
|
||||
/* Set the result if we should. */
|
||||
if (!fs::ResultClearedRealDataVerificationFailed::Includes(cur_result) && m_storage_type != fs::StorageType_Authoring) {
|
||||
if (!fs::ResultClearedRealDataVerificationFailed::Includes(cur_result) && !m_allow_cleared_blocks) {
|
||||
verify_hash_result = cur_result;
|
||||
}
|
||||
|
||||
|
@ -157,14 +158,14 @@ namespace ams::fssystem {
|
|||
/* If we failed, clear and return. */
|
||||
if (R_FAILED(cur_result)) {
|
||||
std::memset(buffer, 0, size);
|
||||
return cur_result;
|
||||
R_THROW(cur_result);
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
verified_count += cur_count;
|
||||
}
|
||||
|
||||
return verify_hash_result;
|
||||
R_RETURN(verify_hash_result);
|
||||
}
|
||||
|
||||
Result IntegrityVerificationStorage::Write(s64 offset, const void *buffer, size_t size) {
|
||||
|
@ -246,30 +247,32 @@ namespace ams::fssystem {
|
|||
/* Write the data. */
|
||||
R_TRY(m_data_storage.Write(offset, buffer, std::min(write_size, updated_count << m_verification_block_order)));
|
||||
|
||||
return update_result;
|
||||
R_RETURN(update_result);
|
||||
}
|
||||
|
||||
Result IntegrityVerificationStorage::GetSize(s64 *out) {
|
||||
return m_data_storage.GetSize(out);
|
||||
R_RETURN(m_data_storage.GetSize(out));
|
||||
}
|
||||
|
||||
Result IntegrityVerificationStorage::Flush() {
|
||||
/* Flush both storages. */
|
||||
R_TRY(m_hash_storage.Flush());
|
||||
R_TRY(m_data_storage.Flush());
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IntegrityVerificationStorage::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(util::IsAligned(offset, static_cast<size_t>(m_verification_block_size)));
|
||||
AMS_ASSERT(util::IsAligned(size, static_cast<size_t>(m_verification_block_size)));
|
||||
if (op_id != fs::OperationId::Invalidate) {
|
||||
AMS_ASSERT(util::IsAligned(offset, static_cast<size_t>(m_verification_block_size)));
|
||||
AMS_ASSERT(util::IsAligned(size, static_cast<size_t>(m_verification_block_size)));
|
||||
}
|
||||
|
||||
switch (op_id) {
|
||||
case fs::OperationId::FillZero:
|
||||
{
|
||||
/* Clear should only be called for save data. */
|
||||
AMS_ASSERT(m_storage_type == fs::StorageType_SaveData);
|
||||
/* FillZero should only be called for writable storages. */
|
||||
AMS_ASSERT(m_is_writable);
|
||||
|
||||
/* Validate the range. */
|
||||
s64 data_size = 0;
|
||||
|
@ -297,12 +300,12 @@ namespace ams::fssystem {
|
|||
remaining_size -= cur_size;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
case fs::OperationId::DestroySignature:
|
||||
{
|
||||
/* Clear Signature should only be called for save data. */
|
||||
AMS_ASSERT(m_storage_type == fs::StorageType_SaveData);
|
||||
/* DestroySignature should only be called for save data. */
|
||||
AMS_ASSERT(m_is_writable);
|
||||
|
||||
/* Validate the range. */
|
||||
s64 data_size = 0;
|
||||
|
@ -327,19 +330,19 @@ namespace ams::fssystem {
|
|||
}
|
||||
|
||||
/* Write the cleared signature. */
|
||||
return m_hash_storage.Write(sign_offset, buf.get(), sign_size);
|
||||
R_RETURN(m_hash_storage.Write(sign_offset, buf.get(), sign_size));
|
||||
}
|
||||
case fs::OperationId::Invalidate:
|
||||
{
|
||||
/* Only allow cache invalidation for RomFs. */
|
||||
R_UNLESS(m_storage_type != fs::StorageType_SaveData, fs::ResultUnsupportedOperateRangeForNonSaveDataIntegrityVerificationStorage());
|
||||
/* Only allow cache invalidation read-only storages. */
|
||||
R_UNLESS(!m_is_writable, fs::ResultUnsupportedOperateRangeForWritableIntegrityVerificationStorage());
|
||||
|
||||
|
||||
/* Operate on our storages. */
|
||||
R_TRY(m_hash_storage.OperateRange(dst, dst_size, op_id, 0, std::numeric_limits<s64>::max(), src, src_size));
|
||||
R_TRY(m_data_storage.OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
R_TRY(m_hash_storage.OperateRange(op_id, 0, std::numeric_limits<s64>::max()));
|
||||
R_TRY(m_data_storage.OperateRange(op_id, offset, size));
|
||||
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
case fs::OperationId::QueryRange:
|
||||
{
|
||||
|
@ -352,31 +355,37 @@ namespace ams::fssystem {
|
|||
const auto actual_size = std::min(size, data_size - offset);
|
||||
|
||||
/* Query the data storage. */
|
||||
R_TRY(m_data_storage.OperateRange(dst, dst_size, op_id, offset, actual_size, src, src_size));
|
||||
|
||||
return ResultSuccess();
|
||||
R_RETURN(m_data_storage.OperateRange(dst, dst_size, op_id, offset, actual_size, src, src_size));
|
||||
}
|
||||
default:
|
||||
return fs::ResultUnsupportedOperateRangeForIntegrityVerificationStorage();
|
||||
R_THROW(fs::ResultUnsupportedOperateRangeForIntegrityVerificationStorage());
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrityVerificationStorage::CalcBlockHash(BlockHash *out, const void *buffer, size_t block_size, std::unique_ptr<fssystem::IHash256Generator> &generator) const {
|
||||
/* Initialize the generator. */
|
||||
generator->Initialize();
|
||||
/* Hash procedure depends on whether or not we're writable. */
|
||||
if (m_is_writable) {
|
||||
/* Compute the hash with or without the hash salt, if we have one. */
|
||||
if (m_salt.has_value()) {
|
||||
/* Initialize the generator. */
|
||||
generator->Initialize();
|
||||
|
||||
/* If calculating for save data, hash the salt. */
|
||||
if (m_storage_type == fs::StorageType_SaveData) {
|
||||
generator->Update(m_salt.value, sizeof(m_salt));
|
||||
}
|
||||
/* Hash the salt. */
|
||||
generator->Update(m_salt->value, sizeof(m_salt->value));
|
||||
|
||||
/* Update with the buffer and get the hash. */
|
||||
generator->Update(buffer, block_size);
|
||||
generator->GetHash(out, sizeof(*out));
|
||||
/* Update with the buffer and get the hash. */
|
||||
generator->Update(buffer, block_size);
|
||||
generator->GetHash(out, sizeof(*out));
|
||||
} else {
|
||||
/* If we have no hash salt, just calculate the hash. */
|
||||
m_hash_generator_factory->GenerateHash(out, sizeof(*out), buffer, block_size);
|
||||
}
|
||||
|
||||
/* Set the validation bit, if the hash is for save data. */
|
||||
if (m_storage_type == fs::StorageType_SaveData) {
|
||||
/* Set the validation bit. */
|
||||
SetValidationBit(out);
|
||||
} else {
|
||||
/* If we're not writable, just calculate the hash. */
|
||||
m_hash_generator_factory->GenerateHash(out, sizeof(*out), buffer, block_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -407,7 +416,7 @@ namespace ams::fssystem {
|
|||
|
||||
/* We succeeded. */
|
||||
clear_guard.Cancel();
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IntegrityVerificationStorage::WriteBlockSignature(const void *src, size_t src_size, s64 offset, size_t size) {
|
||||
|
@ -425,7 +434,7 @@ namespace ams::fssystem {
|
|||
R_TRY(m_hash_storage.Write(sign_offset, src, sign_size));
|
||||
|
||||
/* We succeeded. */
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IntegrityVerificationStorage::VerifyHash(const void *buf, BlockHash *hash, std::unique_ptr<fssystem::IHash256Generator> &generator) {
|
||||
|
@ -436,8 +445,8 @@ namespace ams::fssystem {
|
|||
/* Get the comparison hash. */
|
||||
auto &cmp_hash = *hash;
|
||||
|
||||
/* If save data, check if the data is uninitialized. */
|
||||
if (m_storage_type == fs::StorageType_SaveData) {
|
||||
/* If writable, check if the data is uninitialized. */
|
||||
if (m_is_writable) {
|
||||
bool is_cleared = false;
|
||||
R_TRY(this->IsCleared(std::addressof(is_cleared), cmp_hash));
|
||||
R_UNLESS(!is_cleared, fs::ResultClearedRealDataVerificationFailed());
|
||||
|
@ -454,19 +463,19 @@ namespace ams::fssystem {
|
|||
|
||||
/* Return the appropriate result. */
|
||||
if (m_is_real_data) {
|
||||
return fs::ResultUnclearedRealDataVerificationFailed();
|
||||
R_THROW(fs::ResultUnclearedRealDataVerificationFailed());
|
||||
} else {
|
||||
return fs::ResultNonRealDataVerificationFailed();
|
||||
R_THROW(fs::ResultNonRealDataVerificationFailed());
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IntegrityVerificationStorage::IsCleared(bool *is_cleared, const BlockHash &hash) {
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(is_cleared != nullptr);
|
||||
AMS_ASSERT(m_storage_type == fs::StorageType_SaveData);
|
||||
AMS_ASSERT(m_is_writable);
|
||||
|
||||
/* Default to uncleared. */
|
||||
*is_cleared = false;
|
||||
|
@ -481,7 +490,7 @@ namespace ams::fssystem {
|
|||
|
||||
/* Set cleared. */
|
||||
*is_cleared = true;
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,12 @@ namespace ams::fssystem {
|
|||
constexpr inline s32 SparseTableCacheBlockSize = SparseStorage::NodeSize;
|
||||
constexpr inline s32 SparseTableCacheCount = 4;
|
||||
|
||||
constexpr inline s32 IntegrityDataCacheCount = 24;
|
||||
constexpr inline s32 IntegrityHashCacheCount = 8;
|
||||
|
||||
constexpr inline s32 IntegrityDataCacheCountForMeta = 16;
|
||||
constexpr inline s32 IntegrityHashCacheCountForMeta = 2;
|
||||
|
||||
//TODO: Better names for these?
|
||||
//constexpr inline s32 CompressedDataBlockSize = 64_KB;
|
||||
//constexpr inline s32 CompressedContinuousReadingSizeMax = 640_KB;
|
||||
|
@ -56,21 +62,21 @@ namespace ams::fssystem {
|
|||
AMS_ASSERT(m_storage != nullptr);
|
||||
|
||||
/* Read from the base storage. */
|
||||
return m_storage->Read(offset, buffer, size);
|
||||
R_RETURN(m_storage->Read(offset, buffer, size));
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_storage != nullptr);
|
||||
|
||||
return m_storage->GetSize(out);
|
||||
R_RETURN(m_storage->GetSize(out));
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_storage != nullptr);
|
||||
|
||||
return m_storage->Flush();
|
||||
R_RETURN(m_storage->Flush());
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
|
@ -78,200 +84,21 @@ namespace ams::fssystem {
|
|||
AMS_ASSERT(m_storage != nullptr);
|
||||
|
||||
/* Read from the base storage. */
|
||||
return m_storage->Write(offset, buffer, size);
|
||||
R_RETURN(m_storage->Write(offset, buffer, size));
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_storage != nullptr);
|
||||
|
||||
return m_storage->SetSize(size);
|
||||
R_RETURN(m_storage->SetSize(size));
|
||||
}
|
||||
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_storage != nullptr);
|
||||
|
||||
return m_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size);
|
||||
}
|
||||
};
|
||||
|
||||
class AesCtrStorageExternal : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
||||
NON_COPYABLE(AesCtrStorageExternal);
|
||||
NON_MOVEABLE(AesCtrStorageExternal);
|
||||
public:
|
||||
static constexpr size_t BlockSize = crypto::Aes128CtrEncryptor::BlockSize;
|
||||
static constexpr size_t KeySize = crypto::Aes128CtrEncryptor::KeySize;
|
||||
static constexpr size_t IvSize = crypto::Aes128CtrEncryptor::IvSize;
|
||||
private:
|
||||
std::shared_ptr<fs::IStorage> m_base_storage;
|
||||
u8 m_iv[IvSize];
|
||||
DecryptAesCtrFunction m_decrypt_function;
|
||||
s32 m_key_index;
|
||||
u8 m_encrypted_key[KeySize];
|
||||
public:
|
||||
AesCtrStorageExternal(std::shared_ptr<fs::IStorage> bs, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, DecryptAesCtrFunction df, s32 kidx) : m_base_storage(std::move(bs)), m_decrypt_function(df), m_key_index(kidx) {
|
||||
AMS_ASSERT(m_base_storage != nullptr);
|
||||
AMS_ASSERT(enc_key_size == KeySize);
|
||||
AMS_ASSERT(iv != nullptr);
|
||||
AMS_ASSERT(iv_size == IvSize);
|
||||
AMS_UNUSED(iv_size);
|
||||
|
||||
std::memcpy(m_iv, iv, IvSize);
|
||||
std::memcpy(m_encrypted_key, enc_key, enc_key_size);
|
||||
}
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
/* Allow zero size. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Validate arguments. */
|
||||
/* NOTE: For some reason, Nintendo uses InvalidArgument instead of InvalidOffset/InvalidSize here. */
|
||||
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
|
||||
R_UNLESS(util::IsAligned(offset, BlockSize), fs::ResultInvalidArgument());
|
||||
R_UNLESS(util::IsAligned(size, BlockSize), fs::ResultInvalidArgument());
|
||||
|
||||
/* Read the data. */
|
||||
R_TRY(m_base_storage->Read(offset, buffer, size));
|
||||
|
||||
/* Temporarily increase our thread priority. */
|
||||
ScopedThreadPriorityChanger cp(+1, ScopedThreadPriorityChanger::Mode::Relative);
|
||||
|
||||
/* Allocate a pooled buffer for decryption. */
|
||||
PooledBuffer pooled_buffer;
|
||||
pooled_buffer.AllocateParticularlyLarge(size, BlockSize);
|
||||
AMS_ASSERT(pooled_buffer.GetSize() >= BlockSize);
|
||||
|
||||
/* Setup the counter. */
|
||||
u8 ctr[IvSize];
|
||||
std::memcpy(ctr, m_iv, IvSize);
|
||||
AddCounter(ctr, IvSize, offset / BlockSize);
|
||||
|
||||
/* Setup tracking. */
|
||||
size_t remaining_size = size;
|
||||
s64 cur_offset = 0;
|
||||
|
||||
while (remaining_size > 0) {
|
||||
/* Get the current size to process. */
|
||||
size_t cur_size = std::min(pooled_buffer.GetSize(), remaining_size);
|
||||
char *dst = static_cast<char *>(buffer) + cur_offset;
|
||||
|
||||
/* Decrypt into the temporary buffer */
|
||||
m_decrypt_function(pooled_buffer.GetBuffer(), cur_size, m_key_index, m_encrypted_key, KeySize, ctr, IvSize, dst, cur_size);
|
||||
|
||||
/* Copy to the destination. */
|
||||
std::memcpy(dst, pooled_buffer.GetBuffer(), cur_size);
|
||||
|
||||
/* Update tracking. */
|
||||
cur_offset += cur_size;
|
||||
remaining_size -= cur_size;
|
||||
|
||||
if (remaining_size > 0) {
|
||||
AddCounter(ctr, IvSize, cur_size / BlockSize);
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
|
||||
switch (op_id) {
|
||||
case fs::OperationId::QueryRange:
|
||||
{
|
||||
/* Validate that we have an output range info. */
|
||||
R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
|
||||
R_UNLESS(dst_size == sizeof(fs::QueryRangeInfo), fs::ResultInvalidSize());
|
||||
|
||||
/* Operate on our base storage. */
|
||||
R_TRY(m_base_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
|
||||
/* Add in new flags. */
|
||||
fs::QueryRangeInfo new_info;
|
||||
new_info.Clear();
|
||||
new_info.aes_ctr_key_type = static_cast<s32>(m_key_index >= 0 ? fs::AesCtrKeyTypeFlag::InternalKeyForHardwareAes : fs::AesCtrKeyTypeFlag::ExternalKeyForHardwareAes);
|
||||
|
||||
/* Merge the new info in. */
|
||||
reinterpret_cast<fs::QueryRangeInfo *>(dst)->Merge(new_info);
|
||||
return ResultSuccess();
|
||||
}
|
||||
default:
|
||||
{
|
||||
/* Operate on our base storage. */
|
||||
R_TRY(m_base_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
return m_base_storage->GetSize(out);
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
AMS_UNUSED(offset, buffer, size);
|
||||
return fs::ResultUnsupportedWriteForAesCtrStorageExternal();
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
AMS_UNUSED(size);
|
||||
return fs::ResultUnsupportedSetSizeForAesCtrStorageExternal();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
class SwitchStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
||||
NON_COPYABLE(SwitchStorage);
|
||||
NON_MOVEABLE(SwitchStorage);
|
||||
private:
|
||||
std::shared_ptr<fs::IStorage> m_true_storage;
|
||||
std::shared_ptr<fs::IStorage> m_false_storage;
|
||||
F m_truth_function;
|
||||
private:
|
||||
ALWAYS_INLINE std::shared_ptr<fs::IStorage> &SelectStorage() {
|
||||
return m_truth_function() ? m_true_storage : m_false_storage;
|
||||
}
|
||||
public:
|
||||
SwitchStorage(std::shared_ptr<fs::IStorage> t, std::shared_ptr<fs::IStorage> f, F func) : m_true_storage(std::move(t)), m_false_storage(std::move(f)), m_truth_function(func) { /* ... */ }
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
return this->SelectStorage()->Read(offset, buffer, size);
|
||||
}
|
||||
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
|
||||
switch (op_id) {
|
||||
case fs::OperationId::Invalidate:
|
||||
{
|
||||
R_TRY(m_true_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
R_TRY(m_false_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
return ResultSuccess();
|
||||
}
|
||||
case fs::OperationId::QueryRange:
|
||||
{
|
||||
R_TRY(this->SelectStorage()->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
return ResultSuccess();
|
||||
}
|
||||
default:
|
||||
return fs::ResultUnsupportedOperateRangeForSwitchStorage();
|
||||
}
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
return this->SelectStorage()->GetSize(out);
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
return this->SelectStorage()->Flush();
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
return this->SelectStorage()->Write(offset, buffer, size);
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
return this->SelectStorage()->SetSize(size);
|
||||
R_RETURN(m_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -307,7 +134,7 @@ namespace ams::fssystem {
|
|||
R_UNLESS(*out_splitter != nullptr, fs::ResultAllocationMemoryFailedAllocateShared());
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::OpenStorageImpl(std::shared_ptr<fs::IStorage> *out, NcaFsHeaderReader *out_header_reader, s32 fs_index, StorageContext *ctx) {
|
||||
|
@ -328,8 +155,17 @@ namespace ams::fssystem {
|
|||
/* Process sparse layer. */
|
||||
s64 fs_data_offset = 0;
|
||||
if (out_header_reader->ExistsSparseLayer()) {
|
||||
/* Create the sparse storage. */
|
||||
R_TRY(this->CreateSparseStorage(std::addressof(storage), std::addressof(fs_data_offset), ctx != nullptr ? std::addressof(ctx->current_sparse_storage) : nullptr, ctx != nullptr ? std::addressof(ctx->sparse_storage_meta_storage) : nullptr, fs_index, out_header_reader->GetAesCtrUpperIv(), out_header_reader->GetSparseInfo()));
|
||||
/* Get the sparse info. */
|
||||
const auto &sparse_info = out_header_reader->GetSparseInfo();
|
||||
|
||||
/* Create based on whether we have a meta hash layer. */
|
||||
if (out_header_reader->ExistsSparseMetaHashLayer()) {
|
||||
/* Create the sparse storage with verification. */
|
||||
R_TRY(this->CreateSparseStorageWithVerification(std::addressof(storage), std::addressof(fs_data_offset), ctx != nullptr ? std::addressof(ctx->current_sparse_storage) : nullptr, ctx != nullptr ? std::addressof(ctx->sparse_storage_meta_storage) : nullptr, ctx != nullptr ? std::addressof(ctx->sparse_layer_info_storage) : nullptr, fs_index, out_header_reader->GetAesCtrUpperIv(), sparse_info, out_header_reader->GetSparseMetaDataHashDataInfo(), out_header_reader->GetSparseMetaHashType()));
|
||||
} else {
|
||||
/* Create the sparse storage. */
|
||||
R_TRY(this->CreateSparseStorage(std::addressof(storage), std::addressof(fs_data_offset), ctx != nullptr ? std::addressof(ctx->current_sparse_storage) : nullptr, ctx != nullptr ? std::addressof(ctx->sparse_storage_meta_storage) : nullptr, fs_index, out_header_reader->GetAesCtrUpperIv(), sparse_info));
|
||||
}
|
||||
} else {
|
||||
/* Get the data offsets. */
|
||||
fs_data_offset = GetFsOffset(*m_reader, fs_index);
|
||||
|
@ -350,13 +186,28 @@ namespace ams::fssystem {
|
|||
|
||||
/* Process patch layer. */
|
||||
const auto &patch_info = out_header_reader->GetPatchInfo();
|
||||
std::shared_ptr<fs::IStorage> patch_meta_aes_ctr_ex_meta_storage;
|
||||
std::shared_ptr<fs::IStorage> patch_meta_indirect_meta_storage;
|
||||
if (out_header_reader->ExistsPatchMetaHashLayer()) {
|
||||
/* Check the meta hash type. */
|
||||
R_UNLESS(out_header_reader->GetPatchMetaHashType() == NcaFsHeader::MetaDataHashType::HierarchicalIntegrity, fs::ResultRomNcaInvalidPatchMetaDataHashType());
|
||||
|
||||
/* Create the patch meta storage. */
|
||||
R_TRY(this->CreatePatchMetaStorage(std::addressof(patch_meta_aes_ctr_ex_meta_storage), std::addressof(patch_meta_indirect_meta_storage), ctx != nullptr ? std::addressof(ctx->patch_layer_info_storage) : nullptr, storage, fs_data_offset, out_header_reader->GetAesCtrUpperIv(), patch_info, out_header_reader->GetPatchMetaDataHashDataInfo(), m_hash_generator_factory_selector->GetFactory(fssystem::HashAlgorithmType_Sha2)));
|
||||
}
|
||||
|
||||
if (patch_info.HasAesCtrExTable()) {
|
||||
/* Check the encryption type. */
|
||||
AMS_ASSERT(out_header_reader->GetEncryptionType() == NcaFsHeader::EncryptionType::AesCtrEx);
|
||||
AMS_ASSERT(out_header_reader->GetEncryptionType() == NcaFsHeader::EncryptionType::None || out_header_reader->GetEncryptionType() == NcaFsHeader::EncryptionType::AesCtrEx || out_header_reader->GetEncryptionType() == NcaFsHeader::EncryptionType::AesCtrExSkipLayerHash);
|
||||
|
||||
/* Create the ex meta storage. */
|
||||
std::shared_ptr<fs::IStorage> aes_ctr_ex_storage_meta_storage;
|
||||
R_TRY(this->CreateAesCtrExMetaStorage(std::addressof(aes_ctr_ex_storage_meta_storage), storage, fs_data_offset, out_header_reader->GetAesCtrUpperIv(), patch_info));
|
||||
std::shared_ptr<fs::IStorage> aes_ctr_ex_storage_meta_storage = patch_meta_aes_ctr_ex_meta_storage;
|
||||
if (aes_ctr_ex_storage_meta_storage == nullptr) {
|
||||
/* If we don't have a meta storage, we must not have a patch meta hash layer. */
|
||||
AMS_ASSERT(!out_header_reader->ExistsPatchMetaHashLayer());
|
||||
|
||||
R_TRY(this->CreateAesCtrExStorageMetaStorage(std::addressof(aes_ctr_ex_storage_meta_storage), storage, fs_data_offset, out_header_reader->GetEncryptionType(), out_header_reader->GetAesCtrUpperIv(), patch_info));
|
||||
}
|
||||
|
||||
/* Create the ex storage. */
|
||||
std::shared_ptr<fs::IStorage> aes_ctr_ex_storage;
|
||||
|
@ -383,8 +234,18 @@ namespace ams::fssystem {
|
|||
case NcaFsHeader::EncryptionType::AesCtr:
|
||||
R_TRY(this->CreateAesCtrStorage(std::addressof(storage), std::move(storage), fs_data_offset, out_header_reader->GetAesCtrUpperIv(), AlignmentStorageRequirement_None));
|
||||
break;
|
||||
case NcaFsHeader::EncryptionType::AesCtrSkipLayerHash:
|
||||
{
|
||||
/* Create the aes ctr storage. */
|
||||
std::shared_ptr<fs::IStorage> aes_ctr_storage;
|
||||
R_TRY(this->CreateAesCtrStorage(std::addressof(aes_ctr_storage), storage, fs_data_offset, out_header_reader->GetAesCtrUpperIv(), AlignmentStorageRequirement_None));
|
||||
|
||||
/* Create region switch storage. */
|
||||
R_TRY(this->CreateRegionSwitchStorage(std::addressof(storage), out_header_reader, std::move(storage), std::move(aes_ctr_storage)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return fs::ResultInvalidNcaFsHeaderEncryptionType();
|
||||
R_THROW(fs::ResultInvalidNcaFsHeaderEncryptionType());
|
||||
}
|
||||
|
||||
/* Potentially save storages to our context. */
|
||||
|
@ -396,8 +257,13 @@ namespace ams::fssystem {
|
|||
/* Process indirect layer. */
|
||||
if (patch_info.HasIndirectTable()) {
|
||||
/* Create the indirect meta storage */
|
||||
std::shared_ptr<fs::IStorage> indirect_storage_meta_storage;
|
||||
R_TRY(this->CreateIndirectStorageMetaStorage(std::addressof(indirect_storage_meta_storage), storage, patch_info));
|
||||
std::shared_ptr<fs::IStorage> indirect_storage_meta_storage = patch_meta_indirect_meta_storage;
|
||||
if (indirect_storage_meta_storage == nullptr) {
|
||||
/* If we don't have a meta storage, we must not have a patch meta hash layer. */
|
||||
AMS_ASSERT(!out_header_reader->ExistsPatchMetaHashLayer());
|
||||
|
||||
R_TRY(this->CreateIndirectStorageMetaStorage(std::addressof(indirect_storage_meta_storage), storage, patch_info));
|
||||
}
|
||||
|
||||
/* Potentially save the indirect meta storage to our context. */
|
||||
if (ctx != nullptr) {
|
||||
|
@ -436,7 +302,7 @@ namespace ams::fssystem {
|
|||
/* Check if we're sparse or requested to skip the integrity layer. */
|
||||
if (out_header_reader->ExistsSparseLayer() || (ctx != nullptr && ctx->open_raw_storage)) {
|
||||
*out = std::move(storage);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Create the non-raw storage. */
|
||||
|
@ -456,7 +322,7 @@ namespace ams::fssystem {
|
|||
R_TRY(this->CreateIntegrityVerificationStorage(std::addressof(storage), std::move(storage), header_reader->GetHashData().integrity_meta_info, m_hash_generator_factory_selector->GetFactory(fssystem::HashAlgorithmType_Sha2)));
|
||||
break;
|
||||
default:
|
||||
return fs::ResultInvalidNcaFsHeaderHashType();
|
||||
R_THROW(fs::ResultInvalidNcaFsHeaderHashType());
|
||||
}
|
||||
|
||||
/* Process compression layer. */
|
||||
|
@ -466,7 +332,7 @@ namespace ams::fssystem {
|
|||
|
||||
/* Set output storage. */
|
||||
*out = std::move(storage);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::OpenIndirectableStorageAsOriginal(std::shared_ptr<fs::IStorage> *out, const NcaFsHeaderReader *header_reader, StorageContext *ctx) {
|
||||
|
@ -479,8 +345,17 @@ namespace ams::fssystem {
|
|||
/* Process sparse layer. */
|
||||
s64 fs_data_offset = 0;
|
||||
if (header_reader->ExistsSparseLayer()) {
|
||||
/* Create the sparse storage. */
|
||||
R_TRY(this->CreateSparseStorage(std::addressof(storage), std::addressof(fs_data_offset), ctx != nullptr ? std::addressof(ctx->original_sparse_storage) : nullptr, ctx != nullptr ? std::addressof(ctx->sparse_storage_meta_storage) : nullptr, fs_index, header_reader->GetAesCtrUpperIv(), header_reader->GetSparseInfo()));
|
||||
/* Get the sparse info. */
|
||||
const auto &sparse_info = header_reader->GetSparseInfo();
|
||||
|
||||
/* Create based on whether we have a meta hash layer. */
|
||||
if (header_reader->ExistsSparseMetaHashLayer()) {
|
||||
/* Create the sparse storage with verification. */
|
||||
R_TRY(this->CreateSparseStorageWithVerification(std::addressof(storage), std::addressof(fs_data_offset), ctx != nullptr ? std::addressof(ctx->original_sparse_storage) : nullptr, ctx != nullptr ? std::addressof(ctx->sparse_storage_meta_storage) : nullptr, ctx != nullptr ? std::addressof(ctx->sparse_layer_info_storage) : nullptr, fs_index, header_reader->GetAesCtrUpperIv(), sparse_info, header_reader->GetSparseMetaDataHashDataInfo(), header_reader->GetSparseMetaHashType()));
|
||||
} else {
|
||||
/* Create the sparse storage. */
|
||||
R_TRY(this->CreateSparseStorage(std::addressof(storage), std::addressof(fs_data_offset), ctx != nullptr ? std::addressof(ctx->original_sparse_storage) : nullptr, ctx != nullptr ? std::addressof(ctx->sparse_storage_meta_storage) : nullptr, fs_index, header_reader->GetAesCtrUpperIv(), sparse_info));
|
||||
}
|
||||
} else {
|
||||
/* Get the data offsets. */
|
||||
fs_data_offset = GetFsOffset(*m_reader, fs_index);
|
||||
|
@ -506,12 +381,12 @@ namespace ams::fssystem {
|
|||
R_TRY(this->CreateAesCtrStorage(std::addressof(storage), std::move(storage), fs_data_offset, header_reader->GetAesCtrUpperIv(), AlignmentStorageRequirement_CacheBlockSize));
|
||||
break;
|
||||
default:
|
||||
return fs::ResultInvalidNcaFsHeaderEncryptionType();
|
||||
R_THROW(fs::ResultInvalidNcaFsHeaderEncryptionType());
|
||||
}
|
||||
|
||||
/* Set output storage. */
|
||||
*out = std::move(storage);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateBodySubStorage(std::shared_ptr<fs::IStorage> *out, s64 offset, s64 size) {
|
||||
|
@ -532,7 +407,7 @@ namespace ams::fssystem {
|
|||
|
||||
/* Set the output storage. */
|
||||
*out = std::move(body_substorage);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateAesCtrStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, AlignmentStorageRequirement alignment_storage_requirement) {
|
||||
|
@ -575,7 +450,7 @@ namespace ams::fssystem {
|
|||
/* Create the ctr storage. */
|
||||
std::shared_ptr<fs::IStorage> aes_ctr_storage;
|
||||
if (m_reader->HasExternalDecryptionKey()) {
|
||||
aes_ctr_storage = fssystem::AllocateShared<AesCtrStorageExternal>(std::move(base_storage), m_reader->GetExternalDecryptionKey(), AesCtrStorageExternal::KeySize, iv, AesCtrStorageExternal::IvSize, m_reader->GetExternalDecryptAesCtrFunctionForExternalKey(), -1);
|
||||
aes_ctr_storage = fssystem::AllocateShared<AesCtrStorageExternal>(std::move(base_storage), m_reader->GetExternalDecryptionKey(), AesCtrStorageExternal::KeySize, iv, AesCtrStorageExternal::IvSize, m_reader->GetExternalDecryptAesCtrFunctionForExternalKey(), -1, -1);
|
||||
R_UNLESS(aes_ctr_storage != nullptr, fs::ResultAllocationMemoryFailedAllocateShared());
|
||||
} else {
|
||||
/* Create software decryption storage. */
|
||||
|
@ -584,7 +459,7 @@ namespace ams::fssystem {
|
|||
|
||||
/* If we have a hardware key and should use it, make the hardware decryption storage. */
|
||||
if (m_reader->HasInternalDecryptionKeyForAesHw() && !m_reader->IsSoftwareAesPrioritized()) {
|
||||
auto hw_storage = fssystem::AllocateShared<AesCtrStorageExternal>(base_storage, m_reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesCtrHw), AesCtrStorageExternal::KeySize, iv, AesCtrStorageExternal::IvSize, m_reader->GetExternalDecryptAesCtrFunction(), GetKeyTypeValue(m_reader->GetKeyIndex(), m_reader->GetKeyGeneration()));
|
||||
auto hw_storage = fssystem::AllocateShared<AesCtrStorageExternal>(base_storage, m_reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesCtrHw), AesCtrStorageExternal::KeySize, iv, AesCtrStorageExternal::IvSize, m_reader->GetExternalDecryptAesCtrFunction(), m_reader->GetKeyIndex(), m_reader->GetKeyGeneration());
|
||||
R_UNLESS(hw_storage != nullptr, fs::ResultAllocationMemoryFailedAllocateShared());
|
||||
|
||||
/* Create the selection storage. */
|
||||
|
@ -605,7 +480,7 @@ namespace ams::fssystem {
|
|||
|
||||
/* Set the out storage. */
|
||||
*out = std::move(aligned_storage);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateAesXtsStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 offset) {
|
||||
|
@ -629,7 +504,7 @@ namespace ams::fssystem {
|
|||
|
||||
/* Set the out storage. */
|
||||
*out = std::move(aligned_storage);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateSparseStorageMetaStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info) {
|
||||
|
@ -663,7 +538,7 @@ namespace ams::fssystem {
|
|||
|
||||
/* Set the output. */
|
||||
*out = std::move(meta_storage);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateSparseStorageCore(std::shared_ptr<fssystem::SparseStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 base_size, std::shared_ptr<fs::IStorage> meta_storage, const NcaSparseInfo &sparse_info, bool external_info) {
|
||||
|
@ -700,7 +575,7 @@ namespace ams::fssystem {
|
|||
|
||||
/* Set the output. */
|
||||
*out = std::move(sparse_storage);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateSparseStorage(std::shared_ptr<fs::IStorage> *out, s64 *out_fs_data_offset, std::shared_ptr<fssystem::SparseStorage> *out_sparse_storage, std::shared_ptr<fs::IStorage> *out_meta_storage, s32 index, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info) {
|
||||
|
@ -757,13 +632,125 @@ namespace ams::fssystem {
|
|||
|
||||
/* Set the output storage. */
|
||||
*out = std::move(sparse_storage);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateAesCtrExMetaStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info) {
|
||||
Result NcaFileSystemDriver::CreateSparseStorageMetaStorageWithVerification(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> *out_layer_info_storage, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info, const NcaMetaDataHashDataInfo &meta_data_hash_data_info, IHash256GeneratorFactory *hgf) {
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(out != nullptr);
|
||||
AMS_ASSERT(base_storage != nullptr);
|
||||
AMS_ASSERT(hgf != nullptr);
|
||||
|
||||
/* Get the base storage size. */
|
||||
s64 base_size = 0;
|
||||
R_TRY(base_storage->GetSize(std::addressof(base_size)));
|
||||
|
||||
/* Get the meta extents. */
|
||||
const auto meta_offset = sparse_info.bucket.offset;
|
||||
const auto meta_size = sparse_info.bucket.size;
|
||||
R_UNLESS(meta_offset + meta_size - offset <= base_size, fs::ResultNcaBaseStorageOutOfRangeB());
|
||||
|
||||
/* Get the meta data hash data extents. */
|
||||
const s64 meta_data_hash_data_offset = meta_data_hash_data_info.offset;
|
||||
const s64 meta_data_hash_data_size = util::AlignUp<s64>(meta_data_hash_data_info.size, NcaHeader::CtrBlockSize);
|
||||
R_UNLESS(meta_data_hash_data_offset + meta_data_hash_data_size <= base_size, fs::ResultNcaBaseStorageOutOfRangeB());
|
||||
|
||||
/* Check that the meta is before the hash data. */
|
||||
R_UNLESS(meta_offset + meta_size <= meta_data_hash_data_offset, fs::ResultRomNcaInvalidSparseMetaDataHashDataOffset());
|
||||
|
||||
/* Check that offsets are appropriately aligned. */
|
||||
R_UNLESS(util::IsAligned<s64>(meta_data_hash_data_offset, NcaHeader::CtrBlockSize), fs::ResultRomNcaInvalidSparseMetaDataHashDataOffset());
|
||||
R_UNLESS(util::IsAligned<s64>(meta_offset, NcaHeader::CtrBlockSize), fs::ResultInvalidNcaFsHeader());
|
||||
|
||||
/* Create the meta storage. */
|
||||
auto enc_storage = fssystem::AllocateShared<fs::SubStorage>(std::move(base_storage), meta_offset, meta_data_hash_data_offset + meta_data_hash_data_size - meta_offset);
|
||||
R_UNLESS(enc_storage != nullptr, fs::ResultAllocationMemoryFailedAllocateShared());
|
||||
|
||||
/* Create the decrypted storage. */
|
||||
std::shared_ptr<fs::IStorage> decrypted_storage;
|
||||
R_TRY(this->CreateAesCtrStorage(std::addressof(decrypted_storage), std::move(enc_storage), offset + meta_offset, sparse_info.MakeAesCtrUpperIv(upper_iv), AlignmentStorageRequirement_None));
|
||||
|
||||
/* Create the verification storage. */
|
||||
std::shared_ptr<fs::IStorage> integrity_storage;
|
||||
R_TRY_CATCH(this->CreateIntegrityVerificationStorageForMeta(std::addressof(integrity_storage), out_layer_info_storage, std::move(decrypted_storage), meta_offset, meta_data_hash_data_info, hgf)) {
|
||||
R_CONVERT(fs::ResultInvalidNcaMetaDataHashDataSize, fs::ResultRomNcaInvalidSparseMetaDataHashDataSize())
|
||||
R_CONVERT(fs::ResultInvalidNcaMetaDataHashDataHash, fs::ResultRomNcaInvalidSparseMetaDataHashDataHash())
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* Create the meta storage. */
|
||||
auto meta_storage = fssystem::AllocateShared<fs::SubStorage>(std::move(integrity_storage), 0, meta_size);
|
||||
R_UNLESS(meta_storage != nullptr, fs::ResultAllocationMemoryFailedAllocateShared());
|
||||
|
||||
/* Set the output. */
|
||||
*out = std::move(meta_storage);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateSparseStorageWithVerification(std::shared_ptr<fs::IStorage> *out, s64 *out_fs_data_offset, std::shared_ptr<fssystem::SparseStorage> *out_sparse_storage, std::shared_ptr<fs::IStorage> *out_meta_storage, std::shared_ptr<fs::IStorage> *out_layer_info_storage, s32 index, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info, const NcaMetaDataHashDataInfo &meta_data_hash_data_info, NcaFsHeader::MetaDataHashType meta_data_hash_type) {
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(out != nullptr);
|
||||
AMS_ASSERT(out_fs_data_offset != nullptr);
|
||||
|
||||
/* Check the sparse info generation. */
|
||||
R_UNLESS(sparse_info.generation != 0, fs::ResultInvalidNcaHeader());
|
||||
|
||||
/* Read and verify the bucket tree header. */
|
||||
BucketTree::Header header;
|
||||
std::memcpy(std::addressof(header), sparse_info.bucket.header, sizeof(header));
|
||||
R_TRY(header.Verify());
|
||||
|
||||
/* Determine the storage extents. */
|
||||
const auto fs_offset = GetFsOffset(*m_reader, index);
|
||||
const auto fs_end_offset = GetFsEndOffset(*m_reader, index);
|
||||
const auto fs_size = fs_end_offset - fs_offset;
|
||||
|
||||
/* Create the sparse storage. */
|
||||
std::shared_ptr<fssystem::SparseStorage> sparse_storage;
|
||||
if (header.entry_count != 0) {
|
||||
/* Create the body substorage. */
|
||||
std::shared_ptr<fs::IStorage> body_substorage;
|
||||
R_TRY(this->CreateBodySubStorage(std::addressof(body_substorage), sparse_info.physical_offset, util::AlignUp<s64>(static_cast<s64>(meta_data_hash_data_info.offset) + static_cast<s64>(meta_data_hash_data_info.size), NcaHeader::CtrBlockSize)));
|
||||
|
||||
/* Check the meta data hash type. */
|
||||
R_UNLESS(meta_data_hash_type == NcaFsHeader::MetaDataHashType::HierarchicalIntegrity, fs::ResultRomNcaInvalidSparseMetaDataHashType());
|
||||
|
||||
/* Create the meta storage. */
|
||||
std::shared_ptr<fs::IStorage> meta_storage;
|
||||
R_TRY(this->CreateSparseStorageMetaStorageWithVerification(std::addressof(meta_storage), out_layer_info_storage, body_substorage, sparse_info.physical_offset, upper_iv, sparse_info, meta_data_hash_data_info, m_hash_generator_factory_selector->GetFactory(fssystem::HashAlgorithmType_Sha2)));
|
||||
|
||||
/* Potentially set the output meta storage. */
|
||||
if (out_meta_storage != nullptr) {
|
||||
*out_meta_storage = meta_storage;
|
||||
}
|
||||
|
||||
/* Create the sparse storage. */
|
||||
R_TRY(this->CreateSparseStorageCore(std::addressof(sparse_storage), body_substorage, sparse_info.GetPhysicalSize(), std::move(meta_storage), sparse_info, false));
|
||||
} else {
|
||||
/* If there are no entries, there's nothing to actually do. */
|
||||
sparse_storage = fssystem::AllocateShared<fssystem::SparseStorage>();
|
||||
R_UNLESS(sparse_storage != nullptr, fs::ResultAllocationMemoryFailedAllocateShared());
|
||||
|
||||
sparse_storage->Initialize(fs_size);
|
||||
}
|
||||
|
||||
/* Potentially set the output sparse storage. */
|
||||
if (out_sparse_storage != nullptr) {
|
||||
*out_sparse_storage = sparse_storage;
|
||||
}
|
||||
|
||||
/* Set the output fs data offset. */
|
||||
*out_fs_data_offset = fs_offset;
|
||||
|
||||
/* Set the output storage. */
|
||||
*out = std::move(sparse_storage);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateAesCtrExStorageMetaStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 offset, NcaFsHeader::EncryptionType encryption_type, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info) {
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(out != nullptr);
|
||||
AMS_ASSERT(base_storage != nullptr);
|
||||
AMS_ASSERT(encryption_type == NcaFsHeader::EncryptionType::None || encryption_type == NcaFsHeader::EncryptionType::AesCtrEx || encryption_type == NcaFsHeader::EncryptionType::AesCtrExSkipLayerHash);
|
||||
AMS_ASSERT(patch_info.HasAesCtrExTable());
|
||||
|
||||
/* Validate patch info extents. */
|
||||
|
@ -786,7 +773,12 @@ namespace ams::fssystem {
|
|||
|
||||
/* Create the decrypted storage. */
|
||||
std::shared_ptr<fs::IStorage> decrypted_storage;
|
||||
R_TRY(this->CreateAesCtrStorage(std::addressof(decrypted_storage), std::move(enc_storage), offset + meta_offset, upper_iv, AlignmentStorageRequirement_None));
|
||||
if (encryption_type != NcaFsHeader::EncryptionType::None) {
|
||||
R_TRY(this->CreateAesCtrStorage(std::addressof(decrypted_storage), std::move(enc_storage), offset + meta_offset, upper_iv, AlignmentStorageRequirement_None));
|
||||
} else {
|
||||
/* If encryption type is none, don't do any decryption. */
|
||||
decrypted_storage = std::move(enc_storage);
|
||||
}
|
||||
|
||||
/* Create meta storage. */
|
||||
auto meta_storage = fssystem::AllocateShared<BufferedStorage>();
|
||||
|
@ -802,7 +794,7 @@ namespace ams::fssystem {
|
|||
|
||||
/* Set the output. */
|
||||
*out = std::move(aligned_storage);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateAesCtrExStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::AesCtrCounterExtendedStorage> *out_ext, std::shared_ptr<fs::IStorage> base_storage, std::shared_ptr<fs::IStorage> meta_storage, s64 counter_offset, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info) {
|
||||
|
@ -839,7 +831,7 @@ namespace ams::fssystem {
|
|||
if (m_reader->HasExternalDecryptionKey()) {
|
||||
/* Create the decryptor. */
|
||||
std::unique_ptr<AesCtrCounterExtendedStorage::IDecryptor> decryptor;
|
||||
R_TRY(AesCtrCounterExtendedStorage::CreateExternalDecryptor(std::addressof(decryptor), m_reader->GetExternalDecryptAesCtrFunctionForExternalKey(), -1));
|
||||
R_TRY(AesCtrCounterExtendedStorage::CreateExternalDecryptor(std::addressof(decryptor), m_reader->GetExternalDecryptAesCtrFunctionForExternalKey(), -1, -1));
|
||||
|
||||
/* Create the aes ctr ex storage. */
|
||||
auto impl_storage = fssystem::AllocateShared<AesCtrCounterExtendedStorage>();
|
||||
|
@ -876,7 +868,7 @@ namespace ams::fssystem {
|
|||
if (m_reader->HasInternalDecryptionKeyForAesHw() && !m_reader->IsSoftwareAesPrioritized()) {
|
||||
/* Create the hardware decryptor. */
|
||||
std::unique_ptr<AesCtrCounterExtendedStorage::IDecryptor> hw_decryptor;
|
||||
R_TRY(AesCtrCounterExtendedStorage::CreateExternalDecryptor(std::addressof(hw_decryptor), m_reader->GetExternalDecryptAesCtrFunction(), GetKeyTypeValue(m_reader->GetKeyIndex(), m_reader->GetKeyGeneration())));
|
||||
R_TRY(AesCtrCounterExtendedStorage::CreateExternalDecryptor(std::addressof(hw_decryptor), m_reader->GetExternalDecryptAesCtrFunction(), m_reader->GetKeyIndex(), m_reader->GetKeyGeneration()));
|
||||
|
||||
/* Create the hardware storage. */
|
||||
auto hw_storage = fssystem::AllocateShared<AesCtrCounterExtendedStorage>();
|
||||
|
@ -904,7 +896,7 @@ namespace ams::fssystem {
|
|||
|
||||
/* Set the output. */
|
||||
*out = std::move(aligned_storage);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateIndirectStorageMetaStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, const NcaPatchInfo &patch_info) {
|
||||
|
@ -929,7 +921,7 @@ namespace ams::fssystem {
|
|||
|
||||
/* Set the output. */
|
||||
*out = std::move(meta_storage);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateIndirectStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::IndirectStorage> *out_ind, std::shared_ptr<fs::IStorage> base_storage, std::shared_ptr<fs::IStorage> original_data_storage, std::shared_ptr<fs::IStorage> meta_storage, const NcaPatchInfo &patch_info) {
|
||||
|
@ -985,7 +977,64 @@ namespace ams::fssystem {
|
|||
|
||||
/* Set the output. */
|
||||
*out = std::move(indirect_storage);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreatePatchMetaStorage(std::shared_ptr<fs::IStorage> *out_aes_ctr_ex_meta, std::shared_ptr<fs::IStorage> *out_indirect_meta, std::shared_ptr<fs::IStorage> *out_layer_info_storage, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info, const NcaMetaDataHashDataInfo &meta_data_hash_data_info, IHash256GeneratorFactory *hgf) {
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(out_aes_ctr_ex_meta != nullptr);
|
||||
AMS_ASSERT(out_indirect_meta != nullptr);
|
||||
AMS_ASSERT(base_storage != nullptr);
|
||||
AMS_ASSERT(patch_info.HasAesCtrExTable());
|
||||
AMS_ASSERT(patch_info.HasIndirectTable());
|
||||
AMS_ASSERT(util::IsAligned<s64>(patch_info.aes_ctr_ex_size, NcaHeader::XtsBlockSize));
|
||||
|
||||
/* Validate patch info extents. */
|
||||
R_UNLESS(patch_info.indirect_size > 0, fs::ResultInvalidNcaPatchInfoIndirectSize());
|
||||
R_UNLESS(patch_info.aes_ctr_ex_size >= 0, fs::ResultInvalidNcaPatchInfoAesCtrExSize());
|
||||
R_UNLESS(patch_info.indirect_size + patch_info.indirect_offset <= patch_info.aes_ctr_ex_offset, fs::ResultInvalidNcaPatchInfoAesCtrExOffset());
|
||||
R_UNLESS(patch_info.aes_ctr_ex_offset + patch_info.aes_ctr_ex_size <= meta_data_hash_data_info.offset, fs::ResultRomNcaInvalidPatchMetaDataHashDataOffset());
|
||||
|
||||
/* Get the base storage size. */
|
||||
s64 base_size;
|
||||
R_TRY(base_storage->GetSize(std::addressof(base_size)));
|
||||
|
||||
/* Check that extents remain within range. */
|
||||
R_UNLESS(patch_info.indirect_offset + patch_info.indirect_size <= base_size, fs::ResultNcaBaseStorageOutOfRangeE());
|
||||
R_UNLESS(patch_info.aes_ctr_ex_offset + patch_info.aes_ctr_ex_size <= base_size, fs::ResultNcaBaseStorageOutOfRangeB());
|
||||
|
||||
/* Check that metadata hash data extents remain within range. */
|
||||
const s64 meta_data_hash_data_offset = meta_data_hash_data_info.offset;
|
||||
const s64 meta_data_hash_data_size = util::AlignUp<s64>(meta_data_hash_data_info.size, NcaHeader::CtrBlockSize);
|
||||
R_UNLESS(meta_data_hash_data_offset + meta_data_hash_data_size <= base_size, fs::ResultNcaBaseStorageOutOfRangeB());
|
||||
|
||||
/* Create the encrypted storage. */
|
||||
auto enc_storage = fssystem::AllocateShared<fs::SubStorage>(std::move(base_storage), patch_info.indirect_offset, meta_data_hash_data_offset + meta_data_hash_data_size - patch_info.indirect_offset);
|
||||
R_UNLESS(enc_storage != nullptr, fs::ResultAllocationMemoryFailedAllocateShared());
|
||||
|
||||
/* Create the decrypted storage. */
|
||||
std::shared_ptr<fs::IStorage> decrypted_storage;
|
||||
R_TRY(this->CreateAesCtrStorage(std::addressof(decrypted_storage), std::move(enc_storage), offset + patch_info.indirect_offset, upper_iv, AlignmentStorageRequirement_None));
|
||||
|
||||
/* Create the verification storage. */
|
||||
std::shared_ptr<fs::IStorage> integrity_storage;
|
||||
R_TRY_CATCH(this->CreateIntegrityVerificationStorageForMeta(std::addressof(integrity_storage), out_layer_info_storage, std::move(decrypted_storage), patch_info.indirect_offset, meta_data_hash_data_info, hgf)) {
|
||||
R_CONVERT(fs::ResultInvalidNcaMetaDataHashDataSize, fs::ResultRomNcaInvalidPatchMetaDataHashDataSize())
|
||||
R_CONVERT(fs::ResultInvalidNcaMetaDataHashDataHash, fs::ResultRomNcaInvalidPatchMetaDataHashDataHash())
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* Create the indirect meta storage. */
|
||||
auto indirect_meta_storage = fssystem::AllocateShared<fs::SubStorage>(integrity_storage, patch_info.indirect_offset - patch_info.indirect_offset, patch_info.indirect_size);
|
||||
R_UNLESS(indirect_meta_storage != nullptr, fs::ResultAllocationMemoryFailedAllocateShared());
|
||||
|
||||
/* Create the aes ctr ex meta storage. */
|
||||
auto aes_ctr_ex_meta_storage = fssystem::AllocateShared<fs::SubStorage>(integrity_storage, patch_info.aes_ctr_ex_offset - patch_info.indirect_offset, patch_info.aes_ctr_ex_size);
|
||||
R_UNLESS(aes_ctr_ex_meta_storage != nullptr, fs::ResultAllocationMemoryFailedAllocateShared());
|
||||
|
||||
/* Set the output. */
|
||||
*out_aes_ctr_ex_meta = std::move(aes_ctr_ex_meta_storage);
|
||||
*out_indirect_meta = std::move(indirect_meta_storage);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateSha256Storage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, const NcaFsHeader::HashData::HierarchicalSha256Data &hash_data, IHash256GeneratorFactory *hgf) {
|
||||
|
@ -1052,13 +1101,50 @@ namespace ams::fssystem {
|
|||
|
||||
/* Set the output. */
|
||||
*out = std::move(aligned_storage);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateIntegrityVerificationStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, const NcaFsHeader::HashData::IntegrityMetaInfo &meta_info, IHash256GeneratorFactory *hgf) {
|
||||
R_RETURN(this->CreateIntegrityVerificationStorageImpl(out, base_storage, meta_info, 0, IntegrityDataCacheCount, IntegrityHashCacheCount, fssystem::HierarchicalIntegrityVerificationStorage::GetDefaultDataCacheBufferLevel(meta_info.level_hash_info.max_layers), hgf));
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateIntegrityVerificationStorageForMeta(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> *out_layer_info_storage, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaMetaDataHashDataInfo &meta_data_hash_data_info, IHash256GeneratorFactory *hgf) {
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(out != nullptr);
|
||||
|
||||
/* Check the meta data hash data size. */
|
||||
R_UNLESS(meta_data_hash_data_info.size == sizeof(NcaMetaDataHashData), fs::ResultInvalidNcaMetaDataHashDataSize());
|
||||
|
||||
/* Read the meta data hash data. */
|
||||
NcaMetaDataHashData meta_data_hash_data;
|
||||
R_TRY(base_storage->Read(meta_data_hash_data_info.offset - offset, std::addressof(meta_data_hash_data), sizeof(meta_data_hash_data)));
|
||||
|
||||
/* Check the meta data hash data hash. */
|
||||
u8 meta_data_hash_data_hash[IHash256Generator::HashSize];
|
||||
m_hash_generator_factory_selector->GetFactory(fssystem::HashAlgorithmType_Sha2)->GenerateHash(meta_data_hash_data_hash, sizeof(meta_data_hash_data_hash), std::addressof(meta_data_hash_data), sizeof(meta_data_hash_data));
|
||||
R_UNLESS(crypto::IsSameBytes(meta_data_hash_data_hash, std::addressof(meta_data_hash_data_info.hash), sizeof(meta_data_hash_data_hash)), fs::ResultInvalidNcaMetaDataHashDataHash());
|
||||
|
||||
/* Set the out layer info storage, if necessary. */
|
||||
if (out_layer_info_storage != nullptr) {
|
||||
auto layer_info_storage = fssystem::AllocateShared<fs::SubStorage>(base_storage, meta_data_hash_data.layer_info_offset - offset, meta_data_hash_data_info.offset + meta_data_hash_data_info.size - meta_data_hash_data.layer_info_offset);
|
||||
R_UNLESS(layer_info_storage != nullptr, fs::ResultAllocationMemoryFailedAllocateShared());
|
||||
|
||||
*out_layer_info_storage = std::move(layer_info_storage);
|
||||
}
|
||||
|
||||
/* Create the meta storage. */
|
||||
auto meta_storage = fssystem::AllocateShared<fs::SubStorage>(std::move(base_storage), 0, meta_data_hash_data_info.offset - offset);
|
||||
R_UNLESS(meta_storage != nullptr, fs::ResultAllocationMemoryFailedAllocateShared());
|
||||
|
||||
/* Create the integrity verification storage. */
|
||||
R_RETURN(this->CreateIntegrityVerificationStorageImpl(out, std::move(meta_storage), meta_data_hash_data.integrity_meta_info, meta_data_hash_data.layer_info_offset - offset, IntegrityDataCacheCountForMeta, IntegrityHashCacheCountForMeta, 0, hgf));
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateIntegrityVerificationStorageImpl(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> 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, IHash256GeneratorFactory *hgf) {
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(out != nullptr);
|
||||
AMS_ASSERT(base_storage != nullptr);
|
||||
AMS_ASSERT(layer_info_offset >= 0);
|
||||
|
||||
/* Define storage types. */
|
||||
using VerificationStorage = HierarchicalIntegrityVerificationStorage;
|
||||
|
@ -1079,30 +1165,52 @@ namespace ams::fssystem {
|
|||
StorageInfo storage_info;
|
||||
for (s32 i = 0; i < static_cast<s32>(level_hash_info.max_layers - 2); ++i) {
|
||||
const auto &layer_info = level_hash_info.info[i];
|
||||
R_UNLESS(layer_info.offset + layer_info.size <= base_storage_size, fs::ResultNcaBaseStorageOutOfRangeD());
|
||||
R_UNLESS(layer_info_offset + layer_info.offset + layer_info.size <= base_storage_size, fs::ResultNcaBaseStorageOutOfRangeD());
|
||||
|
||||
storage_info[i + 1] = fs::SubStorage(base_storage, layer_info.offset, layer_info.size);
|
||||
storage_info[i + 1] = fs::SubStorage(base_storage, layer_info_offset + layer_info.offset, layer_info.size);
|
||||
}
|
||||
|
||||
/* Set the last layer info. */
|
||||
const auto &layer_info = level_hash_info.info[level_hash_info.max_layers - 2];
|
||||
R_UNLESS(layer_info.offset + layer_info.size <= base_storage_size, fs::ResultNcaBaseStorageOutOfRangeD());
|
||||
storage_info.SetDataStorage(fs::SubStorage(std::move(base_storage), layer_info.offset, layer_info.size));
|
||||
const s64 last_layer_info_offset = layer_info_offset > 0 ? 0 : layer_info.offset;
|
||||
R_UNLESS(last_layer_info_offset + layer_info.size <= base_storage_size, fs::ResultNcaBaseStorageOutOfRangeD());
|
||||
if (layer_info_offset > 0) {
|
||||
R_UNLESS(last_layer_info_offset + layer_info.size <= layer_info_offset, fs::ResultRomNcaInvalidIntegrityLayerInfoOffset());
|
||||
}
|
||||
storage_info.SetDataStorage(fs::SubStorage(std::move(base_storage), last_layer_info_offset, layer_info.size));
|
||||
|
||||
/* Make the integrity romfs storage. */
|
||||
auto integrity_storage = fssystem::AllocateShared<fssystem::IntegrityRomFsStorage>();
|
||||
R_UNLESS(integrity_storage != nullptr, fs::ResultAllocationMemoryFailedAllocateShared());
|
||||
|
||||
/* Initialize the integrity storage. */
|
||||
R_TRY(integrity_storage->Initialize(level_hash_info, meta_info.master_hash, storage_info, m_buffer_manager, hgf));
|
||||
R_TRY(integrity_storage->Initialize(level_hash_info, meta_info.master_hash, storage_info, m_buffer_manager, max_data_cache_entries, max_hash_cache_entries, buffer_level, hgf));
|
||||
|
||||
/* Set the output. */
|
||||
*out = std::move(integrity_storage);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
Result NcaFileSystemDriver::CreateRegionSwitchStorage(std::shared_ptr<fs::IStorage> *out, const NcaFsHeaderReader *header_reader, std::shared_ptr<fs::IStorage> inside_storage, std::shared_ptr<fs::IStorage> outside_storage) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(header_reader->GetHashType() == NcaFsHeader::HashType::HierarchicalIntegrityHash);
|
||||
|
||||
/* Create the region. */
|
||||
fssystem::RegionSwitchStorage::Region region = {};
|
||||
R_TRY(header_reader->GetHashTargetOffset(std::addressof(region.size)));
|
||||
|
||||
/* Create the region switch storage. */
|
||||
auto region_switch_storage = fssystem::AllocateShared<fssystem::RegionSwitchStorage>(std::move(inside_storage), std::move(outside_storage), region);
|
||||
R_UNLESS(region_switch_storage != nullptr, fs::ResultAllocationMemoryFailedAllocateShared());
|
||||
|
||||
/* Set the output. */
|
||||
*out = std::move(region_switch_storage);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateCompressedStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::CompressedStorage> *out_cmp, std::shared_ptr<fs::IStorage> *out_meta, std::shared_ptr<fs::IStorage> base_storage, const NcaCompressionInfo &compression_info) {
|
||||
return this->CreateCompressedStorage(out, out_cmp, out_meta, std::move(base_storage), compression_info, m_reader->GetDecompressor(), m_allocator, m_buffer_manager);
|
||||
R_RETURN(this->CreateCompressedStorage(out, out_cmp, out_meta, std::move(base_storage), compression_info, m_reader->GetDecompressor(), m_allocator, m_buffer_manager));
|
||||
}
|
||||
|
||||
Result NcaFileSystemDriver::CreateCompressedStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::CompressedStorage> *out_cmp, std::shared_ptr<fs::IStorage> *out_meta, std::shared_ptr<fs::IStorage> base_storage, const NcaCompressionInfo &compression_info, GetDecompressorFunction get_decompressor, MemoryResource *allocator, fs::IBufferManager *buffer_manager) {
|
||||
|
@ -1145,7 +1253,7 @@ namespace ams::fssystem {
|
|||
|
||||
/* Set the output. */
|
||||
*out = std::move(compressed_storage);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace ams::fssystem {
|
|||
|
||||
}
|
||||
|
||||
NcaReader::NcaReader() : m_body_storage(), m_header_storage(), m_decrypt_aes_ctr(), m_decrypt_aes_ctr_external(), m_is_software_aes_prioritized(false), m_header_encryption_type(NcaHeader::EncryptionType::Auto), m_get_decompressor(), m_hash_generator_factory_selector() {
|
||||
NcaReader::NcaReader() : m_body_storage(), m_header_storage(), m_decrypt_aes_ctr(), m_decrypt_aes_ctr_external(), m_is_software_aes_prioritized(false), m_is_available_sw_key(false), m_header_encryption_type(NcaHeader::EncryptionType::Auto), m_get_decompressor(), m_hash_generator_factory_selector() {
|
||||
std::memset(std::addressof(m_header), 0, sizeof(m_header));
|
||||
std::memset(std::addressof(m_decryption_keys), 0, sizeof(m_decryption_keys));
|
||||
std::memset(std::addressof(m_external_decryption_key), 0, sizeof(m_external_decryption_key));
|
||||
|
@ -52,19 +52,42 @@ namespace ams::fssystem {
|
|||
AMS_ASSERT(m_body_storage == nullptr);
|
||||
|
||||
/* Check that the crypto config is valid. */
|
||||
R_UNLESS(crypto_cfg.generate_key != nullptr, fs::ResultInvalidArgument());
|
||||
R_UNLESS(crypto_cfg.verify_sign1 != nullptr, fs::ResultInvalidArgument());
|
||||
|
||||
/* Generate keys for header. */
|
||||
using AesXtsStorageForNcaHeader = AesXtsStorageBySharedPointer;
|
||||
/* Create the work header storage storage. */
|
||||
std::unique_ptr<fs::IStorage> work_header_storage;
|
||||
if (crypto_cfg.is_available_sw_key) {
|
||||
/* If software key is available, we need to be able to generate keys. */
|
||||
R_UNLESS(crypto_cfg.generate_key != nullptr, fs::ResultInvalidArgument());
|
||||
|
||||
u8 header_decryption_keys[NcaCryptoConfiguration::HeaderEncryptionKeyCount][NcaCryptoConfiguration::Aes128KeySize];
|
||||
for (size_t i = 0; i < NcaCryptoConfiguration::HeaderEncryptionKeyCount; i++) {
|
||||
crypto_cfg.generate_key(header_decryption_keys[i], AesXtsStorageForNcaHeader::KeySize, crypto_cfg.header_encrypted_encryption_keys[i], AesXtsStorageForNcaHeader::KeySize, static_cast<s32>(KeyType::NcaHeaderKey), crypto_cfg);
|
||||
/* Generate keys for header. */
|
||||
using AesXtsStorageForNcaHeader = AesXtsStorageBySharedPointer;
|
||||
|
||||
constexpr const s32 HeaderKeyTypeValues[NcaCryptoConfiguration::HeaderEncryptionKeyCount] = {
|
||||
static_cast<s32>(KeyType::NcaHeaderKey1),
|
||||
static_cast<s32>(KeyType::NcaHeaderKey2),
|
||||
};
|
||||
|
||||
u8 header_decryption_keys[NcaCryptoConfiguration::HeaderEncryptionKeyCount][NcaCryptoConfiguration::Aes128KeySize];
|
||||
for (size_t i = 0; i < NcaCryptoConfiguration::HeaderEncryptionKeyCount; i++) {
|
||||
crypto_cfg.generate_key(header_decryption_keys[i], AesXtsStorageForNcaHeader::KeySize, crypto_cfg.header_encrypted_encryption_keys[i], AesXtsStorageForNcaHeader::KeySize, HeaderKeyTypeValues[i]);
|
||||
}
|
||||
|
||||
/* Create the header storage. */
|
||||
const u8 header_iv[AesXtsStorageForNcaHeader::IvSize] = {};
|
||||
work_header_storage = std::make_unique<AesXtsStorageForNcaHeader>(base_storage, header_decryption_keys[0], header_decryption_keys[1], AesXtsStorageForNcaHeader::KeySize, header_iv, AesXtsStorageForNcaHeader::IvSize, NcaHeader::XtsBlockSize);
|
||||
} else {
|
||||
/* Software key isn't available, so we need to be able to decrypt externally. */
|
||||
R_UNLESS(crypto_cfg.decrypt_aes_xts_external, fs::ResultInvalidArgument());
|
||||
|
||||
/* Create the header storage. */
|
||||
using AesXtsStorageExternalForNcaHeader = AesXtsStorageExternalByPointer;
|
||||
|
||||
const u8 header_iv[AesXtsStorageExternalForNcaHeader::IvSize] = {};
|
||||
work_header_storage = std::make_unique<AesXtsStorageExternalForNcaHeader>(base_storage.get(), nullptr, nullptr, AesXtsStorageExternalForNcaHeader::KeySize, header_iv, AesXtsStorageExternalForNcaHeader::IvSize, NcaHeader::XtsBlockSize, crypto_cfg.encrypt_aes_xts_external, crypto_cfg.decrypt_aes_xts_external);
|
||||
}
|
||||
|
||||
/* Create the header storage. */
|
||||
const u8 header_iv[AesXtsStorageForNcaHeader::IvSize] = {};
|
||||
std::unique_ptr<fs::IStorage> work_header_storage = std::make_unique<AesXtsStorageForNcaHeader>(base_storage, header_decryption_keys[0], header_decryption_keys[1], AesXtsStorageForNcaHeader::KeySize, header_iv, AesXtsStorageForNcaHeader::IvSize, NcaHeader::XtsBlockSize);
|
||||
/* Check that we successfully created the storage. */
|
||||
R_UNLESS(work_header_storage != nullptr, fs::ResultAllocationMemoryFailedInNcaReaderA());
|
||||
|
||||
/* Read the header. */
|
||||
|
@ -91,19 +114,15 @@ namespace ams::fssystem {
|
|||
|
||||
/* Validate the fixed key signature. */
|
||||
R_UNLESS(m_header.header1_signature_key_generation <= NcaCryptoConfiguration::Header1SignatureKeyGenerationMax, fs::ResultInvalidNcaHeader1SignatureKeyGeneration());
|
||||
const u8 *header_1_sign_key_modulus = crypto_cfg.header_1_sign_key_moduli[m_header.header1_signature_key_generation];
|
||||
AMS_ABORT_UNLESS(header_1_sign_key_modulus != nullptr);
|
||||
|
||||
/* Verify the header sign1. */
|
||||
{
|
||||
const u8 *sig = m_header.header_sign_1;
|
||||
const size_t sig_size = NcaHeader::HeaderSignSize;
|
||||
const u8 *mod = header_1_sign_key_modulus;
|
||||
const size_t mod_size = NcaCryptoConfiguration::Rsa2048KeyModulusSize;
|
||||
const u8 *exp = crypto_cfg.header_1_sign_key_public_exponent;
|
||||
const size_t exp_size = NcaCryptoConfiguration::Rsa2048KeyPublicExponentSize;
|
||||
const u8 *msg = static_cast<const u8 *>(static_cast<const void *>(std::addressof(m_header.magic)));
|
||||
const size_t msg_size = NcaHeader::Size - NcaHeader::HeaderSignSize * NcaHeader::HeaderSignCount;
|
||||
|
||||
m_is_header_sign1_signature_valid = crypto::VerifyRsa2048PssSha256(sig, sig_size, mod, mod_size, exp, exp_size, msg, msg_size);
|
||||
m_is_header_sign1_signature_valid = crypto_cfg.verify_sign1(sig, sig_size, msg, msg_size, m_header.header1_signature_key_generation, crypto_cfg);
|
||||
|
||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
R_UNLESS(m_is_header_sign1_signature_valid, fs::ResultNcaHeaderSignature1VerificationFailed());
|
||||
|
@ -116,7 +135,7 @@ namespace ams::fssystem {
|
|||
R_UNLESS(m_header.sdk_addon_version >= SdkAddonVersionMin, fs::ResultUnsupportedSdkVersion());
|
||||
|
||||
/* Validate the key index. */
|
||||
R_UNLESS(m_header.key_index < NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount, fs::ResultInvalidNcaKeyIndex());
|
||||
R_UNLESS(m_header.key_index < NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount || m_header.key_index == NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexZeroKey, fs::ResultInvalidNcaKeyIndex());
|
||||
|
||||
/* Set our hash generator factory selector. */
|
||||
m_hash_generator_factory_selector = hgf_selector;
|
||||
|
@ -124,15 +143,17 @@ namespace ams::fssystem {
|
|||
/* Check if we have a rights id. */
|
||||
constexpr const u8 ZeroRightsId[NcaHeader::RightsIdSize] = {};
|
||||
if (crypto::IsSameBytes(ZeroRightsId, m_header.rights_id, NcaHeader::RightsIdSize)) {
|
||||
/* If we do, then we don't have an external key, so we need to generate decryption keys. */
|
||||
crypto_cfg.generate_key(m_decryption_keys[NcaHeader::DecryptionKey_AesCtr], crypto::AesDecryptor128::KeySize, m_header.encrypted_key_area + NcaHeader::DecryptionKey_AesCtr * crypto::AesDecryptor128::KeySize, crypto::AesDecryptor128::KeySize, GetKeyTypeValue(m_header.key_index, m_header.GetProperKeyGeneration()), crypto_cfg);
|
||||
/* If we do, then we don't have an external key, so we need to generate decryption keys if software keys are available. */
|
||||
if (crypto_cfg.is_available_sw_key) {
|
||||
crypto_cfg.generate_key(m_decryption_keys[NcaHeader::DecryptionKey_AesCtr], crypto::AesDecryptor128::KeySize, m_header.encrypted_key_area + NcaHeader::DecryptionKey_AesCtr * crypto::AesDecryptor128::KeySize, crypto::AesDecryptor128::KeySize, GetKeyTypeValue(m_header.key_index, m_header.GetProperKeyGeneration()));
|
||||
|
||||
/* If we're building for non-nx board (i.e., a host tool), generate all keys for debug. */
|
||||
#if !defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
crypto_cfg.generate_key(m_decryption_keys[NcaHeader::DecryptionKey_AesXts1], crypto::AesDecryptor128::KeySize, m_header.encrypted_key_area + NcaHeader::DecryptionKey_AesXts1 * crypto::AesDecryptor128::KeySize, crypto::AesDecryptor128::KeySize, GetKeyTypeValue(m_header.key_index, m_header.GetProperKeyGeneration()), crypto_cfg);
|
||||
crypto_cfg.generate_key(m_decryption_keys[NcaHeader::DecryptionKey_AesXts2], crypto::AesDecryptor128::KeySize, m_header.encrypted_key_area + NcaHeader::DecryptionKey_AesXts2 * crypto::AesDecryptor128::KeySize, crypto::AesDecryptor128::KeySize, GetKeyTypeValue(m_header.key_index, m_header.GetProperKeyGeneration()), crypto_cfg);
|
||||
crypto_cfg.generate_key(m_decryption_keys[NcaHeader::DecryptionKey_AesCtrEx], crypto::AesDecryptor128::KeySize, m_header.encrypted_key_area + NcaHeader::DecryptionKey_AesCtrEx * crypto::AesDecryptor128::KeySize, crypto::AesDecryptor128::KeySize, GetKeyTypeValue(m_header.key_index, m_header.GetProperKeyGeneration()), crypto_cfg);
|
||||
#endif
|
||||
/* If we're building for non-nx board (i.e., a host tool), generate all keys for debug. */
|
||||
#if !defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
crypto_cfg.generate_key(m_decryption_keys[NcaHeader::DecryptionKey_AesXts1], crypto::AesDecryptor128::KeySize, m_header.encrypted_key_area + NcaHeader::DecryptionKey_AesXts1 * crypto::AesDecryptor128::KeySize, crypto::AesDecryptor128::KeySize, GetKeyTypeValue(m_header.key_index, m_header.GetProperKeyGeneration()));
|
||||
crypto_cfg.generate_key(m_decryption_keys[NcaHeader::DecryptionKey_AesXts2], crypto::AesDecryptor128::KeySize, m_header.encrypted_key_area + NcaHeader::DecryptionKey_AesXts2 * crypto::AesDecryptor128::KeySize, crypto::AesDecryptor128::KeySize, GetKeyTypeValue(m_header.key_index, m_header.GetProperKeyGeneration()));
|
||||
crypto_cfg.generate_key(m_decryption_keys[NcaHeader::DecryptionKey_AesCtrEx], crypto::AesDecryptor128::KeySize, m_header.encrypted_key_area + NcaHeader::DecryptionKey_AesCtrEx * crypto::AesDecryptor128::KeySize, crypto::AesDecryptor128::KeySize, GetKeyTypeValue(m_header.key_index, m_header.GetProperKeyGeneration()));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Copy the hardware speed emulation key. */
|
||||
std::memcpy(m_decryption_keys[NcaHeader::DecryptionKey_AesCtrHw], m_header.encrypted_key_area + NcaHeader::DecryptionKey_AesCtrHw * crypto::AesDecryptor128::KeySize, crypto::AesDecryptor128::KeySize);
|
||||
|
@ -141,6 +162,9 @@ namespace ams::fssystem {
|
|||
/* Clear the external decryption key. */
|
||||
std::memset(m_external_decryption_key, 0, sizeof(m_external_decryption_key));
|
||||
|
||||
/* Set software key availability. */
|
||||
m_is_available_sw_key = crypto_cfg.is_available_sw_key;
|
||||
|
||||
/* Set our decryptor functions. */
|
||||
m_decrypt_aes_ctr = crypto_cfg.decrypt_aes_ctr;
|
||||
m_decrypt_aes_ctr_external = crypto_cfg.decrypt_aes_ctr_external;
|
||||
|
@ -309,6 +333,10 @@ namespace ams::fssystem {
|
|||
m_is_software_aes_prioritized = true;
|
||||
}
|
||||
|
||||
bool NcaReader::IsAvailableSwKey() const {
|
||||
return m_is_available_sw_key;
|
||||
}
|
||||
|
||||
bool NcaReader::HasExternalDecryptionKey() const {
|
||||
constexpr const u8 ZeroKey[crypto::AesDecryptor128::KeySize] = {};
|
||||
return !crypto::IsSameBytes(ZeroKey, this->GetExternalDecryptionKey(), crypto::AesDecryptor128::KeySize);
|
||||
|
@ -469,6 +497,18 @@ namespace ams::fssystem {
|
|||
return m_data.aes_ctr_upper_iv;
|
||||
}
|
||||
|
||||
bool NcaFsHeaderReader::IsSkipLayerHashEncryption() const {
|
||||
AMS_ASSERT(this->IsInitialized());
|
||||
return m_data.IsSkipLayerHashEncryption();
|
||||
}
|
||||
|
||||
Result NcaFsHeaderReader::GetHashTargetOffset(s64 *out) const {
|
||||
AMS_ASSERT(out != nullptr);
|
||||
AMS_ASSERT(this->IsInitialized());
|
||||
|
||||
return m_data.GetHashTargetOffset(out);
|
||||
}
|
||||
|
||||
bool NcaFsHeaderReader::ExistsSparseLayer() const {
|
||||
AMS_ASSERT(this->IsInitialized());
|
||||
return m_data.sparse_info.generation != 0;
|
||||
|
@ -499,4 +539,44 @@ namespace ams::fssystem {
|
|||
return m_data.compression_info;
|
||||
}
|
||||
|
||||
bool NcaFsHeaderReader::ExistsPatchMetaHashLayer() const {
|
||||
AMS_ASSERT(this->IsInitialized());
|
||||
return m_data.meta_data_hash_data_info.size != 0 && this->GetPatchInfo().HasIndirectTable();
|
||||
}
|
||||
|
||||
NcaMetaDataHashDataInfo &NcaFsHeaderReader::GetPatchMetaDataHashDataInfo() {
|
||||
AMS_ASSERT(this->IsInitialized());
|
||||
return m_data.meta_data_hash_data_info;
|
||||
}
|
||||
|
||||
const NcaMetaDataHashDataInfo &NcaFsHeaderReader::GetPatchMetaDataHashDataInfo() const {
|
||||
AMS_ASSERT(this->IsInitialized());
|
||||
return m_data.meta_data_hash_data_info;
|
||||
}
|
||||
|
||||
NcaFsHeader::MetaDataHashType NcaFsHeaderReader::GetPatchMetaHashType() const {
|
||||
AMS_ASSERT(this->IsInitialized());
|
||||
return m_data.meta_data_hash_type;
|
||||
}
|
||||
|
||||
bool NcaFsHeaderReader::ExistsSparseMetaHashLayer() const {
|
||||
AMS_ASSERT(this->IsInitialized());
|
||||
return m_data.meta_data_hash_data_info.size != 0 && this->ExistsSparseLayer();
|
||||
}
|
||||
|
||||
NcaMetaDataHashDataInfo &NcaFsHeaderReader::GetSparseMetaDataHashDataInfo() {
|
||||
AMS_ASSERT(this->IsInitialized());
|
||||
return m_data.meta_data_hash_data_info;
|
||||
}
|
||||
|
||||
const NcaMetaDataHashDataInfo &NcaFsHeaderReader::GetSparseMetaDataHashDataInfo() const {
|
||||
AMS_ASSERT(this->IsInitialized());
|
||||
return m_data.meta_data_hash_data_info;
|
||||
}
|
||||
|
||||
NcaFsHeader::MetaDataHashType NcaFsHeaderReader::GetSparseMetaHashType() const {
|
||||
AMS_ASSERT(this->IsInitialized());
|
||||
return m_data.meta_data_hash_type;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -279,6 +279,17 @@ namespace ams::fs {
|
|||
R_DEFINE_ERROR_RESULT(RomHierarchicalSha256BaseStorageTooLarge, 4074);
|
||||
R_DEFINE_ERROR_RESULT(RomHierarchicalSha256HashVerificationFailed, 4075);
|
||||
|
||||
R_DEFINE_ERROR_RESULT(RomNcaInvalidCompressionInfo, 4083);
|
||||
R_DEFINE_ERROR_RESULT(RomNcaInvalidPatchMetaDataHashType, 4084);
|
||||
R_DEFINE_ERROR_RESULT(RomNcaInvalidIntegrityLayerInfoOffset, 4085);
|
||||
R_DEFINE_ERROR_RESULT(RomNcaInvalidPatchMetaDataHashDataSize, 4086);
|
||||
R_DEFINE_ERROR_RESULT(RomNcaInvalidPatchMetaDataHashDataOffset, 4087);
|
||||
R_DEFINE_ERROR_RESULT(RomNcaInvalidPatchMetaDataHashDataHash, 4088);
|
||||
R_DEFINE_ERROR_RESULT(RomNcaInvalidSparseMetaDataHashType, 4089);
|
||||
R_DEFINE_ERROR_RESULT(RomNcaInvalidSparseMetaDataHashDataSize, 4090);
|
||||
R_DEFINE_ERROR_RESULT(RomNcaInvalidSparseMetaDataHashDataOffset, 4091);
|
||||
R_DEFINE_ERROR_RESULT(RomNcaInvalidSparseMetaDataHashDataHash, 4092);
|
||||
|
||||
R_DEFINE_ERROR_RANGE(RomIntegrityVerificationStorageCorrupted, 4141, 4179);
|
||||
R_DEFINE_ERROR_RESULT(IncorrectRomIntegrityVerificationMagic, 4142);
|
||||
R_DEFINE_ERROR_RESULT(InvalidRomZeroHash, 4143);
|
||||
|
@ -349,7 +360,9 @@ namespace ams::fs {
|
|||
R_DEFINE_ERROR_RESULT(InvalidNcaHeader1SignatureKeyGeneration, 4543);
|
||||
|
||||
/* TODO: Range? */
|
||||
R_DEFINE_ERROR_RESULT(InvalidCompressedStorageSize, 4547);
|
||||
R_DEFINE_ERROR_RESULT(InvalidCompressedStorageSize, 4547);
|
||||
R_DEFINE_ERROR_RESULT(InvalidNcaMetaDataHashDataSize, 4548);
|
||||
R_DEFINE_ERROR_RESULT(InvalidNcaMetaDataHashDataHash, 4549);
|
||||
|
||||
R_DEFINE_ERROR_RANGE(IntegrityVerificationStorageCorrupted, 4601, 4639);
|
||||
R_DEFINE_ERROR_RESULT(IncorrectIntegrityVerificationMagic, 4602);
|
||||
|
@ -463,10 +476,10 @@ namespace ams::fs {
|
|||
R_DEFINE_ERROR_RESULT(UnsupportedSetSizeForHierarchicalIntegrityVerificationStorage, 6316);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperateRangeForHierarchicalIntegrityVerificationStorage, 6317);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedSetSizeForIntegrityVerificationStorage, 6318);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperateRangeForNonSaveDataIntegrityVerificationStorage, 6319);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperateRangeForWritableIntegrityVerificationStorage, 6319);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperateRangeForIntegrityVerificationStorage, 6320);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedSetSizeForBlockCacheBufferedStorage, 6321);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperateRangeForNonSaveDataBlockCacheBufferedStorage, 6322);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperateRangeForWritableBlockCacheBufferedStorage, 6322);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperateRangeForBlockCacheBufferedStorage, 6323);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedWriteForIndirectStorage, 6324);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedSetSizeForIndirectStorage, 6325);
|
||||
|
@ -531,6 +544,7 @@ namespace ams::fs {
|
|||
R_DEFINE_ERROR_RESULT(UnsupportedSetSizeForZeroBitmapHashStorageFile, 6386);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedWriteForCompressedStorage, 6387);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperateRangeForCompressedStorage, 6388);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperateRangeForRegionSwitchStorage, 6397);
|
||||
|
||||
|
||||
R_DEFINE_ERROR_RANGE(PermissionDenied, 6400, 6449);
|
||||
|
|
Loading…
Reference in a new issue