Skip to content

+ BitLocker volumes: open, mount and create - #807

Merged
Hawkynt merged 4 commits into
mainfrom
feat/bitlocker
Nov 10, 2026
Merged

Hawkynt merged 4 commits into
mainfrom
feat/bitlocker

Conversation

@Hawkynt

@Hawkynt Hawkynt commented Nov 10, 2026

Copy link
Copy Markdown
Owner

BitLocker (BDE/BITLK) volumes can now be opened, mounted and created.

Changes

  • FileFormat.BitLocker: -FVE-FS- and BitLocker To Go detection, FVE metadata (all three copies), VMK/FVEK unwrapping (AES-CCM) by user password, 48-digit recovery password, .BEK startup key or clear key; AES-XTS 128/256, AES-CBC (EBOIV) and AES-CBC + Elephant diffuser; the decrypted view laid out exactly as cryptsetup maps it (zeroed metadata areas, relocated volume header).
  • IEncryptedBlockDeviceProvider: the filesystem inside mounts read-only/read-write (RecoveryPassword, StartupKey parameters); missing/wrong credentials raise ArchivePasswordException.
  • Creates AES-XTS-128/256 volumes protected by a password and a recovery password.
  • Sourcing: cryptsetup lib/bitlk (LGPL-2.1+) converted (notice packed); Elephant diffuser from Ferguson's 2006 paper. Spec: docs/BITLOCKER-ON-DISK.md.
  • CI step "BitLocker oracle" (pinned, checksummed cryptsetup test images).

Verification

  • All cryptsetup BITLK test images match cryptsetup's whole-device SHA-256 with every listed credential (password, recovery password(s), BEK, clear key) — XTS, CBC, Elephant, 4k, To Go, Windows 11 startup key. The two encrypt-on-write images are refused, as cryptsetup refuses them.
  • cryptsetup 2.8 bitlkDump/bitlkOpen --test-passphrase accept created volumes by password and recovery password and extract our volume key; dm-crypt decrypts every region to our input. The full bitlkOpen mapping needs dm-zero, which runs in CI.
  • Not verified: Windows opening volumes we create (documented).

Closes #803

BitLocker (BDE/BITLK) volumes could not be read at all.

- Reader: -FVE-FS- boot sector and the BitLocker To Go variant (MSWIN4.1
  plus a BitLocker GUID at 424), the first FVE metadata copy whose CRC-32
  holds (falls back past a damaged one), VMK/FVEK/volume-header/description
  entries, and the validation datum that authenticates the metadata with
  the opened VMK.
- Unlock: user password (UTF-16LE, SHA-256 twice, 0x100000-round stretch),
  48-digit recovery password, startup key (.BEK), clear key. VMK and FVEK
  are AES-CCM key datums (AesCcm, 12-byte nonce, 16-byte tag).
- Sectors: AES-XTS 128/256, AES-CBC with the encrypted-byte-offset IV, and
  AES-CBC with the Elephant diffuser 128/256, at 512 and 4096 bytes. Work is
  batched per buffer through cached ECB/CBC transforms.
- View: cryptsetup's BITLK_activate_by_volume_key segment layout. Metadata
  areas read as zeros and swallow writes, the relocated volume header maps
  back to sector 0, and the gaps become crypt segments with their own IVs.
  Encrypt-on-write volumes and abnormal states are refused, as cryptsetup
  refuses them.
- BitLockerFormatDescriptor ("BitLocker"): lists metadata.ini without a
  password, extracts volume.img, and mounts the filesystem inside read-only
  or read-write (RecoveryPassword and StartupKey parameters).
- Writer: AES-XTS-128 by default (XTS-256, CBC and Elephant on request),
  with password and recovery-password protectors, three metadata copies and
  a volume-header backup. Everything is placed past the end of the input
  image, so the decrypted view is the input followed by zeros.

Sourcing: rung 1 for structures, entries, key derivation, the recovery
password, .BEK, clear key and segment layout: cryptsetup lib/bitlk
(LGPL-2.1+), converted, notice beside the code. Rung 3 for the Elephant
diffuser and the CBC IV: Ferguson, "AES-CBC + Elephant diffuser" (2006),
with constants copied from section 4.4. The GPL dm-crypt elephant code was
not used. XTS is IEEE 1619, cross-checked against the repository owner's
own Cipher/algorithms/modes/xts.js. Fields cryptsetup ignores but the
writer fills (datum method codes, nonce layout, BPB) were measured in the
Windows-made test images.

Verified:
- Every cryptsetup BITLK test image decrypts to the SHA-256 of cryptsetup's
  mapping with every listed credential (see the oracle commit). The
  Windows-written VMKs and sectors cut from those images are now
  required-tier known answers.
- cryptsetup 2.8 parses what we write, opens it with both passwords and
  recovers our FVEK, and the kernel's dm-crypt decrypts every crypt
  segment to our input.
- Windows has not been tested against our volumes.
Nothing checked the BitLocker reader and writer against an outside
implementation.

- BitlkImageConformanceTests (CWB_BITLK_IMAGES): every image in
  cryptsetup's tests/bitlk-images.tar.xz is opened with each credential
  images.conf gives (password, recovery password, second recovery
  password, .BEK, clear key). Each must hash to images.conf's SHA256SUM
  of cryptsetup's whole mapping, with blkid's filesystem UUID at sector 0.
  The metadata must match check_dump (GUIDs, cipher, VMK GUIDs). Writes
  through each sector transform round-trip on copies, touching only their
  own ciphertext. The two encrypt-on-write images unlock but are refused
  activation, exactly as cryptsetup refuses them. They have no reference
  view, so the test reports them as skipped with that reason.
- BitLockerExternalConformanceTests: for our XTS-128/256 volumes, bitlkDump
  parses them, both passwords pass --test-passphrase, --dump-volume-key
  returns our FVEK, and bitlkOpen maps our plaintext. A kernel-made FAT
  volume is round-tripped through cryptsetup's mapping and our read-write
  mount. Without dm-zero, every crypt segment is checked through dm-crypt
  directly, and CWB_REQUIRE_DM_ZERO=1 makes the full mapping mandatory.
- CI step "BitLocker oracle": downloads the image archive pinned to
  cryptsetup 086591ce and checks its SHA-256, then runs both classes with
  the CWB_REQUIRE_* gates set.

Verified locally: all 21 images, 41 credential cases pass or match
cryptsetup's refusal. This host's kernel lacks dm-zero (its modules are
missing), so the full bitlkOpen mapping and the kernel FAT round trip run
only in CI.

Closes #803
FileFormat.BitLocker converts cryptsetup's lib/bitlk (LGPL-2.1+); add its
class-A row with revision and notice, as for the other conversions.
Symptom: CI's FilesystemWriteRoundTripTests.CreateRoundTrip_BitLocker failed -
the generic probe creates every format with only a password, and BitLocker
refused without a RecoveryPassword option; with one, it then refused the
512-byte probe as smaller than the relocated 8 KiB volume header.

Fix: BitLockerCreateOptions.AddRecoveryProtector; the descriptor writes a
password-only volume when no RecoveryPassword is given (it cannot hand a
generated one back, and a recovery protector nobody holds is worse than
none), and zero-pads an image below 8 KiB + one sector to the minimum, as
sector-granular formats pad. Docs and API reference updated.

Verified: the generic round trip passes; new tests for password-only creation
and for padding; all BitLocker unit tests pass.
@Hawkynt
Hawkynt merged commit efe2ce1 into main Nov 10, 2026
5 checks passed
@Hawkynt
Hawkynt deleted the feat/bitlocker branch November 10, 2026 02:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BitLocker volumes: open, mount and create

1 participant