Simple macOS GUI frontend for dislocker.
It does not fork or reimplement BitLocker crypto — it shells out to an
installed dislocker-fuse and then attaches/mounts the resulting filesystem
image (NTFS via ntfs-3g, or FAT/ExFAT via system mount helpers).
Version: see VERSION.
Security reports: SECURITY.md. Contributing / Issues:
CONTRIBUTING.md.
- Unlock a BitLocker volume with a user password, recovery password, or
.bekfile. - Mount the decrypted image under
/Volumes(read-only by default): ntfs-3g for NTFS, system mount_msdos / mount_exfat for BitLocker To Go FAT/ExFAT. - Unmount cleanly (volume → detach raw disk → unmount FUSE).
On modern macOS, launch with sudo ./run.sh. The GUI then runs already-root
and mounts in-process (no osascript administrator dialog). An unprivileged
GUI cannot open removable /dev/disk* under TCC, so non-sudo launches cannot
complete a useful mount.
run.sh executes the checkout as root (PYTHONPATH=…/src). Keep the repo
owned by you and not group-/world-writable; do not sudo a shared or untrusted
tree. A separate root-owned app package is out of scope for this personal helper.
Elevated GUI mounts intentionally support physical BitLocker devices
(/dev/diskN or /dev/diskNsM) only; regular image files are out of scope.
| Dependency | Required? | Role |
|---|---|---|
| macOS | Yes | Target platform |
| Python 3.10+ with tkinter | Yes | GUI |
| dislocker | Yes | BitLocker unlock (dislocker-fuse) |
| macFUSE ≥4.10 | Yes | Required for dislocker + ntfs-3g options used here (allow_other / local / uid). Must provide a fuse3 pkg-config entry. |
ntfs-3g |
Yes (for NTFS) | RO and RW NTFS mounts (kernel mount_ntfs is missing on recent macOS). FAT/ExFAT BitLocker To Go uses system mount_msdos / mount_exfat. |
This project does not bundle FUSE or ntfs-3g (system extensions / installers).
If needed: https://brew.sh
macFUSE is required for the Homebrew macOS formulae below. After install, approve the system extension in System Settings → Privacy & Security, then reboot if macOS asks.
brew install --cask macfuse(brew install --cask needs an interactive Terminal for sudo. You can also
open the .dmg from the Homebrew cache and run Install macFUSE.pkg.)
The Homebrew build below satisfies the GUI preflight check only; the
elevated mount flow requires root-owned binaries. Run
scripts/install-root-deps.sh once (section 4) to satisfy both.
Homebrew core’s dislocker / ntfs-3g formulae are awkward on modern macOS
(no bottles / FUSE disabled). Use the community macFUSE tap:
brew tap gromgit/homebrew-fuse
# Newer Homebrew may require:
# brew trust --formula gromgit/fuse/dislocker-mac
# brew trust --formula gromgit/fuse/ntfs-3g-mac
brew install gromgit/fuse/dislocker-mac
brew install gromgit/fuse/ntfs-3g-mac # required for mounts on modern macOSConfirm:
which dislocker-fuse
dislocker-fuse -h | head
which ntfs-3g
ntfs-3g --versionIf dislocker-fuse fails with a missing libmbedcrypto.16.dylib, the bottle
was built against mbedtls 3.x while Homebrew linked mbedtls 4.x. Fix:
brew install mbedtls@3
ln -sf /opt/homebrew/opt/mbedtls@3/lib/libmbedcrypto.16.dylib \
/opt/homebrew/opt/mbedtls/lib/libmbedcrypto.16.dylib(That symlink can break on brew upgrade; re-run if dislocker stops loading.)
Then click Recheck deps in dislocker-ui (or restart it). Without ntfs-3g,
Mount is unavailable. Uncheck Read-only when you need writes.
Notes:
- Community taps are unsupported by Homebrew.
- The GUI's dependency check is advisory. For the elevated mount itself,
the complete toolchain must be trusted: macOS supplies
hdiutil(/usr/bin/hdiutil),diskutil(/usr/sbin/diskutil), andumount(/sbin/umount); an administrator must installdislocker-fuseandntfs-3gas root-owned, non-group/world-writable executables under/usr/local/sbinor/opt/local/sbin. A normal user-owned Homebrew installation is deliberately not executed as root. - Mounts need administrator authorization (macOS dialog). Running
sudo ./run.shremains a power-user escape hatch (already-root path skips osascript). Elevating from a user-writable checkout is no stronger than that.
The Homebrew path above is advisory only. To make the elevated mount flow
trust the toolchain, run the installer once — it builds dislocker-fuse and
ntfs-3g from source and installs them root-owned into /opt/local/sbin:
sudo scripts/install-root-deps.sh # or: --prefix /usr/local- From source, not a copy: a copied Homebrew binary keeps load commands
pointing at the user-owned Homebrew prefix, so a root-trusted binary would
load user-mutable dylibs. A
--prefixbuild plus vendoring keeps the whole dyld closure root-managed. - libfuse is vendored: macFUSE installs its libfuse into
/usr/local/lib, which is often user-owned (true even on Apple Silicon). The installer copies libfuse root-owned into the install prefix and re-points the binaries, so no root-trusted binary loads a library from a user-writable directory. Works on both Apple Silicon and Intel. - macFUSE: if macFUSE isn't installed, the script installs the cask and asks you to re-run. macFUSE's kernel extension is approved the first time you actually mount a FUSE volume (on Apple Silicon this can require enabling kernel extensions in Recovery, then a reboot) — there is no "system extension" to approve in Privacy & Security beforehand.
- This is optional; the manual Homebrew + tap path (section 3) remains valid for the GUI advisory check.
cd /path/to/dislocker-ui
chmod +x run.sh # onceNo Python packages beyond the stdlib (tkinter) are required.
- Plug in / attach the BitLocker disk.
- Launch the UI with sudo (required so mounts can open removable
/dev/disk*under macOS TCC; an unprivileged GUI cannot complete a useful mount):
cd /path/to/dislocker-ui
sudo ./run.shSUDO_UID / SUDO_GID are preserved so mounted files are owned by your user,
not root. Running python3 -m dislocker_ui without sudo is unsupported for
real mounts.
- Select a volume (or type
/dev/diskXsY). - Choose unlock method: user password, recovery password, or
.bekfile.- For user passwords, click the "Show" button to briefly reveal the password in clear text (auto-hides after 30 seconds).
- Leave Read-only checked unless you need writes.
- Click Mount, then open the path under
/Volumes(shown in the dialog / status line). - When finished, click Unmount before ejecting the disk or shutting down.
| Goal | What you need |
|---|---|
| Browse files (read) | dislocker + macFUSE + ntfs-3g (NTFS) or mount_msdos/exfat (FAT/ExFAT) |
| Edit/copy onto the volume (write) | same + uncheck Read-only |
- Dislocker handles BitLocker. With
-r(this app’s default) the FUSE layer is read-only as well. - After decrypt, the app probes the attached image: NTFS uses
ntfs-3g (required on modern macOS; no
mount_ntfs); FAT/ExFAT (BitLocker To Go) uses systemmount_msdos/mount_exfat. - NTFS mounts use
umask=077withallow_other. FAT/ExFAT mounts use-u/-gfromSUDO_UID/SUDO_GIDand-m 700(owner-only mode bits).
- Default mount mode is read-only.
- Passwords / recovery keys are passed to
dislocker-fuseas CLI arguments (visible briefly in process listings /ps). Prefer a private machine and unmount when finished. Secrets are not placed in AppleScript; they travel briefly in a mode-0600 request file during elevation. - Elevation writes only its mode-0600 request file under your
Library/Application Support/dislocker-ui/folder; a killed-run request is swept before the next Mount/Unmount. The privileged helper derives its session state and diagnostics beneath root-controlled/var/db/dislocker-ui/. Prefersudo ./run.shso the GUI is already root and skips that path. - Always use Unmount in the app before ejecting the disk or sleeping the Mac.
- Running the GUI as root does not harden a world-writable source tree — keep the checkout private.
src/dislocker_ui/ # application package
tests/ # test helpers / scripts
plans/ # active implementation plans (archive/ for finished)
TO_DO.md # open work only (remove items when done)
CHANGELOG.md # user-visible product changes
CHANGELOG.dev.md # harness / CI / docs-only notes
tmp/ # local scratch (gitignored)
.context/ # disposable agent scratch (gitignored)
run.sh # launcher
pyproject.toml # packaging + Ruff config
.pre-commit-config.yaml # optional local hooks (incl. pre-push)
hooks/ # local policy scripts (absolute-path / line-count)
.sonarcloud.properties # SonarCloud Automatic Analysis scope
.github/ # CI + issue/PR templates
Runtime needs no pip packages. Optional lint/hooks:
pip install -e ".[dev]" # ruff, pre-commit, pytest, pip-audit
ruff check src tests hooks
ruff format src tests hooks
pytest --cov --cov-report=term-missing
python3 hooks/check_absolute_paths.py
python3 hooks/check_file_size.py
pip-audit # prefer a clean venv, not a shared global env
pre-commit install -t pre-commit -t pre-push
pre-commit run --all-filesCI on GitHub runs compileall, Ruff (incl. complexity), absolute-path and line-count checks, pytest with coverage, pip-audit, Semgrep (OWASP Top 10 + Python rules), and gitleaks.
No SONAR_TOKEN is required for the default setup:
- Sign in at SonarQube Cloud with GitHub and import
kgrizz-git/dislocker-ui(public / open-source plan is fine). - In the project: Administration → Analysis Method → Automatic Analysis → on.
- Optional: keep
.sonarcloud.propertiesfor source/test paths (already in this repo). Set path exclusions in the SonarCloud UI (Administration → General Settings → Analysis Scope); Automatic Analysis does not support wildcardsonar.exclusionsin.sonarcloud.properties.
Do not also run a CI-based Sonar scan while Automatic Analysis is enabled (SonarCloud rejects that combo). Coverage upload needs CI-based analysis later if you want it.
dislocker-ui is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
SPDX: GPL-3.0-or-later. Commercial use is allowed; copyleft applies when you
redistribute this program or a modified version (you must provide source under
the same license terms).
This project does not bundle dislocker,
macFUSE/FUSE-T, or ntfs-3g. Those remain separate system dependencies under
their own licenses (dislocker is typically GPL-2.0-or-later). Using
dislocker-ui does not grant you rights to those projects. If you redistribute a
package that includes their binaries or sources, follow their license terms.