#title Encrypted Arch Linux on SATA-to-USB
#subtitle A Practical Field Guide for Resisting Drive Seizure, Forensic Analysis, and State Coercion
#author The Techno Anarchist
#lang en
#pubdate 2026-07-07T03:28:24.799Z
#notes Legal notice: All techniques described are documented open-source security practices. Encryption, privacy tools, and secure deletion are legal in most jurisdictions and are actively advocated by the Electronic Frontier Foundation, Amnesty International, and Access Now. Understanding your local laws is your responsibility. This guide is published for educational and civil liberties purposes.
Revised July 2026 · 26 Chapters · 7 Parts
** About This Guide
This guide has one specific goal: help you build and operate an encrypted Arch Linux system on a portable SATA-to-USB drive so that if the drive is physically seized by an authoritarian authority, they obtain nothing useful.
It covers the complete picture — from choosing hardware through emergency destruction — organized as a linear book you can follow from beginning to end, or reference chapter by chapter when you need a specific procedure.
What this guide covers:
- Hardware selection and SATA-to-USB enclosure setup
- Full-disk encryption that resists offline brute-force at any scale
- Anti-forensic techniques tailored to how state forensics actually works — not how it is portrayed in television
- Plausible deniability: how to make the drive appear to contain something harmless even if you are forced to unlock it
- Boot security that detects and resists tampering while you are away from the device
- Network anonymization for the actual use of the machine
- Emergency destruction procedures for when physical security fails
- Legal frameworks and what to say — and not say — under duress
What this guide does not cover: This is not a general Linux security guide, a penetration-testing manual, or an offensive security resource. Every chapter exists to solve one specific problem: protecting a portable encrypted drive from a state adversary who can seize it, image it, and attack it offline.
; ** Table of Contents
;
; Part I — Understanding the Threat
;
; 1. [[#ch1][Chapter 1: Your Adversary]]
;
; 1. [[#ch2][Chapter 2: How State Drive Forensics Works]]
;
; Part II — Hardware
;
; 1. [[#ch3][Chapter 3: Choosing Your Drive and Enclosure]]
;
; 1. [[#ch4][Chapter 4: Acquiring Hardware Safely]]
;
; Part III — Building the Encrypted System
;
; 1. [[#ch5][Chapter 5: Preparing a Trusted Installation Environment]]
;
; 1. [[#ch6][Chapter 6: Disk Sanitization]]
;
; 1. [[#ch7][Chapter 7: LUKS2 Full-Disk Encryption]]
;
; 1. [[#ch8][Chapter 8: Installing Arch Linux]]
;
; 1. [[#ch9][Chapter 9: Bootloader Hardening]]
;
; 1. [[#ch10][Chapter 10: Post-Installation System Hardening]]
;
; Part IV — Counter-Forensics
;
; 1. [[#ch11][Chapter 11: What Forensic Tools Can and Cannot Do]]
;
; 1. [[#ch12][Chapter 12: SSD Forensics and Why Encryption Is Your Only Real Defense]]
;
; 1. [[#ch13][Chapter 13: Minimizing Forensic Artifacts During Use]]
;
; 1. [[#ch14][Chapter 14: Anti-Forensic Filesystem Configuration]]
;
; Part V — Drive Seizure Resistance
;
; 1. [[#ch15][Chapter 15: Plausible Deniability]]
;
; 1. [[#ch16][Chapter 16: Detached LUKS Headers]]
;
; 1. [[#ch17][Chapter 17: Hardware Security Tokens for LUKS]]
;
; 1. [[#ch18][Chapter 18: Anti-Evil-Maid: Detecting Tampering]]
;
; 1. [[#ch19][Chapter 19: TPM2 and Measured Boot]]
;
; Part VI — Emergency Procedures
;
; 1. [[#ch20][Chapter 20: Emergency Destruction]]
;
; 1. [[#ch21][Chapter 21: LUKS Nuke Passphrase]]
;
; 1. [[#ch22][Chapter 22: Dead Man’s Switch]]
;
; Part VII — Operational Security
;
; 1. [[#ch23][Chapter 23: Network Anonymization]]
;
; 1. [[#ch24][Chapter 24: Day-to-Day Operational Security]]
;
; 1. [[#ch25][Chapter 25: Encrypted Communications]]
;
; 1. [[#ch26][Chapter 26: Legal Rights and What to Say Under Duress]]
;
; Appendices
;
; 1. [[#appA][Appendix A: Quick Reference Checklist]]
;
; 1. [[#appB][Appendix B: Emergency Contacts and Resources]]
* Part I — Understanding the Threat
Before writing a single configuration file, you must understand who you are defending against and exactly what they do. Incorrect threat modeling produces a system that is hardened in the wrong places.
** Chapter 1. Your Adversary
*** The Authoritarian State Adversary
This guide is written for one specific adversary tier: a national or regional law enforcement or intelligence agency operating under an authoritarian government. This is a narrower, more tractable threat model than “all possible adversaries,” which allows precise, actionable guidance instead of vague generalities.
What they have:
- Legal authority — or willingness to act without it — to seize your device at a checkpoint, during a raid, or at a border crossing
- Access to commercial forensic tools: Cellebrite UFED 4PC, Magnet AXIOM, EnCase, FTK, Passware Kit Forensic, and state-specific equivalents built for their domestic threat environment
- The ability to compel you to decrypt under threat of criminal prosecution, indefinite detention, or physical coercion
- Time — they can take the drive offline and run passphrase attacks indefinitely without any cost pressure
- Potentially: the ability to access a running, networked machine remotely if your device was compromised before seizure
What they do not have (against a properly configured system):
- Any mathematical attack against AES-256. None exists. Even theoretical quantum attacks (Grover’s algorithm) only reduce AES-256 security to 128-bit equivalence — still far beyond any foreseeable computational attack
- The ability to brute-force a six-word diceware passphrase at any realistic timescale when protected by Argon2id with a 4 GiB memory parameter
- A method to decrypt the drive without the passphrase, keyfile, or hardware token
- A way to prove the existence of a VeraCrypt hidden volume in the unallocated space of an outer volume
- A way to detect that a partition is encrypted at all if you use a detached LUKS header
What they will do in practice — in order:
1. Physically seize the device, typically powered off
1. Image the drive forensically before doing anything else, to preserve the evidence state
1. Run automated analysis to identify encryption type and parameters
1. Begin a passphrase recovery attack calibrated to your KDF parameters
1. Apply legal or physical coercion to obtain the passphrase
1. If decryption fails: attempt to use possession of the encrypted drive itself as evidence
Your defense must address each of these steps. This guide is organized around that sequence.
*** The Attack Vectors Ranked by Practicality
Attack || Practical? || Primary Defense ||
Drive seizure + offline brute force | Very common — the default outcome | Strong passphrase + Argon2id |
Legal compulsion to decrypt | Common in authoritarian jurisdictions | Plausible deniability + jurisdiction planning |
Evil maid (tamper while unattended) | Possible if device left unattended | TPM measured boot, Secure Boot, anti-tamper indicators |
Physical coercion (detention, threats) | Real risk for high-profile targets | Duress passphrase, deniability, legal preparation |
Remote access to a running machine | Only if networked while compromised | Tor, network hardening, air-gap for sensitive sessions |
Cold boot (RAM attack while running) | Requires access to a running machine | Shut down immediately when threatened; never sleep |
*** Five Foundational Principles
Principle 1: Encryption at rest is your primary defense. Everything else is defense in depth. A properly encrypted, powered-off drive resists all known cryptanalytic attacks by any actor — state, corporate, or academic.
Principle 2: Deniability matters when compulsion is a risk. Mathematical security alone is insufficient in authoritarian contexts where you can be legally or physically forced to provide the passphrase. Deniability changes the game: you provide the decoy passphrase and they see only what you intended them to see.
Principle 3: Data you don’t have cannot be seized. Minimize what you store. Information that does not need to live on the drive should not exist on it. Use tmpfs (RAM-backed storage) for working data that does not need to survive a reboot.
Principle 4: Your passphrase is the weakest link after encryption itself. A passphrase you can remember is vulnerable to coercion. A short passphrase is vulnerable to brute force. These two constraints pull in opposite directions — diceware with Argon2id resolves this tension.
Principle 5: Physical security enables digital security. No cryptographic scheme protects you if an adversary has undetected access to your running, unlocked machine. Boot security, anti-tamper indicators, and shutdown discipline are not optional extras — they are prerequisites.
** Chapter 2. How State Drive Forensics Works
Understanding your adversary’s tools and methods allows you to design effective countermeasures. This chapter explains in detail what happens when a forensics team receives your seized drive — and why each step of this guide directly counters a specific phase of that process.
*** Phase 1: Documentation and Acquisition
Before touching a seized device, a trained forensics team follows strict procedures to preserve the legal admissibility of evidence:
1. Photography: The device, its placement, all connections, and any visible screen state are photographed. This documents the state at seizure and can later help reconstruct whether the device was running, sleeping, or powered off.
1. State assessment: If the machine is running or sleeping, this is a critical opportunity. The LUKS decryption key exists in RAM while the device is unlocked. A live memory acquisition using tools like Magnet RAM Capture, LiME (Linux Memory Extractor), or Rekall can extract the full RAM contents — potentially including the mounted encryption key. This is why you must never sleep the machine in a threat situation. Always shut down.
1. Hardware write blocker: A physical write blocker (e.g., Tableau T35u, WiebeTech Forensic UltraDock) connects between the drive and the forensic workstation. It passes read commands but blocks any writes, ensuring the original drive is never modified. This preserves the legal evidence chain.
1. Forensic imaging: A bit-for-bit image of the entire drive is created using tools like Guymager, FTK Imager, or dcfldd. Every sector, including unallocated space, is copied. For a 500 GB SSD over USB 3.0, this takes 30–90 minutes.
1. Hash verification: SHA-256 hashes of the original and the image are compared. They must be identical. Any discrepancy invalidates the evidence chain. All subsequent analysis is performed on the copy; the original is sealed as court exhibit.
This means everything on the drive is captured forever at the moment of seizure. There is no “undo.” Emergency destruction (Chapter 20) must happen before the drive is imaged, not after.
*** Phase 2: Automated Analysis
After imaging, tools scan the image for known patterns:
Encryption detection: Commercial tools maintain databases of file signatures and encryption headers. LUKS2 is identified by the 6-byte magic sequence 4C 55 4B 53 BA BE at the start of the partition. When detected, the LUKS header is automatically parsed, revealing:
- The exact cipher and mode (AES-XTS-256)
- The PBKDF and its memory/iteration parameters (Argon2id, 4 GiB)
- How many key slots contain active keys
- The anti-forensic split (the encrypted master key — useless without the passphrase)
This information allows the forensics team to calibrate their brute-force attack precisely to your configuration.
File system analysis on unencrypted partitions: The EFI System Partition (your /boot) is unencrypted. Tools will thoroughly examine it, looking for:
- Hostname embedded in bootloader configuration
- Kernel command-line parameters (can reveal the encrypted device UUID)
- Any scripts, config files, or logs that were accidentally placed in /boot
- Timestamps of bootloader installation and configuration
File carving: On any unencrypted or successfully decrypted partition, carvers like Scalpel, PhotoRec, and Foremost scan raw disk space for file magic bytes. They can recover files from unallocated space — deleted files, filesystem fragments, and partial data — without needing a filesystem structure at all.
Timeline reconstruction: From inode metadata alone (creation time, modification time, access time), analysts reconstruct a detailed timeline of what was done on the system, when, and in what order. This can be used to establish patterns of activity, prove ownership, or contradict statements you may make.
*** Phase 3: Passphrase Recovery Attack
If the drive is identified as LUKS-encrypted, the forensics team extracts the header and runs an offline passphrase attack against a copy. The crucial detail is that they never need the original drive for this — the header backup is sufficient.
# What the forensics team does: cryptsetup luksHeaderBackup /dev/sdX2 --header-backup-file header.img # header.img is taken to a GPU cracking cluster # Attack with hashcat (mode 32100 = LUKS2-Argon2id): hashcat -m 32100 -a 0 header.img /usr/share/wordlists/rockyou.txt hashcat -m 32100 -a 0 header.img --rules-file /usr/share/hashcat/rules/best64.rule wordlist.txt # Targeted attack using personal information: python3 mentalist.py --name "firstname" --birth "1990" --pet "fluffy" > targeted.txt hashcat -m 32100 -a 0 header.img targeted.txt
grep -m1 aes /proc/cpuinfo # The word 'aes' must appear in the flags line # If absent, AES-XTS still works but is slower (still secure)
lsblk --discard /dev/sdX # DISC-GRAN and DISC-MAX must both be non-zero # A zero value means TRIM is not passing through the enclosure
# Verify firmware version is in the expected public release range smartctl -a /dev/sdX | grep -E "Firmware|Model|Serial" # Cross-reference firmware version against manufacturer changelog # An unexpected firmware version is a red flag
# Step 1: Download ISO and signature from archlinux.org wget https://archlinux.org/iso/latest/archlinux-x86_64.iso wget https://archlinux.org/iso/latest/archlinux-x86_64.iso.sig # Step 2: Import the release signing key from the keyserver gpg --keyserver hkps://keys.openpgp.org --recv-keys 3056513887B78AEB # Step 3: MANUALLY verify the fingerprint against https://archlinux.org/people/developers/ gpg --fingerprint 3056513887B78AEB # Expected fingerprint: 4AA4 767B BC9C 4B1D 18AE 28B7 7F2D 434B 9741 E8AC # DO NOT PROCEED if the fingerprint does not match exactly # Step 4: Verify the signature on the ISO gpg --verify archlinux-x86_64.iso.sig archlinux-x86_64.iso # Must show: "Good signature from Pierre Schmitz
# Identify the correct device — be absolutely certain lsblk # Confirm /dev/sdX is the installer USB, NOT your target SATA drive # Write the ISO to the USB dd if=archlinux-x86_64.iso of=/dev/sdX bs=4M status=progress oflag=sync conv=fsync # Verify the write produced an identical copy dd if=/dev/sdX bs=4M count=$(( $(stat -c %s archlinux-x86_64.iso) / (4*1024*1024) + 1 )) \ 2>/dev/null | sha256sum sha256sum archlinux-x86_64.iso # Both hashes must be identical character-for-character
# Open a temporary plain dm-crypt device with a random key # The key is generated from /dev/urandom and never stored cryptsetup open --type plain \ --cipher aes-xts-plain64 \ --key-size 512 \ --key-file /dev/urandom \ /dev/sdX sanitize_container # Write zeros through the encryption layer # Zeros encrypted by AES-XTS with a random key = cryptographically random output on disk dd if=/dev/zero of=/dev/mapper/sanitize_container \ bs=4M status=progress # Close the temporary container — the random key is discarded and forgotten cryptsetup close sanitize_container
# Sample the first 100 MiB and measure entropy dd if=/dev/sdX bs=1M count=100 2>/dev/null | ent # The 'ent' program reports bits of entropy per byte # A properly overwritten drive: ~7.999 bits/byte # Below 7.95 suggests the overwrite did not complete correctly # Install ent if needed: pacman -S ent # on Arch apt install ent # on Debian/Ubuntu
# Check if the drive security is frozen (common default state) hdparm -I /dev/sdX | grep frozen # If "frozen": briefly suspend the machine, then resume # The resume cycle unfreezes the drive on most hardware # Set a temporary security password (required before Secure Erase) hdparm --security-set-pass TemporarySecurityPassword /dev/sdX # Issue the ATA Secure Erase command hdparm --security-erase TemporarySecurityPassword /dev/sdX # Duration: 15–45 seconds for most SSDs; potentially hours for HDDs
lsblk # Find your SATA-to-USB drive — e.g., /dev/sdb # Triple-check: this must NOT be your installer USB or any other drive # Mistake here = data loss on the wrong device
gdisk /dev/sdX # Inside gdisk: o → New GPT partition table (answer y to confirm) n → New partition, accept defaults, size: +1G, type code: ef00 (EFI System) n → New partition, accept defaults, accept full remaining size, type code: 8309 (LUKS) w → Write and exit (answer y to confirm)
/dev/sdX1 | 1 GiB | EF00 — EFI System | Unencrypted; holds bootloader only. Keep it minimal. |
/dev/sdX2 | Remainder | 8309 — Linux LUKS | Encrypted container holding the entire OS and data. |
*** Choosing a Passphrase
This is the single most important decision in this entire guide. The passphrase is what stands between your data and everyone who seizes the drive. It must be simultaneously memorizable under stress and immune to both targeted and systematic attack. There is exactly one method that satisfies both constraints reliably: diceware.
What is diceware? Diceware generates passphrases by rolling physical dice and looking up the results in a standardized wordlist. Because physical dice are used, no software component participates in the generation — there is no random number generator to compromise, no keyboard logger to capture intermediate state, and no possible bias.
# Download the EFF Large Wordlist (7776 words; one word per 5 dice rolls) wget https://www.eff.org/files/2016/07/18/eff_large_wordlist.txt # Process: roll 5 physical dice, note the numbers (e.g., 3-1-4-5-6) # Look up "31456" in the wordlist → that is one word of your passphrase # Repeat 6 times to get a 6-word passphrase # Example result: "correct horse battery staple finch rubber" # (Do not use this example — generate your own with physical dice)
p@ssw0rd-style) — hashcat rule sets cover all common substitutions
- Modify the diceware output — even adding punctuation you chose defeats the randomness guarantee
- Generate it with software instead of physical dice
*** Creating the LUKS2 Volume
cryptsetup luksFormat \ --type luks2 \ --cipher aes-xts-plain64 \ --key-size 512 \ --hash sha512 \ --pbkdf argon2id \ --pbkdf-memory 4194304 \ --pbkdf-parallel 4 \ --pbkdf-force-iterations 6 \ --iter-time 10000 \ --integrity hmac-sha256 \ --sector-size 4096 \ /dev/sdX2 # You must type YES (uppercase) to confirm # Then enter your diceware passphrase twice
--cipher aes-xts-plain64
AES (Advanced Encryption Standard) in XTS (XEX-based tweaked-codebook mode with ciphertext stealing) mode. AES-256 is the global standard for disk encryption, mandated by US NIST and adopted worldwide. XTS was specifically designed for disk encryption (IEEE P1619 standard) to address the problem that the same plaintext at the same position must not produce the same ciphertext — a property called “sector independence.” XTS achieves this by incorporating the physical sector address into each encryption operation. No practical attack against AES-256 exists. The best published academic attacks against AES reduce security by only a few bits and require conditions (related keys, chosen plaintexts) that do not apply to disk encryption.
--key-size 512
In XTS mode, the total key is split into two halves: 256 bits for the data encryption key and 256 bits for the XTS tweak key. Specifying 512 bits therefore gives you full 256-bit AES — the maximum and most secure configuration.
--pbkdf argon2id
Argon2id won the Password Hashing Competition in 2015 after peer review by hundreds of cryptographers. It is a memory-hard function — correctly computing it requires holding a configurable amount of RAM (not just time). This is decisive against GPU-based and ASIC-based attacks. A GPU cluster that can test billions of SHA-256 hashes per second is constrained by available RAM when testing Argon2id — each attempt requires holding the full memory parameter simultaneously. Argon2id specifically combines two Argon2 variants to resist both side-channel attacks (from the “Argon2i” component) and time-memory tradeoff attacks (from the “Argon2d” component).
--pbkdf-memory 4194304
4,194,304 KiB = 4 GiB of RAM required per password attempt. This is the primary cost that makes GPU cracking impractical. A GPU with 24 GB VRAM can test only 6 passwords in parallel. Compare this to SHA-256, where the same GPU can test billions in parallel. The 4 GiB parameter also means this unlock will be slow on your own machine (10+ seconds) — this is intentional and appropriate.
--pbkdf-force-iterations 6 and --iter-time 10000
Six passes over the full 4 GiB memory region, plus 10 seconds of time-based computation regardless of hardware speed. Together, a single unlock attempt takes at minimum 10–15 seconds on modern hardware. This further compounds the Argon2id protection.
--integrity hmac-sha256
Enables dm-integrity: every 4096-byte sector of encrypted data receives an HMAC-SHA256 authentication tag. If any byte of ciphertext is modified on disk — whether by accident, by a forensic tool probing the cipher, or by a sophisticated active attack — the integrity check fails and the read returns an I/O error rather than silently returning potentially useful modified plaintext. This closes a class of “chosen-ciphertext attacks” where an adversary who can modify the encrypted data and observe the system’s behavior can learn information about the plaintext.
--sector-size 4096
Modern SSDs use 4096-byte physical sectors internally. Aligning the LUKS sector size to 4096 bytes ensures each encrypted sector maps directly to one physical sector, maximizing performance and eliminating read-modify-write cycles.
*** Verifying the LUKS Configuration
cryptsetup luksDump /dev/sdX2
Version: 2 Cipher: aes-xts-plain64 Cipher key: 512 bits PBKDF: argon2id Memory: 4194304 Threads: 4 Time cost: 6
luksFormat with the correct parameters before proceeding. Do not continue with an incorrectly configured volume.
*** Opening the LUKS Container
cryptsetup open /dev/sdX2 cryptroot # Prompts for your passphrase # On success: the decrypted device is available at /dev/mapper/cryptroot
# EFI partition — must be FAT32 mkfs.fat -F32 -n EFI /dev/sdX1 # Root filesystem inside the encrypted container mkfs.ext4 -L cryptroot /dev/mapper/cryptroot # Alternative: btrfs if you want subvolumes and snapshots # mkfs.btrfs -L cryptroot /dev/mapper/cryptroot
# Mount encrypted root mount /dev/mapper/cryptroot /mnt # Create and mount EFI partition mkdir /mnt/boot mount /dev/sdX1 /mnt/boot # Network: prefer ethernet (no authentication required) # For WiFi, use iwctl: iwctl # station wlan0 scan # station wlan0 get-networks # station wlan0 connect "NetworkName" # quit # Install base system — linux-hardened is the security-patched kernel pacstrap -K /mnt \ base \ linux \ linux-hardened \ linux-firmware \ base-devel \ cryptsetup \ grub \ efibootmgr \ networkmanager \ vim \ sudo \ git \ curl \ wget \ nftables \ tor \ torsocks
linux-hardened package differs from the standard linux kernel in security-relevant ways:
- SLAB_FREELIST_RANDOM: Randomizes the order of free objects in the kernel memory allocator. Makes heap spray attacks (a common exploit technique) far less reliable — the attacker cannot predict where allocated objects will land.
- SLAB_FREELIST_HARDENED: Adds integrity metadata to the allocator’s freelist. Detecting freelist corruption allows the kernel to kill a process that is attempting a heap overflow instead of continuing with corrupted state.
- HARDENED_USERCOPY: Bounds-checks all copies between kernel memory and userspace. A kernel bug that copies too many bytes to userspace is caught and kills the process rather than leaking kernel memory contents.
- PAGE_TABLE_ISOLATION (KPTI): Isolates the kernel’s page tables from userspace processes. Mitigates the Meltdown speculative execution vulnerability, which allowed userspace processes to read arbitrary kernel memory on affected Intel CPUs.
- Restricted /proc access: Non-root users cannot read other users’ process information from /proc. This prevents information gathering by unprivileged processes.
Performance cost of linux-hardened: typically under 5% for common workloads. This is an entirely acceptable trade-off for the security gains.
*** System Configuration
genfstab -U /mnt >> /mnt/etc/fstab arch-chroot /mnt # Timezone ln -sf /usr/share/zoneinfo/Region/City /etc/localtime hwclock --systohc # Locale echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen locale-gen echo "LANG=en_US.UTF-8" > /etc/locale.conf # Hostname — use something completely generic; avoid anything identifying echo "localhost" > /etc/hostname cat > /etc/hosts << 'EOF' 127.0.0.1 localhost ::1 localhost 127.0.1.1 localhost.localdomain localhost EOF # Set root password (diceware, different from LUKS passphrase) passwd # Create non-root user useradd -m -G wheel -s /bin/bash username passwd username EDITOR=vim visudo # Uncomment: %wheel ALL=(ALL:ALL) ALL
encrypt hook — or the system will not know how to decrypt the drive at boot.
vim /etc/mkinitcpio.conf # Find the HOOKS line and set it to exactly: HOOKS=(base udev autodetect keyboard keymap consolefont modconf block encrypt filesystems fsck) # The ordering of hooks is significant: # - 'keyboard' must appear before 'encrypt' (need keyboard input for passphrase) # - 'block' must appear before 'encrypt' (need block device layer initialized first) # - 'encrypt' must appear before 'filesystems' (decrypt before mounting) mkinitcpio -P # Rebuilds all preset initramfs images
grub-install \ --target=x86_64-efi \ --efi-directory=/boot \ --bootloader-id=GRUB \ --recheck
# Get the UUID of your encrypted partition blkid /dev/sdX2 # Copy the UUID value (format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) vim /etc/default/grub
GRUB_CMDLINE_LINUX to the following (replace with your actual UUID):
GRUB_CMDLINE_LINUX="cryptdevice=UUID=
init_on_alloc=1 | Zeroes memory before allocating it. Prevents “heap spray” attacks that read old memory contents from newly allocated buffers. |
init_on_free=1 | Zeroes memory after freeing it. Prevents use-after-free vulnerabilities from leaking data. |
vsyscall=none | Disables the legacy vsyscall interface at a fixed kernel address. This address is a common known-location exploit trampoline — attackers jump here during code reuse attacks. |
debugfs=off | Disables the debugfs virtual filesystem. Without this, debugfs exposes internal kernel data structures that an attacker with any code execution can use to escalate privileges or read kernel memory. |
lockdown=confidentiality | Activates kernel lockdown mode at the most restrictive level. Prevents userspace from reading or writing kernel memory via /dev/mem, kexec, hibernation, or other privileged interfaces. Even root cannot extract kernel secrets. |
module.sig_enforce=1 | Rejects any kernel module not signed with the kernel’s build-time signing key. Prevents an attacker with root access from loading a malicious kernel module. |
iommu=force | Forces strict IOMMU enforcement. Without IOMMU, any PCIe device (or a device pretending to be one via Thunderbolt DMA) can read and write all physical RAM, including the LUKS decryption key. |
nohibernate | Disables hibernation. A hibernate image on disk contains an exact snapshot of RAM — including the LUKS decryption key — written to disk unencrypted. This would completely defeat drive encryption. |
mitigations=auto | Enables all hardware vulnerability mitigations (Spectre, Meltdown, MDS, etc.) appropriate for the detected CPU. These prevent speculative execution attacks that could leak the LUKS key from kernel memory. |
grub-mkconfig -o /boot/grub/grub.cfg
e to edit the boot entry, add init=/bin/bash to the kernel command line, and boot directly to a root shell. The LUKS encryption would still protect the data on disk, but the attacker could modify the bootloader for a future evil maid attack (Chapter 18) without leaving obvious traces.
# Generate a PBKDF2-hashed GRUB password grub-mkpasswd-pbkdf2 # Enter and confirm your GRUB menu password # Copy the output hash (starts with: grub.pbkdf2.sha512.10000...) cat >>/etc/grub.d/40_custom << 'EOF' set superusers="admin" password_pbkdf2 admin grub.pbkdf2.sha512.10000.YOUR_HASH_HERE EOF grub-mkconfig -o /boot/grub/grub.cfg
grub.cfg is an unsigned, unencrypted text file on the EFI partition. An attacker with a few minutes of physical access can edit it — changing kernel parameters, adding init=/bin/bash, or pointing GRUB at a different kernel — without triggering Secure Boot, because GRUB itself is signed but the config file it reads is not.
A Unified Kernel Image (UKI) solves this by embedding the kernel command line, the initramfs, and the kernel itself into a single EFI binary. The entire binary is Secure Boot signed. Any modification — including changing a single character of the kernel command line — invalidates the signature and causes the UEFI firmware to refuse to boot it.
pacman -S ukify # Write the kernel command line to a file for embedding cat >/etc/kernel/cmdline << 'EOF' cryptdevice=UUID=
pacman -S sbctl # Check the current Secure Boot enrollment state sbctl status # In UEFI firmware: Security → Secure Boot → Clear Keys / Enter Setup Mode # (exact menu path varies by manufacturer) # Generate your custom Platform Key (PK), Key Exchange Key (KEK), and Database key (db) sbctl create-keys # Enroll your keys; --microsoft preserves Microsoft's keys for hardware compatibility # (some drivers and option ROMs require Microsoft's key) sbctl enroll-keys --microsoft # Sign all boot-relevant EFI binaries and record their paths sbctl sign -s /boot/EFI/GRUB/grubx64.efi sbctl sign -s /boot/grub/x86_64-efi/grub.efi sbctl sign -s /boot/vmlinuz-linux-hardened sbctl sign -s /boot/EFI/Linux/arch-hardened.efi # Verify all are signed sbctl verify # In UEFI firmware: re-enable Secure Boot # Automate re-signing when the kernel or bootloader is updated: cat > /etc/pacman.d/hooks/zz-sbctl-sign.hook << 'EOF' [Trigger] Operation = Install Operation = Upgrade Type = Path Target = usr/lib/modules/*/vmlinuz Target = boot/*.efi [Action] Description = Re-signing EFI binaries after update When = PostTransaction Exec = /usr/bin/sbctl sign-all -g EOF
exit # exit the chroot environment umount -R /mnt # unmount all filesystems cleanly cryptsetup close cryptroot # close the LUKS container reboot # remove the installer USB before the machine POST-boots
cat >/etc/sysctl.d/99-security.conf << 'EOF' # Hide kernel symbol addresses and pointers from userspace # Without this, /proc/kallsyms leaks kernel layout — an ASLR bypass kernel.kptr_restrict = 2 kernel.dmesg_restrict = 1 kernel.printk = 3 3 3 3 # Restrict ptrace: only a parent process may trace its own child processes # Without this, any process can debug any other same-user process kernel.yama.ptrace_scope = 2 # Disable magic SysRq keys # Certain SysRq combinations can kill processes or unmount filesystems kernel.sysrq = 0 # Disable kexec: prevents loading a replacement kernel at runtime # An attacker with root can use kexec to boot a kernel that bypasses lockdown kernel.kexec_load_disabled = 1 # Restrict BPF JIT compiler (common ROP chain source in kernel exploits) kernel.unprivileged_bpf_disabled = 1 net.core.bpf_jit_harden = 2 # Maximum ASLR randomisation kernel.randomize_va_space = 2 # Disable core dumps (prevent LUKS key or sensitive data reaching disk in core dumps) fs.suid_dumpable = 0 kernel.core_pattern = |/bin/false # Symlink/hardlink protections (prevent TOCTOU attacks via /tmp) fs.protected_symlinks = 1 fs.protected_hardlinks = 1 fs.protected_fifos = 2 fs.protected_regular = 2 # Restrict performance event access (used in side-channel attacks, cache timing) kernel.perf_event_paranoid = 3 # Network — IP spoofing protection via reverse path filtering net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.default.rp_filter = 1 # Network — reject ICMP redirect messages (MITM route manipulation) net.ipv4.conf.all.accept_redirects = 0 net.ipv6.conf.all.accept_redirects = 0 net.ipv4.conf.all.send_redirects = 0 # Network — TCP SYN flood protection net.ipv4.tcp_syncookies = 1 # Network — disable IP source routing (not used in normal networking) net.ipv4.conf.all.accept_source_route = 0 net.ipv6.conf.all.accept_source_route = 0 # Network — RFC 1337 TIME_WAIT assassination protection net.ipv4.tcp_rfc1337 = 1 # Network — disable TCP timestamps (leaks system uptime, used in fingerprinting) net.ipv4.tcp_timestamps = 0 # Minimize swap to keep sensitive data in RAM rather than on disk vm.swappiness = 1 EOF sysctl --system
pacman -S nftables cat >/etc/nftables.conf << 'EOF' #!/usr/bin/nft -f flush ruleset table inet filter { chain input { type filter hook input priority 0; policy drop; # Accept established and related connections (responses to outbound) ct state established,related accept # Accept loopback traffic iifname "lo" accept # Drop invalid connection state packets ct state invalid drop # Everything else dropped — no inbound ports are open } chain forward { type filter hook forward priority 0; policy drop; } chain output { type filter hook output priority 0; policy accept; # For stricter environments, also filter outbound: # Only allow Tor's connection to the guard node + established } } EOF systemctl enable --now nftables
# Bluetooth — disable entirely if not used systemctl disable --now bluetooth systemctl mask bluetooth # Avahi (mDNS zero-conf) — not needed, exposes local network presence systemctl disable --now avahi-daemon systemctl mask avahi-daemon # CUPS (printing) — disable unless actively printing systemctl disable --now cups systemctl mask cups # Enable the services you do need systemctl enable --now NetworkManager systemctl enable --now tor
# Option 1: Encrypt swap with an ephemeral random key (different on every boot) # Add to /etc/crypttab: swap /dev/sdXY /dev/urandom swap,cipher=aes-xts-plain64,size=512,sector-size=4096 # Add to /etc/fstab: /dev/mapper/swap none swap defaults 0 0 # Option 2 (preferred for high-threat environments): disable swap entirely swapoff -a # Remove all swap entries from /etc/fstab # If more RAM is needed, add physical RAM rather than using swap
# Shell history files are forensic goldmines — they record every command ever typed # Disable them system-wide cat >>/etc/bash.bashrc << 'EOF' export HISTSIZE=0 export HISTFILESIZE=0 export HISTFILE=/dev/null unset HISTFILE EOF # For zsh: cat >> /etc/zsh/zshrc << 'EOF' HISTSIZE=0 SAVEHIST=0 unset HISTFILE EOF
pacman -S swayidle swaylock # for Wayland/Sway desktop # Alternative for X11: xautolock + i3lock # Add to ~/.config/sway/config: exec swayidle -w \ timeout 60 'swaylock -f -c 000000' \ timeout 120 'swaymsg "output * dpms off"' \ resume 'swaymsg "output * dpms on"' \ before-sleep 'swaylock -f -c 000000'
4C 55 4B 53 BA BE at the start of the partition. Once detected, the full LUKS2 header is parsed automatically:
# What the forensics tooling extracts from your LUKS header automatically: cryptsetup luksDump /dev/sdX2 # They see exactly: # - Cipher: aes-xts-plain64 # - PBKDF: argon2id # - Memory: 4194304 (4 GiB) # - Active key slots: however many you have # - Anti-forensic split: the encrypted master key (useless without passphrase) # This lets them calculate exact attack parameters before starting
# They extract the header for offline cracking: cryptsetup luksHeaderBackup /dev/sdX2 --header-backup-file header.img # Attack with hashcat on a GPU cluster: hashcat -m 32100 -a 0 header.img rockyou.txt hashcat -m 32100 -a 3 header.img "?l?l?l?l?l?l?l?l" # brute force 8 lowercase hashcat -m 32100 -a 0 header.img wordlist.txt -r best64.rule # With Argon2id 4 GiB: each attempt requires loading 4 GiB into GPU VRAM # A $10,000 GPU cluster (8× RTX 4090, 24 GB VRAM each): ~4 parallel attempts # Rate: approximately 4 attempts per 15 seconds = ~1,000 per hour # Against 6-word diceware (10^23 possible passphrases): ~10^19 years
dd, shred, and wipe cannot reach it.
The consequence: Standard Linux tools designed for HDD data destruction (shred, wipe, srm, secure-delete) do not reliably delete data on SSDs. They overwrite the logical address, but the physical NAND cell that previously held that data may remain intact and readable.
*** What Specialized Forensic Tools Can Recover From SSDs
Three approaches exist for SSD forensics that bypass the standard SATA/USB interface:
- PC-3000 Flash (ACE Lab): The industry standard for NAND chip-off and in-system forensics. Communicates with the SSD controller using manufacturer-specific diagnostic commands, bypassing the normal FTL entirely. Can access raw NAND data including over-provisioned area.
- Chip-off: Physically remove the NAND chips from the PCB, connect them to a specialized reader, and read the raw NAND pages directly. No encryption = full data recovery. With encryption = only ciphertext.
- JTAG / ISP: Connect directly to debug ports on the SSD controller chip to extract firmware, configuration, or data through the chip’s debug interface.
The solution to all of these techniques is identical: encrypt the entire drive before writing any data to it. If every physical NAND cell — including over-provisioned spare area — contains only AES-256-XTS ciphertext from the moment the drive was first used, chip-off forensics recovers only random-looking noise. This is why Chapter 6 (disk sanitization) and Chapter 7 (LUKS creation) must be done in that order, before the OS is installed.
*** TRIM on an Encrypted Drive: Trade-offs
TRIM is a command the OS sends to the SSD saying “these blocks are no longer in use; you may erase them.” It helps maintain SSD performance over time by allowing the FTL to pre-erase blocks before they need to be rewritten.
Security trade-off: TRIM leaks information to a physical attacker. By analyzing which logical blocks the SSD reports as discarded, a forensic examiner who accesses the NAND directly can determine the approximate size and layout of your filesystem’s used space — even without decrypting anything. They cannot read the data, but they can infer patterns.
For most threat models, TRIM provides a net benefit (drive health + some anti-forensic clearing) and should be enabled. For maximum deniability with hidden VeraCrypt volumes (Chapter 15), disable TRIM — a hidden volume must make the outer volume’s unallocated space indistinguishable from random data, which TRIM can disrupt.
# Enable TRIM passthrough in /etc/crypttab (most threat models): cryptroot UUID=
# Option 1 (best): Never write to disk — use RAM-backed tmpfs for sensitive work mount -t tmpfs -o size=500M,nodev,nosuid,noexec tmpfs /mnt/sensitive # Work here — data exists only in RAM umount /mnt/sensitive # RAM cleared immediately on unmount # Option 2: Destroy the LUKS key (destroys ALL data, not just one file) # THIS IS IRREVERSIBLE cryptsetup erase /dev/sdX2 # Option 3: Fill free space with zeros (overwrites ciphertext of deleted files) # Effective against decrypted-drive carving; does not help against NAND chip-off dd if=/dev/zero of=/mnt/secure/fill_freespace.tmp bs=4M status=progress rm /mnt/secure/fill_freespace.tmp sync
/var/log/journal/ on disk. These journal files contain a timestamped record of every significant system event: services started, USB devices connected, network connections established, kernel messages, and application errors. This is an intelligence windfall for a forensic examiner who decrypts the drive.
cat >/etc/systemd/journald.conf << 'EOF' [Journal] Storage=volatile RuntimeMaxUse=50M MaxRetentionSec=3600 Compress=yes EOF systemctl restart systemd-journald # Verify: no journal files should exist on disk ls /var/log/journal/ # Should be empty or show only the current session's runtime journal (in /run/log/journal/)
Storage=volatile means all logs exist only in /run/log/journal/ (which is tmpfs — RAM-backed). They disappear completely when the machine shuts down. No historical log files ever touch the drive.
*** Application Cache and Working Data in RAM
# Add to /etc/fstab — mount these directories as RAM-backed tmpfs: tmpfs /home/user/.cache tmpfs defaults,nodev,nosuid,noexec,size=512M,mode=0700 0 0 tmpfs /tmp tmpfs defaults,nodev,nosuid,noexec,size=2G,mode=1777 0 0 tmpfs /var/tmp tmpfs defaults,nodev,nosuid,noexec,size=1G 0 0 # Applications that store sensitive data in these locations: # - Browsers: cache, cookies, thumbnails, session data # - Document editors: autosave, recovery files, recent document lists # - Email clients: attachment cache, header cache # - Package managers: downloaded packages (pacman stores in /var/cache/pacman) # All of these disappear on shutdown with tmpfs mounts
pacman -S mat2 perl-image-exiftool # Scrub ALL metadata from a file (mat2 is the safest approach) mat2 --inplace document.pdf mat2 --inplace photo.jpg mat2 --inplace presentation.odp # Verify nothing remains mat2 --show document.pdf # Should output: No metadata found in document.pdf # For more granular control (exiftool): exiftool -all= photo.jpg # remove every metadata tag exiftool -GPS:all= photo.jpg # remove only GPS, keep camera info exiftool -Author= -Creator= document.docx # remove only author fields # Batch process an entire directory: mat2 --inplace /path/to/directory/*.pdf
atime (access time). Over time, these timestamps build a detailed record of your reading patterns — which files you accessed, in what order, and when. A forensic analyst examining a decrypted drive can reconstruct a day-by-day activity log purely from atime entries.
# In /etc/fstab, add noatime and nodiratime to the root mount: /dev/mapper/cryptroot / ext4 defaults,noatime,nodiratime,data=writeback 0 1 # noatime: do not update file access times on read # nodiratime: do not update directory access times on read # data=writeback: only journal metadata, not file data (reduces journal forensic artifacts) # Verify the mount options are active: mount | grep cryptroot # Should show: noatime,nodiratime in the options
# With Storage=volatile for journald, USB connection logs disappear on reboot # For additional control during a session: dmesg --clear # clear the kernel ring buffer (requires root) # USBGuard: maintain a whitelist of allowed USB devices # Unexpected devices are logged and blocked pacman -S usbguard usbguard generate-policy >/etc/usbguard/rules.conf # Review and edit rules.conf — allow only your known devices systemctl enable --now usbguard # Any USB device not in the whitelist will be blocked and logged # If a forensic examiner connects a USB device to an unattended running machine, # USBGuard blocks it and alerts you
# KeePassXC — clear clipboard after password copy: # Settings → Security → Clear clipboard after: 15 seconds # Vim — disable persistent undo and swap files: cat >>~/.vimrc << 'EOF' set noswapfile set noundofile set nobackup set nowritebackup EOF # GNOME/KDE recently used files (if using a desktop environment): rm -f ~/.local/share/recently-used.xbel # Prevent it from being written: mkdir -p ~/.local/share/ touch ~/.local/share/recently-used.xbel chattr +i ~/.local/share/recently-used.xbel # Trash / Recycle Bin — always delete files permanently, never trash them: # In file managers: configure permanent delete as default # Or bypass the trash entirely: rm -f file_to_delete # rather than moving to trash
debugfs, Autopsy, Sleuth Kit) can sometimes recover recently-written data directly from the journal without needing to carve unallocated space.
Mitigation options:
# Option A: data=writeback mode (recommended balance of safety and anti-forensics) # Only filesystem metadata is journaled; file data is written directly # Deleted file content is less likely to appear in the journal # In /etc/fstab: /dev/mapper/cryptroot / ext4 defaults,noatime,nodiratime,data=writeback 0 1 # Option B: Disable the journal entirely (maximum anti-forensics; less crash safety) # Only do this while the filesystem is unmounted tune2fs -O ^has_journal /dev/mapper/cryptroot # Re-enable if needed: tune2fs -O has_journal /dev/mapper/cryptroot # For a portable drive that you shut down properly, disabling the journal # is a reasonable trade-off — crash recovery is less critical when you control shutdowns
# Set up a clean snapshot before first sensitive use mount /dev/mapper/cryptroot /mnt btrfs subvolume snapshot /mnt /mnt/snapshots/clean-baseline # After a sensitive session, revert to the baseline: # (requires careful handling — do this from the Arch installer environment) # Mount the LUKS device btrfs subvolume delete /mnt/@ # delete the current root subvolume btrfs subvolume snapshot /mnt/snapshots/clean-baseline /mnt/@ # Next boot: system is back to the clean baseline state with zero session artifacts
# Mount /proc with hidepid=2 — users cannot see other users' processes # In /etc/fstab: proc /proc proc defaults,nosuid,nodev,noexec,hidepid=2,gid=proc 0 0 # Create the proc group (your user must be in it to see your own processes) groupadd -r proc usermod -aG proc username # hidepid=2 means: /proc/PID directories for processes you don't own are hidden entirely # Relevant if multiple users exist on the system, or if a compromised process # tries to enumerate running processes to find something to attach to
# Always download from https://veracrypt.fr # Always verify the PGP signature before extracting gpg --keyserver hkps://keys.openpgp.org --recv-keys 993B7D7E8E413809828F0F29EB559C7338D44AD5 gpg --verify veracrypt-*-setup.tar.bz2.sig veracrypt-*-setup.tar.bz2 tar xjf veracrypt-*-setup.tar.bz2 ./veracrypt-*-setup-console-x64
# Create a file-based outer volume (20 GiB in this example — size to your needs) veracrypt --text --create /home/user/data/backup.vc \ --size 20G \ --encryption AES \ --hash SHA-512 \ --filesystem FAT \ --volume-type normal \ --password "OUTER_DECOY_PASSPHRASE" \ --pim 0 \ --keyfiles "" \ --random-source /dev/urandom
# Mount the outer volume veracrypt --text --mount /home/user/data/backup.vc /mnt/outer \ --password "OUTER_DECOY_PASSPHRASE" \ --pim 0 --keyfiles "" # Copy decoy content in — photos, documents, music # This step defines the outer volume's used space boundary # The hidden volume will live in the unallocated space cp -r ~/decoy-photos /mnt/outer/ cp -r ~/decoy-documents /mnt/outer/ # Unmount the outer volume veracrypt --text --dismount /mnt/outer
# The hidden volume size must fit within the outer volume's unallocated space # If the outer volume is 20 GiB and decoy content is 5 GiB, the hidden volume # can be up to ~14 GiB (leave some free space in outer for credibility) veracrypt --text --create /home/user/data/backup.vc \ --encryption AES \ --hash SHA-512 \ --filesystem ext4 \ --volume-type hidden \ --size 10G \ --password "INNER_REAL_PASSPHRASE" \ --pim 0 \ --keyfiles "" \ --random-source /dev/urandom
# Mount the real (hidden) volume for daily work: veracrypt --text --mount /home/user/data/backup.vc /mnt/real \ --password "INNER_REAL_PASSPHRASE" \ --pim 0 --keyfiles "" # When working in the outer (decoy) volume, protect the hidden volume from overwrites: veracrypt --text --mount /home/user/data/backup.vc /mnt/outer \ --password "OUTER_DECOY_PASSPHRASE" \ --pim 0 --keyfiles "" \ --protect-hidden yes \ --protection-password "INNER_REAL_PASSPHRASE" # This mounts the outer volume but tells VeraCrypt where the hidden volume is, # so any write to the outer volume stops before overwriting the hidden volume
# Spread file timestamps realistically over 2-3 years of fake history: touch -d "2022-03-15 14:32" decoy-photos/vacation_001.jpg touch -d "2021-08-02 09:15" decoy-docs/tax_return_2021.pdf touch -d "2023-01-20 18:44" decoy-docs/lease_agreement.pdf # Content checklist for a convincing decoy: # [ ] 300–600 photos spanning 2-3 years, varying quality (phone + camera mix) # [ ] Music collection: 30-50 albums in common formats (MP3, FLAC, OGG) # [ ] Financial documents: plausible bank statements, tax forms (fictitious but realistic) # [ ] Personal documents: recipes, shopping lists, hobby notes # [ ] Browser bookmarks export: mix of news, shopping, hobby sites # [ ] Some "embarrassing but legal" content — provides motivation for the encryption # [ ] Application configs with realistic settings (.vimrc, .gitconfig with fake identity) # [ ] A fake shell history file showing mundane everyday commands
# Add a second passphrase to slot 2 cryptsetup luksAddKey --key-slot 2 /dev/sdX2 # The examiner sees two active key slots # They cannot determine which is "real" and which is "secondary" # Both open the same volume, but you control which passphrase you disclose # View active key slot count: cryptsetup luksDump /dev/sdX2 | grep -c "luks2"
# The header will live on a separate device — NOT on the encrypted drive # First create the header file (16 MiB is sufficient for LUKS2 + multiple key slots) truncate -s 16M /tmp/luks-header.img # Format the encrypted partition — the header goes into the separate file cryptsetup luksFormat \ --type luks2 \ --header /tmp/luks-header.img \ --cipher aes-xts-plain64 \ --key-size 512 \ --hash sha512 \ --pbkdf argon2id \ --pbkdf-memory 4194304 \ --pbkdf-parallel 4 \ --pbkdf-force-iterations 6 \ --iter-time 10000 \ /dev/sdX2 # /dev/sdX2 itself receives NOTHING — no header, no signature # Open the encrypted partition using the detached header cryptsetup open /dev/sdX2 cryptroot --header /tmp/luks-header.img
# GPG symmetric encryption with strong parameters gpg --symmetric \ --cipher-algo AES256 \ --s2k-digest-algo SHA512 \ --s2k-count 65011712 \ /tmp/luks-header.img # Produces: luks-header.img.gpg — safe to store anywhere # Decrypt when needed (before booting): gpg --decrypt luks-header.img.gpg > /tmp/luks-header.img
pacman -S ssss # Split passphrase into 5 shares, any 3 reconstruct it echo -n "header-encryption-passphrase" | ssss-split -t 3 -n 5 # Output: 5 hex strings # Distribute one to each of 5 trusted people in different countries # Reconstruct from any 3 shares: ssss-combine -t 3 # Enter 3 of the 5 shares when prompted — passphrase is reconstructed
pacman -S systemd libfido2 # List connected FIDO2 tokens systemd-cryptenroll --fido2-device=list # Bind a new LUKS key slot to the FIDO2 token + PIN systemd-cryptenroll \ --fido2-device=auto \ --fido2-with-client-pin=yes \ /dev/sdX2 # You will be prompted to set a PIN and touch the token # Configure /etc/crypttab for automatic FIDO2 unlocking at boot: # cryptroot UUID=
pacman -S yubikey-full-disk-encryption yubico-pam # Program the YubiKey's second slot for HMAC-SHA1 challenge-response ykpersonalize -2 -o chal-resp -o hmac-lt64 -o variable # Enroll the YubiKey as a LUKS key slot ykfde-enroll -d /dev/sdX2 -s 2 cat > /etc/ykfde.conf << 'EOF' YKFDE_LUKS_DEV="/dev/sdX2" YKFDE_LUKS_SLOT="2" YKFDE_CHALLENGE_SLOT="2" EOF # Add ykfde hook to initramfs (before 'encrypt' in HOOKS): vim /etc/mkinitcpio.conf # HOOKS=(... ykfde encrypt ...) mkinitcpio -P
Apply clear nail polish mixed with fine glitter across all case screws and at case seams. Photograph the glitter pattern from multiple angles before leaving the device unattended. The glitter distributes randomly and creates a pattern that is impossible to replicate exactly — even with samples of the same glitter and the same nail polish. Before each use, compare the current pattern against your saved reference photographs. Any discrepancy indicates the case was opened.
This technique is used by real security researchers, costs less than five dollars, and requires no technical skill. It is cheap, reliable, and widely understood to be effective. Joanna Rutkowska documented its use in the Qubes OS security model.
UV-reactive ink: Apply to screws and seams. Inspect with a UV flashlight. Invisible in normal light, obvious under UV.
Tamper-evident security seals: Single-use labels with “VOID” patterns embedded in the adhesive, available from industrial suppliers. Apply to all case screws.
Standard pre-use physical inspection:
- All screws present, properly seated, no scratching or stripping marks
- Case seams closed flush with no signs of prying
- All external ports checked for inserted devices
- Keyboard fully seated and not raised at any corner
- USB ports inspected for small keylogger devices
*** Defense 4: Hardware Keylogger Detection
# Establish a baseline of connected hardware when the machine is clean: lsusb >~/baseline_usb.txt lspci > ~/baseline_pci.txt # Before each use after the machine has been unattended: lsusb | diff ~/baseline_usb.txt - lspci | diff ~/baseline_pci.txt - # Any unexpected device is a red flag requiring investigation # For internal keyloggers on laptops: these require opening the case to install # Your glitter canary will detect if the case was opened
pacman -S tpm2-tools tpm2-tss clevis # Verify the TPM2 chip is present and responsive ls /dev/tpm* # Should show: /dev/tpm0 and /dev/tpmrm0 # Bind a LUKS key slot to TPM2 PCRs # PCRs 0,1,7: firmware, config, and Secure Boot (minimum) # PCRs 0,1,4,7,8,9: also includes bootloader and kernel (maximum detection) clevis luks bind -d /dev/sdX2 tpm2 '{"pcr_ids":"0,1,7,8,9"}' # You will be prompted for your LUKS passphrase to authorize adding the new slot # Enable the clevis systemd integration for boot-time TPM unlocking systemctl enable clevis-luks-askpass.path # Rebuild initramfs to include TPM2 support mkinitcpio -P
# After any update that modifies the kernel, initramfs, GRUB, or grub.cfg: clevis luks regen -d /dev/sdX2 -s 1 # -s 1: the key slot number clevis used (check with cryptsetup luksDump) # Automate this with a pacman hook: cat > /etc/pacman.d/hooks/zz-clevis-regen.hook << 'EOF' [Trigger] Operation = Install Operation = Upgrade Type = Path Target = usr/lib/modules/*/vmlinuz Target = boot/*.efi Target = boot/grub/grub.cfg [Action] Description = Re-sealing TPM2 PCR binding after boot component update When = PostTransaction Exec = /usr/bin/clevis luks regen -d /dev/sdX2 -s 1 EOF
# Option A: cryptsetup erase — most targeted, fastest # Overwrites all key slots with random data — takes 1-2 seconds cryptsetup erase /dev/sdX2 # Option B: dd random data over the header region # Overwrites the first 32 MiB (covers the LUKS header + margin) — takes 5-10 seconds dd if=/dev/urandom of=/dev/sdX bs=4K count=8192 conv=fsync # Option C: erase and immediately power off — most complete cryptsetup erase /dev/sdX2 && sync && poweroff -f
# Install from AUR git clone https://aur.archlinux.org/cryptsetup-nuke.git cd cryptsetup-nuke makepkg -si # Add the nuke passphrase to key slot 31 (the last slot) cryptsetup-nuke-add /dev/sdX2 # Enter the passphrase you want to use as the nuke trigger # Choose something very different from your real passphrase — # you must not confuse them under stress # Test the nuke (on a test drive, not your real drive): # Enter the nuke passphrase at a cryptsetup prompt # Expected behavior: pause of 1-5 seconds, then "No key available" # The key slots have been wiped — the drive is permanently locked
# List all active key slots cryptsetup luksDump /dev/sdX2 | grep -A3 "Keyslots" # Remove a specific key slot (you must know the passphrase for another active slot) cryptsetup luksKillSlot /dev/sdX2
# Reset the clock on every bash command: # Add to /etc/bash.bashrc or ~/.bashrc: PROMPT_COMMAND="touch ~/.deadman_alive; ${PROMPT_COMMAND}" # Or use a systemd timer that checks for active login sessions: cat > /etc/systemd/system/deadman-checkin.service << 'EOF' [Unit] Description=Dead Man's Switch auto check-in (active sessions only) [Service] Type=oneshot ExecStart=/bin/bash -c 'who | grep -q . && touch /home/user/.deadman_alive' EOF cat > /etc/systemd/system/deadman-checkin.timer << 'EOF' [Unit] Description=Dead Man's Switch auto check-in timer [Timer] OnBootSec=5min OnUnitActiveSec=30min [Install] WantedBy=timers.target EOF systemctl enable --now deadman-checkin.timer
pacman -S tor torsocks systemctl enable --now tor # Verify Tor is routing traffic correctly torify curl --silent https://check.torproject.org/api/ip # Should return a JSON with "IsTor": true
cat >/etc/tor/torrc << 'EOF' DataDirectory /var/lib/tor Log warn file /var/log/tor/notices.log SocksPort 9050 ControlPort 9051 CookieAuthentication 1 # Build new circuits frequently to limit exposure per circuit MaxCircuitDirtiness 300 NewCircuitPeriod 30 CircuitBuildTimeout 60 # Maintain stable guard nodes (too-frequent guard rotation is worse for anonymity) UseEntryGuards 1 NumEntryGuards 3 # Exclude nodes in countries your specific adversary controls: # Adjust these country codes for YOUR adversary ExcludeNodes {cn},{ru},{ir},{kp},{sy},{by},{tm},{uz},{vn} ExcludeExitNodes {cn},{ru},{ir},{kp},{sy},{by},{tm},{uz},{vn} StrictNodes 1 # Prevent Tor from using multiple relays on the same /16 subnet # (reduces effectiveness of a network-level adversary controlling a subnet) EnforceDistinctSubnets 1 EOF systemctl restart tor
# Install obfs4 transport client pacman -S obfs4proxy # Get bridge addresses from bridges.torproject.org # (access this from an uncensored connection or Tor Browser) # Or email bridges@torproject.org with subject line: "get transport obfs4" # Configure obfs4 in /etc/tor/torrc: cat >> /etc/tor/torrc << 'EOF' UseBridges 1 ClientTransportPlugin obfs4 exec /usr/bin/obfs4proxy # Bridge lines from bridges.torproject.org: Bridge obfs4
cat >/etc/NetworkManager/conf.d/mac-randomization.conf << 'EOF' [device] wifi.scan-rand-mac-address=yes [connection] wifi.cloned-mac-address=random ethernet.cloned-mac-address=random connection.stable-id=${CONNECTION}/${BOOT} EOF systemctl restart NetworkManager # Verify randomization is active nmcli connection show --active | head -5 ip link show wlan0 | grep ether # should change on each reboot
pacman -S dnscrypt-proxy cat >/etc/dnscrypt-proxy/dnscrypt-proxy.toml << 'EOF' listen_addresses = ['127.0.0.1:53'] server_names = ['cloudflare', 'cloudflare-ipv6', 'quad9-dnscrypt-ip4-filter-pri'] require_dnssec = true require_nolog = true # only use resolvers with no-logging policies require_nofilter = false # Anonymized DNS: relay queries through a separate relay server # The resolver sees the query but not your IP; the relay sees your IP but not the query anonymized_dns { routes = [ { server_name='cloudflare', via=['anon-cs-nl', 'anon-cs-de'] }, { server_name='quad9-dnscrypt-ip4-filter-pri', via=['anon-cs-ch'] }, ] } EOF systemctl enable --now dnscrypt-proxy # Point the system resolver at dnscrypt-proxy: echo "nameserver 127.0.0.1" >/etc/resolv.conf chattr +i /etc/resolv.conf # prevent NetworkManager from overwriting this
nohibernate), the LUKS decryption key remains in RAM. Cold boot attacks require a powered-on or recently powered-off machine with RAM contents that have not yet decayed. When you power off completely, RAM loses all contents within seconds to minutes (faster if cooled; slower in freezing temperatures). Always shut down:
# Configure automatic shutdown on lid close: # In /etc/systemd/logind.conf: HandleLidSwitch=poweroff HandleLidSwitchExternalPower=poweroff HandleLidSwitchDocked=poweroff systemctl restart systemd-logind
pacman -S keepassxc # KeePassXC configuration for high-threat environments: # Settings → Security: # Master key: diceware passphrase (separate from LUKS, different words) # KDF: Argon2id, memory 1024 MiB, iterations 10 # Key file: 128-byte random file stored on your hardware token or separate USB # Auto-lock after: 1 minute # Clear clipboard: 15 seconds after copy # Every service gets a unique 32+ character random password from KeePassXC # Database stored on the encrypted drive only — never synced to cloud services # Key file stored separately from the database (on hardware token)
# Install via Flatpak for automatic updates: flatpak install flathub org.signal.Signal flatpak update org.signal.Signal
linux-hardened kernel installed
- encrypt hook present in /etc/mkinitcpio.conf, in correct position
- initramfs rebuilt after any hook or kernel change
*** Boot Security
- UEFI administrator password set
- Boot order locked: only your SATA-to-USB drive, all other paths disabled
- Secure Boot enabled with your custom keys (sbctl)
- GRUB password set with PBKDF2 hash
- TPM2 measured boot configured (clevis) if TPM2 chip is present
- pacman hook re-signs EFI binaries and re-seals TPM after updates
*** System Hardening
- sysctl security parameters applied via /etc/sysctl.d/99-security.conf
- nftables active with default-deny inbound policy
- Unnecessary services disabled and masked
- Shell history disabled system-wide
- Swap disabled or encrypted with ephemeral random key
- Screen lock: 60-second timeout, activates before sleep
- Lid close triggers shutdown (not sleep)
*** Counter-Forensics
- journald: Storage=volatile (logs in RAM only, gone on shutdown)
- ~/.cache, /tmp, /var/tmp on tmpfs
- noatime,nodiratime on all persistent mounts
- mat2 and exiftool installed; metadata scrubbed from all files before sharing
- ext4 journal configured to data=writeback or disabled
*** Drive Seizure Resistance
- Deniability strategy implemented (VeraCrypt hidden volume and/or detached header)
- Decoy volume populated with convincing content spanning realistic timeframe
- Hardware security token enrolled as second factor (if threat model warrants)
- Glitter nail polish canary applied; reference photo taken and stored
- Pre-use physical inspection procedure understood and practiced
- USB baseline documented (lsusb, lspci outputs saved)
*** Emergency Procedures
- Emergency destruction script at /usr/local/bin/emergency-destroy.sh
- Emergency destruction keyboard shortcut configured and tested on a spare drive
- LUKS nuke passphrase configured and memorized (not written down)
- Dead man’s switch daemon enabled and check-in timer working
- Physical destruction procedure understood; practice run completed
- Support network briefed; check-in schedule established; distress signal agreed
*** Operational Security
- Tor daemon running; pluggable transport (obfs4 or Snowflake) configured
- Tor Browser installed and verified; security level set to Safest
- MAC address randomization active for WiFi and Ethernet
- dnscrypt-proxy running; /etc/resolv.conf locked
- Signal installed; disappearing messages enabled on all conversations; safety numbers verified
- KeePassXC database on encrypted drive; key file on hardware token
- Lawyer contact known and documented on paper, separate from devices
** Appendix B: Emergency Contacts and Resources
*** Digital Security Emergency Helplines
Organization || What They Offer || Contact ||
Access Now Digital Security Helpline | Free 24/7 emergency digital security support for journalists, activists, NGOs. Multiple languages. | accessnow.org/help — help@accessnow.org |
Front Line Defenders | Security and protection for human rights defenders; emergency response. | frontlinedefenders.org/emergency |
Committee to Protect Journalists | Digital security specifically for journalists. | cpj.org/safety |
EFF Surveillance Self-Defense | Guides, threat modeling, tool recommendations by threat level. | ssd.eff.org |
Reporters Without Borders | Support for journalists facing digital threats. | rsf.org |
*** Privacy-Respecting Tools Referenced in This Guide
Tool || Purpose || Source ||
Tor Browser | Anonymous web browsing with anti-fingerprinting | torproject.org |
Tails OS | Amnesiac live operating system — no trace on host machine | tails.boum.org |
Signal | End-to-end encrypted messaging and calls | signal.org |
Briar | P2P encrypted messaging — no server, works offline via Bluetooth | briarproject.org (F-Droid) |
SimpleX | Encrypted messaging with no user identifier | simplex.chat |
KeePassXC | Local password manager with Argon2id KDF | keepassxc.org |
VeraCrypt | Full-disk and container encryption with hidden volumes | veracrypt.fr |
Protonmail | Encrypted email with servers in Switzerland | proton.me |
Riseup | Email and VPN operated by and for activists | riseup.net (invite required) |
mat2 | Metadata stripping for documents and images | 0xacab.org/jvoisin/mat2 |
*** Key Academic References
“Self-Encrypting Deception: Weaknesses in the Encryption of Solid State Drives”
Carlo Meijer and Bernard van Gastel — IEEE Symposium on Security and Privacy, 2019
[[https://www.ru.nl/publish/pages/909282/draft-paper.pdf][ru.nl/publish/pages/909282/draft-paper.pdf]]
The paper that documented catastrophic weaknesses in hardware SSD encryption (Samsung, Crucial), motivating this guide’s insistence on LUKS2 software encryption.
“Lest We Remember: Cold Boot Attacks on Encryption Keys”
J. Alex Halderman et al. — USENIX Security Symposium, 2008
[[https://www.usenix.org/legacy/event/sec08/tech/full_papers/halderman/halderman.pdf][usenix.org/…/halderman.pdf]]
Demonstrated that DRAM retains contents for seconds to minutes after power loss (longer when cooled). Motivates this guide’s emphasis on full shutdown over sleep.
“Security Analysis of a Full-Disk-Encryption Protocol”
The Cryptography and Security research community’s ongoing work on XTS mode and LUKS security properties.
See: ArchWiki dm-crypt at [[https://wiki.archlinux.org/title/Dm-crypt][wiki.archlinux.org/title/Dm-crypt]]