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, MetadataEntry, PROTECTION_CLEAR, PROTECTION_PASSWORD, PROTECTION_RECOVERY,
16 PROTECTION_STARTUP_KEY, VALUE_TYPE_AES_CCM, VALUE_TYPE_EXTERNAL_KEY, VALUE_TYPE_KEY,
17 VALUE_TYPE_STRETCH,
18};
19use crate::method::{EncryptionMethod, SectorCipherKind};
20
21const METHOD_NONE: u16 = 0x0000;
23const SECTOR_SIZE: usize = 512;
25const METADATA_READ_LEN: usize = 64 * 1024;
28
29pub struct BitLockerVolume;
32
33impl BitLockerVolume {
34 pub fn read_metadata<R: Read + Seek>(reader: &mut R) -> Result<FveMetadata> {
42 let mut header = [0u8; SECTOR_SIZE];
43 reader.seek(SeekFrom::Start(0))?;
44 read_fill(reader, &mut header)?;
45 let volume_header = VolumeHeader::parse(&header)?;
46
47 let offsets = volume_header.fve_metadata_offsets;
48 for &offset in &offsets {
49 if offset == 0 {
50 continue;
51 }
52 let mut block = vec![0u8; METADATA_READ_LEN];
53 reader.seek(SeekFrom::Start(offset))?;
54 let n = read_available(reader, &mut block)?;
55 block.truncate(n);
56 if let Some(meta) = FveMetadata::parse(&block, volume_header.bytes_per_sector) {
57 return Ok(meta);
58 }
59 }
60 Err(BdeError::NoValidMetadata { offsets })
61 }
62
63 pub fn unlock_with_password<R: Read + Seek>(
70 reader: R,
71 password: &str,
72 ) -> Result<DecryptedVolume<R>> {
73 Self::unlock(
74 reader,
75 PROTECTION_PASSWORD,
76 "password",
77 &VmkUnwrap::Stretch(password_hash(password)),
78 )
79 }
80
81 pub fn unlock_with_recovery_password<R: Read + Seek>(
91 reader: R,
92 recovery: &str,
93 ) -> Result<DecryptedVolume<R>> {
94 let key_hash = recovery_key_hash(recovery)
95 .map_err(|reason| BdeError::InvalidRecoveryPassword { reason })?;
96 Self::unlock(
97 reader,
98 PROTECTION_RECOVERY,
99 "recovery password",
100 &VmkUnwrap::Stretch(key_hash),
101 )
102 }
103
104 pub fn unlock_clear_key<R: Read + Seek>(reader: R) -> Result<DecryptedVolume<R>> {
115 Self::unlock(reader, PROTECTION_CLEAR, "clear key", &VmkUnwrap::Clear)
116 }
117
118 pub fn unlock_with_startup_key<R: Read + Seek>(
129 reader: R,
130 startup_key: &[u8],
131 ) -> Result<DecryptedVolume<R>> {
132 let external_key = parse_startup_key(startup_key)?;
133 Self::unlock(
134 reader,
135 PROTECTION_STARTUP_KEY,
136 "startup key",
137 &VmkUnwrap::External(external_key),
138 )
139 }
140
141 fn unlock<R: Read + Seek>(
144 mut reader: R,
145 protector_type: u16,
146 protector_name: &'static str,
147 unwrap: &VmkUnwrap,
148 ) -> Result<DecryptedVolume<R>> {
149 let metadata = Self::read_metadata(&mut reader)?;
150
151 let raw = metadata.encryption_method;
156 let kind = EncryptionMethod::decode(raw)
157 .ok_or(BdeError::UnsupportedEncryptionMethod { method: raw })?
158 .validated_kind()
159 .ok_or(BdeError::UnvalidatedEncryptionMethod { method: raw })?;
160
161 let cipher = derive_cipher(&metadata, kind, protector_type, protector_name, unwrap)?;
162 let total_size = reader.seek(SeekFrom::End(0))?;
163
164 Ok(DecryptedVolume {
165 reader,
166 cipher,
167 metadata,
168 total_size,
169 position: 0,
170 })
171 }
172}
173
174enum VmkUnwrap {
176 Stretch([u8; 32]),
179 Clear,
182 External([u8; 32]),
186}
187
188fn parse_startup_key(bek: &[u8]) -> Result<[u8; 32]> {
196 let body = bek.get(48..).unwrap_or_default();
197 let external = MetadataEntry::parse_sequence(body)
198 .into_iter()
199 .find(|e| e.value_type == VALUE_TYPE_EXTERNAL_KEY)
200 .ok_or(BdeError::MissingKeyMaterial {
201 what: "external key",
202 })?;
203 let key = external
206 .nested(24)
207 .into_iter()
208 .find(|e| e.value_type == VALUE_TYPE_KEY)
209 .ok_or(BdeError::MissingKeyMaterial {
210 what: "external key property",
211 })?;
212 take_key32(&key.data, 4, "external key")
213}
214
215fn derive_cipher(
220 metadata: &FveMetadata,
221 kind: SectorCipherKind,
222 protector_type: u16,
223 protector_name: &'static str,
224 unwrap: &VmkUnwrap,
225) -> Result<SectorCipher> {
226 let vmk = metadata
228 .vmk_entries()
229 .find(|e| e.protection_type() == Some(protector_type))
230 .ok_or_else(|| BdeError::NoUnlockProtector {
231 protector: protector_name,
232 found: metadata.protector_types(),
233 })?;
234
235 let props = vmk.nested(28);
237 let vmk_ccm = props
238 .iter()
239 .find(|e| e.value_type == VALUE_TYPE_AES_CCM)
240 .ok_or(BdeError::MissingKeyMaterial {
241 what: "VMK AES-CCM key",
242 })?;
243
244 let unwrap_key = match unwrap {
248 VmkUnwrap::Stretch(key_hash) => {
249 let stretch = props
250 .iter()
251 .find(|e| e.value_type == VALUE_TYPE_STRETCH)
252 .ok_or(BdeError::MissingKeyMaterial {
253 what: "stretch key",
254 })?;
255 let mut salt = [0u8; 16];
257 let salt_src = stretch
258 .data
259 .get(4..20)
260 .ok_or(BdeError::MissingKeyMaterial {
261 what: "stretch salt",
262 })?;
263 salt.copy_from_slice(salt_src);
264 stretch_key(key_hash, &salt)
265 }
266 VmkUnwrap::Clear => {
267 let key = props
269 .iter()
270 .find(|e| e.value_type == VALUE_TYPE_KEY)
271 .ok_or(BdeError::MissingKeyMaterial { what: "clear key" })?;
272 take_key32(&key.data, 4, "clear")?
273 }
274 VmkUnwrap::External(key) => *key,
276 };
277
278 let vmk_container =
279 aes_ccm_unwrap(&unwrap_key, &vmk_ccm.data).ok_or(BdeError::AuthenticationFailed {
280 what: "volume master key",
281 })?;
282 let vmk_key = take_key32(&vmk_container, 12, "volume master key")?;
283
284 let fvek_entry = metadata
287 .fvek_entry()
288 .ok_or(BdeError::MissingKeyMaterial { what: "FVEK entry" })?;
289 let fvek_container = aes_ccm_unwrap(&vmk_key, &fvek_entry.data)
290 .ok_or(BdeError::AuthenticationFailed { what: "FVEK" })?;
291
292 match kind {
293 SectorCipherKind::Cbc128Diffuser => {
294 let fvek = take_key16(&fvek_container, 12, "FVEK")?;
295 let tweak = take_key16(&fvek_container, 44, "FVEK")?;
296 Ok(SectorCipher::new(fvek, tweak))
297 }
298 SectorCipherKind::Cbc128 => {
299 let fvek = take_key16(&fvek_container, 12, "FVEK")?;
300 Ok(SectorCipher::new_cbc(fvek))
301 }
302 SectorCipherKind::Cbc256 => {
303 let fvek = take_key32(&fvek_container, 12, "FVEK")?;
304 Ok(SectorCipher::new_cbc256(fvek))
305 }
306 SectorCipherKind::Xts128 => {
307 let fvek = take_key32(&fvek_container, 12, "FVEK")?;
309 Ok(SectorCipher::new_xts128(fvek))
310 }
311 SectorCipherKind::Xts256 => {
312 let fvek = take_key64(&fvek_container, 12, "FVEK")?;
314 Ok(SectorCipher::new_xts256(fvek))
315 }
316 }
317}
318
319fn take_key64(container: &[u8], off: usize, what: &'static str) -> Result<[u8; 64]> {
320 let s = container
321 .get(off..off + 64)
322 .ok_or(BdeError::MalformedKeyContainer {
323 what,
324 got: container.len(),
325 need: off + 64,
326 })?;
327 let mut k = [0u8; 64];
328 k.copy_from_slice(s);
329 Ok(k)
330}
331
332fn take_key32(container: &[u8], off: usize, what: &'static str) -> Result<[u8; 32]> {
333 let s = container
334 .get(off..off + 32)
335 .ok_or(BdeError::MalformedKeyContainer {
336 what,
337 got: container.len(),
338 need: off + 32,
339 })?;
340 let mut k = [0u8; 32];
341 k.copy_from_slice(s);
342 Ok(k)
343}
344
345fn take_key16(container: &[u8], off: usize, what: &'static str) -> Result<[u8; 16]> {
346 let s = container
347 .get(off..off + 16)
348 .ok_or(BdeError::MalformedKeyContainer {
349 what,
350 got: container.len(),
351 need: off + 16,
352 })?;
353 let mut k = [0u8; 16];
354 k.copy_from_slice(s);
355 Ok(k)
356}
357
358pub struct DecryptedVolume<R> {
360 reader: R,
361 cipher: SectorCipher,
362 metadata: FveMetadata,
363 total_size: u64,
364 position: u64,
365}
366
367impl<R: Read + Seek> DecryptedVolume<R> {
368 #[must_use]
370 pub fn metadata(&self) -> &FveMetadata {
371 &self.metadata
372 }
373
374 #[must_use]
376 pub fn volume_size(&self) -> u64 {
377 self.total_size
378 }
379
380 pub fn read_at(&mut self, offset: u64, buf: &mut [u8]) -> Result<()> {
386 let mut done = 0usize;
387 while done < buf.len() {
388 let pos = offset + done as u64;
389 let sector_start = pos - (pos % SECTOR_SIZE as u64);
390 let within = (pos - sector_start) as usize;
391 let plain = self.decrypt_logical_sector(sector_start)?;
392 let take = (SECTOR_SIZE - within).min(buf.len() - done);
393 buf[done..done + take].copy_from_slice(&plain[within..within + take]);
394 done += take;
395 }
396 Ok(())
397 }
398
399 fn decrypt_logical_sector(&mut self, sector_start: u64) -> Result<[u8; SECTOR_SIZE]> {
403 let meta_size = u64::from(self.metadata.metadata_size);
405 for &m in &self.metadata.metadata_offsets {
406 if m != 0 && sector_start >= m && sector_start < m + meta_size {
407 return Ok([0u8; SECTOR_SIZE]);
408 }
409 }
410
411 let physical = if sector_start < self.metadata.volume_header_size {
414 self.metadata.volume_header_offset + sector_start
415 } else {
416 sector_start
417 };
418
419 let mut ct = [0u8; SECTOR_SIZE];
420 self.reader.seek(SeekFrom::Start(physical))?;
421 read_available(&mut self.reader, &mut ct)?;
422
423 let evs = self.metadata.encrypted_volume_size;
425 if self.metadata.encryption_method == METHOD_NONE || (evs != 0 && physical >= evs) {
426 return Ok(ct);
427 }
428
429 let plain = self.cipher.decrypt_sector(&ct, physical);
430 let mut out = [0u8; SECTOR_SIZE];
431 let n = plain.len().min(SECTOR_SIZE);
432 out[..n].copy_from_slice(&plain[..n]);
433 Ok(out)
434 }
435}
436
437impl<R: Read + Seek> Read for DecryptedVolume<R> {
438 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
439 if self.position >= self.total_size {
440 return Ok(0);
441 }
442 let remaining = self.total_size - self.position;
443 let n = (buf.len() as u64).min(remaining) as usize;
444 self.read_at(self.position, &mut buf[..n])
445 .map_err(|e| std::io::Error::other(e.to_string()))?;
446 self.position += n as u64;
447 Ok(n)
448 }
449}
450
451impl<R: Read + Seek> Seek for DecryptedVolume<R> {
452 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
453 let new = match pos {
454 SeekFrom::Start(o) => i128::from(o),
455 SeekFrom::End(o) => i128::from(self.total_size) + i128::from(o),
456 SeekFrom::Current(o) => i128::from(self.position) + i128::from(o),
457 };
458 if new < 0 {
459 return Err(std::io::Error::new(
460 std::io::ErrorKind::InvalidInput,
461 "seek before start",
462 ));
463 }
464 self.position = new as u64;
465 Ok(self.position)
466 }
467}
468
469fn read_fill<R: Read>(reader: &mut R, buf: &mut [u8]) -> Result<()> {
471 reader.read_exact(buf)?;
472 Ok(())
473}
474
475fn read_available<R: Read>(reader: &mut R, buf: &mut [u8]) -> Result<usize> {
478 let mut filled = 0;
479 while filled < buf.len() {
480 match reader.read(&mut buf[filled..]) {
481 Ok(0) => break,
482 Ok(n) => filled += n,
483 Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {}
484 Err(e) => return Err(e.into()),
485 }
486 }
487 for b in &mut buf[filled..] {
488 *b = 0;
489 }
490 Ok(filled)
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496 use crate::crypto::{aes_ccm_wrap, password_hash, stretch_key, SectorCipher};
497 use crate::metadata::{
498 ENTRY_TYPE_FVEK, ENTRY_TYPE_VMK, ENTRY_TYPE_VOLUME_HEADER, PROTECTION_CLEAR,
499 PROTECTION_PASSWORD,
500 };
501 use std::io::Cursor;
502
503 const RELOCATED_OFFSET: u64 = 0x4000;
504 const META_BLOCK_OFFSET: u64 = 0x1000;
505 const IMAGE_SIZE: usize = 0x5000;
506 const ENCRYPTED_SIZE: u64 = 0x4800;
509
510 fn entry(entry_type: u16, value_type: u16, data: &[u8]) -> Vec<u8> {
511 let size = (8 + data.len()) as u16;
512 let mut v = Vec::new();
513 v.extend_from_slice(&size.to_le_bytes());
514 v.extend_from_slice(&entry_type.to_le_bytes());
515 v.extend_from_slice(&value_type.to_le_bytes());
516 v.extend_from_slice(&1u16.to_le_bytes());
517 v.extend_from_slice(data);
518 v
519 }
520
521 fn build_volume(password: &str) -> (Vec<u8>, [u8; 512]) {
524 let salt = [0x33u8; 16];
525 let fvek = [0x11u8; 16];
526 let tweak = [0x22u8; 16];
527 let vmk = [0x44u8; 32];
528
529 let mut vmk_container = vec![0u8; 44];
530 vmk_container[12..44].copy_from_slice(&vmk);
531 let stretched = stretch_key(&password_hash(password), &salt);
532 let vmk_ccm = aes_ccm_wrap(&stretched, &[0x55; 12], &vmk_container);
533
534 let mut fvek_container = vec![0u8; 76];
535 fvek_container[12..28].copy_from_slice(&fvek);
536 fvek_container[44..60].copy_from_slice(&tweak);
537 let fvek_ccm = aes_ccm_wrap(&vmk, &[0x66; 12], &fvek_container);
538
539 let mut stretch_data = vec![0u8; 4]; stretch_data.extend_from_slice(&salt);
541 let stretch_entry = entry(0, VALUE_TYPE_STRETCH, &stretch_data);
542 let vmk_ccm_entry = entry(0, VALUE_TYPE_AES_CCM, &vmk_ccm);
543
544 let mut vmk_data = vec![0u8; 28];
545 vmk_data[26..28].copy_from_slice(&PROTECTION_PASSWORD.to_le_bytes());
546 vmk_data.extend_from_slice(&stretch_entry);
547 vmk_data.extend_from_slice(&vmk_ccm_entry);
548 let vmk_entry = entry(ENTRY_TYPE_VMK, VALUE_TYPE_VMK, &vmk_data);
549
550 let fvek_entry = entry(ENTRY_TYPE_FVEK, VALUE_TYPE_AES_CCM, &fvek_ccm);
551
552 let mut vh_data = Vec::new();
553 vh_data.extend_from_slice(&RELOCATED_OFFSET.to_le_bytes());
554 vh_data.extend_from_slice(&512u64.to_le_bytes());
555 let vh_entry = entry(ENTRY_TYPE_VOLUME_HEADER, ENTRY_TYPE_VOLUME_HEADER, &vh_data);
556
557 let mut entries = Vec::new();
558 entries.extend_from_slice(&vh_entry);
559 entries.extend_from_slice(&vmk_entry);
560 entries.extend_from_slice(&fvek_entry);
561 let metadata_size = 48 + entries.len();
562
563 let mut image = vec![0u8; IMAGE_SIZE];
564 image[0..3].copy_from_slice(&[0xeb, 0x58, 0x90]);
565 image[3..11].copy_from_slice(b"MSWIN4.1");
566 image[12] = 0x02; image[440..448].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
568
569 let mb = META_BLOCK_OFFSET as usize;
570 image[mb..mb + 8].copy_from_slice(b"-FVE-FS-");
571 image[mb + 10..mb + 12].copy_from_slice(&2u16.to_le_bytes());
572 image[mb + 16..mb + 24].copy_from_slice(&ENCRYPTED_SIZE.to_le_bytes());
573 image[mb + 28..mb + 32].copy_from_slice(&1u32.to_le_bytes());
574 image[mb + 32..mb + 40].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
575 image[mb + 56..mb + 64].copy_from_slice(&RELOCATED_OFFSET.to_le_bytes());
576 image[mb + 64..mb + 68].copy_from_slice(&(metadata_size as u32).to_le_bytes());
577 image[mb + 64 + 36..mb + 64 + 38].copy_from_slice(&0x8000u16.to_le_bytes());
578 image[mb + 64 + 48..mb + 64 + 48 + entries.len()].copy_from_slice(&entries);
579
580 let mut plain = [0u8; 512];
581 for (i, b) in plain.iter_mut().enumerate() {
582 *b = (i as u8) ^ 0x5a;
583 }
584 let cipher = SectorCipher::new(fvek, tweak);
585 let ct = cipher.encrypt_sector(&plain, RELOCATED_OFFSET);
586 let ro = RELOCATED_OFFSET as usize;
587 image[ro..ro + 512].copy_from_slice(&ct);
588
589 (image, plain)
590 }
591
592 use crate::metadata::{VALUE_TYPE_AES_CCM, VALUE_TYPE_KEY, VALUE_TYPE_STRETCH, VALUE_TYPE_VMK};
593
594 #[test]
595 fn unlock_and_read_synthetic_volume() {
596 let (image, plain) = build_volume("test-pw");
600 let mut vol = BitLockerVolume::unlock_with_password(Cursor::new(image), "test-pw").unwrap();
601 let mut buf = [0u8; 512];
602 vol.read_at(0, &mut buf).unwrap();
603 assert_eq!(buf, plain);
604 assert_eq!(vol.metadata().encryption_method, 0x8000);
605
606 let mut z = [1u8; 512];
608 vol.read_at(META_BLOCK_OFFSET, &mut z).unwrap();
609 assert_eq!(z, [0u8; 512]);
610
611 let mut e = [0u8; 512];
614 vol.read_at(0x600, &mut e).unwrap();
615
616 let mut r = [0u8; 512];
619 vol.read_at(ENCRYPTED_SIZE, &mut r).unwrap();
620 assert_eq!(r, [0u8; 512]);
621 }
622
623 fn cbc_encrypt_sector(fvek: &[u8; 16], byte_offset: u64, plain: &[u8; 512]) -> [u8; 512] {
628 use aes::cipher::block_padding::NoPadding;
629 use aes::cipher::generic_array::GenericArray;
630 use aes::cipher::{BlockEncrypt, BlockEncryptMut, KeyInit, KeyIvInit};
631 use aes::Aes128;
632
633 let mut iv = [0u8; 16];
634 iv[0..8].copy_from_slice(&byte_offset.to_le_bytes());
635 let mut iv_block = GenericArray::clone_from_slice(&iv);
636 Aes128::new(GenericArray::from_slice(fvek)).encrypt_block(&mut iv_block);
637
638 let mut buf = *plain;
639 cbc::Encryptor::<Aes128>::new(GenericArray::from_slice(fvek), &iv_block)
640 .encrypt_padded_mut::<NoPadding>(&mut buf, 512)
641 .unwrap();
642 buf
643 }
644
645 fn build_cbc_volume(protector_type: u16, key_hash: [u8; 32]) -> (Vec<u8>, [u8; 512]) {
651 let salt = [0x37u8; 16];
652 let fvek = [0x13u8; 16];
653 let vmk = [0x46u8; 32];
654
655 let mut vmk_container = vec![0u8; 44];
656 vmk_container[12..44].copy_from_slice(&vmk);
657 let stretched = stretch_key(&key_hash, &salt);
658 let vmk_ccm = aes_ccm_wrap(&stretched, &[0x57; 12], &vmk_container);
659
660 let mut fvek_container = vec![0u8; 44];
662 fvek_container[12..28].copy_from_slice(&fvek);
663 let fvek_ccm = aes_ccm_wrap(&vmk, &[0x68; 12], &fvek_container);
664
665 let mut stretch_data = vec![0u8; 4];
666 stretch_data.extend_from_slice(&salt);
667 let stretch_entry = entry(0, VALUE_TYPE_STRETCH, &stretch_data);
668 let vmk_ccm_entry = entry(0, VALUE_TYPE_AES_CCM, &vmk_ccm);
669
670 let mut vmk_data = vec![0u8; 28];
671 vmk_data[26..28].copy_from_slice(&protector_type.to_le_bytes());
672 vmk_data.extend_from_slice(&stretch_entry);
673 vmk_data.extend_from_slice(&vmk_ccm_entry);
674 let vmk_entry = entry(ENTRY_TYPE_VMK, VALUE_TYPE_VMK, &vmk_data);
675
676 let fvek_entry = entry(ENTRY_TYPE_FVEK, VALUE_TYPE_AES_CCM, &fvek_ccm);
677
678 let mut vh_data = Vec::new();
679 vh_data.extend_from_slice(&RELOCATED_OFFSET.to_le_bytes());
680 vh_data.extend_from_slice(&512u64.to_le_bytes());
681 let vh_entry = entry(ENTRY_TYPE_VOLUME_HEADER, ENTRY_TYPE_VOLUME_HEADER, &vh_data);
682
683 let mut entries = Vec::new();
684 entries.extend_from_slice(&vh_entry);
685 entries.extend_from_slice(&vmk_entry);
686 entries.extend_from_slice(&fvek_entry);
687 let metadata_size = 48 + entries.len();
688
689 let mut image = vec![0u8; IMAGE_SIZE];
690 image[0..3].copy_from_slice(&[0xeb, 0x58, 0x90]);
691 image[3..11].copy_from_slice(b"MSWIN4.1");
692 image[12] = 0x02;
693 image[440..448].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
694
695 let mb = META_BLOCK_OFFSET as usize;
696 image[mb..mb + 8].copy_from_slice(b"-FVE-FS-");
697 image[mb + 10..mb + 12].copy_from_slice(&2u16.to_le_bytes());
698 image[mb + 16..mb + 24].copy_from_slice(&ENCRYPTED_SIZE.to_le_bytes());
699 image[mb + 28..mb + 32].copy_from_slice(&1u32.to_le_bytes());
700 image[mb + 32..mb + 40].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
701 image[mb + 56..mb + 64].copy_from_slice(&RELOCATED_OFFSET.to_le_bytes());
702 image[mb + 64..mb + 68].copy_from_slice(&(metadata_size as u32).to_le_bytes());
703 image[mb + 64 + 36..mb + 64 + 38].copy_from_slice(&0x8002u16.to_le_bytes());
704 image[mb + 64 + 48..mb + 64 + 48 + entries.len()].copy_from_slice(&entries);
705
706 let mut plain = [0u8; 512];
707 for (i, b) in plain.iter_mut().enumerate() {
708 *b = (i as u8) ^ 0x3c;
709 }
710 let ct = cbc_encrypt_sector(&fvek, RELOCATED_OFFSET, &plain);
711 let ro = RELOCATED_OFFSET as usize;
712 image[ro..ro + 512].copy_from_slice(&ct);
713
714 (image, plain)
715 }
716
717 #[test]
718 fn unlock_and_read_synthetic_cbc_volume() {
719 let (image, plain) = build_cbc_volume(PROTECTION_PASSWORD, password_hash("cbc-pw"));
724 let mut vol = BitLockerVolume::unlock_with_password(Cursor::new(image), "cbc-pw").unwrap();
725 let mut buf = [0u8; 512];
726 vol.read_at(0, &mut buf).unwrap();
727 assert_eq!(buf, plain);
728 assert_eq!(vol.metadata().encryption_method, 0x8002);
729 }
730
731 #[test]
732 fn unlock_and_read_synthetic_recovery_volume() {
733 let rk = "111111-111111-111111-111111-111111-111111-111111-111111";
738 let key_hash = crate::crypto::recovery_key_hash(rk).unwrap();
739 let (image, plain) = build_cbc_volume(crate::metadata::PROTECTION_RECOVERY, key_hash);
740 let mut vol =
741 BitLockerVolume::unlock_with_recovery_password(Cursor::new(image), rk).unwrap();
742 let mut buf = [0u8; 512];
743 vol.read_at(0, &mut buf).unwrap();
744 assert_eq!(buf, plain);
745 assert_eq!(vol.metadata().encryption_method, 0x8002);
746 }
747
748 #[test]
749 fn recovery_unlock_rejects_malformed_password() {
750 let rk = "111111-111111-111111-111111-111111-111111-111111-111111";
751 let key_hash = crate::crypto::recovery_key_hash(rk).unwrap();
752 let (image, _) = build_cbc_volume(crate::metadata::PROTECTION_RECOVERY, key_hash);
753 let res = BitLockerVolume::unlock_with_recovery_password(Cursor::new(image), "nope");
754 assert!(matches!(res, Err(BdeError::InvalidRecoveryPassword { .. })));
755 }
756
757 #[test]
758 fn no_recovery_protector_errors() {
759 let rk = "111111-111111-111111-111111-111111-111111-111111-111111";
761 let (image, _) = build_cbc_volume(PROTECTION_PASSWORD, password_hash("cbc-pw"));
762 let res = BitLockerVolume::unlock_with_recovery_password(Cursor::new(image), rk);
763 assert!(matches!(res, Err(BdeError::NoUnlockProtector { .. })));
764 }
765
766 fn build_cbc256_volume(key_hash: [u8; 32]) -> (Vec<u8>, [u8; 512]) {
770 let salt = [0x39u8; 16];
771 let fvek = [0x24u8; 32];
772 let vmk = [0x48u8; 32];
773
774 let mut vmk_container = vec![0u8; 44];
775 vmk_container[12..44].copy_from_slice(&vmk);
776 let stretched = stretch_key(&key_hash, &salt);
777 let vmk_ccm = aes_ccm_wrap(&stretched, &[0x59; 12], &vmk_container);
778
779 let mut fvek_container = vec![0u8; 76];
781 fvek_container[12..44].copy_from_slice(&fvek);
782 let fvek_ccm = aes_ccm_wrap(&vmk, &[0x6a; 12], &fvek_container);
783
784 let mut stretch_data = vec![0u8; 4];
785 stretch_data.extend_from_slice(&salt);
786 let stretch_entry = entry(0, VALUE_TYPE_STRETCH, &stretch_data);
787 let vmk_ccm_entry = entry(0, VALUE_TYPE_AES_CCM, &vmk_ccm);
788
789 let mut vmk_data = vec![0u8; 28];
790 vmk_data[26..28].copy_from_slice(&PROTECTION_RECOVERY.to_le_bytes());
791 vmk_data.extend_from_slice(&stretch_entry);
792 vmk_data.extend_from_slice(&vmk_ccm_entry);
793 let vmk_entry = entry(ENTRY_TYPE_VMK, VALUE_TYPE_VMK, &vmk_data);
794
795 let fvek_entry = entry(ENTRY_TYPE_FVEK, VALUE_TYPE_AES_CCM, &fvek_ccm);
796
797 let mut vh_data = Vec::new();
798 vh_data.extend_from_slice(&RELOCATED_OFFSET.to_le_bytes());
799 vh_data.extend_from_slice(&512u64.to_le_bytes());
800 let vh_entry = entry(ENTRY_TYPE_VOLUME_HEADER, ENTRY_TYPE_VOLUME_HEADER, &vh_data);
801
802 let mut entries = Vec::new();
803 entries.extend_from_slice(&vh_entry);
804 entries.extend_from_slice(&vmk_entry);
805 entries.extend_from_slice(&fvek_entry);
806 let metadata_size = 48 + entries.len();
807
808 let mut image = vec![0u8; IMAGE_SIZE];
809 image[0..3].copy_from_slice(&[0xeb, 0x58, 0x90]);
810 image[3..11].copy_from_slice(b"MSWIN4.1");
811 image[12] = 0x02;
812 image[440..448].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
813
814 let mb = META_BLOCK_OFFSET as usize;
815 image[mb..mb + 8].copy_from_slice(b"-FVE-FS-");
816 image[mb + 10..mb + 12].copy_from_slice(&2u16.to_le_bytes());
817 image[mb + 16..mb + 24].copy_from_slice(&ENCRYPTED_SIZE.to_le_bytes());
818 image[mb + 28..mb + 32].copy_from_slice(&1u32.to_le_bytes());
819 image[mb + 32..mb + 40].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
820 image[mb + 56..mb + 64].copy_from_slice(&RELOCATED_OFFSET.to_le_bytes());
821 image[mb + 64..mb + 68].copy_from_slice(&(metadata_size as u32).to_le_bytes());
822 image[mb + 64 + 36..mb + 64 + 38].copy_from_slice(&0x8003u16.to_le_bytes());
823 image[mb + 64 + 48..mb + 64 + 48 + entries.len()].copy_from_slice(&entries);
824
825 let mut plain = [0u8; 512];
826 for (i, b) in plain.iter_mut().enumerate() {
827 *b = (i as u8) ^ 0x71;
828 }
829 let ct = SectorCipher::new_cbc256(fvek).encrypt_sector(&plain, RELOCATED_OFFSET);
830 let ro = RELOCATED_OFFSET as usize;
831 image[ro..ro + 512].copy_from_slice(&ct);
832
833 (image, plain)
834 }
835
836 #[test]
837 fn unlock_and_read_synthetic_cbc256_volume() {
838 let rk = "222222-222222-222222-222222-222222-222222-222222-222222";
841 let key_hash = crate::crypto::recovery_key_hash(rk).unwrap();
842 let (image, plain) = build_cbc256_volume(key_hash);
843 let mut vol =
844 BitLockerVolume::unlock_with_recovery_password(Cursor::new(image), rk).unwrap();
845 let mut buf = [0u8; 512];
846 vol.read_at(0, &mut buf).unwrap();
847 assert_eq!(buf, plain);
848 assert_eq!(vol.metadata().encryption_method, 0x8003);
849 }
850
851 fn build_xts128_volume(key_hash: [u8; 32]) -> (Vec<u8>, [u8; 512]) {
855 let salt = [0x3bu8; 16];
856 let xts_key = [0x35u8; 32];
857 let vmk = [0x4au8; 32];
858
859 let mut vmk_container = vec![0u8; 44];
860 vmk_container[12..44].copy_from_slice(&vmk);
861 let stretched = stretch_key(&key_hash, &salt);
862 let vmk_ccm = aes_ccm_wrap(&stretched, &[0x5b; 12], &vmk_container);
863
864 let mut fvek_container = vec![0u8; 76];
866 fvek_container[12..44].copy_from_slice(&xts_key);
867 let fvek_ccm = aes_ccm_wrap(&vmk, &[0x6c; 12], &fvek_container);
868
869 let mut stretch_data = vec![0u8; 4];
870 stretch_data.extend_from_slice(&salt);
871 let stretch_entry = entry(0, VALUE_TYPE_STRETCH, &stretch_data);
872 let vmk_ccm_entry = entry(0, VALUE_TYPE_AES_CCM, &vmk_ccm);
873
874 let mut vmk_data = vec![0u8; 28];
875 vmk_data[26..28].copy_from_slice(&PROTECTION_RECOVERY.to_le_bytes());
876 vmk_data.extend_from_slice(&stretch_entry);
877 vmk_data.extend_from_slice(&vmk_ccm_entry);
878 let vmk_entry = entry(ENTRY_TYPE_VMK, VALUE_TYPE_VMK, &vmk_data);
879
880 let fvek_entry = entry(ENTRY_TYPE_FVEK, VALUE_TYPE_AES_CCM, &fvek_ccm);
881
882 let mut vh_data = Vec::new();
883 vh_data.extend_from_slice(&RELOCATED_OFFSET.to_le_bytes());
884 vh_data.extend_from_slice(&512u64.to_le_bytes());
885 let vh_entry = entry(ENTRY_TYPE_VOLUME_HEADER, ENTRY_TYPE_VOLUME_HEADER, &vh_data);
886
887 let mut entries = Vec::new();
888 entries.extend_from_slice(&vh_entry);
889 entries.extend_from_slice(&vmk_entry);
890 entries.extend_from_slice(&fvek_entry);
891 let metadata_size = 48 + entries.len();
892
893 let mut image = vec![0u8; IMAGE_SIZE];
894 image[0..3].copy_from_slice(&[0xeb, 0x58, 0x90]);
895 image[3..11].copy_from_slice(b"MSWIN4.1");
896 image[12] = 0x02;
897 image[440..448].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
898
899 let mb = META_BLOCK_OFFSET as usize;
900 image[mb..mb + 8].copy_from_slice(b"-FVE-FS-");
901 image[mb + 10..mb + 12].copy_from_slice(&2u16.to_le_bytes());
902 image[mb + 16..mb + 24].copy_from_slice(&ENCRYPTED_SIZE.to_le_bytes());
903 image[mb + 28..mb + 32].copy_from_slice(&1u32.to_le_bytes());
904 image[mb + 32..mb + 40].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
905 image[mb + 56..mb + 64].copy_from_slice(&RELOCATED_OFFSET.to_le_bytes());
906 image[mb + 64..mb + 68].copy_from_slice(&(metadata_size as u32).to_le_bytes());
907 image[mb + 64 + 36..mb + 64 + 38].copy_from_slice(&0x8004u16.to_le_bytes());
908 image[mb + 64 + 48..mb + 64 + 48 + entries.len()].copy_from_slice(&entries);
909
910 let mut plain = [0u8; 512];
911 for (i, b) in plain.iter_mut().enumerate() {
912 *b = (i as u8) ^ 0x2e;
913 }
914 let ct = SectorCipher::new_xts128(xts_key).encrypt_sector(&plain, RELOCATED_OFFSET);
915 let ro = RELOCATED_OFFSET as usize;
916 image[ro..ro + 512].copy_from_slice(&ct);
917
918 (image, plain)
919 }
920
921 #[test]
922 fn unlock_and_read_synthetic_xts128_volume() {
923 let rk = "333333-333333-333333-333333-333333-333333-333333-333333";
926 let key_hash = crate::crypto::recovery_key_hash(rk).unwrap();
927 let (image, plain) = build_xts128_volume(key_hash);
928 let mut vol =
929 BitLockerVolume::unlock_with_recovery_password(Cursor::new(image), rk).unwrap();
930 let mut buf = [0u8; 512];
931 vol.read_at(0, &mut buf).unwrap();
932 assert_eq!(buf, plain);
933 assert_eq!(vol.metadata().encryption_method, 0x8004);
934 }
935
936 fn build_clearkey_volume(include_key: bool) -> (Vec<u8>, [u8; 512]) {
942 let clear_key = [0x77u8; 32];
943 let xts_key = [0x35u8; 32];
944 let vmk = [0x4au8; 32];
945
946 let mut vmk_container = vec![0u8; 44];
947 vmk_container[12..44].copy_from_slice(&vmk);
948 let vmk_ccm = aes_ccm_wrap(&clear_key, &[0x5b; 12], &vmk_container);
950
951 let mut fvek_container = vec![0u8; 76];
953 fvek_container[12..44].copy_from_slice(&xts_key);
954 let fvek_ccm = aes_ccm_wrap(&vmk, &[0x6c; 12], &fvek_container);
955
956 let mut key_data = vec![0u8; 4];
958 key_data.extend_from_slice(&clear_key);
959 let key_entry = entry(0, VALUE_TYPE_KEY, &key_data);
960 let vmk_ccm_entry = entry(0, VALUE_TYPE_AES_CCM, &vmk_ccm);
961
962 let mut vmk_data = vec![0u8; 28];
963 vmk_data[26..28].copy_from_slice(&PROTECTION_CLEAR.to_le_bytes());
964 if include_key {
965 vmk_data.extend_from_slice(&key_entry);
966 }
967 vmk_data.extend_from_slice(&vmk_ccm_entry);
968 let vmk_entry = entry(ENTRY_TYPE_VMK, VALUE_TYPE_VMK, &vmk_data);
969
970 let fvek_entry = entry(ENTRY_TYPE_FVEK, VALUE_TYPE_AES_CCM, &fvek_ccm);
971
972 let mut vh_data = Vec::new();
973 vh_data.extend_from_slice(&RELOCATED_OFFSET.to_le_bytes());
974 vh_data.extend_from_slice(&512u64.to_le_bytes());
975 let vh_entry = entry(ENTRY_TYPE_VOLUME_HEADER, ENTRY_TYPE_VOLUME_HEADER, &vh_data);
976
977 let mut entries = Vec::new();
978 entries.extend_from_slice(&vh_entry);
979 entries.extend_from_slice(&vmk_entry);
980 entries.extend_from_slice(&fvek_entry);
981 let metadata_size = 48 + entries.len();
982
983 let mut image = vec![0u8; IMAGE_SIZE];
984 image[0..3].copy_from_slice(&[0xeb, 0x58, 0x90]);
985 image[3..11].copy_from_slice(b"MSWIN4.1");
986 image[12] = 0x02;
987 image[440..448].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
988
989 let mb = META_BLOCK_OFFSET as usize;
990 image[mb..mb + 8].copy_from_slice(b"-FVE-FS-");
991 image[mb + 10..mb + 12].copy_from_slice(&2u16.to_le_bytes());
992 image[mb + 16..mb + 24].copy_from_slice(&ENCRYPTED_SIZE.to_le_bytes());
993 image[mb + 28..mb + 32].copy_from_slice(&1u32.to_le_bytes());
994 image[mb + 32..mb + 40].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
995 image[mb + 56..mb + 64].copy_from_slice(&RELOCATED_OFFSET.to_le_bytes());
996 image[mb + 64..mb + 68].copy_from_slice(&(metadata_size as u32).to_le_bytes());
997 image[mb + 64 + 36..mb + 64 + 38].copy_from_slice(&0x8004u16.to_le_bytes());
998 image[mb + 64 + 48..mb + 64 + 48 + entries.len()].copy_from_slice(&entries);
999
1000 let mut plain = [0u8; 512];
1001 for (i, b) in plain.iter_mut().enumerate() {
1002 *b = (i as u8) ^ 0x2e;
1003 }
1004 let ct = SectorCipher::new_xts128(xts_key).encrypt_sector(&plain, RELOCATED_OFFSET);
1005 let ro = RELOCATED_OFFSET as usize;
1006 image[ro..ro + 512].copy_from_slice(&ct);
1007
1008 (image, plain)
1009 }
1010
1011 #[test]
1012 fn unlock_and_read_synthetic_clearkey_volume() {
1013 let (image, plain) = build_clearkey_volume(true);
1018 let mut vol = BitLockerVolume::unlock_clear_key(Cursor::new(image)).unwrap();
1019 let mut buf = [0u8; 512];
1020 vol.read_at(0, &mut buf).unwrap();
1021 assert_eq!(buf, plain);
1022 assert_eq!(vol.metadata().encryption_method, 0x8004);
1023 }
1024
1025 #[test]
1026 fn no_clear_key_protector_errors() {
1027 let (image, _) = build_volume("test-pw");
1029 let res = BitLockerVolume::unlock_clear_key(Cursor::new(image));
1030 assert!(matches!(
1031 res,
1032 Err(BdeError::NoUnlockProtector { protector, .. }) if protector == "clear key"
1033 ));
1034 }
1035
1036 #[test]
1037 fn clear_key_missing_key_property_errors() {
1038 let (image, _) = build_clearkey_volume(false);
1040 let res = BitLockerVolume::unlock_clear_key(Cursor::new(image));
1041 assert!(matches!(
1042 res,
1043 Err(BdeError::MissingKeyMaterial { what }) if what == "clear key"
1044 ));
1045 }
1046
1047 fn build_bek(external_key: &[u8; 32]) -> Vec<u8> {
1053 let mut key_data = vec![0u8; 4]; key_data.extend_from_slice(external_key);
1055 let key_entry = entry(0, VALUE_TYPE_KEY, &key_data);
1056
1057 let mut ext_data = vec![0u8; 24]; ext_data.extend_from_slice(&key_entry);
1059 let ext_entry = entry(0, crate::metadata::VALUE_TYPE_EXTERNAL_KEY, &ext_data);
1060
1061 let mut bek = vec![0u8; 48]; bek.extend_from_slice(&ext_entry);
1063 bek
1064 }
1065
1066 fn build_startupkey_volume() -> (Vec<u8>, [u8; 512], Vec<u8>) {
1071 let external_key = [0x9au8; 32];
1072 let xts_key = [0x35u8; 32];
1073 let vmk = [0x4au8; 32];
1074
1075 let mut vmk_container = vec![0u8; 44];
1076 vmk_container[12..44].copy_from_slice(&vmk);
1077 let vmk_ccm = aes_ccm_wrap(&external_key, &[0x5c; 12], &vmk_container);
1079
1080 let mut fvek_container = vec![0u8; 76];
1081 fvek_container[12..44].copy_from_slice(&xts_key);
1082 let fvek_ccm = aes_ccm_wrap(&vmk, &[0x6d; 12], &fvek_container);
1083
1084 let vmk_ccm_entry = entry(0, VALUE_TYPE_AES_CCM, &vmk_ccm);
1085
1086 let mut vmk_data = vec![0u8; 28];
1087 vmk_data[26..28].copy_from_slice(&crate::metadata::PROTECTION_STARTUP_KEY.to_le_bytes());
1088 vmk_data.extend_from_slice(&vmk_ccm_entry);
1089 let vmk_entry = entry(ENTRY_TYPE_VMK, VALUE_TYPE_VMK, &vmk_data);
1090
1091 let fvek_entry = entry(ENTRY_TYPE_FVEK, VALUE_TYPE_AES_CCM, &fvek_ccm);
1092
1093 let mut vh_data = Vec::new();
1094 vh_data.extend_from_slice(&RELOCATED_OFFSET.to_le_bytes());
1095 vh_data.extend_from_slice(&512u64.to_le_bytes());
1096 let vh_entry = entry(ENTRY_TYPE_VOLUME_HEADER, ENTRY_TYPE_VOLUME_HEADER, &vh_data);
1097
1098 let mut entries = Vec::new();
1099 entries.extend_from_slice(&vh_entry);
1100 entries.extend_from_slice(&vmk_entry);
1101 entries.extend_from_slice(&fvek_entry);
1102 let metadata_size = 48 + entries.len();
1103
1104 let mut image = vec![0u8; IMAGE_SIZE];
1105 image[0..3].copy_from_slice(&[0xeb, 0x58, 0x90]);
1106 image[3..11].copy_from_slice(b"MSWIN4.1");
1107 image[12] = 0x02;
1108 image[440..448].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
1109
1110 let mb = META_BLOCK_OFFSET as usize;
1111 image[mb..mb + 8].copy_from_slice(b"-FVE-FS-");
1112 image[mb + 10..mb + 12].copy_from_slice(&2u16.to_le_bytes());
1113 image[mb + 16..mb + 24].copy_from_slice(&ENCRYPTED_SIZE.to_le_bytes());
1114 image[mb + 28..mb + 32].copy_from_slice(&1u32.to_le_bytes());
1115 image[mb + 32..mb + 40].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
1116 image[mb + 56..mb + 64].copy_from_slice(&RELOCATED_OFFSET.to_le_bytes());
1117 image[mb + 64..mb + 68].copy_from_slice(&(metadata_size as u32).to_le_bytes());
1118 image[mb + 64 + 36..mb + 64 + 38].copy_from_slice(&0x8004u16.to_le_bytes());
1119 image[mb + 64 + 48..mb + 64 + 48 + entries.len()].copy_from_slice(&entries);
1120
1121 let mut plain = [0u8; 512];
1122 for (i, b) in plain.iter_mut().enumerate() {
1123 *b = (i as u8) ^ 0x2e;
1124 }
1125 let ct = SectorCipher::new_xts128(xts_key).encrypt_sector(&plain, RELOCATED_OFFSET);
1126 let ro = RELOCATED_OFFSET as usize;
1127 image[ro..ro + 512].copy_from_slice(&ct);
1128
1129 (image, plain, build_bek(&external_key))
1130 }
1131
1132 #[test]
1133 fn unlock_and_read_synthetic_startupkey_volume() {
1134 let (image, plain, bek) = build_startupkey_volume();
1139 let mut vol = BitLockerVolume::unlock_with_startup_key(Cursor::new(image), &bek).unwrap();
1140 let mut buf = [0u8; 512];
1141 vol.read_at(0, &mut buf).unwrap();
1142 assert_eq!(buf, plain);
1143 assert_eq!(vol.metadata().encryption_method, 0x8004);
1144 }
1145
1146 #[test]
1147 fn no_startup_key_protector_errors() {
1148 let (image, _) = build_volume("test-pw");
1150 let (_, _, bek) = build_startupkey_volume();
1151 let res = BitLockerVolume::unlock_with_startup_key(Cursor::new(image), &bek);
1152 assert!(matches!(
1153 res,
1154 Err(BdeError::NoUnlockProtector { protector, .. }) if protector == "startup key"
1155 ));
1156 }
1157
1158 #[test]
1159 fn startup_key_without_external_key_errors() {
1160 let (image, _, _) = build_startupkey_volume();
1162 let empty_bek = vec![0u8; 48]; let res = BitLockerVolume::unlock_with_startup_key(Cursor::new(image), &empty_bek);
1164 assert!(matches!(
1165 res,
1166 Err(BdeError::MissingKeyMaterial { what }) if what == "external key"
1167 ));
1168 }
1169
1170 #[test]
1171 fn startup_key_external_key_without_key_property_errors() {
1172 let (image, _, _) = build_startupkey_volume();
1174 let ext_entry = entry(0, crate::metadata::VALUE_TYPE_EXTERNAL_KEY, &[0u8; 24]);
1176 let mut bek = vec![0u8; 48];
1177 bek.extend_from_slice(&ext_entry);
1178 let res = BitLockerVolume::unlock_with_startup_key(Cursor::new(image), &bek);
1179 assert!(matches!(
1180 res,
1181 Err(BdeError::MissingKeyMaterial { what }) if what == "external key property"
1182 ));
1183 }
1184
1185 fn build_xts256_volume(key_hash: [u8; 32]) -> (Vec<u8>, [u8; 512]) {
1189 let salt = [0x3du8; 16];
1190 let mut xts_key = [0u8; 64];
1191 for (i, b) in xts_key.iter_mut().enumerate() {
1192 *b = 0x46u8.wrapping_add(i as u8);
1193 }
1194 let vmk = [0x4cu8; 32];
1195
1196 let mut vmk_container = vec![0u8; 44];
1197 vmk_container[12..44].copy_from_slice(&vmk);
1198 let stretched = stretch_key(&key_hash, &salt);
1199 let vmk_ccm = aes_ccm_wrap(&stretched, &[0x5d; 12], &vmk_container);
1200
1201 let mut fvek_container = vec![0u8; 76];
1203 fvek_container[12..76].copy_from_slice(&xts_key);
1204 let fvek_ccm = aes_ccm_wrap(&vmk, &[0x6e; 12], &fvek_container);
1205
1206 let mut stretch_data = vec![0u8; 4];
1207 stretch_data.extend_from_slice(&salt);
1208 let stretch_entry = entry(0, VALUE_TYPE_STRETCH, &stretch_data);
1209 let vmk_ccm_entry = entry(0, VALUE_TYPE_AES_CCM, &vmk_ccm);
1210
1211 let mut vmk_data = vec![0u8; 28];
1212 vmk_data[26..28].copy_from_slice(&PROTECTION_RECOVERY.to_le_bytes());
1213 vmk_data.extend_from_slice(&stretch_entry);
1214 vmk_data.extend_from_slice(&vmk_ccm_entry);
1215 let vmk_entry = entry(ENTRY_TYPE_VMK, VALUE_TYPE_VMK, &vmk_data);
1216
1217 let fvek_entry = entry(ENTRY_TYPE_FVEK, VALUE_TYPE_AES_CCM, &fvek_ccm);
1218
1219 let mut vh_data = Vec::new();
1220 vh_data.extend_from_slice(&RELOCATED_OFFSET.to_le_bytes());
1221 vh_data.extend_from_slice(&512u64.to_le_bytes());
1222 let vh_entry = entry(ENTRY_TYPE_VOLUME_HEADER, ENTRY_TYPE_VOLUME_HEADER, &vh_data);
1223
1224 let mut entries = Vec::new();
1225 entries.extend_from_slice(&vh_entry);
1226 entries.extend_from_slice(&vmk_entry);
1227 entries.extend_from_slice(&fvek_entry);
1228 let metadata_size = 48 + entries.len();
1229
1230 let mut image = vec![0u8; IMAGE_SIZE];
1231 image[0..3].copy_from_slice(&[0xeb, 0x58, 0x90]);
1232 image[3..11].copy_from_slice(b"MSWIN4.1");
1233 image[12] = 0x02;
1234 image[440..448].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
1235
1236 let mb = META_BLOCK_OFFSET as usize;
1237 image[mb..mb + 8].copy_from_slice(b"-FVE-FS-");
1238 image[mb + 10..mb + 12].copy_from_slice(&2u16.to_le_bytes());
1239 image[mb + 16..mb + 24].copy_from_slice(&ENCRYPTED_SIZE.to_le_bytes());
1240 image[mb + 28..mb + 32].copy_from_slice(&1u32.to_le_bytes());
1241 image[mb + 32..mb + 40].copy_from_slice(&META_BLOCK_OFFSET.to_le_bytes());
1242 image[mb + 56..mb + 64].copy_from_slice(&RELOCATED_OFFSET.to_le_bytes());
1243 image[mb + 64..mb + 68].copy_from_slice(&(metadata_size as u32).to_le_bytes());
1244 image[mb + 64 + 36..mb + 64 + 38].copy_from_slice(&0x8005u16.to_le_bytes());
1245 image[mb + 64 + 48..mb + 64 + 48 + entries.len()].copy_from_slice(&entries);
1246
1247 let mut plain = [0u8; 512];
1248 for (i, b) in plain.iter_mut().enumerate() {
1249 *b = (i as u8) ^ 0x17;
1250 }
1251 let ct = SectorCipher::new_xts256(xts_key).encrypt_sector(&plain, RELOCATED_OFFSET);
1252 let ro = RELOCATED_OFFSET as usize;
1253 image[ro..ro + 512].copy_from_slice(&ct);
1254
1255 (image, plain)
1256 }
1257
1258 #[test]
1259 fn unlock_and_read_synthetic_xts256_volume() {
1260 let rk = "444444-444444-444444-444444-444444-444444-444444-444444";
1263 let key_hash = crate::crypto::recovery_key_hash(rk).unwrap();
1264 let (image, plain) = build_xts256_volume(key_hash);
1265 let mut vol =
1266 BitLockerVolume::unlock_with_recovery_password(Cursor::new(image), rk).unwrap();
1267 let mut buf = [0u8; 512];
1268 vol.read_at(0, &mut buf).unwrap();
1269 assert_eq!(buf, plain);
1270 assert_eq!(vol.metadata().encryption_method, 0x8005);
1271 }
1272
1273 #[test]
1274 fn seek_variants_and_eof() {
1275 use std::io::{Read as _, Seek as _};
1276 let (image, _) = build_volume("test-pw");
1277 let mut vol = BitLockerVolume::unlock_with_password(Cursor::new(image), "test-pw").unwrap();
1278
1279 assert_eq!(vol.seek(SeekFrom::End(0)).unwrap(), IMAGE_SIZE as u64);
1280 let mut b = [0u8; 16];
1281 assert_eq!(vol.read(&mut b).unwrap(), 0); assert_eq!(vol.seek(SeekFrom::Start(10)).unwrap(), 10);
1284 assert_eq!(vol.seek(SeekFrom::Current(5)).unwrap(), 15);
1285 assert!(vol.seek(SeekFrom::Current(-100)).is_err()); }
1287
1288 fn meta_only_image(
1291 method: u16,
1292 protectors: &[u16],
1293 header_offsets: [u64; 3],
1294 block_at: Option<usize>,
1295 ) -> Vec<u8> {
1296 let mut entries = Vec::new();
1297 for p in protectors {
1298 let mut d = vec![0u8; 28];
1299 d[26..28].copy_from_slice(&p.to_le_bytes());
1300 entries.extend(entry(0x0002, 0x0008, &d));
1301 }
1302 let metadata_size = 48 + entries.len();
1303 let mut image = vec![0u8; 0x2000];
1304 image[0..3].copy_from_slice(&[0xeb, 0x58, 0x90]);
1305 image[3..11].copy_from_slice(b"MSWIN4.1");
1306 image[12] = 0x02;
1307 image[440..448].copy_from_slice(&header_offsets[0].to_le_bytes());
1308 image[448..456].copy_from_slice(&header_offsets[1].to_le_bytes());
1309 image[456..464].copy_from_slice(&header_offsets[2].to_le_bytes());
1310 if let Some(mb) = block_at {
1311 image[mb..mb + 8].copy_from_slice(b"-FVE-FS-");
1312 image[mb + 10..mb + 12].copy_from_slice(&2u16.to_le_bytes());
1313 image[mb + 32..mb + 40].copy_from_slice(&(mb as u64).to_le_bytes());
1314 image[mb + 64..mb + 68].copy_from_slice(&(metadata_size as u32).to_le_bytes());
1315 image[mb + 64 + 36..mb + 64 + 38].copy_from_slice(&method.to_le_bytes());
1316 image[mb + 64 + 48..mb + 64 + 48 + entries.len()].copy_from_slice(&entries);
1317 }
1318 image
1319 }
1320
1321 #[test]
1322 fn recognized_but_unvalidated_method_refuses() {
1323 let img = meta_only_image(0x8001, &[0x2000], [0x1000, 0, 0], Some(0x1000));
1328 let res = BitLockerVolume::unlock_with_password(Cursor::new(img), "x");
1329 assert!(matches!(
1330 res,
1331 Err(BdeError::UnvalidatedEncryptionMethod { method: 0x8001 })
1332 ));
1333 }
1334
1335 #[test]
1336 fn unrecognized_method_errors() {
1337 let img = meta_only_image(0x1234, &[0x2000], [0x1000, 0, 0], Some(0x1000));
1339 let res = BitLockerVolume::unlock_with_password(Cursor::new(img), "x");
1340 assert!(matches!(
1341 res,
1342 Err(BdeError::UnsupportedEncryptionMethod { method: 0x1234 })
1343 ));
1344 }
1345
1346 #[test]
1347 fn no_password_protector_errors() {
1348 let img = meta_only_image(0x8000, &[0x0800], [0x1000, 0, 0], Some(0x1000));
1350 let res = BitLockerVolume::unlock_with_password(Cursor::new(img), "x");
1351 assert!(matches!(
1352 res,
1353 Err(BdeError::NoUnlockProtector { protector, .. }) if protector == "password"
1354 ));
1355 }
1356
1357 #[test]
1358 fn no_valid_metadata_errors() {
1359 let img = meta_only_image(0x8000, &[0x2000], [0x1000, 0x1200, 0x1400], None);
1361 let err = BitLockerVolume::read_metadata(&mut Cursor::new(img)).unwrap_err();
1362 assert!(matches!(err, BdeError::NoValidMetadata { .. }));
1363 }
1364
1365 #[test]
1366 fn read_metadata_skips_zero_first_offset() {
1367 let img = meta_only_image(0x8000, &[0x2000], [0, 0x1000, 0], Some(0x1000));
1369 let meta = BitLockerVolume::read_metadata(&mut Cursor::new(img)).unwrap();
1370 assert_eq!(meta.encryption_method, 0x8000);
1371 }
1372
1373 struct FlakyReader {
1376 header: Vec<u8>,
1377 phase: usize,
1378 }
1379
1380 impl Read for FlakyReader {
1381 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1382 self.phase += 1;
1383 match self.phase {
1384 1 => {
1385 let n = buf.len().min(self.header.len());
1386 buf[..n].copy_from_slice(&self.header[..n]);
1387 Ok(n)
1388 }
1389 2 => Err(std::io::Error::new(
1390 std::io::ErrorKind::Interrupted,
1391 "eintr",
1392 )),
1393 _ => Err(std::io::Error::other("boom")),
1394 }
1395 }
1396 }
1397
1398 impl Seek for FlakyReader {
1399 fn seek(&mut self, _pos: SeekFrom) -> std::io::Result<u64> {
1400 Ok(0)
1401 }
1402 }
1403
1404 #[test]
1405 fn io_error_during_block_read_propagates() {
1406 let mut header = vec![0u8; 512];
1407 header[0..3].copy_from_slice(&[0xeb, 0x58, 0x90]);
1408 header[3..11].copy_from_slice(b"MSWIN4.1");
1409 header[12] = 0x02;
1410 header[440..448].copy_from_slice(&0x1000u64.to_le_bytes());
1411 let mut reader = FlakyReader { header, phase: 0 };
1412 let res = BitLockerVolume::read_metadata(&mut reader);
1413 assert!(matches!(res, Err(BdeError::Io(_))));
1414 }
1415
1416 #[test]
1417 fn wrong_password_fails_authentication() {
1418 let (image, _) = build_volume("test-pw");
1419 let res = BitLockerVolume::unlock_with_password(Cursor::new(image), "wrong");
1420 assert!(matches!(
1421 res,
1422 Err(BdeError::AuthenticationFailed { what }) if what == "volume master key"
1423 ));
1424 }
1425
1426 #[test]
1427 fn read_and_seek_traits() {
1428 use std::io::{Read as _, Seek as _};
1429 let (image, plain) = build_volume("test-pw");
1430 let mut vol = BitLockerVolume::unlock_with_password(Cursor::new(image), "test-pw").unwrap();
1431 vol.seek(SeekFrom::Start(0)).unwrap();
1432 let mut buf = [0u8; 256];
1433 vol.read_exact(&mut buf).unwrap();
1434 assert_eq!(&buf[..], &plain[..256]);
1435 assert_eq!(vol.volume_size(), IMAGE_SIZE as u64);
1436 }
1437
1438 #[test]
1439 fn non_bitlocker_reader_errors() {
1440 let r = BitLockerVolume::unlock_with_password(Cursor::new(vec![0u8; 1024]), "x");
1441 assert!(matches!(r, Err(BdeError::NotBitLocker { .. })));
1442 }
1443
1444 #[test]
1445 fn read_metadata_reports_protectors() {
1446 let (image, _) = build_volume("test-pw");
1447 let meta = BitLockerVolume::read_metadata(&mut Cursor::new(image)).unwrap();
1448 assert_eq!(meta.protector_types(), vec![PROTECTION_PASSWORD]);
1449 }
1450}