Come creare un file container cifrato

cartella con lucchetto 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.

  1. Introduzione a cryptsetup + LUKS
    1. Inizializzazione
    2. Apertura
    3. Chiusura
  2. Come creare il file container cifrato
  3. Header detachable e keyfile
  4. 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:

Inizializzazione

Apertura

Chiusura

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:

Gli oggetti su cui andrà a lavorare sono:

## 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:

  1. facendo in modo che l’intestazione sia detachable;
  2. 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:

  1. cryptsetup --open luks2 e cryptsetup --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
    
  2. 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 [↵]

#cryptsetup #devicemapper #dmcrypt #gpg #loseup #luks #lvm