Come creare un file container cifrato
Image by d3images on Freepik
Introduzione a cryptsetup + LUKS
Supponiamo di voler creare una piccola cassaforte digitale come faremmo con Veracrypt. Ma senza Veracrypt.
Ciò sarà possibile grazie a dm-crypt. dm-crypt è un modulo del kernel che usa il framework device mapper per fornire funzionalità trasparenti di crittografia per dispositivi a blocchi usando le crypto api del kernel. L’uso di device mapper consente, tra l’altro, di “poggiare” dm-crypt sopra ogni possibile mapping dei dispositivi e quindi può cifrare partizioni, volumi raid o volumi logici.
- Introduzione a cryptsetup + LUKS
- Come creare il file container cifrato
- Header detachable e keyfile
- Partizionare un “volume” cifrato
Poi, essendo a tutti gli effetti anche un dispositivo a blocchi (virtuale), può essere utilizzabile a sua volta come volume nel file system, come swap o come disco fisico per lvm.
La configurazione della cifratura avviene di solito (non è l’unico modo ma è lo standard de facto) con l’utility cryptsetup + LUKS
Come avviene di base la cifratura di un dispositivo?
Le operazioni principali di cryptsetup (con estensioni LUKS) sono:
- luksFormat: stabilisce le modalità di cifratura (consigliato il default)
- luksOpen: (deprecato. Si usa open --type luks2) attiva la cifratura sul device associandolo (grazie a device mapper) ad un dispositivo a blocchi virtuale
- luksClose (deprecato. Si usa close --type luks2): disattiva la cifratura
Inizializzazione
- attacco il dispositivo fisico, viene creata la entry per /dev/<id_dispositivo_a_blocchi_fisico>
- cifratura del dispositivo a blocchi /dev/<id_dispositivo_a_blocchi_fisico> con LUKS (cryptsetup luksFormat)
- al di sopra del dispositivo a blocchi fisico, viene creato un dispositivo a blocchi virtuale, che troverò sotto /dev/mapper/<nome_fittizio>, che eroga le funzionalità di cifratura (cryptsetup open) al dispositivo a blocchi fisico sottostante.
- Infine, partiziono e/o formatto nella maniera canonica il dispositivo a blocchi virtuale (cifrato) /dev/mapper/<nome_fittizio>.
Apertura
- attacco il dispositivo, viene creata la entry per /dev/<id_dispositivo_a_blocchi_fisico>
- attivo il dispositivo a blocchi virtuale che eroga le funzionalità di cifratura (cryptsetup open) e che creerà l’occorrenza sotto /dev/mapper/<nome_fittizio>
- faccio il mount del dispositivo a blocchi virtuale /dev/mapper/ su un punto di montaggio che è una cartella che creerò per l’occasione
Chiusura
- faccio l’unmount del punto di montaggio
- chiudo il dispositivo a blocchi virtuale (cryptsetup close) /dev/mapper/<nome_fittizio>
- stacco il dispositivo fisico
Fermo restando che questi restano i passaggi generali per qualunque dispositivo fisico, per creare un file container cifrato come farebbe Veracrypt, basta che cryptsetup formatti un file invece che un dispositivo a blocchi e automaticamente assocerà il file al primo loop device disponibile. È veramente semplice [1] .
Come creare il file container cifrato
Divido le operazioni in 3 fasi:
- Init: fase di inizializzazione del “dispositivo” (il file). Andrà fatta solo la prima volta.
- Open e Close: sono le operazioni che farò ogniqualvolta dovrò usare il container.
Gli oggetti su cui andrà a lavorare sono:
- disco_cifrato.img: il file container cifrato.
- disco_cifrato: il nome con cui disco_cifrato.img (o meglio, la sua rappresentazione come loop device visibile con losetup) viene mappato da device mapper.
- disco_in_chiaro: il punto di montaggio.
## Init
# creazione di una cartella che sarà il nostro punto di mount
mkdir disco_in_chiaro
# creazione di un file vuoto di 2 GiB
sudo fallocate -l 2g disco_cifrato.img
# preparazione cifratura LUKS
sudo cryptsetup luksFormat \
--type luks2 \
--hash=sha512 \
--key-size=512 \
disco_cifrato.img
# creazione dispositivo a blocchi virtuale che eroga le funzionalità di cifratura
sudo cryptsetup open \
--type luks2 \
disco_cifrato.img disco_cifrato
# formattazione
sudo mkfs.ext4 /dev/mapper/disco_cifrato
## Open
# creazione dispositivo a blocchi virtuale
sudo cryptsetup open \
--type luks2
disco_cifrato.img disco_cifrato
# monta il dispositivo nel punto di montaggio
sudo mount -t ext4 -o defaults /dev/mapper/disco_cifrato disco_in_chiaro
## Close
# smonta il dispositivo
sudo umount disco_in_chiaro
# chiudo il dispositivo a blocchi virtuale staccandolo dai loop device
sudo cryptsetup close disco_cifrato
Header detachable e keyfile
Volendo aggiungere una complessità ulteriore, si può creare il dispositivo cifrato:
- facendo in modo che l’intestazione sia detachable;
- e che usi un keyfile, qualcosa di più robusto di una password;
L’apertura del volume cifrato sarà così vincolata alla disponibiltà sia del keyfile che dell’header, senza il quale il volume cifrato sarebbe comunque inservibile.
Creo il keyfile protetto da una cifratura simmetrica di gpg [2] :
## Creazione di un keyfile di 4KiB basata sulla pseudocasualità di /dev/urandom
# creazione del keyfile
dd if=/dev/urandom bs=1024 count=4 | gpg --yes -o disco_cifrato.key.gpg -c --s2k-cipher-algo aes256 --s2k-digest-algo sha512 -
Creo l’header detachable del volume cifrato fornendo il keyfile invece della passphrase classica:
# creazione header LUKS
gpg -d disco_cifrato.key.gpg | \
sudo cryptsetup luksFormat --type luks2 \
--hash=sha512 \
--key-size=512 disco_cifrato.img \
--header header.img \
--key-file -
Ed è così che l’apertura del volume cifrato è ora vincolata al possesso dell’header e del keyfile:
# attivazione dispositivo a blocchi virtuale passando header e keyfile protetto da gpg
gpg -d disco_cifrato.key.gpg | \
sudo cryptsetup open --type luks2 \
--header header.img \
--key-file - \
disco_cifrato.img disco_cifrato
Provando ad aprire container cifrato senza fornire l’header (non tanto il keyfile che è una passphrase evoluta) succede questo:
sudo cryptsetup open --type luks2 disco_cifrato.img disco_cifrato
Il dispositivo disco_cifrato.img non è un dispositivo LUKS valido.
Partizionare un “volume” cifrato
Per concludere, visto che prima ho accennato al fatto che dm-crypt, grazie al sottosistema device mapper, fa in modo che i dispositivi a blocchi (virtuali) mascherino i dispositivo a blocchi sottostanti (un file montato su un loop device, nel nostro caso), i dispositivi a blocchi mappati possono anche essere partizionati invece che sempicemente formattati. O potrebbe essere volumi fisici di un gruppo di volumi LVM, and so on…
Difficilmente avremo bisogno di partizionare un file container cifrato, è solo l’occasione per speculare un po’ su quello che potrebbe succedere su un device fisico.
## Init
mkdir disco_in_chiaro
sudo fallocate -l 2g disco_cifrato.img
sudo cryptsetup luksFormat --hash=sha512 --key-size=512 disco_cifrato.img
sudo cryptsetup open --type luks2 disco_cifrato.img disco_cifrato
# Creo 1 partizione primaria da 600 MiB, una estesa da 1400 MiB
# contenente 2 partizioni logiche da 600 MiB e 829 MiB (mancano i
# 16 MiB dell'intestazione luks e i 3 MiB delle intestazioni delle
# partizioni primarie ed estese per un totale di 2GiB tondi tondi)
sudo fdisk /dev/mapper/disco_cifrato
n,,,,+600M,n,e,,,,n,,+600M,n,,,w
# il partizionamento potrebbe richiedere l'esecuzione di partprobe
# affinché il kernel carichi la tabella delle partizioni aggiornata
sudo partprobe /dev/mapper/disco_cifrato
# stato delle partizioni
sudo fdisk -l /dev/mapper/disco_cifrato
Disk /dev/mapper/disco_cifrato: 1,98 GiB, 2130706432 bytes, 520192 sectors
Units: sectors of 1 * 4096 = 4096 bytes
Sector size (logical/physical): 4096 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: dos
Disk identifier: 0x6d65a06f
Device Boot Start End Sectors Size Id Type
/dev/mapper/disco_cifrato1 256 153855 153600 600M 83 Linux
/dev/mapper/disco_cifrato2 153856 520191 366336 1,4G 5 Extended
/dev/mapper/disco_cifrato5 154112 307711 153600 600M 83 Linux
/dev/mapper/disco_cifrato6 307968 520191 212224 829M 83 Linux
#procedo con la formattazione
sudo mkfs.ext4 /dev/mapper/disco_cifrato1 #formatto la partizione
sudo mkfs.ext4 /dev/mapper/disco_cifrato5 #formatto la partizione
sudo mkfs.ext4 /dev/mapper/disco_cifrato6 #formatto la partizione
Rispetto all’init di prima, invece che formattare subito il device “fisico”, l’ho partizionato. Esattamente come avrei fatto con un dispositivo realmente fisico.
## Open
sudo cryptsetup open --type luks2 disco_cifrato.img disco_cifrato
# di nuovo, potrebbe essere necessario a meno di non riavviare
sudo partprobe /dev/mapper/disco_cifrato
# mount delle partizioni
sudo mount -t ext4 -o defaults /dev/mapper/disco_cifrato1 disco_in_chiaro_1
sudo mount -t ext4 -o defaults /dev/mapper/disco_cifrato5 disco_in_chiaro_2
sudo mount -t ext4 -o defaults /dev/mapper/disco_cifrato6 disco_in_chiaro_3
## Close
sudo umount disco_in_chiaro_1
sudo umount disco_in_chiaro_2
sudo umount disco_in_chiaro_3
sudo cryptsetup close disco_cifrato1
sudo cryptsetup close disco_cifrato2
sudo cryptsetup close disco_cifrato5
sudo cryptsetup close disco_cifrato6
sudo cryptsetup close disco_cifrato
Note:
cryptsetup --open luks2
ecryptsetup --close luks2
, quando applicate direttamente al file container, includono implicitamente la parte di accoppiamento al loop device. In altre parole,cryptsetup open --type luks2 disco_cifrato.img disco_cifrato
equivale a
cryptsetup open --type luks2 $(losetup -Pf --show disco_cifrato.img) disco_cifrato
e
cryptsetup close --type luks2 disco_cifrato
equivale a [↵]
cryptsetup close --type luks2 disco_cifrato losetup -D
Se si incorre in questo errore usando gpg:
gpg: cancelled by user gpg: error creating passphrase: Operation cancelled gpg: symmetric encryption of '[stdin]' failed: Operation cancelled
vuol dire che la variabile d’ambiente
GPG_TTY
non ha lo stesso valore del comando tty. Per ovviare basta settare correttamente la variabile:GPG_TTY=$(tty) export GPG_TTY
come suggerito dalla documentazione GnuPG [↵]