1use std::io::{self, Read, Seek, SeekFrom};
5
6use crate::error::FileVaultError;
7use crate::unlock::VolumeKeys;
8use crate::xts;
9
10const SECTOR_SIZE: usize = 512;
12
13#[derive(Debug)]
19pub struct DecryptedVolume<R: Read + Seek> {
20 reader: R,
21 keys: VolumeKeys,
22 physical_base: u64,
24 size: u64,
26 position: u64,
28}
29
30impl<R: Read + Seek> DecryptedVolume<R> {
31 pub(crate) fn new(reader: R, keys: VolumeKeys, physical_base: u64, size: u64) -> Self {
33 DecryptedVolume {
34 reader,
35 keys,
36 physical_base,
37 size,
38 position: 0,
39 }
40 }
41
42 #[must_use]
44 pub fn size(&self) -> u64 {
45 self.size
46 }
47
48 pub fn read_at(&mut self, offset: u64, buf: &mut [u8]) -> Result<usize, FileVaultError> {
58 if offset >= self.size || buf.is_empty() {
59 return Ok(0);
60 }
61 let available = self.size - offset;
62 let want = (buf.len() as u64).min(available) as usize;
63
64 let first_sector = offset / SECTOR_SIZE as u64;
65 let end = offset + want as u64;
66 let last_sector = (end - 1) / SECTOR_SIZE as u64;
67 let sector_count = (last_sector - first_sector + 1) as usize;
68
69 let region_len =
70 sector_count
71 .checked_mul(SECTOR_SIZE)
72 .ok_or(FileVaultError::OutOfRange {
73 what: "read region length",
74 })?;
75 let mut region = vec![0u8; region_len];
76
77 let physical = self.physical_base + first_sector * SECTOR_SIZE as u64;
78 self.reader.seek(SeekFrom::Start(physical))?;
79 read_full_or_eof(&mut self.reader, &mut region)?;
83
84 xts::decrypt_units(
85 &mut region,
86 &self.keys.vmk,
87 &self.keys.tweak_key,
88 SECTOR_SIZE,
89 u128::from(first_sector),
90 );
91
92 let inner = (offset - first_sector * SECTOR_SIZE as u64) as usize;
93 let slice = region
94 .get(inner..inner + want)
95 .ok_or(FileVaultError::OutOfRange {
96 what: "decrypted window",
97 })?;
98 buf.get_mut(..want)
99 .ok_or(FileVaultError::OutOfRange {
100 what: "output buffer window",
101 })?
102 .copy_from_slice(slice);
103 Ok(want)
104 }
105}
106
107fn read_full_or_eof<R: Read>(reader: &mut R, buf: &mut [u8]) -> io::Result<()> {
110 let mut filled = 0;
111 while filled < buf.len() {
112 match reader.read(&mut buf[filled..]) {
113 Ok(0) => break,
114 Ok(n) => filled += n,
115 Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
116 Err(e) => return Err(e),
117 }
118 }
119 Ok(())
120}
121
122impl<R: Read + Seek> Read for DecryptedVolume<R> {
123 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
124 let n = self.read_at(self.position, buf).map_err(io::Error::other)?;
125 self.position += n as u64;
126 Ok(n)
127 }
128}
129
130impl<R: Read + Seek> Seek for DecryptedVolume<R> {
131 fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
132 let new = match pos {
133 SeekFrom::Start(o) => o,
134 SeekFrom::End(o) => add_signed(self.size, o)?,
135 SeekFrom::Current(o) => add_signed(self.position, o)?,
136 };
137 self.position = new;
138 Ok(new)
139 }
140}
141
142fn add_signed(base: u64, offset: i64) -> io::Result<u64> {
144 let result = if offset >= 0 {
145 base.checked_add(offset as u64)
146 } else {
147 base.checked_sub(offset.unsigned_abs())
148 };
149 result.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "seek out of range"))
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155 use std::io::Cursor;
156
157 fn keys() -> VolumeKeys {
158 VolumeKeys {
159 vmk: [0x55u8; 16],
160 tweak_key: [0x66u8; 16],
161 }
162 }
163
164 fn build_backing(base: u64, plaintext: &[u8], keys: &VolumeKeys) -> Vec<u8> {
167 let mut cipher = plaintext.to_vec();
168 crate::xts::encrypt_units(&mut cipher, &keys.vmk, &keys.tweak_key, SECTOR_SIZE, 0);
169 let mut image = vec![0u8; base as usize];
170 image.extend_from_slice(&cipher);
171 image
172 }
173
174 #[test]
175 fn read_at_decrypts_aligned_sector() {
176 let k = keys();
177 let plaintext: Vec<u8> = (0..1024u32).map(|i| (i & 0xff) as u8).collect();
178 let image = build_backing(4096, &plaintext, &k);
179 let mut vol =
180 DecryptedVolume::new(Cursor::new(image), keys(), 4096, plaintext.len() as u64);
181 let mut buf = [0u8; 512];
182 assert_eq!(vol.read_at(0, &mut buf).unwrap(), 512);
183 assert_eq!(&buf[..], &plaintext[..512]);
184 assert_eq!(vol.read_at(512, &mut buf).unwrap(), 512);
185 assert_eq!(&buf[..], &plaintext[512..1024]);
186 }
187
188 #[test]
189 fn read_at_handles_unaligned_window() {
190 let k = keys();
191 let plaintext: Vec<u8> = (0..1024u32).map(|i| (i & 0xff) as u8).collect();
192 let image = build_backing(0, &plaintext, &k);
193 let mut vol = DecryptedVolume::new(Cursor::new(image), keys(), 0, plaintext.len() as u64);
194 let mut buf = [0u8; 300];
195 assert_eq!(vol.read_at(600, &mut buf).unwrap(), 300);
197 assert_eq!(&buf[..], &plaintext[600..900]);
198 }
199
200 #[test]
201 fn read_at_past_end_returns_zero() {
202 let mut vol = DecryptedVolume::new(Cursor::new(vec![0u8; 4096]), keys(), 0, 512);
203 let mut buf = [0u8; 16];
204 assert_eq!(vol.read_at(512, &mut buf).unwrap(), 0);
205 assert_eq!(vol.read_at(1000, &mut buf).unwrap(), 0);
206 }
207
208 #[test]
209 fn read_at_truncates_to_volume_end() {
210 let k = keys();
211 let plaintext: Vec<u8> = (0..512u32).map(|i| (i & 0xff) as u8).collect();
212 let image = build_backing(0, &plaintext, &k);
213 let mut vol = DecryptedVolume::new(Cursor::new(image), keys(), 0, 512);
214 let mut buf = [0u8; 512];
215 assert_eq!(vol.read_at(256, &mut buf).unwrap(), 256);
217 assert_eq!(&buf[..256], &plaintext[256..512]);
218 }
219
220 #[test]
221 fn empty_buffer_reads_nothing() {
222 let mut vol = DecryptedVolume::new(Cursor::new(vec![0u8; 4096]), keys(), 0, 512);
223 assert_eq!(vol.read_at(0, &mut []).unwrap(), 0);
224 }
225
226 #[test]
227 fn size_accessor() {
228 let vol = DecryptedVolume::new(Cursor::new(vec![0u8; 16]), keys(), 0, 167_772_160);
229 assert_eq!(vol.size(), 167_772_160);
230 }
231
232 #[test]
233 fn read_and_seek_impls() {
234 let k = keys();
235 let plaintext: Vec<u8> = (0..1024u32).map(|i| (i & 0xff) as u8).collect();
236 let image = build_backing(0, &plaintext, &k);
237 let mut vol = DecryptedVolume::new(Cursor::new(image), keys(), 0, plaintext.len() as u64);
238 let mut buf = [0u8; 512];
239 assert_eq!(std::io::Read::read(&mut vol, &mut buf).unwrap(), 512);
240 assert_eq!(&buf[..], &plaintext[..512]);
241 assert_eq!(vol.seek(SeekFrom::Start(512)).unwrap(), 512);
243 assert_eq!(std::io::Read::read(&mut vol, &mut buf).unwrap(), 512);
244 assert_eq!(&buf[..], &plaintext[512..1024]);
245 assert_eq!(vol.seek(SeekFrom::End(0)).unwrap(), 1024);
247 assert_eq!(vol.seek(SeekFrom::Current(-1024)).unwrap(), 0);
248 }
249
250 #[test]
251 fn seek_out_of_range_errors() {
252 let mut vol = DecryptedVolume::new(Cursor::new(vec![0u8; 16]), keys(), 0, 512);
253 assert!(vol.seek(SeekFrom::Current(-1)).is_err());
254 assert!(vol.seek(SeekFrom::End(1)).is_ok());
255 }
256
257 struct FlakyReader {
259 data: Vec<u8>,
260 pos: usize,
261 interrupted_once: bool,
262 }
263
264 impl Read for FlakyReader {
265 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
266 if !self.interrupted_once {
267 self.interrupted_once = true;
268 return Err(io::Error::from(io::ErrorKind::Interrupted));
269 }
270 if self.pos >= self.data.len() {
271 return Err(io::Error::other("backing read failed"));
272 }
273 let n = (buf.len()).min(self.data.len() - self.pos).min(4);
274 buf[..n].copy_from_slice(&self.data[self.pos..self.pos + n]);
275 self.pos += n;
276 Ok(n)
277 }
278 }
279
280 #[test]
281 fn read_full_or_eof_retries_interrupt_and_propagates_error() {
282 let mut r = FlakyReader {
284 data: vec![1u8; 8],
285 pos: 0,
286 interrupted_once: false,
287 };
288 let mut buf = [0u8; 16];
289 let err = read_full_or_eof(&mut r, &mut buf).unwrap_err();
290 assert_eq!(err.to_string(), "backing read failed");
291 assert_eq!(&buf[..8], &[1u8; 8]);
293 }
294
295 struct EofReader;
297 impl Read for EofReader {
298 fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
299 Ok(0)
300 }
301 }
302
303 #[test]
304 fn read_full_or_eof_stops_at_clean_eof() {
305 let mut r = EofReader;
306 let mut buf = [0xffu8; 8];
307 read_full_or_eof(&mut r, &mut buf).unwrap();
308 assert_eq!(buf, [0xffu8; 8]);
310 }
311}