Simple guide for creating and managing a LUKS container (5 GB, ext4). Сontainer creation, key management, slot operations, testing keys, creating custom key files from images, managing detached headers, handling complex passphrases, and generating passphrases from BIP-39 seed phrases.
- System: Used Arch Linux.
- Packages: Install
cryptsetupande2fsprogs:sudo pacman -S cryptsetup e2fsprogs --noconfirm
-
Create a 5 GB container file:
dd if=/dev/urandom of=/home/user/luks_container.img bs=1M count=5000 status=progress
-
Fast LUKS container setup
This configuration prioritizes speed while maintaining reasonable security, suitable for quick setups or less sensitive data.
cryptsetup luksFormat --type luks2 --cipher aes-xts-plain64 --key-size 256 --hash sha256 --pbkdf pbkdf2 --pbkdf-force-iterations 1000000 --use-random /home/user/luks_container.img --debug --verbose- Cryptography: AES-XTS (AES with XTS mode), 256-bit key (128-bit AES keys split for XTS).
- Hash: SHA-256 for key derivation.
- PBKDF: PBKDF2 with 1,000,000 iterations, balancing speed and security.
2.1 Secure LUKS container setup
This configuration maximizes security for sensitive data, using stronger cryptographic parameters and Argon2id for key derivation, at the cost of slower setup and unlocking.
cryptsetup luksFormat --type luks2 --cipher aes-xts-plain64 --key-size 512 --hash sha512 --pbkdf argon2id --pbkdf-memory 4194304 --pbkdf-parallel 8 --iter-time 5000 --use-random --sector-size 4096 --keyslot-cipher aes-xts-plain64 --keyslot-key-size 512 --luks2-keyslots-size 16m --luks2-metadata-size 256k /home/user/luks_container.img --debug --verbose- Cryptography: AES-XTS, 512-bit key (256-bit AES keys split for XTS); keyslots also use AES-XTS with 512-bit keys.
- Hash: SHA-512 for key derivation.
- PBKDF: Argon2id with 4 GiB memory, 8 threads, and 5-second iteration time.
- Header: 16 MiB keyslot area, 256 KiB metadata per copy, optimized for security and flexibility.
- Note: Uses
--pbkdf-memory 1048576(1 GiB) instead of 4 GiB for better compatibility with systems having limited RAM (e.g., 4–8 GiB).
-
Open container:
sudo cryptsetup luksOpen /home/user/luks_container.img luks_container
-
Format with ext4:
sudo mkfs.ext4 /dev/mapper/luks_container
-
Mount:
sudo mkdir /mnt/luks_container sudo mount /dev/mapper/luks_container /mnt/luks_container sudo chown $USER:$USER /mnt/luks_container
-
Unmount:
sudo umount /mnt/luks_container
-
Close:
sudo cryptsetup luksClose luks_container --verbose --debug
-
Create key file:
dd if=/dev/urandom of=/path/to/keyfile bs=4096 count=1
-
Add to slot 3 using passphrase:
echo -n "your_passphrase" | cryptsetup luksAddKey /home/user/luks_container.img /path/to/keyfile --key-slot 3 --verbose --debug --key-file=-
-
Add to slot 0 using key file from slot 3:
dd if=/dev/urandom of=/path/to/new_keyfile bs=4096 count=1 cryptsetup luksKillSlot /home/user/luks_container.img 0 --key-file /path/to/keyfile_slot3 --verbose --debug cryptsetup luksAddKey /home/user/luks_container.img --key-slot 0 /path/to/new_keyfile --key-file /path/to/keyfile_slot3 --verbose --debug
-
Add complex passphrase to slot 4 using existing key file:
echo -n '<0bn}0X9O<RUf:tR_CZ;ma8",:\DQ*}9A*xB48S-LP6L7HJ(KiH`60L\nI3OAHj{' | cryptsetup luksAddKey /home/user/luks_container.img --key-slot 4 --key-file /path/to/keyfile --verbose --debug --new-keyfile=-
- Replace
/path/to/keyfilewith the path to your existing key file. --new-keyfile=-reads the new passphrase from stdin.
- Replace
-
Add to LUKS with escaped passphrase:
echo -n '<0bn}0X9O<RUf:tR_CZ;ma8",:\DQ*}9A*xB48S-LP6L7HJ(KiH`60L\nI3OAHj{' | cryptsetup luksAddKey /home/user/luks_container.img --key-slot 4 --key-file /path/to/keyfile --verbose --debug --new-keyfile=-
-
Test complex passphrase:
echo -n '<0bn}0X9O<RUf:tR_CZ;ma8",:\DQ*}9A*xB48S-LP6L7HJ(KiH`60L\nI3OAHj{' | cryptsetup open --test-passphrase --key-slot 4 /home/user/luks_container.img --verbose --debug --key-file=-
Generate passphrase from a 12-word BIP-39 seed phrase using a deterministic password generator (7w) and add it to a LUKS key slot. This method allows passphrase recovery from the seed phrase alone.
-
Generate a 12-word BIP-39 seed phrase:
-
Use a secure BIP-39 seed phrase generator, preferably offline, to create a 12-word mnemonic compliant with the BIP-39 standard. Example tool: https://cryptony.app/seed-phase-generator/
-
Example seed phrase:
property iron shove feature tide parent army neglect stable lab ostrich zone
-
-
Convert seed phrase to passphrase:
- Use the 7w deterministic password generator (https://dlepex.github.io/7w/index.html) to convert the seed phrase into a complex passphrase. The tool operates client-side in the browser and does not store or transmit data.
- Configuration:
- Set
w=12(12 words). - Set
mod=v(visible mode).
- Set
- Steps:
- Open https://dlepex.github.io/7w/index.html#w=12&mod=v
- Enter the seed phrase (e.g.,
property iron shove feature tide parent army neglect stable lab ostrich zone). - Generate the passphrase.
- Example Output:
<0bn}0X9O<RUf:tR_CZ;ma8",:\DQ*}9A*xB48S-LP6L7HJ(KiH`60L\nI3OAHj{
-
Add passphrase to LUKS key slot:
-
Add the generated passphrase to slot 4 using an existing key file for authentication.
-
Command:
echo -n '<0bn}0X9O<RUf:tR_CZ;ma8",:\DQ*}9A*xB48S-LP6L7HJ(KiH`60L\nI3OAHj{' | cryptsetup luksAddKey /home/user/luks_container.img --key-slot 4 --key-file /path/to/keyfile --verbose --debug --new-keyfile=-
- Replace
/path/to/keyfilewith the path to your existing key file. - Ensure proper escaping (e.g.,
\\for\,\`` for ```,\nfor\n`).
- Replace
-
Verify output:
echo -n '<0bn}0X9O<RUf:tR_CZ;ma8",:\DQ*}9A*xB48S-LP6L7HJ(KiH`60L\nI3OAHj{' | hexdump -C
Expected output:
00000000 3c 30 62 6e 7d 30 58 39 4f 3c 52 55 66 3a 74 52 |<0bn}0X9O<RUf:tR| 00000010 5f 43 5a 3b 6d 61 38 22 2c 3a 5c 44 51 2a 7d 39 |_CZ;ma8",:\DQ*}9| 00000020 41 2a 78 42 34 38 53 2d 4c 50 36 4c 37 48 4a 28 |A*xB48S-LP6L7HJ(| 00000030 4b 69 48 60 36 30 4c 0a 49 33 4f 41 48 6a 7b |KiH`60L.I3OAHj{| 0000003f -
Test passphrase:
echo -n '<0bn}0X9O<RUf:tR_CZ;ma8",:\DQ*}9A*xB48S-LP6L7HJ(KiH`60L\nI3OAHj{' | cryptsetup open --test-passphrase --key-slot 4 /home/user/luks_container.img --verbose --debug --key-file=-
-
-
Test passphrase:
cryptsetup open --test-passphrase /home/user/luks_container.img --verbose --debug
-
Test key file:
cryptsetup open --test-passphrase --key-file /path/to/keyfile /home/user/luks_container.img --verbose --debug
-
Test specific slot:
cryptsetup open --test-passphrase --key-slot 4 /home/user/luks_container.img --verbose --debug
- Remove slot 0:
cryptsetup luksKillSlot /home/user/luks_container.img 0 --key-file /path/to/keyfile_slot3 --verbose --debug
Create a key file from an image by downloading it, verifying its SHA512 hash, modifying it with binary data (prepend 10 0x31 bytes, append 10 0x2E bytes), and adding it to a LUKS container.
- Download and modify image: Use this script to download image file, verify hash, and create a key file.
#!/bin/bash
set -e
# Log function with timestamp (Python-style)
log() {
local level="$1"
local message="$2"
local extra="${3:-}"
printf "[%s] [%-5s] %s %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$level" "$message" "$extra"
}
# Error exit with cleanup
error_exit() {
log "ERROR" "$1"
rm -f "$TMP_FILE" "$OUTPUT_KEY"
exit 1
}
# Check dependencies
for cmd in wget sha512sum printf; do
command -v "$cmd" &>/dev/null || error_exit "missing $cmd"
done
URL="https://upload.wikimedia.org/wikipedia/commons/2/2c/TerryFoxToronto19800712.JPG"
EXPECTED_HASH="f1db6ca08d665e29e4462a5384ee7cb440345d7147fd6589a2515b876f053b6f048de0bdb6507443336fcee51552e571ea2af3bc4e180472ccddf608ee06fc6f"
EXPECTED_MODIFIED_HASH="726f581b49461e5d48de1d94f2c64cd2510afd312bb093fe3a4ec106f7b035e1eaf10233112e8aeb04a5eeaf0bedacce503cfcd27a8c70b107538528a802301d"
TMP_FILE="/tmp/tmp_key"
OUTPUT_KEY="/tmp/luks_key"
# Download image
log "INFO" "downloading file" "$URL"
wget --no-check-certificate -O "$TMP_FILE" "$URL" 2>/dev/null || error_exit "download failed"
# Verify original file hash
log "INFO" "verifying original file hash"
HASH=$(sha512sum "$TMP_FILE" | cut -d' ' -f1)
[ "$HASH" = "$EXPECTED_HASH" ] || error_exit "hash mismatch: got $HASH, expected $EXPECTED_HASH"
# Modify: prepend 10 '1' (0x31), append 10 '.' (0x2E)
log "INFO" "modifying key file"
{ printf "\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31"; cat "$TMP_FILE"; printf "\x2E\x2E\x2E\x2E\x2E\x2E\x2E\x2E\x2E\x2E"; } > "$OUTPUT_KEY" || error_exit "modification failed"
log "INFO" "key file modification completed"
# Verify modified file hash
log "INFO" "verifying modified file hash"
MODIFIED_HASH=$(sha512sum "$OUTPUT_KEY" | cut -d' ' -f1)
[ "$MODIFIED_HASH" = "$EXPECTED_MODIFIED_HASH" ] || error_exit "modified file hash mismatch: got $MODIFIED_HASH, expected $EXPECTED_MODIFIED_HASH"
log "INFO" "modified file hash verified successfully"
# Cleanup
log "INFO" "cleaning up temporary file"
rm -f "$TMP_FILE" || error_exit "cleanup failed"
log "INFO" "created $OUTPUT_KEY"Prepend 10 0x31 bytes:
00000000: 3131 3131 3131 3131 3131 ffd8 ffe0 0010 1111111111......
00000010: 4a46 4946 0001 0101 0060 0060 0000 ffe1 JFIF.....`.`....
00000020: 1b02 4578 6966 0000 4949 2a00 0800 0000 ..Exif..II*.....
00000030: 0700 1201 0300 0100 0000 0100 0000 1a01 ................
00000040: 0500 0100 0000 6200 0000 1b01 0500 0100 ......b.........
00000050: 0000 6a00 0000 2801 0300 0100 0000 0200 ..j...(.........
00000060: 0000 3101 ..1.
Append 10 0x2E bytes:
00000000: d618 dff8 6ecf ff00 9353 e81f 2fff 00ae ....n....S../...
00000010: 8fff 007c 8fff 00c1 f51c f2af f5f1 dffd ...|............
00000020: e13f fd7f a7e7 10ff 00a3 e9ff 00f8 1a7f .?..............
00000030: fd7b 1f4d dd8f fa2c 7ff0 f57f f5a3 e9a4 .{.M...,........
00000040: effa e74b ff00 88ff 00fe ad6f a6cb 3fea ...K.......o..?.
00000050: ec6f fdca 7ff9 87f5 ffd9 2e2e 2e2e 2e2e .o..............
00000060: 2e2e 2e2e ....
-
Add key to LUKS:
sudo cryptsetup luksAddKey /home/user/luks_container.img /tmp/luks_key --key-slot 3 --verbose --debug
-
Test key:
cryptsetup open --test-passphrase --key-file /tmp/luks_key /home/user/luks_container.img --verbose --debug
-
Backup header:
cryptsetup luksHeaderBackup /home/user/luks_container.img --header-backup-file /path/to/luks_header_backup --verbose --debug
-
Restore header:
cryptsetup luksHeaderRestore /home/user/luks_container.img --header-backup-file /path/to/luks_header_backup --verbose --debug
Manage a LUKS container with a detached header for enhanced security by storing the header separately from the container data. This can be done by extracting the header from an existing container or creating a new one with a detached header.
-
Create container and header:
dd if=/dev/urandom of=/home/user/luks_container.img bs=1M count=5000 status=progress cryptsetup luksFormat --type luks2 --header /path/to/header.bin /home/user/luks_container.img --verbose --debug
Set a strong passphrase.
-
Open container:
cryptsetup luksOpen --header /path/to/header.bin /home/user/luks_container.img luks_container- Format and mount:
sudo mkfs.ext4 /dev/mapper/luks_container sudo mkdir /mnt/luks_container sudo mount /dev/mapper/luks_container /mnt/luks_container sudo chown $USER:$USER /mnt/luks_container
-
Backup existing header:
cryptsetup luksHeaderBackup /home/user/luks_container.img --header-backup-file /path/to/header.bin --verbose --debug
-
Verify header:
cryptsetup luksDump --header /path/to/header.bin /home/user/luks_container.img
-
Remove header from container (optional, for security):
dd if=/dev/zero of=/home/user/luks_container.img bs=1M count=4 conv=notrunc
This overwrites the first 4 MB (typical LUKS header size) to remove the header from the container.
- Open container:
cryptsetup luksOpen --header /path/to/header.bin /home/user/luks_container.img luks_container-
Mount:
sudo mount /dev/mapper/luks_container /mnt/luks_container
-
Unmount and close:
sudo umount /mnt/luks_container sudo cryptsetup luksClose luks_container
-
Backup header:
cryptsetup luksHeaderBackup --header /path/to/header.bin /home/user/luks_container.img --header-backup-file /path/to/header_backup.bin --verbose --debug
-
Restore header:
cryptsetup luksHeaderRestore --header /path/to/header.bin /home/user/luks_container.img --header-backup-file /path/to/header_backup.bin --verbose --debug
