2025-09-21 21:58:59 +02:00
|
|
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
|
2022-04-23 04:59:50 -04:00
|
|
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2018-08-16 17:01:32 -04:00
|
|
|
|
2018-08-24 22:15:32 -04:00
|
|
|
#include <algorithm>
|
2025-10-17 05:08:51 +02:00
|
|
|
#include <array>
|
2018-08-16 17:01:32 -04:00
|
|
|
#include <cstring>
|
|
|
|
|
#include "core/crypto/xts_encryption_layer.h"
|
|
|
|
|
|
|
|
|
|
namespace Core::Crypto {
|
|
|
|
|
|
2025-10-17 05:08:51 +02:00
|
|
|
constexpr std::size_t XTS_SECTOR_SIZE = 0x4000;
|
2018-08-18 21:14:57 -04:00
|
|
|
|
2018-08-16 17:01:32 -04:00
|
|
|
XTSEncryptionLayer::XTSEncryptionLayer(FileSys::VirtualFile base_, Key256 key_)
|
|
|
|
|
: EncryptionLayer(std::move(base_)), cipher(key_, Mode::XTS) {}
|
|
|
|
|
|
2018-09-15 15:21:06 +02:00
|
|
|
std::size_t XTSEncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const {
|
2018-08-16 17:01:32 -04:00
|
|
|
if (length == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2025-10-17 05:08:51 +02:00
|
|
|
std::size_t total_read = 0;
|
|
|
|
|
// Handle initial unaligned part within a sector.
|
|
|
|
|
if (auto const sector_offset = offset % XTS_SECTOR_SIZE; sector_offset != 0) {
|
|
|
|
|
const std::size_t aligned_off = offset - sector_offset;
|
|
|
|
|
std::array<u8, XTS_SECTOR_SIZE> block{};
|
|
|
|
|
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,
|
2018-08-18 21:14:57 -04:00
|
|
|
XTS_SECTOR_SIZE, Op::Decrypt);
|
2025-10-17 05:08:51 +02:00
|
|
|
|
|
|
|
|
auto const to_copy = std::min<std::size_t>(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;
|
2018-08-16 17:01:32 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-17 05:08:51 +02:00
|
|
|
if (length > 0) {
|
|
|
|
|
// Process aligned middle inplace, in sector sized multiples.
|
|
|
|
|
while (length >= XTS_SECTOR_SIZE) {
|
|
|
|
|
const std::size_t req = (length / XTS_SECTOR_SIZE) * XTS_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 % XTS_SECTOR_SIZE);
|
|
|
|
|
if (got_rounded > 0) {
|
|
|
|
|
cipher.XTSTranscode(data, got_rounded, data, offset / XTS_SECTOR_SIZE, XTS_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<u8, XTS_SECTOR_SIZE> block{};
|
|
|
|
|
const std::size_t got = base->Read(block.data(), XTS_SECTOR_SIZE, offset);
|
|
|
|
|
if (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(),
|
|
|
|
|
offset / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt);
|
|
|
|
|
const std::size_t to_copy = std::min<std::size_t>(length, got);
|
|
|
|
|
std::memcpy(data, block.data(), to_copy);
|
|
|
|
|
total_read += to_copy;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-16 17:01:32 -04:00
|
|
|
}
|
2025-10-17 05:08:51 +02:00
|
|
|
return total_read;
|
2018-08-16 17:01:32 -04:00
|
|
|
}
|
|
|
|
|
} // namespace Core::Crypto
|