Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save bicisteadm/ee32d8442c51173d36d02c80856d8b96 to your computer and use it in GitHub Desktop.
Save bicisteadm/ee32d8442c51173d36d02c80856d8b96 to your computer and use it in GitHub Desktop.

Revisions

  1. @nelsonaloysio nelsonaloysio created this gist Oct 17, 2023.
    125 changes: 125 additions & 0 deletions bitlocker-on-macos.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,125 @@
    # Mounting BitLocker-encrypted NTFS drives as read/write on MacOS Ventura

    BitLocker encryption has become a common alternative for securing personal files and is nowadays natively supported by Linux, at least within GNOME. Some additional steps, however, are still required to ensure full MacOS compatibility. This guide describes the necessary steps to achieve it.

    ## Requirements

    We require three packages: **macFUSE**, **ntfs-3g** (and **brew**), and **dislocker**.

    ### 1/3: Install macFUSE

    [macFUSE](https://osxfuse.github.io/) is a compatibility layer, previously known as OSXFUSE, that extends MacOS's native file system with third-party ones - like NTFS.

    Simply obtain the .dmg package from the official website (or the [GitHub repository](https://github.com/osxfuse/osxfuse/releases)) and install it - it is required for the next steps to succeed. Alternatively, you may try and install it using Homebrew instead:

    ```
    brew install --cask macfuse
    ```

    You will need to reboot your PC in order to complete the installation.

    ### 2/3: Install ntfs-3g

    > Huge thanks to [gromgit](https://github.com/gromgit/homebrew-fuse) for making ntfs-3g easily available as a formula.
    The [ntfs-3g](https://github.com/tuxera/ntfs-3g) package is an open source implementation for mounting NTFS file systems as read and write, and may too be installed using Homebrew:

    ```
    brew tap gromgit/homebrew-fuse &&
    brew install ntfs-3g-mac
    ```

    After installing, the mount_ntfs binary becomes available to mount as `r+w`.

    ### 3/3: Install dislocker

    Compiling [dislocker](https://github.com/Aorimn/dislocker) requires the second version of [Mbed-TSL](https://github.com/Mbed-TLS/mbedtls) (previously PolarSSL). Trying to compile with the latest (third) version causes an error:

    ```
    ssl_bindings.h:29:10: fatal error: 'mbedtls/config.h' file not found
    ```

    To solve it, first make sure you install the second version of Mbed-TLS:

    ```
    brew install mbedtls@2
    ```

    As the `mbedlts@2` package is only available as a *keg*, no symbolic links are created into `/usr/local` by default. Thankfully, we may easily temporarily replace the linked libraries from mbedtls (if installed) with `mbedtls@2`:

    ```
    brew unlink mbedtls
    brew link mbedtls@2
    ```

    Now we may get the latest version of dislocker, compile and install it:

    ```
    mkdir dislocker &&
    curl -L https://github.com/Aorimn/dislocker/tarball/master |
    tar -xz --strip 1 -C dislocker &&
    cd dislocker &&
    cmake . &&
    make &&
    sudo make install
    ```

    Finally with dislocker installed, we may undo the previous changes:

    ```
    brew unlink mbedtls@2
    brew link mbedtls
    ```

    ## Mounting and unmounting

    If everything worked out before, it's now just a matter of issuing a series of commands - boring, but quick.

    **[Here's a handy script for that](#file-bitlocker-on-macos-sh)**, which automates both the process of mounting and unmounting the device.

    ### Manually mounting

    > Another huge thanks to [Christian Engvall](https://www.christianengvall.se/decrypt-bitlocker-encrypted-partition-on-linux-and-macos/) for describing these steps on MacOS.
    First, connect your device and find the identifier (e.g., `/dev/diskXsY`) with:

    ```
    diskutil list
    ```

    Let's unlock it (replace `diskXsY` with your device's identifier) to `~/.dislocker`:

    ```
    mkdir -p ~/.dislocker/diskXsY &&
    sudo dislocker -V /dev/diskXsY -u -- ~/.dislocker/diskXsY
    ```

    We then create a new block device (take note of the output returned here):

    ```
    sudo hdiutil attach \
    -imagekey diskimage-class=CRawDiskImage -nomount \
    ~/.dislocker/diskXsY/dislocker-file
    ```

    And finally mount it (replace `/dev/diskZ` with the previous returned output):

    ```
    sudo mkdir -p /Volumes/BitLocker &&
    sudo mount_ntfs /dev/diskZ /Volumes/BitLocker
    ```

    The device should now appear on the sidebar of your Files window.

    ### Manually unmounting

    When done, unmount with (replace `diskXsY` and `diskZ` appropriately):

    ```
    sudo diskutil umount /Volumes/BitLocker
    sudo diskutil umountdisk /dev/diskZ
    sudo diskutil umount ~/.dislocker/diskXsY # or 'umount force' if required
    sudo diskutil eject /dev/diskX # optional
    ```

    Note that the first two commands may be replaced by simply clicking on the eject button near the device's name of the Files' window sidebar.
    87 changes: 87 additions & 0 deletions bitlocker-on-macos.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,87 @@
    #!/usr/bin/env bash

    # Mount BitLocker-encrypted device on MacOS.
    # Requires macFUSE, dislocker, and ntfs-3g.
    #
    # Tested with MacOS Ventura.

    USAGE="""usage: $(basename $0) DEVICE_ID [--force] [--eject]
    """

    DEVICE="$1"
    DEVICE_ID="$(echo "$1" | sed 's:/dev/::')"
    DEVICE_NUMBER="$(echo "$DEVICE_ID" | rev | cut -f2- -d's' | rev)"
    DEVICE_NAME="BitLocker"

    # Parse command line arguments.
    ARGS=()

    while [[ $# -gt 0 ]]; do
    case $1 in
    --eject)
    EJECT_DISK=1
    shift
    ;;
    --force)
    FORCE_UMOUNT=force
    shift
    ;;
    *)
    ARGS+=("$1") # save positional arg
    shift # past argument
    ;;
    esac
    done

    set -- "${ARGS[@]}"

    # Verify if running as superuser.
    ( [ "$1" != -h ] && [ "$1" != --help ] && [ $EUID -ne 0 ] ) &&
    echo "[ERROR] This script must be run as root (with sudo)." &&
    exit 1

    # Display usage.
    ( [ "$1" = -h ] || [ "$1" = --help ] ) &&
    echo $USAGE &&
    exit 0

    # Verify if input argument has been passed.
    [ -z "$1" ] &&
    echo """$USAGE
    [ERROR] Missing device identifier, e.g., '/dev/diskXsY'.
    Tip: List your currently available devices with 'diskutil list'.""" &&
    exit 1

    # Eject device if already mounted.
    if ( [ -f ~/.dislocker/$DEVICE_ID/dislocker-file ] && [ -f ~/.dislocker/$DEVICE_ID.tmp ] ); then
    DEVICE_BLOCK="$(cat ~/.dislocker/$DEVICE_ID.tmp)"
    [ -d /Volumes/$DEVICE_NAME ] && diskutil umount "/Volumes/$DEVICE_NAME"
    diskutil umountdisk $DEVICE_BLOCK &&
    diskutil umount $FORCE_UMOUNT ~/.dislocker/$DEVICE_ID &&
    echo "[OK] Successfully unmounted $DEVICE_NAME ($DEVICE_ID => $DEVICE_BLOCK)." &&
    rm -f ~/.dislocker/$DEVICE_ID.tmp ||
    echo -e "\n[ERROR] Failed to unmount $DEVICE_NAME ($DEVICE_ID => $DEVICE_BLOCK).\nTip: Retry running the command with '--force'."
    [ -n $EJECT_DISK ] && diskutil eject /dev/$DEVICE_NUMBER
    exit
    fi

    # Create folder for mounting (#1).
    mkdir -p ~/.dislocker/$DEVICE_ID &&

    # Mount device with dislocker.
    dislocker -V /dev/$DEVICE_ID -u -- ~/.dislocker/$DEVICE_ID &&

    # Create block device.
    DEVICE_BLOCK=$(hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount ~/.dislocker/$DEVICE_ID/dislocker-file | awk '{print $1}' ) &&

    # Create folder for mounting (#2).
    mkdir -p "/Volumes/$DEVICE_NAME" &&

    # Mount block device.
    mount_ntfs $DEVICE_BLOCK "/Volumes/$DEVICE_NAME" &&

    # Store block device identifier.
    echo $DEVICE_BLOCK > ~/.dislocker/$DEVICE_ID.tmp &&

    # Display message.
    echo -e "[OK] Successfully mounted $DEVICE_NAME ($DEVICE_ID => $DEVICE_BLOCK).\nTip: Run the command again to unmount the device."