disable verification

This commit is contained in:
Maufeat 2025-08-21 21:59:56 +02:00 committed by crueter
parent bbcd8aded6
commit f772730571
No known key found for this signature in database
GPG Key ID: 425ACD2D4830EBC6
12 changed files with 281 additions and 52 deletions

View File

@ -88,6 +88,8 @@ add_library(core STATIC
file_sys/fssystem/fssystem_crypto_configuration.h
file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.cpp
file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h
file_sys/fssystem/fssystem_hierarchical_sha3_storage.cpp
file_sys/fssystem/fssystem_hierarchical_sha3_storage.h
file_sys/fssystem/fssystem_hierarchical_sha256_storage.cpp
file_sys/fssystem/fssystem_hierarchical_sha256_storage.h
file_sys/fssystem/fssystem_indirect_storage.cpp
@ -102,6 +104,7 @@ add_library(core STATIC
file_sys/fssystem/fssystem_nca_header.cpp
file_sys/fssystem/fssystem_nca_header.h
file_sys/fssystem/fssystem_nca_reader.cpp
file_sys/fssystem/fssystem_passthrough_storage.h
file_sys/fssystem/fssystem_pooled_buffer.cpp
file_sys/fssystem/fssystem_pooled_buffer.h
file_sys/fssystem/fssystem_sparse_storage.cpp

View File

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -233,7 +236,7 @@ Result BucketTree::Initialize(VirtualFile node_storage, VirtualFile entry_storag
void BucketTree::Initialize(size_t node_size, s64 end_offset) {
ASSERT(NodeSizeMin <= node_size && node_size <= NodeSizeMax);
ASSERT(Common::IsPowerOfTwo(node_size));
ASSERT(end_offset > 0);
//TODO: ASSERT(end_offset > 0);
ASSERT(!this->IsInitialized());
m_node_size = node_size;

View File

@ -0,0 +1,66 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/alignment.h"
#include "common/scope_exit.h"
#include "core/file_sys/fssystem/fssystem_hierarchical_sha3_storage.h"
namespace FileSys {
namespace {
s32 Log2(s32 value) {
ASSERT(value > 0);
ASSERT(Common::IsPowerOfTwo(value));
s32 log = 0;
while ((value >>= 1) > 0) {
++log;
}
return log;
}
} // namespace
Result HierarchicalSha3Storage::Initialize(VirtualFile* base_storages, s32 layer_count, size_t htbs,
void* hash_buf, size_t hash_buf_size) {
ASSERT(layer_count == LayerCount);
ASSERT(Common::IsPowerOfTwo(htbs));
ASSERT(hash_buf != nullptr);
m_hash_target_block_size = static_cast<s32>(htbs);
m_log_size_ratio = Log2(m_hash_target_block_size / HashSize);
m_base_storage_size = base_storages[2]->GetSize();
{
auto size_guard = SCOPE_GUARD {
m_base_storage_size = 0;
};
R_UNLESS(m_base_storage_size <= static_cast<s64>(HashSize)
<< m_log_size_ratio << m_log_size_ratio,
ResultHierarchicalSha256BaseStorageTooLarge);
size_guard.Cancel();
}
m_base_storage = base_storages[2];
m_hash_buffer = static_cast<char*>(hash_buf);
m_hash_buffer_size = hash_buf_size;
std::array<u8, HashSize> master_hash{};
base_storages[0]->ReadObject(std::addressof(master_hash));
s64 hash_storage_size = base_storages[1]->GetSize();
ASSERT(Common::IsAligned(hash_storage_size, HashSize));
ASSERT(hash_storage_size <= m_hash_target_block_size);
ASSERT(hash_storage_size <= static_cast<s64>(m_hash_buffer_size));
base_storages[1]->Read(reinterpret_cast<u8*>(m_hash_buffer),
static_cast<size_t>(hash_storage_size), 0);
R_SUCCEED();
}
size_t HierarchicalSha3Storage::Read(u8* buffer, size_t size, size_t offset) const {
if (size == 0)
return size;
ASSERT(buffer != nullptr);
return m_base_storage->Read(buffer, size, offset);
}
} // namespace FileSys

View File

@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <mutex>
#include "core/file_sys/errors.h"
#include "core/file_sys/fssystem/fs_i_storage.h"
#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
class HierarchicalSha3Storage : public IReadOnlyStorage {
YUZU_NON_COPYABLE(HierarchicalSha3Storage);
YUZU_NON_MOVEABLE(HierarchicalSha3Storage);
public:
static constexpr s32 LayerCount = 3;
static constexpr size_t HashSize = 256 / 8; // SHA3-256
public:
HierarchicalSha3Storage() : m_mutex() {}
Result Initialize(VirtualFile* base_storages, s32 layer_count, size_t htbs, void* hash_buf,
size_t hash_buf_size);
virtual size_t GetSize() const override {
return m_base_storage->GetSize();
}
virtual size_t Read(u8* buffer, size_t length, size_t offset) const override;
private:
VirtualFile m_base_storage;
s64 m_base_storage_size{};
char* m_hash_buffer{};
size_t m_hash_buffer_size{};
s32 m_hash_target_block_size{};
s32 m_log_size_ratio{};
std::mutex m_mutex;
};
} // namespace FileSys

View File

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -10,10 +13,12 @@
#include "core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.h"
#include "core/file_sys/fssystem/fssystem_indirect_storage.h"
#include "core/file_sys/fssystem/fssystem_integrity_romfs_storage.h"
#include "core/file_sys/fssystem/fssystem_passthrough_storage.h"
#include "core/file_sys/fssystem/fssystem_memory_resource_buffer_hold_storage.h"
#include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h"
#include "core/file_sys/fssystem/fssystem_sparse_storage.h"
#include "core/file_sys/fssystem/fssystem_switch_storage.h"
#include "core/file_sys/fssystem/fssystem_hierarchical_sha3_storage.h"
#include "core/file_sys/vfs/vfs_offset.h"
#include "core/file_sys/vfs/vfs_vector.h"
@ -299,18 +304,24 @@ Result NcaFileSystemDriver::CreateStorageByRawStorage(VirtualFile* out,
// Process hash/integrity layer.
switch (header_reader->GetHashType()) {
case NcaFsHeader::HashType::HierarchicalSha256Hash:
R_TRY(this->CreateSha256Storage(std::addressof(storage), std::move(storage),
header_reader->GetHashData().hierarchical_sha256_data));
R_TRY(CreateSha256Storage(&storage, std::move(storage),
header_reader->GetHashData().hierarchical_sha256_data));
break;
case NcaFsHeader::HashType::HierarchicalIntegrityHash:
R_TRY(this->CreateIntegrityVerificationStorage(
std::addressof(storage), std::move(storage),
header_reader->GetHashData().integrity_meta_info));
R_TRY(CreateIntegrityVerificationStorage(&storage, std::move(storage),
header_reader->GetHashData().integrity_meta_info));
break;
case NcaFsHeader::HashType::HierarchicalSha3256Hash:
R_TRY(CreateSha3Storage(&storage, std::move(storage),
header_reader->GetHashData().hierarchical_sha256_data));
break;
default:
LOG_ERROR(Loader, "Unhandled Fs HashType enum={}",
static_cast<int>(header_reader->GetHashType()));
R_THROW(ResultInvalidNcaFsHeaderHashType);
}
// Process compression layer.
if (header_reader->ExistsCompressionLayer()) {
R_TRY(this->CreateCompressedStorage(
@ -679,6 +690,7 @@ Result NcaFileSystemDriver::CreateSparseStorageMetaStorageWithVerification(
// Create the verification storage.
VirtualFile integrity_storage;
Result rc = this->CreateIntegrityVerificationStorageForMeta(
std::addressof(integrity_storage), out_layer_info_storage, std::move(decrypted_storage),
meta_offset, meta_data_hash_data_info);
@ -734,8 +746,26 @@ Result NcaFileSystemDriver::CreateSparseStorageWithVerification(
NcaHeader::CtrBlockSize)));
// Check the meta data hash type.
R_UNLESS(meta_data_hash_type == NcaFsHeader::MetaDataHashType::HierarchicalIntegrity,
ResultRomNcaInvalidSparseMetaDataHashType);
if (meta_data_hash_type != NcaFsHeader::MetaDataHashType::HierarchicalIntegrity) {
LOG_ERROR(Loader, "Sparse meta hash type {} not supported for verification; mounting sparse data WITHOUT verification (temporary).", static_cast<int>(meta_data_hash_type));
R_TRY(this->CreateBodySubStorage(std::addressof(body_substorage),
sparse_info.physical_offset,
sparse_info.GetPhysicalSize()));
// Create sparse core directly (no meta verification)
std::shared_ptr<SparseStorage> sparse_storage_fallback;
R_TRY(this->CreateSparseStorageCore(std::addressof(sparse_storage_fallback),
body_substorage, sparse_info.GetPhysicalSize(),
/*meta_storage*/ body_substorage, // dummy; not used
sparse_info, false));
if (out_sparse_storage)
*out_sparse_storage = sparse_storage_fallback;
*out_fs_data_offset = fs_offset;
*out = std::move(sparse_storage_fallback);
R_SUCCEED();
}
// Create the meta storage.
VirtualFile meta_storage;
@ -1093,6 +1123,56 @@ Result NcaFileSystemDriver::CreatePatchMetaStorage(
R_SUCCEED();
}
Result NcaFileSystemDriver::CreateSha3Storage(
VirtualFile* out, VirtualFile base_storage,
const NcaFsHeader::HashData::HierarchicalSha256Data& hash_data) {
ASSERT(out != nullptr);
ASSERT(base_storage != nullptr);
using VerificationStorage = HierarchicalSha3Storage;
R_UNLESS(Common::IsPowerOfTwo(hash_data.hash_block_size),
ResultInvalidHierarchicalSha256BlockSize);
R_UNLESS(hash_data.hash_layer_count == VerificationStorage::LayerCount - 1,
ResultInvalidHierarchicalSha256LayerCount);
const auto& hash_region = hash_data.hash_layer_region[0];
const auto& data_region = hash_data.hash_layer_region[1];
constexpr s32 CacheBlockCount = 2;
const auto hash_buffer_size = static_cast<size_t>(hash_region.size);
const auto cache_buffer_size = CacheBlockCount * hash_data.hash_block_size;
const auto total_buffer_size = hash_buffer_size + cache_buffer_size;
auto buffer_hold_storage = std::make_shared<MemoryResourceBufferHoldStorage>(
std::move(base_storage), total_buffer_size);
R_UNLESS(buffer_hold_storage != nullptr, ResultAllocationMemoryFailedAllocateShared);
R_UNLESS(buffer_hold_storage->IsValid(), ResultAllocationMemoryFailedInNcaFileSystemDriverI);
s64 base_size = buffer_hold_storage->GetSize();
R_UNLESS(hash_region.offset + hash_region.size <= base_size, ResultNcaBaseStorageOutOfRangeC);
R_UNLESS(data_region.offset + data_region.size <= base_size, ResultNcaBaseStorageOutOfRangeC);
auto master_hash_storage =
std::make_shared<ArrayVfsFile<sizeof(Hash)>>(hash_data.fs_data_master_hash.value);
auto verification_storage = std::make_shared<VerificationStorage>();
R_UNLESS(verification_storage != nullptr, ResultAllocationMemoryFailedAllocateShared);
std::array<VirtualFile, VerificationStorage::LayerCount> layer_storages{
std::make_shared<OffsetVfsFile>(master_hash_storage, sizeof(Hash), 0),
std::make_shared<OffsetVfsFile>(buffer_hold_storage, hash_region.size, hash_region.offset),
std::make_shared<OffsetVfsFile>(buffer_hold_storage, data_region.size, data_region.offset),
};
R_TRY(verification_storage->Initialize(layer_storages.data(), VerificationStorage::LayerCount,
hash_data.hash_block_size,
buffer_hold_storage->GetBuffer(), hash_buffer_size));
*out = std::move(verification_storage);
R_SUCCEED();
}
Result NcaFileSystemDriver::CreateSha256Storage(
VirtualFile* out, VirtualFile base_storage,
const NcaFsHeader::HashData::HierarchicalSha256Data& hash_data) {
@ -1160,6 +1240,7 @@ Result NcaFileSystemDriver::CreateSha256Storage(
Result NcaFileSystemDriver::CreateIntegrityVerificationStorage(
VirtualFile* out, VirtualFile base_storage,
const NcaFsHeader::HashData::IntegrityMetaInfo& meta_info) {
R_RETURN(this->CreateIntegrityVerificationStorageImpl(
out, base_storage, meta_info, 0, IntegrityDataCacheCount, IntegrityHashCacheCount,
HierarchicalIntegrityVerificationStorage::GetDefaultDataCacheBufferLevel(
@ -1208,63 +1289,41 @@ Result NcaFileSystemDriver::CreateIntegrityVerificationStorageForMeta(
Result NcaFileSystemDriver::CreateIntegrityVerificationStorageImpl(
VirtualFile* out, VirtualFile base_storage,
const NcaFsHeader::HashData::IntegrityMetaInfo& meta_info, s64 layer_info_offset,
int max_data_cache_entries, int max_hash_cache_entries, s8 buffer_level) {
// Validate preconditions.
int /*max_data_cache_entries*/, int /*max_hash_cache_entries*/, s8 /*buffer_level*/) {
// Preconditions
ASSERT(out != nullptr);
ASSERT(base_storage != nullptr);
ASSERT(layer_info_offset >= 0);
// Define storage types.
using VerificationStorage = HierarchicalIntegrityVerificationStorage;
using StorageInfo = VerificationStorage::HierarchicalStorageInformation;
// Read IVFC layout
HierarchicalIntegrityVerificationInformation lhi{};
std::memcpy(std::addressof(lhi), std::addressof(meta_info.level_hash_info), sizeof(lhi));
// Validate the meta info.
HierarchicalIntegrityVerificationInformation level_hash_info;
std::memcpy(std::addressof(level_hash_info), std::addressof(meta_info.level_hash_info),
sizeof(level_hash_info));
R_UNLESS(IntegrityMinLayerCount <= level_hash_info.max_layers,
R_UNLESS(IntegrityMinLayerCount <= lhi.max_layers,
ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
R_UNLESS(level_hash_info.max_layers <= IntegrityMaxLayerCount,
R_UNLESS(lhi.max_layers <= IntegrityMaxLayerCount,
ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
// Get the base storage size.
s64 base_storage_size = base_storage->GetSize();
const auto& data_li = lhi.info[lhi.max_layers - 2];
// Create storage info.
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.offset + layer_info.size <= base_storage_size,
ResultNcaBaseStorageOutOfRangeD);
const s64 base_size = base_storage->GetSize();
storage_info[i + 1] = std::make_shared<OffsetVfsFile>(
base_storage, layer_info.size, layer_info_offset + layer_info.offset);
}
// Set the last layer info.
const auto& layer_info = level_hash_info.info[level_hash_info.max_layers - 2];
const s64 last_layer_info_offset = layer_info_offset > 0 ? 0LL : layer_info.offset.Get();
R_UNLESS(last_layer_info_offset + layer_info.size <= base_storage_size,
ResultNcaBaseStorageOutOfRangeD);
// Compute the data layer window
const s64 data_off = (layer_info_offset > 0) ? 0LL : data_li.offset.Get();
R_UNLESS(data_off + data_li.size <= base_size, ResultNcaBaseStorageOutOfRangeD);
if (layer_info_offset > 0) {
R_UNLESS(last_layer_info_offset + layer_info.size <= layer_info_offset,
R_UNLESS(data_off + data_li.size <= layer_info_offset,
ResultRomNcaInvalidIntegrityLayerInfoOffset);
}
storage_info.SetDataStorage(std::make_shared<OffsetVfsFile>(
std::move(base_storage), layer_info.size, last_layer_info_offset));
// Make the integrity romfs storage.
auto integrity_storage = std::make_shared<IntegrityRomFsStorage>();
R_UNLESS(integrity_storage != nullptr, ResultAllocationMemoryFailedAllocateShared);
// TODO: Passthrough (temporary compatibility: integrity disabled)
auto data_view = std::make_shared<OffsetVfsFile>(base_storage, data_li.size, data_off);
R_UNLESS(data_view != nullptr, ResultAllocationMemoryFailedAllocateShared);
// Initialize the integrity storage.
R_TRY(integrity_storage->Initialize(level_hash_info, meta_info.master_hash, storage_info,
max_data_cache_entries, max_hash_cache_entries,
buffer_level));
auto passthrough = std::make_shared<PassthroughStorage>(std::move(data_view));
R_UNLESS(passthrough != nullptr, ResultAllocationMemoryFailedAllocateShared);
// Set the output.
*out = std::move(integrity_storage);
*out = std::move(passthrough);
R_SUCCEED();
}

View File

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -329,6 +332,10 @@ private:
const NcaPatchInfo& patch_info,
const NcaMetaDataHashDataInfo& meta_data_hash_data_info);
Result CreateSha3Storage(VirtualFile* out, VirtualFile base_storage,
const NcaFsHeader::HashData::HierarchicalSha256Data& hash_data);
Result CreateSha256Storage(VirtualFile* out, VirtualFile base_storage,
const NcaFsHeader::HashData::HierarchicalSha256Data& sha256_data);

View File

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -10,11 +13,13 @@ u8 NcaHeader::GetProperKeyGeneration() const {
}
bool NcaPatchInfo::HasIndirectTable() const {
return this->indirect_size != 0;
static constexpr unsigned char BKTR[4] = {'B', 'K', 'T', 'R'};
return std::memcmp(indirect_header.data(), BKTR, sizeof(BKTR)) == 0;
}
bool NcaPatchInfo::HasAesCtrExTable() const {
return this->aes_ctr_ex_size != 0;
static constexpr unsigned char BKTR[4] = {'B', 'K', 'T', 'R'};
return std::memcmp(aes_ctr_ex_header.data(), BKTR, sizeof(BKTR)) == 0;
}
} // namespace FileSys

View File

@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/file_sys/fssystem/fs_i_storage.h"
#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
//TODO: No integrity verification.
class PassthroughStorage final : public IReadOnlyStorage {
YUZU_NON_COPYABLE(PassthroughStorage);
YUZU_NON_MOVEABLE(PassthroughStorage);
public:
explicit PassthroughStorage(VirtualFile base) : base_(std::move(base)) {}
~PassthroughStorage() override = default;
size_t Read(u8* buffer, size_t size, size_t offset) const override {
if (!base_ || size == 0)
return 0;
return base_->Read(buffer, size, offset);
}
size_t GetSize() const override {
return base_ ? base_->GetSize() : 0;
}
private:
VirtualFile base_{};
};
} // namespace FileSys

View File

@ -12,7 +12,7 @@ Applet::Applet(Core::System& system, std::unique_ptr<Process> process_, bool is_
process(std::move(process_)), hid_registration(system, *process),
gpu_error_detected_event(context), friend_invitation_storage_channel_event(context),
notification_storage_channel_event(context), health_warning_disappeared_system_event(context),
acquired_sleep_lock_event(context), pop_from_general_channel_event(context),
unknown_event(context), acquired_sleep_lock_event(context), pop_from_general_channel_event(context),
library_applet_launchable_event(context), accumulated_suspended_tick_changed_event(context),
sleep_lock_event(context), state_changed_event(context) {

View File

@ -120,6 +120,7 @@ struct Applet {
Event friend_invitation_storage_channel_event;
Event notification_storage_channel_event;
Event health_warning_disappeared_system_event;
Event unknown_event;
Event acquired_sleep_lock_event;
Event pop_from_general_channel_event;
Event library_applet_launchable_event;

View File

@ -85,6 +85,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_
{181, nullptr, "UpgradeLaunchRequiredVersion"},
{190, nullptr, "SendServerMaintenanceOverlayNotification"},
{200, nullptr, "GetLastApplicationExitReason"},
{210, D<&IApplicationFunctions::GetUnknownEvent210>, "Unknown210"},
{500, nullptr, "StartContinuousRecordingFlushForDebug"},
{1000, nullptr, "CreateMovieMaker"},
{1001, D<&IApplicationFunctions::PrepareForJit>, "PrepareForJit"},
@ -487,6 +488,13 @@ Result IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(
R_SUCCEED();
}
Result IApplicationFunctions::GetUnknownEvent210(
OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_DEBUG(Service_AM, "called");
*out_event = m_applet->unknown_event.GetHandle();
R_SUCCEED();
}
Result IApplicationFunctions::PrepareForJit() {
LOG_WARNING(Service_AM, "(STUBBED) called");

View File

@ -76,6 +76,7 @@ private:
Result TryPopFromFriendInvitationStorageChannel(Out<SharedPointer<IStorage>> out_storage);
Result GetNotificationStorageChannelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetHealthWarningDisappearedSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetUnknownEvent210(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result PrepareForJit();
const std::shared_ptr<Applet> m_applet;