Caveat lector: This information is intended for entertainment purposes only.

When individuals speak of doing “big fist pumps” after their Arch installs successfully boot it can be hard to contain one’s curiosity about the path that led them there. But it’s hard to understand until you try it yourself.

This was my journey to first install. It was an encrypted one.

In this tutorial I will show you how I repurposed the bomb I took on 30 flights to double-down on privacy using deniable encryption and how you can too.

In cryptography and steganography, deniable encryption is encryption that allows its users to convincingly deny the fact that the data is encrypted or, assuming that the data is obviously encrypted, its users can convincingly deny that they are able to decrypt it. Source: cryptography.fandom.com.

While you could deniably encrypt any system the mid-2014 Mac is special because it had a battery recall. The recall repair work gave owners the perfect excuse to shred their devices using crypto-randomness prior to service. And in that randomness it’s possible to install and run an invisible operating system.

Here’s a gist of the setup:

Deniable encryption on Apple SSD [ , , , ]

Arch Linux full offline installation

Ciphertext and unlock keys kept separate

2FA via pre-boot password authentication

Large, easy to read font for the console

Support for multiple encrypted disks

And some technical details:

LUKS2 with detached LUKS header

LVM on LUKS with 2 logical volumes: 1 logical /root (ext4) 1 logical /home (ext4)

UEFI boot using systemd-boot

Dedicated USB header backup partition

Are there reasons not to use deniable encryption? Gentoo mentions technical ones. What they don’t stay, however, is that it’s possible you may be breaking laws in some countries. But I won’t tell if you don’t.

Requirements

You will need the following hardware to complete setup:

1 2GB+ USB flash drive with archiso installed usb1

1 256MB+ USB flash drive to boot the system usb2

1 mid-2014 MacBook Pro (MacBookPro11,5) sda

Expect a deep discount on the Mac if its battery has yet to be replaced or if the previous owner cannot prove the recall work was performed on the machine.

Breaks

During this tutorial you may power down your machine after completing any section, reboot and pick up where you left off using this script:

Expand to view script

test -b /dev/sdd1 && \ mkdir -p /mnt/ { usb2,vg1/home } && \ mount -r /dev/sdd1 /mnt/usb2 && \ test -f /mnt/usb2/ob1 && \ cryptsetup open --header /mnt/usb2/ob1 --type luks /dev/sda c1 && \ sleep 1 && \ mount /dev/mapper/vg1-root /mnt/vg1 && \ mount /dev/mapper/vg1-home /mnt/vg1/home && \ test -b /dev/sdd2 && \ mkdir -p /mnt/boot && \ mount /dev/sdd2 /mnt/boot

Verify with lsblk -o NAME,FSTYPE,TYPE,MOUNTPOINT . You should see output like:

Expand to view output

NAME FSTYPE TYPE MOUNTPOINT loop0 squashfs loop sda disk └─c1 LVM2_member crypt ├──vg1-root ext4 lvm /mnt/vg1 └──vg1-home ext4 lvm /mnt/vg1/home sdb iso9660 disk ├──sdb1 iso9660 part /run/archiso/bootmnt └──sdb2 vfat part sdd disk ├──sdd1 vfat part /mnt/usb2 └──sdd2 vfat part /mnt/boot

Note: Partition named sdd2 is created during Booting and should not appear beforehand unless usb2 already contained it prior to starting tutorial.

Assumes you have completed Setup and reinserted usb2 after booting Archiso.

Setup

First boot from usb1 and erase the Apple SSD. One of the fastest, more secure ways to do this is to perform a zero-write inside a DMCrypt container.

Tip: setfont sun12x22 and setfont latarcyrheb-sun32 increase font size.

Then connect and mount usb2 , and use cryptsetup to prepare a LUKS container where sdd1 is a writable partition on usb2 for detached LUKS header file ob1 :

mkdir /mnt/usb2 && mount /dev/sdd1 /mnt/usb2 && \ cryptsetup --header /mnt/usb2/ob1 luksFormat /dev/sda

Now run lsblk -I 8 -o NAME,FSTYPE,SIZE,FSUSED . You should see output like:

Expand to view output

NAME FSTYPE SIZE FSUSED sda 465.9G sdb iso9660 7.5G ├──sdb1 iso9660 639M 639M └──sdb2 vfat 64M sdd 250M └──sdd1 vfat 250M 16M

Confirm FSTYPE of sda is not crypto_LUKS and FSUSED of usb2 is 16M .

If everything looks good, continue. Otherwise, run wipefs -a /dev/sda to wipe the SSD file system info, rm /mnt/usb2/ob1 and restart the setup.

Map LUKS container using detached header and prepare logical volumes:

cryptsetup open --header /mnt/usb2/ob1 --type luks /dev/sda c1 && \ pvcreate /dev/mapper/c1 && vgcreate vg1 /dev/mapper/c1 && \ lvcreate -L 32G vg1 -n root && lvcreate -l 100%FREE vg1 -n home

Then format and mount root and home filesystems:

mkfs.ext4 /dev/vg1/root && mkfs.ext4 /dev/vg1/home && \ mkdir /mnt/vg1 && mount /dev/vg1/root /mnt/vg1 && \ mkdir /mnt/vg1/home && mount /dev/vg1/home /mnt/vg1/home

And run lsblk -I 8 -o NAME,FSTYPE,MOUNTPOINT . You should see output like:

Expand to view output

NAME FSTYPE MOUNTPOINT sda └─c1 LVM2_member ├──vg1-root ext4 /mnt/vg1 └──vg1-home ext4 /mnt/vg1/home sdb iso9660 ├──sdb1 iso9660 /run/archiso/bootmnt └──sdb2 vfat sdd └──sdd1 vfat /mnt/usb2

Confirm FSTYPE of c1 of sda is LVM2_member , FSTYPE of vg1-root and vg1-home of c1 is ext4 , and MOUNTPOINT of vg1-root and vg1-home are /mnt/vg1 and /mnt/vg1/home .

(Optional) If everything checks out, unplug usb2 and follow the steps in Breaks so you can be confident you won’t lose any work and have to start over again.

Installation

Everything needed to install and run Arch is already available on Archiso. Steps in this section from Offline Installation and licensed under GFDL-1.3-or-later.

Install Archiso to new root:

cp -ax / /mnt/vg1 && \ cp -vaT /run/archiso/bootmount/arch/boot/x86_64/vmlinuz \ /mnt/vg1/boot/vmlinuz-linux && \ genfstab -L /mnt/vg1 >> /mnt/vg1/etc/fstab

Configure base system:

arch-chroot /mnt/vg1 /bin/bash -c "\ sed -i 's/Storage=volatile/#Storage=auto/' /etc/systemd/journald.conf && \ rm /etc/udev/rules.d/81-dhcpcd.rules && \ systemctl disable pacman-init.service choose-mirror.service && \ rm -r /etc/systemd/system/{choose-mirror.service,pacman-init.service,etc-pacman.d-gnupg.mount,getty@tty1.service.d} && \ rm /etc/systemd/scripts/choose-mirror && \ rm /root/{.automated_script.sh,.zlogin} && \ rm /etc/mkinitcpio-archiso.conf && \ rm -r /etc/initcpio && \ pacman-key --init && pacman-key --populate archlinux"

This is a scripted approach and it omits one redundant step listed in the ArchWiki as retrieved on 21 Dec 2019. Tested and known to work with archiso 5.3.13-arch1-1 . Modifications may be required for other Archiso versions.

Now run lsblk -I 8 -o NAME,SIZE,FSUSED,FSUSE% . You should see output like:

Expand to view output

NAME SIZE FSUSED FSUSE% sda 465.9G └─c1 465.9G ├──vg1-root 32G 1.7G 5% └──vg1-home 433.9G 72M 0% sdb 7.5G ├──sdb1 639M 639M 100% └──sdb2 64M sdd 250M └──sdd1 250M 16M 6%

Confirm FSUSE% of vg1-root is 5% and FSUSED of vg1-home is 72M .

Booting

With Arch installed you need a way to boot it. Assuming only one partition exists on usb2 repurpose it for both detached LUKS header and bootability.

Start by reformating usb2 with 16MiB hidden partition:

test -f /mnt/usb2/ob1 && \ cp /mnt/usb2/ob1 /mnt/vg1/home && \ umount /mnt/usb2 && \ parted /dev/sdd rm 1 mkpart primary fat16 2048s 17463KiB \ set 1 lba off set 1 hidden on && \ mkfs.fat /dev/sdd1 && \ mount /dev/sdd1 /mnt/usb2 && \ mv /mnt/vg1/home/ob1 /mnt/usb2

Check the partition size with df /dev/sdd . You should see output like:

Expand to view output

Filesystem 1K-blocks Used Available Use% Mounted on /dev/sdd1 16384 16384 0 100% /mnt/usb2

Confirm Use% is 100% and Available is 0 .

Prepare your boot partition and update the file system table:

parted mkpart /dev/sdd primary fat16 36864s 228720000B \ set 2 esp on && \ mkfs.fat /dev/sdd2 && \ mkdir -p /mnt/boot && \ mount /dev/sdd2 /mnt/boot && \ genfstab -U /mnt/boot >> /mnt/vg1/etc/fstab

Warning: That last genfstab appended one entry to the bottom of file fstab but it’s incorrect. Edit the fstab file so the last item references /boot and not / . There should be three items in total: / , /home and /boot .

Show partitions with parted /dev/sdd print . You should see output like:

Expand to view output

Number Start End Size Type File system Flags 1 1049kB 17.9MB 16.8MB primary fat16 hidden 2 18.9MB 229MB 210MB primary fat16 esp

Confirm Size of 2 is greater than 209.72MB , Type of 1 and 2 is primary with File system of fat16 , and Flags of 1 and 2 are hidden and esp , respectively.

Make USB bootable, relaxing validation for boot from non-GPT partition table:

export SYSTEMD_RELAX_ESP_CHECKS = 1 && \ test "yes" ! = " $( bootctl --esp-path = /mnt/boot is-installed ) " && \ bootctl --esp-path = /mnt/boot install

You should see output like:

Expand to view output

Created "/mnt/boot/EFI". Created "/mnt/boot/EFI/systemd". Created "/mnt/boot/EFI/BOOT". Created "/mnt/boot/loader". Created "/mnt/boot/loader/entries". Created "/mnt/boot/EFI/Linux". Copied "/usr/lib/systemd/boot/efi/systemd-bootx64.efi" to "/mnt/boot/EFI/systemd/systemd-bootx64.efi". Copied "/usr/lib/systemd/boot/efi/systemd-bootx64.efi" to "/mnt/boot/EFI/BOOT/BOOTX64.EFI". Created "/mnt/boot/01234567890abcdef1234567890abdf0". Random seed file /mnt/boot/loader/random-seed successfully written (512 bytes). Created EFI boot entry "Linux Boot Manager".

Where 01234567890abcdef1234567890abdf0 is a pseudo-random default boot loader and currently specified in the loader config generated by bootctl in the last step.

Create an entry for the default loader using a subshell:

( default = " $( grep '^default' /mnt/boot/loader/loader.conf ) " prefix = " ${ default : 8 : 33 } " version = 1 cp "/usr/share/systemd/bootctl/arch.conf \ /mnt/boot/loader/entries/ $prefix$version .conf" && \ sed -in '/options\|##\^$/d' /mnt/boot/loader/entries/ $prefix$version .conf && \ echo "options root=/dev/vg1/root" >> /mnt/boot/loader/entries/ $prefix$version .conf )

Note: Using a subshell prevents assigned variables from lingering.

Update mkinitcpio.conf to use systemd and configure suggested settings:

( conf = " $( </mnt/vg1/etc/mkinitcpio.conf ) " hooks = " $( grep '^HOOKS' <<< $conf ) " files = " $( grep '^FILES' <<< $conf ) " conf = " ${ conf // $hooks /# $hooks \\ nHOOKS =(base systemd autodetect keyboard sd-vconsole modconf block sd-encrypt sd-lvm2 filesystems fsck) } " conf = " ${ conf // $files /# $files \\ nFILES =(/boot/ob1) } " echo " $conf " > /mnt/vg1/etc/mkinitcpio.conf )

Tip: Type mkinitcpio -H then tab tab to list hooks & view hook help docs.

Compare the updated file against the Archiso original:

diff -u0 /etc/mkinitcpio.conf /mnt/vg1/etc/mkinitcpio.conf

You should see output like:

Expand to view output

--- /etc/mkinitcpio.conf 1970-01-01 00:32:50.000000000 +0000 +++ /mnt/vg1/etc/mkinitcpio.conf 1970-01-01 00:32:50.000000000 +0000 @@ -19 +19,2 @@ -FILES=() +#FILES=() +FILES=(/boot/ob1) @@ -52 +53,2 @@ -HOOKS=(base udev autodetect modconf block filesystems keyboard fsck) +#HOOKS=(base udev autodetect modconf block filesystems keyboard fsck) +HOOKS=(base systemd autodetect keyboard sd-vconsole modconf block sd-encrypt sd-lvm2 filesystems keyboard fsck)

Verify new FILES and HOOKS were added and their previous values commented out.

Afterwards create the vconsole.conf and crypttab.initramfs files for the sd-vconsole and sd-encrypt hooks, respectively, as noted in their help docs:

echo "FONT=latarcyrheb-sun32

KEYMAP=us" > /mnt/vg1/etc/vconsole.conf && \ echo "c1 $( find /dev/disk/by-id -name '*APPLE_SSD*' ) - header=/boot/ob1" \ > /mnt/vg1/etc/crypttab.initramfs

Copy LUKS header and mount the ESP inside the chroot jail:

cp /mnt/usb2/ob1 /mnt/vg1/boot && \ mkdir -p /mnt/vg1/boot/efi && \ arch-chroot /mnt/vg1 /bin/bash -c "\ findmnt /boot/efi || mount /dev/sdd2 /boot/efi"

Confirm mountpoint exists and ESP installed:

arch-chroot /mnt/vg1 /bin/bash -c "\ export SYSTEMD_RELAX_ESP_CHECKS=1 && \ bootctl is-installed || findmnt /boot/efi || lsblk"

Verify result is yes otherwise use the findmnt or lsblk output to help debug. And with the ESP installed generate the image required to boot the system:

arch-chroot /mnt/vg1 /bin/bash -c "mkinitcpio -P"

You should see output like:

Expand to view output

==> Buildiing image from preset: /etc/mkinitcpio.d/linux.preset: 'default' -> -k /boot/vmlinux-linux -c /etc/mkinitcpio.conf -g /boot/initramfs-linux.img ==> Starting build: 5.3.13-arch1-1 -> Running build hook: [base] -> Running build hook: [systemd] -> Running build hook: [autodetect] -> Running build hook: [keyboard] -> Running build hook: [sd-vconsole] -> Running build hook: [modconf] -> Running build hook: [block] -> Running build hook: [sd-encrypt] -> Running build hook: [sd-lvm2] -> Running build hook: [filesystems] -> Running build hook: [fsck] ==> Generating module dependencies ==> Creating gzip-compressed initcpio image: /boot/initramfs-linux.img ==> Image generation successful ==> Buildiing image from preset: /etc/mkinitcpio.d/linux.preset: 'fallback' -> -k /boot/vmlinux-linux -c /etc/mkinitcpio.conf -g /boot/initramfs-linux-fallback.img -S autodetect ==> Starting build: 5.3.13-arch1-1 -> Running build hook: [base] -> Running build hook: [systemd] -> Running build hook: [autodetect] -> Running build hook: [keyboard] -> Running build hook: [sd-vconsole] -> Running build hook: [modconf] -> Running build hook: [block] ==> WARNING: Possibly missing firmware for module: wd719x ==> WARNING: Possibly missing firmware for module: aic94xx -> Running build hook: [sd-encrypt] -> Running build hook: [sd-lvm2] -> Running build hook: [filesystems] -> Running build hook: [fsck] ==> Generating module dependencies ==> Creating gzip-compressed initcpio image: /boot/initramfs-linux-fallback.img ==> Image generation successful arch-chroot /mnt/vg1 /bin/bash -c "mkinitcpio -P 20.24s user 4.82s system 107% cpu 23.390 total

Copy both initcpio image files generated in the last step to /mnt/boot and then remove the duplicate LUKS header now embedded in the images:

cp -ai /mnt/vg1/boot/initramfs-linux* /mnt/boot && \ cp -ai /mnt/vg1/boot/vmlinuz-linux /mnt/boot && \ rm /mnt/vg1/boot/ob1

Finally, set a root password and power down the machine:

arch-chroot /mnt/vg1 /bin/bash -c "passwd" && shutdown -P now

Reboot

Unplug both usb1 and usb2 then power on the machine.

As the computer boots you should see a blinking folder icon with a question mark inside as described in Troubleshooting. If you do, go ahead and insert usb2 into one of the USB ports on the machine.

At this point the folder will stop blinking and the boot process will begin. Once authenticated and booted you will be presented with a login prompt:

Arch Linux 5.3.13-arch1-1 (tty1) archlinux login: _

Type root for the login and press Enter . A password prompt will then appear where you can enter the password created in the last step of Booting. Once logged in you are finished and may begin using your invisible arch.

Summary

In this tutorial you learned how to use a mid-2014 Mac to achieve a form of deniable encryption by installing Arch Linux offline. For improved deniability bear in mind UEFI boot loaders are stored in firmware at /sys/firmware/efi and you may wish to use efibootmgr to clear some of those out at some point.

Once the Mac has reached EOL it is important you dispose of it properly. I have prearranged a drop-off location where you may safely destroy the device:

Just be sure to tip the local Banjar appropriately.

Troubleshooting

If during system boot, all you see is a blinking folder with a question mark:

You need to install an operating system

You need to boot to your invisible operating system

Note the blinking folder is the expected behavior after after performing a zero-write as described in the first step of Setup. It is also the expected behavior when attempting to start your machine without usb2 attached. If you decide to bail on your Arch install there’s always Manjaro.

If during Reboot you do not receive a password prompt and see:

Expand to view output

[ TIME ] Timed out waiting for device /dev/disk/by-id/dm-uuid/CRYPT-LUKS2-fe06f149a69e844682323674ee70d4bf-c1. [DEPEND] Dependency failed for Cryptography Setup for c1. [DEPEND] Dependency failed for Local Encrypted Volumes. ... [FAILED] Failed to start Switch Root.

You likely need to modify crypttab.initramfs to point to your physical SSD. When finished, regenerate your RAM disk and try booting from usb2 again.

If during Reboot you do receive a password prompt and see:

Expand to view output

[ OK ] Created slice system-lvm2\x2dpvscan.slice. Starting LVM2 PV scan on device 254:0... [ OK ] Started Cryptography Setup for c1. [ OK ] ... [ OK ] Create list of static device nodes in /dev. [ OK ] ... [FAILED] Failed to start Switch Root.

You likely need to modify the root in your ESP loader entry like options root=/dev/vg1/root , regenerate your RAM disk and try booting from usb2 again.