|
|
|
@ -16,44 +16,80 @@ 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; |
|
|
|
|
|
|
|
const auto sector_offset = offset & 0x3FFF; |
|
|
|
if (sector_offset == 0) { |
|
|
|
if (length % XTS_SECTOR_SIZE == 0) { |
|
|
|
std::vector<u8> raw = base->ReadBytes(length, offset); |
|
|
|
cipher.XTSTranscode(raw.data(), raw.size(), data, offset / XTS_SECTOR_SIZE, |
|
|
|
XTS_SECTOR_SIZE, Op::Decrypt); |
|
|
|
return raw.size(); |
|
|
|
// Handle initial unaligned part within a sector.
|
|
|
|
if (sector_offset != 0) { |
|
|
|
const std::size_t aligned_off = offset - sector_offset; |
|
|
|
std::array<u8, XTS_SECTOR_SIZE> block{}; |
|
|
|
std::size_t got = base->Read(block.data(), sector_size, aligned_off); |
|
|
|
if (got == 0) { |
|
|
|
return 0; |
|
|
|
} |
|
|
|
if (length > XTS_SECTOR_SIZE) { |
|
|
|
const auto rem = length % XTS_SECTOR_SIZE; |
|
|
|
const auto read = length - rem; |
|
|
|
return Read(data, read, offset) + Read(data + read, rem, offset + read); |
|
|
|
if (got < sector_size) { |
|
|
|
std::fill(block.begin() + got, block.end(), 0); |
|
|
|
} |
|
|
|
cipher.XTSTranscode(block.data(), sector_size, block.data(), aligned_off / sector_size, |
|
|
|
sector_size, Op::Decrypt); |
|
|
|
|
|
|
|
const std::size_t 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; |
|
|
|
} |
|
|
|
std::vector<u8> buffer = base->ReadBytes(XTS_SECTOR_SIZE, offset); |
|
|
|
if (buffer.size() < XTS_SECTOR_SIZE) |
|
|
|
buffer.resize(XTS_SECTOR_SIZE); |
|
|
|
cipher.XTSTranscode(buffer.data(), buffer.size(), buffer.data(), offset / XTS_SECTOR_SIZE, |
|
|
|
XTS_SECTOR_SIZE, Op::Decrypt); |
|
|
|
std::memcpy(data, buffer.data(), (std::min)(buffer.size(), length)); |
|
|
|
return (std::min)(buffer.size(), length); |
|
|
|
} |
|
|
|
|
|
|
|
// offset does not fall on block boundary (0x4000)
|
|
|
|
std::vector<u8> block = base->ReadBytes(0x4000, offset - sector_offset); |
|
|
|
if (block.size() < XTS_SECTOR_SIZE) |
|
|
|
block.resize(XTS_SECTOR_SIZE); |
|
|
|
cipher.XTSTranscode(block.data(), block.size(), block.data(), |
|
|
|
(offset - sector_offset) / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt); |
|
|
|
const std::size_t read = XTS_SECTOR_SIZE - sector_offset; |
|
|
|
|
|
|
|
if (length + sector_offset < XTS_SECTOR_SIZE) { |
|
|
|
std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read)); |
|
|
|
return std::min<u64>(length, read); |
|
|
|
if (length == 0) { |
|
|
|
return total_read; |
|
|
|
} |
|
|
|
std::memcpy(data, block.data() + sector_offset, read); |
|
|
|
return read + Read(data + read, length - read, offset + read); |
|
|
|
|
|
|
|
// 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<u8, XTS_SECTOR_SIZE> block{}; |
|
|
|
const std::size_t got = base->Read(block.data(), sector_size, offset); |
|
|
|
if (got > 0) { |
|
|
|
if (got < sector_size) { |
|
|
|
std::fill(block.begin() + got, block.end(), 0); |
|
|
|
} |
|
|
|
cipher.XTSTranscode(block.data(), sector_size, block.data(), offset / sec |
|
|
|
tor_size, 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; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return total_read; |
|
|
|
} |
|
|
|
} // namespace Core::Crypto
|