diff options
Diffstat (limited to 'src/core/file_sys/fssystem/fssystem_aes_xts_storage.cpp')
-rw-r--r-- | src/core/file_sys/fssystem/fssystem_aes_xts_storage.cpp | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/src/core/file_sys/fssystem/fssystem_aes_xts_storage.cpp b/src/core/file_sys/fssystem/fssystem_aes_xts_storage.cpp new file mode 100644 index 000000000..022424229 --- /dev/null +++ b/src/core/file_sys/fssystem/fssystem_aes_xts_storage.cpp @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/alignment.h" +#include "common/swap.h" +#include "core/file_sys/errors.h" +#include "core/file_sys/fssystem/fssystem_aes_xts_storage.h" +#include "core/file_sys/fssystem/fssystem_pooled_buffer.h" +#include "core/file_sys/fssystem/fssystem_utility.h" + +namespace FileSys { + +void AesXtsStorage::MakeAesXtsIv(void* dst, size_t dst_size, s64 offset, size_t block_size) { + ASSERT(dst != nullptr); + ASSERT(dst_size == IvSize); + ASSERT(offset >= 0); + + const uintptr_t out_addr = reinterpret_cast<uintptr_t>(dst); + + *reinterpret_cast<s64_be*>(out_addr + sizeof(s64)) = offset / block_size; +} + +AesXtsStorage::AesXtsStorage(VirtualFile base, const void* key1, const void* key2, size_t key_size, + const void* iv, size_t iv_size, size_t block_size) + : m_base_storage(std::move(base)), m_block_size(block_size), m_mutex() { + ASSERT(m_base_storage != nullptr); + ASSERT(key1 != nullptr); + ASSERT(key2 != nullptr); + ASSERT(iv != nullptr); + ASSERT(key_size == KeySize); + ASSERT(iv_size == IvSize); + ASSERT(Common::IsAligned(m_block_size, AesBlockSize)); + + std::memcpy(m_key.data() + 0, key1, KeySize); + std::memcpy(m_key.data() + 0x10, key2, KeySize); + std::memcpy(m_iv.data(), iv, IvSize); + + m_cipher.emplace(m_key, Core::Crypto::Mode::XTS); +} + +size_t AesXtsStorage::Read(u8* buffer, size_t size, size_t offset) const { + // Allow zero-size reads. + if (size == 0) { + return size; + } + + // Ensure buffer is valid. + ASSERT(buffer != nullptr); + + // We can only read at block aligned offsets. + ASSERT(Common::IsAligned(offset, AesBlockSize)); + ASSERT(Common::IsAligned(size, AesBlockSize)); + + // Read the data. + m_base_storage->Read(buffer, size, offset); + + // Setup the counter. + std::array<u8, IvSize> ctr; + std::memcpy(ctr.data(), m_iv.data(), IvSize); + AddCounter(ctr.data(), 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 - Common::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); + 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); + + m_cipher->SetIV(ctr); + m_cipher->Transcode(tmp_buf.GetBuffer(), m_block_size, tmp_buf.GetBuffer(), + Core::Crypto::Op::Decrypt); + + std::memcpy(buffer, tmp_buf.GetBuffer() + skip_size, data_size); + } + + AddCounter(ctr.data(), IvSize, 1); + processed_size += data_size; + ASSERT(processed_size == std::min(size, m_block_size - skip_size)); + } + + // Decrypt aligned chunks. + char* cur = reinterpret_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); + + m_cipher->SetIV(ctr); + m_cipher->Transcode(cur, cur_size, cur, Core::Crypto::Op::Decrypt); + + remaining -= cur_size; + cur += cur_size; + + AddCounter(ctr.data(), IvSize, 1); + } + + return size; +} + +size_t AesXtsStorage::GetSize() const { + return m_base_storage->GetSize(); +} + +} // namespace FileSys |