1use std::io::{Read, Seek, SeekFrom};
5
6use crate::crypto::{xts_decrypt, Cipher, Prf};
7use crate::error::{Result, VeraError};
8use crate::header::{
9 Flavor, VeraHeader, HEADER_LEN, HIDDEN_HEADER_OFFSET, NORMAL_HEADER_OFFSET, SALT_LEN,
10 VOLUME_HEADER_LEN,
11};
12
13const DATA_SECTOR: usize = 512;
15
16pub struct VeraVolume;
18
19#[derive(Debug, Clone)]
21pub struct VolumeInfo {
22 pub flavor: Flavor,
24 pub prf: Prf,
26 pub cipher: Cipher,
28 pub version: u16,
30 pub encrypted_area_start: u64,
32 pub encrypted_area_size: u64,
34}
35
36impl VeraVolume {
37 pub fn unlock_with_password<R: Read + Seek>(
43 reader: R,
44 password: &[u8],
45 ) -> Result<DecryptedVolume<R>> {
46 Self::unlock_at(reader, password, 0, NORMAL_HEADER_OFFSET)
47 }
48
49 pub fn unlock_with_pim<R: Read + Seek>(
54 reader: R,
55 password: &[u8],
56 pim: u32,
57 ) -> Result<DecryptedVolume<R>> {
58 Self::unlock_at(reader, password, pim, NORMAL_HEADER_OFFSET)
59 }
60
61 pub fn unlock_hidden_with_password<R: Read + Seek>(
67 reader: R,
68 password: &[u8],
69 ) -> Result<DecryptedVolume<R>> {
70 Self::unlock_at(reader, password, 0, HIDDEN_HEADER_OFFSET)
71 }
72
73 pub fn unlock_hidden_with_pim<R: Read + Seek>(
78 reader: R,
79 password: &[u8],
80 pim: u32,
81 ) -> Result<DecryptedVolume<R>> {
82 Self::unlock_at(reader, password, pim, HIDDEN_HEADER_OFFSET)
83 }
84
85 fn unlock_at<R: Read + Seek>(
88 mut reader: R,
89 password: &[u8],
90 pim: u32,
91 header_offset: u64,
92 ) -> Result<DecryptedVolume<R>> {
93 let total_size = reader.seek(SeekFrom::End(0))?;
94 if total_size < header_offset + VOLUME_HEADER_LEN as u64 {
95 return Err(VeraError::TooSmall {
96 got: total_size as usize,
97 });
98 }
99
100 let mut hdr = [0u8; VOLUME_HEADER_LEN];
101 reader.seek(SeekFrom::Start(header_offset))?;
102 read_fill(&mut reader, &mut hdr)?;
103 let salt = &hdr[0..SALT_LEN];
104 let header_ct = &hdr[SALT_LEN..VOLUME_HEADER_LEN];
105
106 for prf in Prf::all() {
107 let hk = prf.derive(password, salt, prf.iterations_pim(pim), 64);
108 for cipher in Cipher::all() {
109 let mut dec = header_ct.to_vec();
110 xts_decrypt(cipher, &hk, &mut dec, HEADER_LEN, 0)?;
111 let Some(h) = VeraHeader::validate(&dec) else {
112 continue;
113 };
114 let mut master_key = vec![0u8; cipher.key_len()];
115 master_key.copy_from_slice(&h.master_keys[..cipher.key_len()]);
116 return Ok(DecryptedVolume {
117 reader,
118 cipher,
119 master_key,
120 data_offset: h.encrypted_area_start,
121 base_unit: u128::from(h.encrypted_area_start / DATA_SECTOR as u64),
122 total_size,
123 position: 0,
124 info: VolumeInfo {
125 flavor: h.flavor,
126 prf,
127 cipher,
128 version: h.version,
129 encrypted_area_start: h.encrypted_area_start,
130 encrypted_area_size: h.encrypted_area_size,
131 },
132 });
133 }
134 }
135 Err(VeraError::AuthenticationFailed)
136 }
137}
138
139pub struct DecryptedVolume<R> {
141 reader: R,
142 cipher: Cipher,
143 master_key: Vec<u8>,
144 data_offset: u64,
145 base_unit: u128,
146 total_size: u64,
147 position: u64,
148 info: VolumeInfo,
149}
150
151impl<R: Read + Seek> DecryptedVolume<R> {
152 #[must_use]
154 pub fn info(&self) -> &VolumeInfo {
155 &self.info
156 }
157
158 #[must_use]
160 pub fn master_key(&self) -> &[u8] {
161 &self.master_key
162 }
163
164 #[must_use]
166 pub fn data_size(&self) -> u64 {
167 if self.info.encrypted_area_size != 0 {
168 self.info.encrypted_area_size
169 } else {
170 self.total_size.saturating_sub(self.data_offset)
171 }
172 }
173
174 pub fn read_at(&mut self, offset: u64, buf: &mut [u8]) -> Result<()> {
180 let mut done = 0usize;
181 while done < buf.len() {
182 let pos = offset + done as u64;
183 let unit = pos / DATA_SECTOR as u64;
184 let within = (pos % DATA_SECTOR as u64) as usize;
185 let physical = self.data_offset + unit * DATA_SECTOR as u64;
186
187 let mut ct = [0u8; DATA_SECTOR];
188 self.reader.seek(SeekFrom::Start(physical))?;
189 read_available(&mut self.reader, &mut ct)?;
190 xts_decrypt(
191 self.cipher,
192 &self.master_key,
193 &mut ct,
194 DATA_SECTOR,
195 self.base_unit + u128::from(unit),
196 )?; let take = (DATA_SECTOR - within).min(buf.len() - done);
199 buf[done..done + take].copy_from_slice(&ct[within..within + take]);
200 done += take;
201 }
202 Ok(())
203 }
204}
205
206impl<R: Read + Seek> Read for DecryptedVolume<R> {
207 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
208 let size = self.data_size();
209 if self.position >= size {
210 return Ok(0);
211 }
212 let n = (buf.len() as u64).min(size - self.position) as usize;
213 self.read_at(self.position, &mut buf[..n])
214 .map_err(|e| std::io::Error::other(e.to_string()))?;
215 self.position += n as u64;
216 Ok(n)
217 }
218}
219
220impl<R: Read + Seek> Seek for DecryptedVolume<R> {
221 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
222 let size = self.data_size();
223 let new = match pos {
224 SeekFrom::Start(o) => i128::from(o),
225 SeekFrom::End(o) => i128::from(size) + i128::from(o),
226 SeekFrom::Current(o) => i128::from(self.position) + i128::from(o),
227 };
228 if new < 0 {
229 return Err(std::io::Error::new(
230 std::io::ErrorKind::InvalidInput,
231 "seek before start",
232 ));
233 }
234 self.position = new as u64;
235 Ok(self.position)
236 }
237}
238
239fn read_fill<R: Read>(reader: &mut R, buf: &mut [u8]) -> Result<()> {
240 reader.read_exact(buf)?;
241 Ok(())
242}
243
244fn read_available<R: Read>(reader: &mut R, buf: &mut [u8]) -> Result<usize> {
245 let mut filled = 0;
246 while filled < buf.len() {
247 match reader.read(&mut buf[filled..]) {
248 Ok(0) => break,
249 Ok(n) => filled += n,
250 Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {}
251 Err(e) => return Err(e.into()),
252 }
253 }
254 for b in &mut buf[filled..] {
255 *b = 0;
256 }
257 Ok(filled)
258}
259
260#[cfg(test)]
261mod tests {
262 use std::io::{Cursor, Read as _, Seek as _, SeekFrom};
263
264 use aes::cipher::KeyInit;
265 use aes::Aes256;
266 use xts_mode::Xts128;
267
268 use super::*;
269 use crate::crypto::Prf;
270
271 const PASSWORD: &[u8] = b"correct horse";
272 const DATA_START: u64 = 512; const DATA_SECTORS: usize = 3;
274
275 fn xts_encrypt_aes(key64: &[u8; 64], buf: &mut [u8], unit_size: usize, base: u128) {
277 let (k1, k2) = key64.split_at(32);
278 let xts = Xts128::new(Aes256::new(k1.into()), Aes256::new(k2.into()));
279 for (u, chunk) in buf.chunks_mut(unit_size).enumerate() {
280 xts.encrypt_sector(chunk, (base + u as u128).to_le_bytes());
281 }
282 }
283
284 fn build_volume() -> (Vec<u8>, Vec<u8>) {
290 build_volume_with(true)
291 }
292
293 fn build_volume_with(declare_size: bool) -> (Vec<u8>, Vec<u8>) {
294 let salt = [0x11u8; SALT_LEN];
295 let master_key = [0x24u8; 64];
296
297 let mut dec = [0u8; HEADER_LEN];
299 dec[0..4].copy_from_slice(b"VERA");
300 dec[4..6].copy_from_slice(&5u16.to_be_bytes());
301 let data_size = (DATA_SECTORS * DATA_SECTOR) as u64;
302 dec[36..44].copy_from_slice(&(DATA_START + data_size).to_be_bytes()); dec[44..52].copy_from_slice(&DATA_START.to_be_bytes()); let declared = if declare_size { data_size } else { 0 };
306 dec[52..60].copy_from_slice(&declared.to_be_bytes()); dec[64..68].copy_from_slice(&512u32.to_be_bytes());
308 dec[192..256].copy_from_slice(&master_key); let crc_mk = crc32fast::hash(&dec[192..448]);
310 dec[8..12].copy_from_slice(&crc_mk.to_be_bytes());
311 let crc_hdr = crc32fast::hash(&dec[0..188]);
312 dec[188..192].copy_from_slice(&crc_hdr.to_be_bytes());
313
314 let header_key = Prf::Sha512.derive(PASSWORD, &salt, Prf::Sha512.iterations_pim(1), 64);
316 let mut header_ct = dec.to_vec();
317 let hk: [u8; 64] = header_key.try_into().unwrap();
318 xts_encrypt_aes(&hk, &mut header_ct, HEADER_LEN, 0);
319
320 let mut plain = vec![0u8; DATA_SECTORS * DATA_SECTOR];
322 for (i, b) in plain.iter_mut().enumerate() {
323 *b = (i as u8).wrapping_mul(7) ^ 0xa5;
324 }
325 let base_unit = u128::from(DATA_START / DATA_SECTOR as u64);
326 let mut data_ct = plain.clone();
327 xts_encrypt_aes(&master_key, &mut data_ct, DATA_SECTOR, base_unit);
328
329 let mut container = Vec::new();
330 container.extend_from_slice(&salt);
331 container.extend_from_slice(&header_ct);
332 container.extend_from_slice(&data_ct);
333 (container, plain)
334 }
335
336 #[test]
337 fn hermetic_unlock_and_read_roundtrip() {
338 let (container, plain) = build_volume();
339 let mut vol = VeraVolume::unlock_with_pim(Cursor::new(container), PASSWORD, 1)
340 .expect("unlock synthetic volume");
341
342 assert_eq!(vol.info().prf.name(), "sha512");
343 assert_eq!(vol.info().cipher.name(), "aes");
344 assert_eq!(vol.info().flavor, Flavor::VeraCrypt);
345 assert_eq!(vol.info().version, 5);
346 assert_eq!(vol.info().encrypted_area_start, DATA_START);
347 assert_eq!(vol.master_key().len(), 64);
348 assert_eq!(vol.data_size(), plain.len() as u64);
349
350 for lba in 0..DATA_SECTORS as u64 {
352 let mut buf = [0u8; DATA_SECTOR];
353 vol.read_at(lba * DATA_SECTOR as u64, &mut buf).unwrap();
354 let want = &plain[(lba as usize) * DATA_SECTOR..(lba as usize + 1) * DATA_SECTOR];
355 assert_eq!(&buf[..], want, "sector {lba}");
356 }
357
358 let mut span = [0u8; 10];
360 vol.read_at(510, &mut span).unwrap();
361 assert_eq!(&span[..], &plain[510..520]);
362 }
363
364 #[test]
365 fn hermetic_read_and_seek_traits() {
366 let (container, plain) = build_volume();
367 let mut vol =
368 VeraVolume::unlock_with_pim(Cursor::new(container), PASSWORD, 1).expect("unlock");
369
370 let mut all = Vec::new();
372 vol.read_to_end(&mut all).unwrap();
373 assert_eq!(all, plain);
374 assert_eq!(vol.read(&mut [0u8; 16]).unwrap(), 0);
376
377 let pos = vol.seek(SeekFrom::End(-512)).unwrap();
379 assert_eq!(pos, (plain.len() - 512) as u64);
380 assert_eq!(vol.seek(SeekFrom::Current(0)).unwrap(), pos);
381 assert_eq!(vol.seek(SeekFrom::Start(0)).unwrap(), 0);
382 assert!(vol.seek(SeekFrom::Current(-1)).is_err());
383 }
384
385 #[test]
386 fn hidden_offset_too_small_errors() {
387 let small = vec![0u8; VOLUME_HEADER_LEN];
389 assert!(matches!(
390 VeraVolume::unlock_hidden_with_password(Cursor::new(small), PASSWORD),
391 Err(VeraError::TooSmall { .. })
392 ));
393 }
394
395 #[test]
396 fn hidden_pim_offset_too_small_errors() {
397 let small = vec![0u8; VOLUME_HEADER_LEN];
399 assert!(matches!(
400 VeraVolume::unlock_hidden_with_pim(Cursor::new(small), PASSWORD, 1),
401 Err(VeraError::TooSmall { .. })
402 ));
403 }
404
405 #[test]
406 fn too_small_container_errors() {
407 assert!(matches!(
408 VeraVolume::unlock_with_password(Cursor::new(vec![0u8; 100]), PASSWORD),
409 Err(VeraError::TooSmall { got }) if got == 100
410 ));
411 }
412
413 #[test]
414 fn wrong_password_fails() {
415 let (container, _) = build_volume();
416 assert!(matches!(
417 VeraVolume::unlock_with_pim(Cursor::new(container), b"wrong", 1),
418 Err(VeraError::AuthenticationFailed)
419 ));
420 }
421
422 #[test]
423 fn undeclared_size_falls_back_to_container_length() {
424 let (container, plain) = build_volume_with(false);
425 let vol = VeraVolume::unlock_with_pim(Cursor::new(container), PASSWORD, 1).expect("unlock");
426 assert_eq!(vol.data_size(), plain.len() as u64);
428 assert_eq!(vol.info().encrypted_area_size, 0);
429 }
430
431 #[test]
432 fn read_available_handles_eof_interrupt_and_hard_error() {
433 use std::io;
434
435 struct Eof;
437 impl io::Read for Eof {
438 fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
439 Ok(0)
440 }
441 }
442 let mut buf = [0xffu8; 8];
443 assert_eq!(read_available(&mut Eof, &mut buf).unwrap(), 0);
444 assert_eq!(buf, [0u8; 8]);
445
446 struct Flaky(u8);
448 impl io::Read for Flaky {
449 fn read(&mut self, b: &mut [u8]) -> io::Result<usize> {
450 self.0 += 1;
451 match self.0 {
452 1 => Err(io::Error::new(io::ErrorKind::Interrupted, "eintr")),
453 2 => {
454 b[0] = 0xAB;
455 Ok(1)
456 }
457 _ => Ok(0),
458 }
459 }
460 }
461 let mut b2 = [0u8; 4];
462 assert_eq!(read_available(&mut Flaky(0), &mut b2).unwrap(), 1);
463 assert_eq!(b2[0], 0xAB);
464
465 struct Boom;
467 impl io::Read for Boom {
468 fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
469 Err(io::Error::other("boom"))
470 }
471 }
472 let mut b3 = [0u8; 4];
473 assert!(read_available(&mut Boom, &mut b3).is_err());
474 }
475}