From e374baffa88c0b1b8db3d92b09fd4f3c16cde8cb Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 16 Oct 2025 04:20:37 +0000 Subject: [PATCH] use stack pooled vectors Signed-off-by: lizzie --- src/core/crypto/aes_util.cpp | 13 +- src/core/crypto/ctr_encryption_layer.cpp | 52 ++++---- src/core/crypto/xts_encryption_layer.cpp | 111 ++++++++---------- .../fssystem/fssystem_aes_ctr_storage.cpp | 31 ++--- .../fssystem/fssystem_aes_xts_storage.cpp | 62 ++++------ 5 files changed, 109 insertions(+), 160 deletions(-) diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp index c52c0141f7..eff323db45 100644 --- a/src/core/crypto/aes_util.cpp +++ b/src/core/crypto/aes_util.cpp @@ -73,15 +73,11 @@ void AESCipher::Transcode(const u8* src, std::size_t size, u8* des mbedtls_cipher_reset(context); // Only ECB strictly requires block sized chunks. - const auto mode = mbedtls_cipher_get_cipher_mode(context); - std::size_t written = 0; - if (mode != MBEDTLS_MODE_ECB) { + if (mbedtls_cipher_get_cipher_mode(context) != MBEDTLS_MODE_ECB) { mbedtls_cipher_update(context, src, size, dest, &written); - if (written != size) { - LOG_WARNING(Crypto, "Not all data was processed requested={:016X}, actual={:016X}.", - size, written); - } + if (written != size) + LOG_WARNING(Crypto, "Not all data was processed requested={:016X}, actual={:016X}.", size, written); return; } @@ -106,8 +102,7 @@ void AESCipher::Transcode(const u8* src, std::size_t size, u8* des std::memcpy(dest + offset, block.data(), length); return; } - LOG_WARNING(Crypto, "Not all data was processed requested={:016X}, actual={:016X}.", - length, written); + LOG_WARNING(Crypto, "Not all data was processed requested={:016X}, actual={:016X}.", length, written); } } } diff --git a/src/core/crypto/ctr_encryption_layer.cpp b/src/core/crypto/ctr_encryption_layer.cpp index e664559e9e..32d424d8cf 100644 --- a/src/core/crypto/ctr_encryption_layer.cpp +++ b/src/core/crypto/ctr_encryption_layer.cpp @@ -12,45 +12,37 @@ CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, : EncryptionLayer(std::move(base_)), base_offset(base_offset_), cipher(key_, Mode::CTR) {} std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const { - if (length == 0) { + if (length == 0) return 0; - } std::size_t total_read = 0; - // Handle an initial misaligned portion if needed. - const auto sector_offset = offset & 0xF; - if (sector_offset != 0) { + if (auto const sector_offset = offset & 0xF; sector_offset != 0) { const std::size_t aligned_off = offset - sector_offset; std::array block{}; - const std::size_t got = base->Read(block.data(), block.size(), aligned_off); - if (got == 0) { + if (auto const got = base->Read(block.data(), block.size(), aligned_off); got != 0) { + UpdateIV(base_offset + aligned_off); + cipher.Transcode(block.data(), got, block.data(), Op::Decrypt); + auto const to_copy = std::min(length, got > sector_offset ? got - sector_offset : 0); + if (to_copy > 0) { + std::memcpy(data, block.data() + sector_offset, to_copy); + data += to_copy; + offset += to_copy; + length -= to_copy; + total_read += to_copy; + } + } else { return 0; } - - UpdateIV(base_offset + aligned_off); - cipher.Transcode(block.data(), got, block.data(), Op::Decrypt); - - const std::size_t to_copy = std::min(length, got > sector_offset ? got - sector_offset : 0); - if (to_copy > 0) { - std::memcpy(data, block.data() + sector_offset, to_copy); - data += to_copy; - offset += to_copy; - length -= to_copy; - total_read += to_copy; - } } - - if (length == 0) { - return total_read; - } - - // Now aligned to 0x10 - UpdateIV(base_offset + offset); - const std::size_t got = base->Read(data, length, offset); - if (got > 0) { - cipher.Transcode(data, got, data, Op::Decrypt); - total_read += got; + if (length > 0) { + // Now aligned to 0x10 + UpdateIV(base_offset + offset); + const std::size_t got = base->Read(data, length, offset); + if (got > 0) { + cipher.Transcode(data, got, data, Op::Decrypt); + total_read += got; + } } return total_read; } diff --git a/src/core/crypto/xts_encryption_layer.cpp b/src/core/crypto/xts_encryption_layer.cpp index 7ea9821838..25ca34c43d 100644 --- a/src/core/crypto/xts_encryption_layer.cpp +++ b/src/core/crypto/xts_encryption_layer.cpp @@ -17,80 +17,71 @@ XTSEncryptionLayer::XTSEncryptionLayer(FileSys::VirtualFile base_, Key256 key_) : EncryptionLayer(std::move(base_)), cipher(key_, Mode::XTS) {} std::size_t XTSEncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const { - if (length == 0) { + if (length == 0) return 0; - } std::size_t total_read = 0; - const std::size_t sector_size = XTS_SECTOR_SIZE; - const std::size_t sector_offset = offset % sector_size; - // Handle initial unaligned part within a sector. - if (sector_offset != 0) { + if (auto const sector_offset = offset % XTS_SECTOR_SIZE; sector_offset != 0) { const std::size_t aligned_off = offset - sector_offset; std::array block{}; - std::size_t got = base->Read(block.data(), sector_size, aligned_off); - if (got == 0) { - return 0; - } - if (got < sector_size) { - std::memset(block.data() + got, 0, sector_size - got); - } - cipher.XTSTranscode(block.data(), sector_size, block.data(), aligned_off / sector_size, - sector_size, Op::Decrypt); - - const std::size_t to_copy = std::min(length, got > sector_offset ? got - sector_offset : 0); - if (to_copy > 0) { - std::memcpy(data, block.data() + sector_offset, to_copy); - data += to_copy; - offset += to_copy; - length -= to_copy; - total_read += to_copy; - } - } - - if (length == 0) { - return total_read; - } + if (auto const got = base->Read(block.data(), XTS_SECTOR_SIZE, aligned_off); got > 0) { + if (got < XTS_SECTOR_SIZE) + std::memset(block.data() + got, 0, XTS_SECTOR_SIZE - got); + cipher.XTSTranscode(block.data(), XTS_SECTOR_SIZE, block.data(), aligned_off / XTS_SECTOR_SIZE, + XTS_SECTOR_SIZE, Op::Decrypt); - // Process aligned middle inplace, in sector sized multiples. - while (length >= sector_size) { - const std::size_t req = (length / sector_size) * sector_size; - const std::size_t got = base->Read(data, req, offset); - if (got == 0) { - return total_read; - } - const std::size_t got_rounded = got - (got % sector_size); - if (got_rounded > 0) { - cipher.XTSTranscode(data, got_rounded, data, offset / sector_size, sector_size, - Op::Decrypt); - data += got_rounded; - offset += got_rounded; - length -= got_rounded; - total_read += got_rounded; - } - // If we didn't get a full sector next, break to handle tail. - if (got_rounded != got) { - break; + auto const to_copy = std::min(length, got > sector_offset ? got - sector_offset : 0); + if (to_copy > 0) { + std::memcpy(data, block.data() + sector_offset, to_copy); + data += to_copy; + offset += to_copy; + length -= to_copy; + total_read += to_copy; + } + } else { + return 0; } } - // Handle tail within a sector, if any. if (length > 0) { - std::array block{}; - const std::size_t got = base->Read(block.data(), sector_size, offset); - if (got > 0) { - if (got < sector_size) { - std::memset(block.data() + got, 0, sector_size - got); + // Process aligned middle inplace, in sector sized multiples. + while (length >= sector_size) { + const std::size_t req = (length / sector_size) * sector_size; + const std::size_t got = base->Read(data, req, offset); + if (got == 0) { + return total_read; + } + const std::size_t got_rounded = got - (got % sector_size); + if (got_rounded > 0) { + cipher.XTSTranscode(data, got_rounded, data, offset / sector_size, sector_size, + Op::Decrypt); + data += got_rounded; + offset += got_rounded; + length -= got_rounded; + total_read += got_rounded; + } + // If we didn't get a full sector next, break to handle tail. + if (got_rounded != got) { + break; + } + } + // Handle tail within a sector, if any. + if (length > 0) { + std::array block{}; + const std::size_t got = base->Read(block.data(), sector_size, offset); + if (got > 0) { + if (got < sector_size) { + std::memset(block.data() + got, 0, sector_size - got); + } + cipher.XTSTranscode(block.data(), sector_size, block.data(), + offset / sector_size, sector_size, Op::Decrypt); + const std::size_t to_copy = std::min(length, got); + std::memcpy(data, block.data(), to_copy); + total_read += to_copy; } - cipher.XTSTranscode(block.data(), sector_size, block.data(), - offset / sector_size, sector_size, Op::Decrypt); - const std::size_t to_copy = std::min(length, got); - std::memcpy(data, block.data(), to_copy); - total_read += to_copy; } } - return total_read; } } // namespace Core::Crypto diff --git a/src/core/file_sys/fssystem/fssystem_aes_ctr_storage.cpp b/src/core/file_sys/fssystem/fssystem_aes_ctr_storage.cpp index 4e74a5c01e..1e11d70d8d 100644 --- a/src/core/file_sys/fssystem/fssystem_aes_ctr_storage.cpp +++ b/src/core/file_sys/fssystem/fssystem_aes_ctr_storage.cpp @@ -4,8 +4,8 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include "common/alignment.h" -#include #include "common/swap.h" #include "core/file_sys/fssystem/fssystem_aes_ctr_storage.h" #include "core/file_sys/fssystem/fssystem_utility.h" @@ -84,35 +84,24 @@ size_t AesCtrStorage::Write(const u8* buffer, size_t size, size_t offset) { std::memcpy(ctr.data(), m_iv.data(), IvSize); AddCounter(ctr.data(), IvSize, offset / BlockSize); - // Loop until all data is written. - size_t remaining = size; - s64 cur_offset = 0; - - // Get a pooled buffer. - thread_local std::vector pooled_buffer; - if (pooled_buffer.size() < BlockSize) { - pooled_buffer.resize(BlockSize); - } - while (remaining > 0) { + // Loop until all data is written using a pooled buffer residing on the stack (blocksize = 0x10) + boost::container::static_vector pooled_buffer; + for (size_t remaining = size; remaining > 0; ) { // Determine data we're writing and where. - const size_t write_size = std::min(pooled_buffer.size(), remaining); + auto const write_size = (std::min)(pooled_buffer.size(), remaining); u8* write_buf = pooled_buffer.data(); - // Encrypt the data. + // Encrypt the data and then write it. m_cipher->SetIV(ctr); m_cipher->Transcode(buffer, write_size, write_buf, Core::Crypto::Op::Encrypt); + m_base_storage->Write(write_buf, write_size, offset); - // Write the encrypted data. - m_base_storage->Write(write_buf, write_size, offset + cur_offset); - - // Advance. - cur_offset += write_size; + // Advance next write chunk + offset += write_size; remaining -= write_size; - if (remaining > 0) { + if (remaining > 0) AddCounter(ctr.data(), IvSize, write_size / BlockSize); - } } - return size; } diff --git a/src/core/file_sys/fssystem/fssystem_aes_xts_storage.cpp b/src/core/file_sys/fssystem/fssystem_aes_xts_storage.cpp index 2ba8644851..6e07d44cde 100644 --- a/src/core/file_sys/fssystem/fssystem_aes_xts_storage.cpp +++ b/src/core/file_sys/fssystem/fssystem_aes_xts_storage.cpp @@ -6,11 +6,11 @@ #include #include -#include - +#include #include "common/alignment.h" #include "common/swap.h" #include "core/file_sys/fssystem/fssystem_aes_xts_storage.h" +#include "core/file_sys/fssystem/fssystem_nca_header.h" #include "core/file_sys/fssystem/fssystem_utility.h" namespace FileSys { @@ -45,18 +45,12 @@ AesXtsStorage::AesXtsStorage(VirtualFile base, const void* key1, const void* key size_t AesXtsStorage::Read(u8* buffer, size_t size, size_t offset) const { // Allow zero-size reads. - if (size == 0) { + if (size == 0) return size; - } - // Ensure buffer is valid. + // Ensure buffer is valid and we can only read at block aligned offsets. ASSERT(buffer != nullptr); - - // We can only read at block aligned offsets. - ASSERT(Common::IsAligned(offset, AesBlockSize)); - ASSERT(Common::IsAligned(size, AesBlockSize)); - - // Read the data. + ASSERT(Common::IsAligned(offset, AesBlockSize) && Common::IsAligned(size, AesBlockSize)); m_base_storage->Read(buffer, size, offset); // Setup the counter. @@ -64,29 +58,21 @@ size_t AesXtsStorage::Read(u8* buffer, size_t size, size_t offset) const { std::memcpy(ctr.data(), m_iv.data(), IvSize); AddCounter(ctr.data(), IvSize, offset / m_block_size); - // Handle any unaligned data before the start. + // Handle any unaligned data before the start; then read said data into a local pooled + // buffer that resides on the stack, do not use the global memory allocator this is a + // very tiny (512 bytes) buffer so should be fine to keep on the stack (Nca::XtsBlockSize wide buffer) size_t processed_size = 0; if ((offset % m_block_size) != 0) { + // Decrypt into our pooled stack buffer (max bound = NCA::XtsBlockSize) + boost::container::static_vector tmp_buf; // Determine the size of the pre-data read. - const size_t skip_size = - static_cast(offset - Common::AlignDown(offset, m_block_size)); - const size_t data_size = (std::min)(size, m_block_size - skip_size); - - // Decrypt into a thread-local pooled buffer to avoid per-call allocations. - { - thread_local std::vector tmp_buf; - if (tmp_buf.size() < m_block_size) { - tmp_buf.resize(m_block_size); - } - std::fill_n(tmp_buf.begin(), m_block_size, u8{0}); - std::memcpy(tmp_buf.data() + skip_size, buffer, data_size); - - m_cipher->SetIV(ctr); - m_cipher->Transcode(tmp_buf.data(), m_block_size, tmp_buf.data(), - Core::Crypto::Op::Decrypt); - - std::memcpy(buffer, tmp_buf.data() + skip_size, data_size); - } + auto const skip_size = size_t(offset - Common::AlignDown(offset, m_block_size)); + auto const data_size = (std::min)(size, m_block_size - skip_size); + std::fill_n(tmp_buf.begin(), skip_size, u8{0}); + std::memcpy(tmp_buf.data() + skip_size, buffer, data_size); + m_cipher->SetIV(ctr); + m_cipher->Transcode(tmp_buf.data(), m_block_size, tmp_buf.data(), Core::Crypto::Op::Decrypt); + std::memcpy(buffer, tmp_buf.data() + skip_size, data_size); AddCounter(ctr.data(), IvSize, 1); processed_size += data_size; @@ -94,20 +80,16 @@ size_t AesXtsStorage::Read(u8* buffer, size_t size, size_t offset) const { } // Decrypt aligned chunks. - char* cur = reinterpret_cast(buffer) + processed_size; - size_t remaining = size - processed_size; - while (remaining > 0) { - const size_t cur_size = (std::min)(m_block_size, remaining); - + auto* cur = buffer + processed_size; + for (size_t remaining = size - processed_size; remaining > 0; ) { + auto const cur_size = (std::min)(m_block_size, remaining); m_cipher->SetIV(ctr); - m_cipher->Transcode(cur, cur_size, cur, Core::Crypto::Op::Decrypt); - + auto* char_cur = reinterpret_cast(cur); //same repr cur - diff signedness + m_cipher->Transcode(char_cur, cur_size, char_cur, Core::Crypto::Op::Decrypt); remaining -= cur_size; cur += cur_size; - AddCounter(ctr.data(), IvSize, 1); } - return size; }