summaryrefslogtreecommitdiffstats
path: root/src/core/loader/nca.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/loader/nca.cpp')
-rw-r--r--src/core/loader/nca.cpp76
1 files changed, 76 insertions, 0 deletions
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 09d40e695..4feb6968a 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -3,6 +3,8 @@
#include <utility>
+#include "common/hex_util.h"
+#include "common/scope_exit.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
@@ -12,6 +14,7 @@
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/deconstructed_rom_directory.h"
#include "core/loader/nca.h"
+#include "mbedtls/sha256.h"
namespace Loader {
@@ -80,6 +83,79 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::KProcess& process, Core::S
return load_result;
}
+ResultStatus AppLoader_NCA::VerifyIntegrity(std::function<bool(size_t, size_t)> progress_callback) {
+ using namespace Common::Literals;
+
+ constexpr size_t NcaFileNameWithHashLength = 36;
+ constexpr size_t NcaFileNameHashLength = 32;
+ constexpr size_t NcaSha256HashLength = 32;
+ constexpr size_t NcaSha256HalfHashLength = NcaSha256HashLength / 2;
+
+ // Get the file name.
+ const auto name = file->GetName();
+
+ // We won't try to verify meta NCAs.
+ if (name.ends_with(".cnmt.nca")) {
+ return ResultStatus::Success;
+ }
+
+ // Check if we can verify this file. NCAs should be named after their hashes.
+ if (!name.ends_with(".nca") || name.size() != NcaFileNameWithHashLength) {
+ LOG_WARNING(Loader, "Unable to validate NCA with name {}", name);
+ return ResultStatus::ErrorIntegrityVerificationNotImplemented;
+ }
+
+ // Get the expected truncated hash of the NCA.
+ const auto input_hash =
+ Common::HexStringToVector(file->GetName().substr(0, NcaFileNameHashLength), false);
+
+ // Declare buffer to read into.
+ std::vector<u8> buffer(4_MiB);
+
+ // Initialize sha256 verification context.
+ mbedtls_sha256_context ctx;
+ mbedtls_sha256_init(&ctx);
+ mbedtls_sha256_starts_ret(&ctx, 0);
+
+ // Ensure we maintain a clean state on exit.
+ SCOPE_EXIT({ mbedtls_sha256_free(&ctx); });
+
+ // Declare counters.
+ const size_t total_size = file->GetSize();
+ size_t processed_size = 0;
+
+ // Begin iterating the file.
+ while (processed_size < total_size) {
+ // Refill the buffer.
+ const size_t intended_read_size = std::min(buffer.size(), total_size - processed_size);
+ const size_t read_size = file->Read(buffer.data(), intended_read_size, processed_size);
+
+ // Update the hash function with the buffer contents.
+ mbedtls_sha256_update_ret(&ctx, buffer.data(), read_size);
+
+ // Update counters.
+ processed_size += read_size;
+
+ // Call the progress function.
+ if (!progress_callback(processed_size, total_size)) {
+ return ResultStatus::ErrorIntegrityVerificationFailed;
+ }
+ }
+
+ // Finalize context and compute the output hash.
+ std::array<u8, NcaSha256HashLength> output_hash;
+ mbedtls_sha256_finish_ret(&ctx, output_hash.data());
+
+ // Compare to expected.
+ if (std::memcmp(input_hash.data(), output_hash.data(), NcaSha256HalfHashLength) != 0) {
+ LOG_ERROR(Loader, "NCA hash mismatch detected for file {}", name);
+ return ResultStatus::ErrorIntegrityVerificationFailed;
+ }
+
+ // File verified.
+ return ResultStatus::Success;
+}
+
ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
if (nca == nullptr) {
return ResultStatus::ErrorNotInitialized;