Full Disk Encryption su Linux con LUKS

Full Disk Encryption

La Full disk encryption è la crittografia a livello del disco (come suggerisce il nome) ed è trasparente per l’utente, poiché opera al di sotto del livello del filesystem. Fondamentalmente è una crittografia a blocchi, il che significa che quando un blocco del disco viene letto (o scritto) il modulo di crittografia a livello di kernel lavora come un “traduttore”.

Questo tipo di crittografia non distingue tra informazioni sensibili e non sensibili, semplicemente cripta tutto.

Mentre la file-based encryption deve essere implementata dagli sviluppatori (usando librerie) per ogni contesto e potrebbe non funzionare in altri computer se non correttamente implementata, la FDE cripta tutto ed è agnostica rispetto al filesystem utilizzato (perché stai criptando i blocchi sul dispositivo), la full disk encryption è utile con una configurazione LVM criptata o una partition table.

Uno degli svantaggi della full disk encryption è che quando il sistema è acceso il disco è sbloccato, mentre la crittografia basata su file deve essere decrittata ogni volta che se ne richiede l’accesso.
Essenzialmente la FDE protegge solo quando il sistema è spento, quindi nel caso in cui qualcuno rubi il tuo portatile o pc, i tuoi file sono tendenzialmente al sicuro.

Panoramica su dm-crypt

La crittografia di default del device-mapper in Linux è fornita da dm-crypt nel kernel Linux, quindi se state cercando di avere il pieno controllo sulla gestione delle partizioni e delle chiavi questo è ciò che dovreste usare.
La gestione di dm-crypt è fatta attraverso cryptsetup: LUKS è un frontend aggiuntivo per dm-crypt con lo scopo di semplificare tutte le procedure crittografiche.
L’immagine seguente descrive come sono strutturati i livelli menzionati sopra (filesystem, directory, block device, ecc.):

Struttura dei layer in caso di encryption FDE

Come funziona LUKS

LUKS (Linux Unified Key Setup), in particulare LUKS2, fornisce un archivio generico di chiavi in un’area dedicata su disco, con la possibilità di utilizzare più passphrases per sbloccare la chiave memorizzata. LUKS2 ha più flessibilità nello storage dei metadata rispetto alla versione precedente (informazioni ridondanti per fornire supporto per il recupero dei dati in caso di corruzione).

Gli header LUKS forniscono i metadata necessari al setup dell’encryption, queste sono alcune funzionalità:

  • Checksum per detection di corruzione o manipolazione degli header
  • L’area di metadata è salvata in duplice copia per avere la possibilità di recovery
  • I metadata sono salvati in formato JSON. Questo permette eventuali future estensioni senza modificare la struttura binaria
  • Gli header contengono oggetti chiamati token, che a loro volta contengono informazioni su dove recuperare la passphrase di sblocco

Header

Gli header di LUKS possono essere divisi in tre parti:

  • Binary header (4096 byte, solo 512 byte sono davvero usati)
  • Metadata in JSON
  • Keyslot area

Come si può vedere dall’immagine sopra, la parte binaria e l’area in JSON sono salvate due volte e, in condizioni normali (ad es. non durante gli aggiornamenti degli header), contengono gli stessi valori.

La dimensione del binary header assicura che sia sempre scritta in un singolo settore (scrittura atomica/atomic write) per prevenire errori e/o corruzioni durante i salvataggi.

Header binario

L’header binario contiene tutte le informazioni necessarie per informare il sistema che sta dialogando con un dispositivo LUKS. Le informazioni salvate qui sono informazioni di base come labels, una signature che indica che questo è un dispositivo LUKS, la dimensione dell’header e il checksum dei metadati (molto importante!).

Il primary header deve essere memorizzato nel settore 0 (zero) del dispositivo, il secondo inizia dopo la prima area JSON in una posizione di offset sempre fissa come indicato di seguito:

Offset  | JSON
[bytes] | [kB]
---------------
16384 | 12
32768 | 28
65536 | 60
131072 | 124
262144 | 252
524288 | 508
1048576 | 1020
2097152 | 2044
4194304 | 4092

JSON Area

L’area JSON inizia dopo l’header binario e la fine deve essere allineata all’offset di un settore di 4096 byte, quindi la dimensione dell’area JSON è

JSON_Area_size = header_size - 4096

Quindi l’offset in cui inizia la seconda intestazione binaria (riportato nella tabella sopra) ora ha un senso: La dimensione dell’area JSON + bin_header_size (4096 byte) deve corrispondere all’offset. Lo spazio inutilizzato viene riempito con degli zeri.

Keyslot Area

La Keyslot Area è uno spazio su disco che può essere allocato per i dati binari provenienti dai keyslot, infatti ci sono memorizzate chiavi criptate puntate da metadati keyslot.

L’area allocata è definita in un keyslot da un oggetto “area” che contiene i campi offset (dall’inizio del dispositivo) e dimensione, entrambi i campi devono essere validati altrimenti verranno rifiutati.

Alignment padding

Il padding di allineamento (alignment padding) ha lo scopo di allineare i dati crittografati all’inizio di un blocco (i blocchi sono crittografati uno per uno, tipicamente un blocco è di 512 byte) con il giusto offset per far funzionare correttamente LUKS con i settori crittografati.

Metadata

I metadati LUKS permettono di definire un oggetto con una funzionalità specifica. Gli oggetti non riconosciuti sono ignorati, ma comunque mantenuti nei metadati JSON.

L’implementazione di LUKS deve validare la struttura JSON prima di aggiornare gli header su disco.

LUKS ha i seguenti oggetti obbligatori:

  • config — contiene gli attributi di configurazione dell’header persistente
  • keyslot — sono oggetti che descrivono le aree di memorizzazione delle chiavi criptate
  • digest — utilizzati per verificare la correttezza delle chiavi decifrate
  • segment — descrive le aree del disco che contengono i dati cifrati dell’utente
  • token — questo campo può, in alcuni casi, contenere metadata aggiuntivi, collegamenti ad altri sistemi (ad es. se gli header non sono su disco)

I dati binari all’interno del JSON sono memorizzati in Base64 e gli interi a 64-bit sono memorizzati come stringhe in notazione decimale.

Ora scendiamo nel dettaglio degli oggetti obbligatori nei metadata LUKS:

Config object

L’oggetto config contiene questi campi, che sono globali in tutto il device LUKS:

  • json_size — dimensione dell’area JSON (in byte), questo campo deve essere uguale all’header binario
  • keyslots_size — dimensione dell’area del keyslot binario (in byte), deve essere allineato a 4096 byte
  • flags — è un array di stringhe con flag persistenti per il device
  • requirements — array di stringhe per feature addizionali per il device

Keyslot object

Gli oggetti Keyslot contengono informazioni sulle chiavi memorizzate, le aree, dove i keyslot binari sono memorizzati, il tipo di crittografia, la funzione anti-forensics utilizzata, la funzione di derivazione della chiave basata su password e i parametri correlati.

Ogni oggetto keyslot contiene:

  • type — tipologia di keyslot
  • key_size — dimensione della chiave (in byte) memorizzata nel keyslot
  • area — area assegnata nell’area keylot binaria
  • kdf — PBKDF
  • af — anti-forensics. Non in uso nei sistemi odierni (LUKS2)
  • priority — la priorità del keyslot: 0 = ignore, 1 = normal, 2 = high.

Digest object

Per verificare che una chiave decriptata (da un keyslot) sia corretta, LUKS utilizza gli oggetti digest. Questi oggetti sono assegnati a keyslot e segmenti, se non assegnati ad un segmento invece, fanno riferimento ad una chiave “unbound”.

Gli oggetti digest contengono questi campi:

  • type — tipologia di digest
  • keyslots — array di nomi di oggetti keyslot assegnati al digest
  • segments — array di nomi di oggetti segment assegnati al digest
  • salt — salt binario per il digest
  • digest — dati binari per il digest

Segment object

L’oggetto segment contiene la definizione delle aree criptate sul disco. Per un normale dispositivo LUKS è presente solo un segmento di dati.

Questi sono i campi:

  • type — tipologia di segment (al momento viene usato solo crypt)
  • offset — offset dall’inizio del device all’inizio del segmento
  • size — dimensione del segmento (in byte) oppure dynamic (in caso di resize dinamico del device)
  • iv_tweak — offset per l’Initializaion Vector
  • encryption — algoritmo di cifratura del segmento in notazione dm-crypt
  • sector_size — dimensione del settore per il segmento (512, 1024, 2048 o 4096 byte)
  • integrity — protezione dell’integrità dei dati utente LUKS2
  • flags — array di oggetti stringa con informazioni aggiuntive per il segmento in questione

Token object

Il token è un oggetto che descrive come ottenere una passphrase per sbloccare un particolare keyslot, e può contenere ulteriori metadati JSON.
Questi sono i campi obbligatori:

  • type — definisce la tipologia di token
  • keyslots — array di oggetti keyslot assignati al token

LUKS Backup e restore

LUKS Header Backup

Il backup degli header LUKS è utile nel caso in cui si corrompa o si cancelli l’header stesso. Se lavorate direttamente sul disco (e non sul device mapper) per qualsiasi motivo, un backup potrebbe salvarvi nel caso succeda qualcosa.

Questo comando creerà un file con i vostri header LUKS:

sudo cryptsetup luksHeaderBackup /dev/MY_DRIVE --header-backup-file /path/to/backup-file

È possibile eseguire il backup degli header LUKS anche con il comando dd:

sudo dd if=/dev/MY_DRIVE of=/path/to/backup-file bs=X count=Y

Per usare dd bisogna conoscere prima la grandezza dell’header in modo da poter copiare tutti i dati presenti. Utilizzare il comando luksDump per conoscere la dimensione e le informazioni sull’offset.

LUKS Header Restore

Questo è il peggior caso che possa capitare: perdere o corrompere gli header LUKS. Se esiste una copia di backup è possibile eseguire il restore tramite un Live OS, questa procedura riporterà gli header sul device e permetterà di utilizzare nuovamente il disco.

sudo cryptsetup luksHeaderRestore /dev/MY_DRIVE --header-backup-file /path/to/backup-file

LUKS chiederà una conferma ulteriore per eseguire il comando:

WARNING!
========
Device /dev/MY_DRIVE already contains LUKS2 header. Replacing header will destroy existing keyslots.

Are you sure? (Type uppercase yes):

Come cancellare un disco cifrato con LUKS

Se hai un disco cifrato con LUKS ci sono due strade per poter cancellare i dati contenuti al suo interno: eliminare gli header LUKS collocati all’inizio del disco cifrato, eliminare l’intero contenuto del disco come un disco qualsiasi.

Eliminare gli header LUKS

Gli header, come abbiamo constatato sopra, contengono tutte le informazioni per sbloccare un disco, senza queste informazioni (le chiavi in particolare) è praticamente impossibile ripristinare i dati.
Quindi potrebbe essere sufficiente eliminare gli header con il comando shred:

sudo shred --size=LUKS_HEADER_SIZE /dev/MY_DRIVE

Se hai un disco con più partizioni fai attenzione a selezionare la partizione LUKS e non il disco intero o la partition table!


Si possono eliminare le chiavi anche con cryptsetup:

sudo cryptsetup erase /dev/MY_DRIVE

Oppure eliminare l’header con wipefs:

sudo wipefs -a /dev/MY_DRIVE

Cancellare il disco

Cancellare il disco è la soluzione più semplice, ma non la più veloce. Per procedere con l’eliminazione dei dati dal disco bisogna fare il boot da un Live OS.

Utilizzando shred possiamo eliminare i dati nel nostro disco e prevenire qualsiasi tipo di attività forense su di esso:

sudo shred /dev/MY_DRIVE

Anche dd può essere utilizzato selezionando una sorgente dati randomica come urandom/random oppure zero:

sudo dd if=/dev/urandom of=/dev/MY_DRIVE

oppure

sudo dd if=/dev/zero of=/dev/MY_DRIVE

Considerazioni finali

In questo articolo abbiamo analizzato LUKS, un valido frontend per la crittografia completa del disco. È importante assicurarsi che la configurazione di LUKS sia sicura (cipher suites forti); se non avete bisogno delle caratteristiche della prima versione di LUKS, usate LUKS2.

Abbiamo anche visto che cancellare una partizione LUKS potrebbe essere più facile e veloce di un disco normale, questo è un punto interessante poiché al momento (2021) non c’è modo di recuperare i dati criptati.

Se vuoi capire come rendere sicuro il tuo sistema Linux, non solo abilitando la Full Disk Encryption, puoi approfondire il tema con questo articolo:

Lascia un commento