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