1use std::io::{Read, Seek, SeekFrom};
10
11use crate::crypto::{aes_ccm_unwrap, password_hash, recovery_key_hash, stretch_key, SectorCipher};
12use crate::error::{BdeError, Result};
13use crate::header::VolumeHeader;
14use crate::metadata::{
15 FveMetadata, PROTECTION_PASSWORD, PROTECTION_RECOVERY, VALUE_TYPE_AES_CCM, VALUE_TYPE_STRETCH,
16};
17use crate::method::{EncryptionMethod, SectorCipherKind};
18
19const METHOD_NONE: u16 = 0x0000;
21const SECTOR_SIZE: usize = 512;
23const METADATA_READ_LEN: usize = 64 * 1024;
26
27pub struct BitLockerVolume;
30
31impl BitLockerVolume {
32 pub fn read_metadata<R: Read + Seek>(reader: &mut R) -> Result<FveMetadata> {
40 let mut header = [0u8; SECTOR_SIZE];
41 reader.seek(SeekFrom::Start(0))?;
42 read_fill(reader, &mut header)?;
43 let volume_header = VolumeHeader::parse(&header)?;
44
45 let offsets = volume_header.fve_metadata_offsets;
46 for &offset in &offsets {
47 if offset == 0 {
48 continue;
49 }
50 let mut block = vec![0u8; METADATA_READ_LEN];
51 reader.seek(SeekFrom::Start(offset))?;
52 let n = read_available(reader, &mut block)?;
53 block.truncate(n);
54 if let Some(meta) = FveMetadata::parse(&block, volume_header.bytes_per_sector) {
55 return Ok(meta);
56 }
57 }
58 Err(BdeError::NoValidMetadata { offsets })
59 }
60
61 pub fn unlock_with_password<R: Read + Seek>(
68 reader: R,
69 password: &str,
70 ) -> Result<DecryptedVolume<R>> {
71 Self::unlock_with_protector(
72 reader,
73 PROTECTION_PASSWORD,
74 "password",
75 password_hash(password),
76 )
77 }
78
79 pub fn unlock_with_recovery_password<R: Read + Seek>(
89 reader: R,
90 recovery: &str,
91 ) -> Result<DecryptedVolume<R>> {
92 let key_hash = recovery_key_hash(recovery)
93 .map_err(|reason| BdeError::InvalidRecoveryPassword { reason })?;
94 Self::unlock_with_protector(reader, PROTECTION_RECOVERY, "recovery password", key_hash)
95 }
96
97 fn unlock_with_protector<R: Read + Seek>(
100 mut reader: R,
101 protector_type: u16,
102 protector_name: &'static str,
103 key_hash: [u8; 32],
104 ) -> Result<DecryptedVolume<R>> {
105 let metadata = Self::read_metadata(&mut reader)?;
106
107 let raw = metadata.encryption_method;
112 let kind = EncryptionMethod::decode(raw)
113 .ok_or(BdeError::UnsupportedEncryptionMethod { method: raw })?
114 .validated_kind()
115 .ok_or(BdeError::UnvalidatedEncryptionMethod { method: raw })?;
116
117 let cipher = derive_cipher(&metadata, kind, protector_type, protector_name, &key_hash)?;
118 let total_size = reader.seek(SeekFrom::End(0))?;
119
120 Ok(DecryptedVolume {
121 reader,
122 cipher,
123 metadata,
124 total_size,
125 position: 0,
126 })
127 }
128}
129
130fn derive_cipher(
135 metadata: &FveMetadata,
136 kind: SectorCipherKind,
137 protector_type: u16,
138 protector_name: &'static str,
139 key_hash: &[u8; 32],
140) -> Result<SectorCipher> {
141 let vmk = metadata
143 .vmk_entries()
144 .find(|e| e.protection_type() == Some(protector_type))
145 .ok_or_else(|| BdeError::NoUnlockProtector {
146 protector: protector_name,
147 found: metadata.protector_types(),
148 })?;
149
150 let props = vmk.nested(28);
152 let stretch = props
153 .iter()
154 .find(|e| e.value_type == VALUE_TYPE_STRETCH)
155 .ok_or(BdeError::MissingKeyMaterial {
156 what: "stretch key",
157 })?;
158 let vmk_ccm = props
159 .iter()
160 .find(|e| e.value_type == VALUE_TYPE_AES_CCM)
161 .ok_or(BdeError::MissingKeyMaterial {
162 what: "VMK AES-CCM key",
163 })?;
164
165 let mut salt = [0u8; 16];
167 let salt_src = stretch
168 .data
169 .get(4..20)
170 .ok_or(BdeError::MissingKeyMaterial {
171 what: "stretch salt",
172 })?;
173 salt.copy_from_slice(salt_src);
174
175 let stretched = stretch_key(key_hash, &salt);
177 let vmk_container =
178 aes_ccm_unwrap(&stretched, &vmk_ccm.data).ok_or(BdeError::AuthenticationFailed {
179 what: "volume master key",
180 })?;
181 let vmk_key = take_key32(&vmk_container, 12, "volume master key")?;
182
183 let fvek_entry = metadata
186 .fvek_entry()
187 .ok_or(BdeError::MissingKeyMaterial { what: "FVEK entry" })?;
188 let fvek_container = aes_ccm_unwrap(&vmk_key, &fvek_entry.data)
189 .ok_or(BdeError::AuthenticationFailed { what: "FVEK" })?;
190
191 match kind {
192 SectorCipherKind::Cbc128Diffuser => {
193 let fvek = take_key16(&fvek_container, 12, "FVEK")?;
194 let tweak = take_key16(&fvek_container, 44, "FVEK")?;
195 Ok(SectorCipher::new(fvek, tweak))
196 }
197 SectorCipherKind::Cbc128 => {
198 let fvek = take_key16(&fvek_container, 12, "FVEK")?;
199 Ok(SectorCipher::new_cbc(fvek))
200 }
201 SectorCipherKind::Cbc256 => {
202 let fvek = take_key32(&fvek_container, 12, "FVEK")?;
203 Ok(SectorCipher::new_cbc256(fvek))
204 }
205 SectorCipherKind::Xts128 => {
206 let fvek = take_key32(&fvek_container, 12, "FVEK")?;
208 Ok(SectorCipher::new_xts128(fvek))
209 }
210 SectorCipherKind::Xts256 => {
211 let fvek = take_key64(&fvek_container, 12, "FVEK")?;
213 Ok(SectorCipher::new_xts256(fvek))
214 }
215 }
216}
217
218fn take_key64(container: &[u8], off: usize, what: &'static str) -> Result<[u8; 64]> {
219 let s = container
220 .get(off..off + 64)
221 .ok_or(BdeError::MalformedKeyContainer {
222 what,
223 got: container.len(),
224 need: off + 64,
225 })?;
226 let mut k = [0u8; 64];
227 k.copy_from_slice(s);
228 Ok(k)
229}
230
231fn take_key32(container: &[u8], off: usize, what: &'static str) -> Result<[u8; 32]> {
232 let s = container
233 .get(off..off + 32)
234 .ok_or(BdeError::MalformedKeyContainer {
235 what,
236 got: container.len(),
237 need: off + 32,
238 })?;
239 let mut k = [0u8; 32];
240 k.copy_from_slice(s);
241 Ok(k)
242}
243
244fn take_key16(container: &[u8], off: usize, what: &'static str) -> Result<[u8; 16]> {
245 let s = container
246 .get(off..off + 16)
247 .ok_or(BdeError::MalformedKeyContainer {
248 what,
249 got: container.len(),
250 need: off + 16,
251 })?;
252 let mut k = [0u8; 16];
253 k.copy_from_slice(s);
254 Ok(k)
255}
256
257pub struct DecryptedVolume<R> {
259 reader: R,
260 cipher: SectorCipher,
261 metadata: FveMetadata,
262 total_size: u64,
263 position: u64,
264}
265
266impl<R: Read + Seek> DecryptedVolume<R> {
267 #[must_use]
269 pub fn metadata(&self) -> &FveMetadata {
270 &self.metadata
271 }
272
273 #[must_use]
275 pub fn volume_size(&self) -> u64 {
276 self.total_size
277 }
278
279 pub fn read_at(&mut self, offset: u64, buf: &mut [u8]) -> Result<()> {
285 let mut done = 0usize;
286 while done < buf.len() {
287 let pos = offset + done as u64;
288 let sector_start = pos - (pos % SECTOR_SIZE as u64);
289 let within = (pos - sector_start) as usize;
290 let plain = self.decrypt_logical_sector(sector_start)?;
291 let take = (SECTOR_SIZE - within).min(buf.len() - done);
292 buf[done..done + take].copy_from_slice(&plain[within..within + take]);
293 done += take;
294 }
295 Ok(())
296 }
297
298 fn decrypt_logical_sector(&mut self, sector_start: u64) -> Result<[u8; SECTOR_SIZE]> {
302 let meta_size = u64::from(self.metadata.metadata_size);
304 for &m in &self.metadata.metadata_offsets {
305 if m != 0 && sector_start >= m && sector_start < m + meta_size {
306 return Ok([0u8; SECTOR_SIZE]);
307 }
308 }
309
310 let physical = if sector_start < self.metadata.volume_header_size {
313 self.metadata.volume_header_offset + sector_start
314 } else {
315 sector_start
316 };
317
318 let mut ct = [0u8; SECTOR_SIZE];
319 self.reader.seek(SeekFrom::Start(physical))?;
320 read_available(&mut self.reader, &mut ct)?;
321
322 let evs = self.metadata.encrypted_volume_size;
324 if self.metadata.encryption_method == METHOD_NONE || (evs != 0 && physical >= evs) {
325 return Ok(ct);
326 }
327
328 let plain = self.cipher.decrypt_sector(&ct, physical);
329 let mut out = [0u8; SECTOR_SIZE];
330 let n = plain.len().min(SECTOR_SIZE);
331 out[..n].copy_from_slice(&plain[..n]);
332 Ok(out)
333 }
334}
335
336impl<R: Read + Seek> Read for DecryptedVolume<R> {
337 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
338 if self.position >= self.total_size {
339 return Ok(0);
340 }
341 let remaining = self.total_size - self.position;
342 let n = (buf.len() as u64).min(remaining) as usize;
343 self.read_at(self.position, &mut buf[..n])
344 .map_err(|e| std::io::Error::other(e.to_string()))?;
345 self.position += n as u64;
346 Ok(n)
347 }
348}
349
350impl<R: Read + Seek> Seek for DecryptedVolume<R> {
351 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
352 let new = match pos {
353 SeekFrom::Start(o) => i128::from(o),
354 SeekFrom::End(o) => i128::from(self.total_size) + i128::from(o),
355 SeekFrom::Current(o) => i128::from(self.position) + i128::from(o),
356 };
357 if new < 0 {
358 return Err(std::io::Error::new(
359 std::io::ErrorKind::InvalidInput,
360 "seek before start",
361 ));
362 }
363 self.position = new as u64;
364 Ok(self.position)
365 }
366}
367
368fn read_fill<R: Read>(reader: &mut R, buf: &mut [u8]) -> Result<()> {
370 reader.read_exact(buf)?;
371 Ok(())
372}
373
374fn read_available<R: Read>(reader: &mut R, buf: &mut [u8]) -> Result<usize> {
377 let mut filled = 0;
378 while filled < buf.len() {
379 match reader.read(&mut buf[filled..]) {
380 Ok(0) => break,
381 Ok(n) => filled += n,
382 Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {}
383 Err(e) => return Err(e.into()),
384 }
385 }
386 for b in &mut buf[filled..] {
387 *b = 0;
388 }
389 Ok(filled)
390}
391
392#[cfg(test)]
393mod tests {
394 use super::*;
395 use crate::crypto::{aes_ccm_wrap, password_hash, stretch_key, SectorCipher};
396 use crate::metadata::{
397 ENTRY_TYPE_FVEK, ENTRY_TYPE_VMK, ENTRY_TYPE_VOLUME_HEADER, PROTECTION_PASSWORD,
398 };
399 use std::io::Cursor;
400
401 const RELOCATED_OFFSET: u64 = 0x4000;
402 const META_BLOCK_OFFSET: u64 = 0x1000;
403 const IMAGE_SIZE: usize = 0x5000;
404 const ENCRYPTED_SIZE: u64 = 0x4800;
407
408 fn entry(entry_type: u16, value_type: u16, data: &[u8]) -> Vec<u8> {
409 let size = (8 + data.len()) as u16;
410 let mut v = Vec::new();
411 v.extend_from_slice(&size.to_le_bytes());
412 v.extend_from_slice(&entry_type.to_le_bytes());
413 v.extend_from_slice(&value_type.to_le_bytes());
414 v.extend_from_slice(&1u16.to_le_bytes());
415 v.extend_from_slice(data);
416 v
417 }
418
419 fn build_volume(password: &str) -> (Vec<u8>, [u8; 512]) {
422 let salt = [0x33u8; 16];
423 let fvek = [0x11u8; 16];
424 let tweak = [0x22u8; 16];
425 let vmk = [0x44u8; 32];
426
427 let mut vmk_container = vec![0u8; 44];
428 vmk_container[12..44].copy_from_slice(&vmk);
429 let stretched = stretch_key(&password_hash(password), &salt);
430 let vmk_ccm = aes_ccm_wrap(&stretched, &[0x55; 12], &vmk_container);
431
432 let mut fvek_container = vec![0u8; 76];
433 fvek_container[12..28].copy_from_slice(&fvek);
434 fvek_container[44..60].copy_from_slice(&tweak);
435 let fvek_ccm = aes_ccm_wrap(&vmk, &[0x66; 12], &fvek_container);
436
437 let mut stretch_data = vec![0u8; 4]; stretch_data.extend_from_slice(&salt);
439 let stretch_entry = entry(0, VALUE_TYPE_STRETCH, &stretch_data);
440 let vmk_ccm_entry = entry(0, VALUE_TYPE_AES_CCM, &vmk_ccm);
441
442 let mut vmk_data = vec![0u8; 28];
443 vmk_data[26..28].copy_from_slice(&PROTECTION_PASSWORD.to_le_bytes());
444 vmk_data.extend_from_slice(&stretch_entry);
445 vmk_data.extend_from_slice(&vmk_ccm_entry);
446 let vmk_entry = entry(ENTRY_TYPE_VMK, VALUE_TYPE_VMK, &vmk_data);
447
448 let fvek_entry = entry(ENTRY_TYPE_FVEK, VALUE_TYPE_AES_CCM, &fvek_ccm);
449
450 let mut vh_data = Vec::new();
451 vh_data.extend_from_slice(&RELOCATED_OFFSET.to_le_bytes());
452 vh_data.extend_from_slice(&512u64.to_le_bytes());
453 let vh_entry = entry(ENTRY_TYPE_VOLUME_HEADER, ENTRY_TYPE_VOLUME_HEADER, &vh_data);
454
455 let mut entries = Vec::new();
456 entries.extend_from_slice(&vh_entry);
457 entries.extend_from_slice(&vmk_entry);
458 entries.extend_from_slice(&fvek_entry);
459 let metadata_size = 48 + entries.len();
460
461 let mut image = vec![0u8; IMAGE_SIZE];
462 image[0..3].copy_from_slice(&[0xeb, 0x58, 0x90]);
463 image[3..11].copy_from_slice(b"MSWIN4.1");
464 image[12] = 0x02; image[440..448].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
466
467 let mb = META_BLOCK_OFFSET as usize;
468 image[mb..mb + 8].copy_from_slice(b"-FVE-FS-");
469 image[mb + 10..mb + 12].copy_from_slice(&2u16.to_le_bytes());
470 image[mb + 16..mb + 24].copy_from_slice(&ENCRYPTED_SIZE.to_le_bytes());
471 image[mb + 28..mb + 32].copy_from_slice(&1u32.to_le_bytes());
472 image[mb + 32..mb + 40].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
473 image[mb + 56..mb + 64].copy_from_slice(&RELOCATED_OFFSET.to_le_bytes());
474 image[mb + 64..mb + 68].copy_from_slice(&(metadata_size as u32).to_le_bytes());
475 image[mb + 64 + 36..mb + 64 + 38].copy_from_slice(&0x8000u16.to_le_bytes());
476 image[mb + 64 + 48..mb + 64 + 48 + entries.len()].copy_from_slice(&entries);
477
478 let mut plain = [0u8; 512];
479 for (i, b) in plain.iter_mut().enumerate() {
480 *b = (i as u8) ^ 0x5a;
481 }
482 let cipher = SectorCipher::new(fvek, tweak);
483 let ct = cipher.encrypt_sector(&plain, RELOCATED_OFFSET);
484 let ro = RELOCATED_OFFSET as usize;
485 image[ro..ro + 512].copy_from_slice(&ct);
486
487 (image, plain)
488 }
489
490 use crate::metadata::{VALUE_TYPE_AES_CCM, VALUE_TYPE_STRETCH, VALUE_TYPE_VMK};
491
492 #[test]
493 fn unlock_and_read_synthetic_volume() {
494 let (image, plain) = build_volume("test-pw");
498 let mut vol = BitLockerVolume::unlock_with_password(Cursor::new(image), "test-pw").unwrap();
499 let mut buf = [0u8; 512];
500 vol.read_at(0, &mut buf).unwrap();
501 assert_eq!(buf, plain);
502 assert_eq!(vol.metadata().encryption_method, 0x8000);
503
504 let mut z = [1u8; 512];
506 vol.read_at(META_BLOCK_OFFSET, &mut z).unwrap();
507 assert_eq!(z, [0u8; 512]);
508
509 let mut e = [0u8; 512];
512 vol.read_at(0x600, &mut e).unwrap();
513
514 let mut r = [0u8; 512];
517 vol.read_at(ENCRYPTED_SIZE, &mut r).unwrap();
518 assert_eq!(r, [0u8; 512]);
519 }
520
521 fn cbc_encrypt_sector(fvek: &[u8; 16], byte_offset: u64, plain: &[u8; 512]) -> [u8; 512] {
526 use aes::cipher::block_padding::NoPadding;
527 use aes::cipher::generic_array::GenericArray;
528 use aes::cipher::{BlockEncrypt, BlockEncryptMut, KeyInit, KeyIvInit};
529 use aes::Aes128;
530
531 let mut iv = [0u8; 16];
532 iv[0..8].copy_from_slice(&byte_offset.to_le_bytes());
533 let mut iv_block = GenericArray::clone_from_slice(&iv);
534 Aes128::new(GenericArray::from_slice(fvek)).encrypt_block(&mut iv_block);
535
536 let mut buf = *plain;
537 cbc::Encryptor::<Aes128>::new(GenericArray::from_slice(fvek), &iv_block)
538 .encrypt_padded_mut::<NoPadding>(&mut buf, 512)
539 .unwrap();
540 buf
541 }
542
543 fn build_cbc_volume(protector_type: u16, key_hash: [u8; 32]) -> (Vec<u8>, [u8; 512]) {
549 let salt = [0x37u8; 16];
550 let fvek = [0x13u8; 16];
551 let vmk = [0x46u8; 32];
552
553 let mut vmk_container = vec![0u8; 44];
554 vmk_container[12..44].copy_from_slice(&vmk);
555 let stretched = stretch_key(&key_hash, &salt);
556 let vmk_ccm = aes_ccm_wrap(&stretched, &[0x57; 12], &vmk_container);
557
558 let mut fvek_container = vec![0u8; 44];
560 fvek_container[12..28].copy_from_slice(&fvek);
561 let fvek_ccm = aes_ccm_wrap(&vmk, &[0x68; 12], &fvek_container);
562
563 let mut stretch_data = vec![0u8; 4];
564 stretch_data.extend_from_slice(&salt);
565 let stretch_entry = entry(0, VALUE_TYPE_STRETCH, &stretch_data);
566 let vmk_ccm_entry = entry(0, VALUE_TYPE_AES_CCM, &vmk_ccm);
567
568 let mut vmk_data = vec![0u8; 28];
569 vmk_data[26..28].copy_from_slice(&protector_type.to_le_bytes());
570 vmk_data.extend_from_slice(&stretch_entry);
571 vmk_data.extend_from_slice(&vmk_ccm_entry);
572 let vmk_entry = entry(ENTRY_TYPE_VMK, VALUE_TYPE_VMK, &vmk_data);
573
574 let fvek_entry = entry(ENTRY_TYPE_FVEK, VALUE_TYPE_AES_CCM, &fvek_ccm);
575
576 let mut vh_data = Vec::new();
577 vh_data.extend_from_slice(&RELOCATED_OFFSET.to_le_bytes());
578 vh_data.extend_from_slice(&512u64.to_le_bytes());
579 let vh_entry = entry(ENTRY_TYPE_VOLUME_HEADER, ENTRY_TYPE_VOLUME_HEADER, &vh_data);
580
581 let mut entries = Vec::new();
582 entries.extend_from_slice(&vh_entry);
583 entries.extend_from_slice(&vmk_entry);
584 entries.extend_from_slice(&fvek_entry);
585 let metadata_size = 48 + entries.len();
586
587 let mut image = vec![0u8; IMAGE_SIZE];
588 image[0..3].copy_from_slice(&[0xeb, 0x58, 0x90]);
589 image[3..11].copy_from_slice(b"MSWIN4.1");
590 image[12] = 0x02;
591 image[440..448].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
592
593 let mb = META_BLOCK_OFFSET as usize;
594 image[mb..mb + 8].copy_from_slice(b"-FVE-FS-");
595 image[mb + 10..mb + 12].copy_from_slice(&2u16.to_le_bytes());
596 image[mb + 16..mb + 24].copy_from_slice(&ENCRYPTED_SIZE.to_le_bytes());
597 image[mb + 28..mb + 32].copy_from_slice(&1u32.to_le_bytes());
598 image[mb + 32..mb + 40].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
599 image[mb + 56..mb + 64].copy_from_slice(&RELOCATED_OFFSET.to_le_bytes());
600 image[mb + 64..mb + 68].copy_from_slice(&(metadata_size as u32).to_le_bytes());
601 image[mb + 64 + 36..mb + 64 + 38].copy_from_slice(&0x8002u16.to_le_bytes());
602 image[mb + 64 + 48..mb + 64 + 48 + entries.len()].copy_from_slice(&entries);
603
604 let mut plain = [0u8; 512];
605 for (i, b) in plain.iter_mut().enumerate() {
606 *b = (i as u8) ^ 0x3c;
607 }
608 let ct = cbc_encrypt_sector(&fvek, RELOCATED_OFFSET, &plain);
609 let ro = RELOCATED_OFFSET as usize;
610 image[ro..ro + 512].copy_from_slice(&ct);
611
612 (image, plain)
613 }
614
615 #[test]
616 fn unlock_and_read_synthetic_cbc_volume() {
617 let (image, plain) = build_cbc_volume(PROTECTION_PASSWORD, password_hash("cbc-pw"));
622 let mut vol = BitLockerVolume::unlock_with_password(Cursor::new(image), "cbc-pw").unwrap();
623 let mut buf = [0u8; 512];
624 vol.read_at(0, &mut buf).unwrap();
625 assert_eq!(buf, plain);
626 assert_eq!(vol.metadata().encryption_method, 0x8002);
627 }
628
629 #[test]
630 fn unlock_and_read_synthetic_recovery_volume() {
631 let rk = "111111-111111-111111-111111-111111-111111-111111-111111";
636 let key_hash = crate::crypto::recovery_key_hash(rk).unwrap();
637 let (image, plain) = build_cbc_volume(crate::metadata::PROTECTION_RECOVERY, key_hash);
638 let mut vol =
639 BitLockerVolume::unlock_with_recovery_password(Cursor::new(image), rk).unwrap();
640 let mut buf = [0u8; 512];
641 vol.read_at(0, &mut buf).unwrap();
642 assert_eq!(buf, plain);
643 assert_eq!(vol.metadata().encryption_method, 0x8002);
644 }
645
646 #[test]
647 fn recovery_unlock_rejects_malformed_password() {
648 let rk = "111111-111111-111111-111111-111111-111111-111111-111111";
649 let key_hash = crate::crypto::recovery_key_hash(rk).unwrap();
650 let (image, _) = build_cbc_volume(crate::metadata::PROTECTION_RECOVERY, key_hash);
651 let res = BitLockerVolume::unlock_with_recovery_password(Cursor::new(image), "nope");
652 assert!(matches!(res, Err(BdeError::InvalidRecoveryPassword { .. })));
653 }
654
655 #[test]
656 fn no_recovery_protector_errors() {
657 let rk = "111111-111111-111111-111111-111111-111111-111111-111111";
659 let (image, _) = build_cbc_volume(PROTECTION_PASSWORD, password_hash("cbc-pw"));
660 let res = BitLockerVolume::unlock_with_recovery_password(Cursor::new(image), rk);
661 assert!(matches!(res, Err(BdeError::NoUnlockProtector { .. })));
662 }
663
664 fn build_cbc256_volume(key_hash: [u8; 32]) -> (Vec<u8>, [u8; 512]) {
668 let salt = [0x39u8; 16];
669 let fvek = [0x24u8; 32];
670 let vmk = [0x48u8; 32];
671
672 let mut vmk_container = vec![0u8; 44];
673 vmk_container[12..44].copy_from_slice(&vmk);
674 let stretched = stretch_key(&key_hash, &salt);
675 let vmk_ccm = aes_ccm_wrap(&stretched, &[0x59; 12], &vmk_container);
676
677 let mut fvek_container = vec![0u8; 76];
679 fvek_container[12..44].copy_from_slice(&fvek);
680 let fvek_ccm = aes_ccm_wrap(&vmk, &[0x6a; 12], &fvek_container);
681
682 let mut stretch_data = vec![0u8; 4];
683 stretch_data.extend_from_slice(&salt);
684 let stretch_entry = entry(0, VALUE_TYPE_STRETCH, &stretch_data);
685 let vmk_ccm_entry = entry(0, VALUE_TYPE_AES_CCM, &vmk_ccm);
686
687 let mut vmk_data = vec![0u8; 28];
688 vmk_data[26..28].copy_from_slice(&PROTECTION_RECOVERY.to_le_bytes());
689 vmk_data.extend_from_slice(&stretch_entry);
690 vmk_data.extend_from_slice(&vmk_ccm_entry);
691 let vmk_entry = entry(ENTRY_TYPE_VMK, VALUE_TYPE_VMK, &vmk_data);
692
693 let fvek_entry = entry(ENTRY_TYPE_FVEK, VALUE_TYPE_AES_CCM, &fvek_ccm);
694
695 let mut vh_data = Vec::new();
696 vh_data.extend_from_slice(&RELOCATED_OFFSET.to_le_bytes());
697 vh_data.extend_from_slice(&512u64.to_le_bytes());
698 let vh_entry = entry(ENTRY_TYPE_VOLUME_HEADER, ENTRY_TYPE_VOLUME_HEADER, &vh_data);
699
700 let mut entries = Vec::new();
701 entries.extend_from_slice(&vh_entry);
702 entries.extend_from_slice(&vmk_entry);
703 entries.extend_from_slice(&fvek_entry);
704 let metadata_size = 48 + entries.len();
705
706 let mut image = vec![0u8; IMAGE_SIZE];
707 image[0..3].copy_from_slice(&[0xeb, 0x58, 0x90]);
708 image[3..11].copy_from_slice(b"MSWIN4.1");
709 image[12] = 0x02;
710 image[440..448].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
711
712 let mb = META_BLOCK_OFFSET as usize;
713 image[mb..mb + 8].copy_from_slice(b"-FVE-FS-");
714 image[mb + 10..mb + 12].copy_from_slice(&2u16.to_le_bytes());
715 image[mb + 16..mb + 24].copy_from_slice(&ENCRYPTED_SIZE.to_le_bytes());
716 image[mb + 28..mb + 32].copy_from_slice(&1u32.to_le_bytes());
717 image[mb + 32..mb + 40].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
718 image[mb + 56..mb + 64].copy_from_slice(&RELOCATED_OFFSET.to_le_bytes());
719 image[mb + 64..mb + 68].copy_from_slice(&(metadata_size as u32).to_le_bytes());
720 image[mb + 64 + 36..mb + 64 + 38].copy_from_slice(&0x8003u16.to_le_bytes());
721 image[mb + 64 + 48..mb + 64 + 48 + entries.len()].copy_from_slice(&entries);
722
723 let mut plain = [0u8; 512];
724 for (i, b) in plain.iter_mut().enumerate() {
725 *b = (i as u8) ^ 0x71;
726 }
727 let ct = SectorCipher::new_cbc256(fvek).encrypt_sector(&plain, RELOCATED_OFFSET);
728 let ro = RELOCATED_OFFSET as usize;
729 image[ro..ro + 512].copy_from_slice(&ct);
730
731 (image, plain)
732 }
733
734 #[test]
735 fn unlock_and_read_synthetic_cbc256_volume() {
736 let rk = "222222-222222-222222-222222-222222-222222-222222-222222";
739 let key_hash = crate::crypto::recovery_key_hash(rk).unwrap();
740 let (image, plain) = build_cbc256_volume(key_hash);
741 let mut vol =
742 BitLockerVolume::unlock_with_recovery_password(Cursor::new(image), rk).unwrap();
743 let mut buf = [0u8; 512];
744 vol.read_at(0, &mut buf).unwrap();
745 assert_eq!(buf, plain);
746 assert_eq!(vol.metadata().encryption_method, 0x8003);
747 }
748
749 fn build_xts128_volume(key_hash: [u8; 32]) -> (Vec<u8>, [u8; 512]) {
753 let salt = [0x3bu8; 16];
754 let xts_key = [0x35u8; 32];
755 let vmk = [0x4au8; 32];
756
757 let mut vmk_container = vec![0u8; 44];
758 vmk_container[12..44].copy_from_slice(&vmk);
759 let stretched = stretch_key(&key_hash, &salt);
760 let vmk_ccm = aes_ccm_wrap(&stretched, &[0x5b; 12], &vmk_container);
761
762 let mut fvek_container = vec![0u8; 76];
764 fvek_container[12..44].copy_from_slice(&xts_key);
765 let fvek_ccm = aes_ccm_wrap(&vmk, &[0x6c; 12], &fvek_container);
766
767 let mut stretch_data = vec![0u8; 4];
768 stretch_data.extend_from_slice(&salt);
769 let stretch_entry = entry(0, VALUE_TYPE_STRETCH, &stretch_data);
770 let vmk_ccm_entry = entry(0, VALUE_TYPE_AES_CCM, &vmk_ccm);
771
772 let mut vmk_data = vec![0u8; 28];
773 vmk_data[26..28].copy_from_slice(&PROTECTION_RECOVERY.to_le_bytes());
774 vmk_data.extend_from_slice(&stretch_entry);
775 vmk_data.extend_from_slice(&vmk_ccm_entry);
776 let vmk_entry = entry(ENTRY_TYPE_VMK, VALUE_TYPE_VMK, &vmk_data);
777
778 let fvek_entry = entry(ENTRY_TYPE_FVEK, VALUE_TYPE_AES_CCM, &fvek_ccm);
779
780 let mut vh_data = Vec::new();
781 vh_data.extend_from_slice(&RELOCATED_OFFSET.to_le_bytes());
782 vh_data.extend_from_slice(&512u64.to_le_bytes());
783 let vh_entry = entry(ENTRY_TYPE_VOLUME_HEADER, ENTRY_TYPE_VOLUME_HEADER, &vh_data);
784
785 let mut entries = Vec::new();
786 entries.extend_from_slice(&vh_entry);
787 entries.extend_from_slice(&vmk_entry);
788 entries.extend_from_slice(&fvek_entry);
789 let metadata_size = 48 + entries.len();
790
791 let mut image = vec![0u8; IMAGE_SIZE];
792 image[0..3].copy_from_slice(&[0xeb, 0x58, 0x90]);
793 image[3..11].copy_from_slice(b"MSWIN4.1");
794 image[12] = 0x02;
795 image[440..448].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
796
797 let mb = META_BLOCK_OFFSET as usize;
798 image[mb..mb + 8].copy_from_slice(b"-FVE-FS-");
799 image[mb + 10..mb + 12].copy_from_slice(&2u16.to_le_bytes());
800 image[mb + 16..mb + 24].copy_from_slice(&ENCRYPTED_SIZE.to_le_bytes());
801 image[mb + 28..mb + 32].copy_from_slice(&1u32.to_le_bytes());
802 image[mb + 32..mb + 40].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
803 image[mb + 56..mb + 64].copy_from_slice(&RELOCATED_OFFSET.to_le_bytes());
804 image[mb + 64..mb + 68].copy_from_slice(&(metadata_size as u32).to_le_bytes());
805 image[mb + 64 + 36..mb + 64 + 38].copy_from_slice(&0x8004u16.to_le_bytes());
806 image[mb + 64 + 48..mb + 64 + 48 + entries.len()].copy_from_slice(&entries);
807
808 let mut plain = [0u8; 512];
809 for (i, b) in plain.iter_mut().enumerate() {
810 *b = (i as u8) ^ 0x2e;
811 }
812 let ct = SectorCipher::new_xts128(xts_key).encrypt_sector(&plain, RELOCATED_OFFSET);
813 let ro = RELOCATED_OFFSET as usize;
814 image[ro..ro + 512].copy_from_slice(&ct);
815
816 (image, plain)
817 }
818
819 #[test]
820 fn unlock_and_read_synthetic_xts128_volume() {
821 let rk = "333333-333333-333333-333333-333333-333333-333333-333333";
824 let key_hash = crate::crypto::recovery_key_hash(rk).unwrap();
825 let (image, plain) = build_xts128_volume(key_hash);
826 let mut vol =
827 BitLockerVolume::unlock_with_recovery_password(Cursor::new(image), rk).unwrap();
828 let mut buf = [0u8; 512];
829 vol.read_at(0, &mut buf).unwrap();
830 assert_eq!(buf, plain);
831 assert_eq!(vol.metadata().encryption_method, 0x8004);
832 }
833
834 fn build_xts256_volume(key_hash: [u8; 32]) -> (Vec<u8>, [u8; 512]) {
838 let salt = [0x3du8; 16];
839 let mut xts_key = [0u8; 64];
840 for (i, b) in xts_key.iter_mut().enumerate() {
841 *b = 0x46u8.wrapping_add(i as u8);
842 }
843 let vmk = [0x4cu8; 32];
844
845 let mut vmk_container = vec![0u8; 44];
846 vmk_container[12..44].copy_from_slice(&vmk);
847 let stretched = stretch_key(&key_hash, &salt);
848 let vmk_ccm = aes_ccm_wrap(&stretched, &[0x5d; 12], &vmk_container);
849
850 let mut fvek_container = vec![0u8; 76];
852 fvek_container[12..76].copy_from_slice(&xts_key);
853 let fvek_ccm = aes_ccm_wrap(&vmk, &[0x6e; 12], &fvek_container);
854
855 let mut stretch_data = vec![0u8; 4];
856 stretch_data.extend_from_slice(&salt);
857 let stretch_entry = entry(0, VALUE_TYPE_STRETCH, &stretch_data);
858 let vmk_ccm_entry = entry(0, VALUE_TYPE_AES_CCM, &vmk_ccm);
859
860 let mut vmk_data = vec![0u8; 28];
861 vmk_data[26..28].copy_from_slice(&PROTECTION_RECOVERY.to_le_bytes());
862 vmk_data.extend_from_slice(&stretch_entry);
863 vmk_data.extend_from_slice(&vmk_ccm_entry);
864 let vmk_entry = entry(ENTRY_TYPE_VMK, VALUE_TYPE_VMK, &vmk_data);
865
866 let fvek_entry = entry(ENTRY_TYPE_FVEK, VALUE_TYPE_AES_CCM, &fvek_ccm);
867
868 let mut vh_data = Vec::new();
869 vh_data.extend_from_slice(&RELOCATED_OFFSET.to_le_bytes());
870 vh_data.extend_from_slice(&512u64.to_le_bytes());
871 let vh_entry = entry(ENTRY_TYPE_VOLUME_HEADER, ENTRY_TYPE_VOLUME_HEADER, &vh_data);
872
873 let mut entries = Vec::new();
874 entries.extend_from_slice(&vh_entry);
875 entries.extend_from_slice(&vmk_entry);
876 entries.extend_from_slice(&fvek_entry);
877 let metadata_size = 48 + entries.len();
878
879 let mut image = vec![0u8; IMAGE_SIZE];
880 image[0..3].copy_from_slice(&[0xeb, 0x58, 0x90]);
881 image[3..11].copy_from_slice(b"MSWIN4.1");
882 image[12] = 0x02;
883 image[440..448].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
884
885 let mb = META_BLOCK_OFFSET as usize;
886 image[mb..mb + 8].copy_from_slice(b"-FVE-FS-");
887 image[mb + 10..mb + 12].copy_from_slice(&2u16.to_le_bytes());
888 image[mb + 16..mb + 24].copy_from_slice(&ENCRYPTED_SIZE.to_le_bytes());
889 image[mb + 28..mb + 32].copy_from_slice(&1u32.to_le_bytes());
890 image[mb + 32..mb + 40].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
891 image[mb + 56..mb + 64].copy_from_slice(&RELOCATED_OFFSET.to_le_bytes());
892 image[mb + 64..mb + 68].copy_from_slice(&(metadata_size as u32).to_le_bytes());
893 image[mb + 64 + 36..mb + 64 + 38].copy_from_slice(&0x8005u16.to_le_bytes());
894 image[mb + 64 + 48..mb + 64 + 48 + entries.len()].copy_from_slice(&entries);
895
896 let mut plain = [0u8; 512];
897 for (i, b) in plain.iter_mut().enumerate() {
898 *b = (i as u8) ^ 0x17;
899 }
900 let ct = SectorCipher::new_xts256(xts_key).encrypt_sector(&plain, RELOCATED_OFFSET);
901 let ro = RELOCATED_OFFSET as usize;
902 image[ro..ro + 512].copy_from_slice(&ct);
903
904 (image, plain)
905 }
906
907 #[test]
908 fn unlock_and_read_synthetic_xts256_volume() {
909 let rk = "444444-444444-444444-444444-444444-444444-444444-444444";
912 let key_hash = crate::crypto::recovery_key_hash(rk).unwrap();
913 let (image, plain) = build_xts256_volume(key_hash);
914 let mut vol =
915 BitLockerVolume::unlock_with_recovery_password(Cursor::new(image), rk).unwrap();
916 let mut buf = [0u8; 512];
917 vol.read_at(0, &mut buf).unwrap();
918 assert_eq!(buf, plain);
919 assert_eq!(vol.metadata().encryption_method, 0x8005);
920 }
921
922 #[test]
923 fn seek_variants_and_eof() {
924 use std::io::{Read as _, Seek as _};
925 let (image, _) = build_volume("test-pw");
926 let mut vol = BitLockerVolume::unlock_with_password(Cursor::new(image), "test-pw").unwrap();
927
928 assert_eq!(vol.seek(SeekFrom::End(0)).unwrap(), IMAGE_SIZE as u64);
929 let mut b = [0u8; 16];
930 assert_eq!(vol.read(&mut b).unwrap(), 0); assert_eq!(vol.seek(SeekFrom::Start(10)).unwrap(), 10);
933 assert_eq!(vol.seek(SeekFrom::Current(5)).unwrap(), 15);
934 assert!(vol.seek(SeekFrom::Current(-100)).is_err()); }
936
937 fn meta_only_image(
940 method: u16,
941 protectors: &[u16],
942 header_offsets: [u64; 3],
943 block_at: Option<usize>,
944 ) -> Vec<u8> {
945 let mut entries = Vec::new();
946 for p in protectors {
947 let mut d = vec![0u8; 28];
948 d[26..28].copy_from_slice(&p.to_le_bytes());
949 entries.extend(entry(0x0002, 0x0008, &d));
950 }
951 let metadata_size = 48 + entries.len();
952 let mut image = vec![0u8; 0x2000];
953 image[0..3].copy_from_slice(&[0xeb, 0x58, 0x90]);
954 image[3..11].copy_from_slice(b"MSWIN4.1");
955 image[12] = 0x02;
956 image[440..448].copy_from_slice(&header_offsets[0].to_le_bytes());
957 image[448..456].copy_from_slice(&header_offsets[1].to_le_bytes());
958 image[456..464].copy_from_slice(&header_offsets[2].to_le_bytes());
959 if let Some(mb) = block_at {
960 image[mb..mb + 8].copy_from_slice(b"-FVE-FS-");
961 image[mb + 10..mb + 12].copy_from_slice(&2u16.to_le_bytes());
962 image[mb + 32..mb + 40].copy_from_slice(&(mb as u64).to_le_bytes());
963 image[mb + 64..mb + 68].copy_from_slice(&(metadata_size as u32).to_le_bytes());
964 image[mb + 64 + 36..mb + 64 + 38].copy_from_slice(&method.to_le_bytes());
965 image[mb + 64 + 48..mb + 64 + 48 + entries.len()].copy_from_slice(&entries);
966 }
967 image
968 }
969
970 #[test]
971 fn recognized_but_unvalidated_method_refuses() {
972 let img = meta_only_image(0x8001, &[0x2000], [0x1000, 0, 0], Some(0x1000));
977 let res = BitLockerVolume::unlock_with_password(Cursor::new(img), "x");
978 assert!(matches!(
979 res,
980 Err(BdeError::UnvalidatedEncryptionMethod { method: 0x8001 })
981 ));
982 }
983
984 #[test]
985 fn unrecognized_method_errors() {
986 let img = meta_only_image(0x1234, &[0x2000], [0x1000, 0, 0], Some(0x1000));
988 let res = BitLockerVolume::unlock_with_password(Cursor::new(img), "x");
989 assert!(matches!(
990 res,
991 Err(BdeError::UnsupportedEncryptionMethod { method: 0x1234 })
992 ));
993 }
994
995 #[test]
996 fn no_password_protector_errors() {
997 let img = meta_only_image(0x8000, &[0x0800], [0x1000, 0, 0], Some(0x1000));
999 let res = BitLockerVolume::unlock_with_password(Cursor::new(img), "x");
1000 assert!(matches!(
1001 res,
1002 Err(BdeError::NoUnlockProtector { protector, .. }) if protector == "password"
1003 ));
1004 }
1005
1006 #[test]
1007 fn no_valid_metadata_errors() {
1008 let img = meta_only_image(0x8000, &[0x2000], [0x1000, 0x1200, 0x1400], None);
1010 let err = BitLockerVolume::read_metadata(&mut Cursor::new(img)).unwrap_err();
1011 assert!(matches!(err, BdeError::NoValidMetadata { .. }));
1012 }
1013
1014 #[test]
1015 fn read_metadata_skips_zero_first_offset() {
1016 let img = meta_only_image(0x8000, &[0x2000], [0, 0x1000, 0], Some(0x1000));
1018 let meta = BitLockerVolume::read_metadata(&mut Cursor::new(img)).unwrap();
1019 assert_eq!(meta.encryption_method, 0x8000);
1020 }
1021
1022 struct FlakyReader {
1025 header: Vec<u8>,
1026 phase: usize,
1027 }
1028
1029 impl Read for FlakyReader {
1030 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1031 self.phase += 1;
1032 match self.phase {
1033 1 => {
1034 let n = buf.len().min(self.header.len());
1035 buf[..n].copy_from_slice(&self.header[..n]);
1036 Ok(n)
1037 }
1038 2 => Err(std::io::Error::new(
1039 std::io::ErrorKind::Interrupted,
1040 "eintr",
1041 )),
1042 _ => Err(std::io::Error::other("boom")),
1043 }
1044 }
1045 }
1046
1047 impl Seek for FlakyReader {
1048 fn seek(&mut self, _pos: SeekFrom) -> std::io::Result<u64> {
1049 Ok(0)
1050 }
1051 }
1052
1053 #[test]
1054 fn io_error_during_block_read_propagates() {
1055 let mut header = vec![0u8; 512];
1056 header[0..3].copy_from_slice(&[0xeb, 0x58, 0x90]);
1057 header[3..11].copy_from_slice(b"MSWIN4.1");
1058 header[12] = 0x02;
1059 header[440..448].copy_from_slice(&0x1000u64.to_le_bytes());
1060 let mut reader = FlakyReader { header, phase: 0 };
1061 let res = BitLockerVolume::read_metadata(&mut reader);
1062 assert!(matches!(res, Err(BdeError::Io(_))));
1063 }
1064
1065 #[test]
1066 fn wrong_password_fails_authentication() {
1067 let (image, _) = build_volume("test-pw");
1068 let res = BitLockerVolume::unlock_with_password(Cursor::new(image), "wrong");
1069 assert!(matches!(
1070 res,
1071 Err(BdeError::AuthenticationFailed { what }) if what == "volume master key"
1072 ));
1073 }
1074
1075 #[test]
1076 fn read_and_seek_traits() {
1077 use std::io::{Read as _, Seek as _};
1078 let (image, plain) = build_volume("test-pw");
1079 let mut vol = BitLockerVolume::unlock_with_password(Cursor::new(image), "test-pw").unwrap();
1080 vol.seek(SeekFrom::Start(0)).unwrap();
1081 let mut buf = [0u8; 256];
1082 vol.read_exact(&mut buf).unwrap();
1083 assert_eq!(&buf[..], &plain[..256]);
1084 assert_eq!(vol.volume_size(), IMAGE_SIZE as u64);
1085 }
1086
1087 #[test]
1088 fn non_bitlocker_reader_errors() {
1089 let r = BitLockerVolume::unlock_with_password(Cursor::new(vec![0u8; 1024]), "x");
1090 assert!(matches!(r, Err(BdeError::NotBitLocker { .. })));
1091 }
1092
1093 #[test]
1094 fn read_metadata_reports_protectors() {
1095 let (image, _) = build_volume("test-pw");
1096 let meta = BitLockerVolume::read_metadata(&mut Cursor::new(image)).unwrap();
1097 assert_eq!(meta.protector_types(), vec![PROTECTION_PASSWORD]);
1098 }
1099}