<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>luks &amp;mdash; Cyberdyne Systems</title>
    <link>https://noblogo.org/aytin/tag:luks</link>
    <description>&#34;Fare o non fare. Non c&#39;è provare!&#34;</description>
    <pubDate>Thu, 30 Apr 2026 06:57:52 +0000</pubDate>
    <item>
      <title>Device Mapper: LUKS + LVM</title>
      <link>https://noblogo.org/aytin/device-mapper-luks-lvm</link>
      <description>&lt;![CDATA[luks-lvm&#xA;(segue da &#34;Cenni sulla creazione di pool di storage con LVM&#34;)&#xA;...anche se è la base per una serie di sviluppi interessanti. &#xA;&#xA;Come ormai sappiamo, device mapper è il framework del kernel Linux col quale mappare dispositivi a blocchi fisici su dispositivi a blocchi logici, che costituisce la base per fornire funzionalità ulteriori quali:&#xA;!--more--&#xA;&#xA;volumi logici&#xA;raid&#xA;cifratura (Full Disk Encryption)&#xA;snapshot di volumi&#xA;&#xA;Scenario 1&#xA;È molto comune per es. cifrare l&#39;intero disco e &#34;affettarlo&#34; con volumi logici in base alle proprie esigenze di partizionamento .&#xA;&#xA;Il doppio vantaggio è dato da:&#xA;&#xA;offuscamento totale dello schema di partizionamento&#xA;estrema versatilità / flessibilità del partizionamento grazie ai volumi logici &#xA;&#xA;Come si agisce?&#xA;&#xA;si cifra il dispositivo fisico&#xA;si crea un gruppo di volumi avente come volume fisico il volume cifrato&#xA;si creano i volumi logici in base allo schema di partizionamento desiderato.&#xA;&#xA;luks-lvm&#xA;Passo 1 - Inizializzazione&#xA;Simulo il mio dispositivo fisico ricorrendo ai loop device.&#xA;&#xA;Il &#34;disco&#34; avrà una grandezza simbolica di 2 GiB, non avrà header detachable. L&#39;algoritmo di hash sarà sha512 e la chiave sarà da 512 bit. Il resto è il default di luks2 (argon2id come pbkdf, per maggiori dettagli vedi cryptsetup --help)&#xA;1. Preparazione disco &#34;fisico&#34;&#xA;fallocate -l 2g cipherdisk.img&#xA;&#xA;2. loop device che simula l&#39;attach del dispositivo&#xA;DEV=$(losetup -Pf --show cipherdisk.img)&#xA;&#xA;3. Inizializzazione cifratura&#xA;cryptsetup luksFormat  \&#xA;    --type luks2 \&#xA;    --hash sha512 \&#xA;    --key-size 512 \&#xA;    $DEV&#xA;Passo 2&#xA;Il passo successivo consiste nell&#39;apertura del dispositivo cifrato e nella definizione dello schema di partizionamento in volumi logici&#xA;1. apertura del disco cifrato&#xA;cryptsetup open \&#xA;    --type luks2 \&#xA;    $DEV cipherdisk&#xA;Nell&#39;apertura, device mapper fa la sua prima magia.&#xA;&#xA;Infatti, se osserviamo lo stato dei dispositivi, vedremo una situazione simile:&#xA;lsblk&#xA;...&#xA;loop9                7:9    Kib0     2G  0 loop  &#xA;└─cipherdisk      252:3    0     2G  0 crypt &#xA;...&#xA;Cio vuol dire che sopra il dispositivo fisico, /dev/loop9 in questo caso, device mapper ha &#34;poggiato&#34; cipherdisk (/dev/mapper/cipherdisk).&#xA;&#xA;Questo sarà il nostro volume fisico per definire gruppi di volume e, conseguentemente, i volumi logici.&#xA;2. creazione gruppo di volumi&#xA;vgcreate vglab /dev/mapper/cipherdisk&#xA;&#xA;3. creazione volumi logici&#xA;lvcreate -n lvlab1 vglab -L 700M&#xA;lvcreate -n lvlab2 vglab -L 600M&#xA;lvcreate -n lvlab3 vglab -l 100%FREE&#xA;La situazione dei dispositivi è ora questa:&#xA;lsblk&#xA;...&#xA;loop9                7:9    0     2G  0 loop  &#xA;└─cipherdisk      252:3    0     2G  0 crypt&#xA;  ├─vglab-lvlab1 252:4    0   700M  0 lvm&#xA;  ├─vglab-lvlab2 252:5    0   600M  0 lvm&#xA;  └─vglab-lvlab3 252:6    0   728M  0 lvm&#xA;...&#xA;Sopra /dev/loop9 c&#39;è cipherdisk (/dev/mapper/cipherdisk) e sopra di esso, i 3 volumi logici&#xA;&#xA;lvlab1 (/dev/mapper/vglab-lvlab1)&#xA;lvlab2 (/dev/mapper/vglab-lvlab2)&#xA;lvlab3 (/dev/mapper/vglab-lvlab3)&#xA;&#xA;Formatto e monto i volumi logici&#xA;4. formattazione dei 3 volumi logici&#xA;mkfs.ext4 /dev/mapper/vglab-lvlab1&#xA;mkfs.ext4 /dev/mapper/vglab-lvlab2&#xA;mkfs.ext4 /dev/mapper/vglab-lvlab3&#xA;&#xA;5. creo e preparo i punti di mmontaggio&#xA;mkdir disk1 disk2 disk3&#xA;mount -t ext4 -o user,noauto,rw  /dev/mapper/vglab-lvlab1 disk1&#xA;mount -t ext4 -o user,noauto,rw  /dev/mapper/vglab-lvlab2 disk2&#xA;mount -t ext4 -o user,noauto,rw  /dev/mapper/vglab-lvlab3 disk3&#xA;chown $USER disk1 disk2 disk3&#xA;&#xA;6. unmount di tutti i dispositivi&#xA;umount disk1 disk2 disk3&#xA;vgchange -an vglab&#xA;cryptsetup close cipherdisk&#xA;losetup -d $DEV&#xA;Passo 3&#xA;Infine, per completezza, gli script di mount e unmount del dispositivo.&#xA;mount&#xA;1. attach del dispositivo&#xA;DEV=$(losetup -Pf --show cipherdisk.img)&#xA;&#xA;2. Apre il disco cifrato &#xA;cryptsetup open \&#xA;    --type luks2 \&#xA;    $DEV cipherdisk&#xA;&#xA;3. monta il gruppo di volume&#xA;(opzionale. L&#39;apertura del disco cifrato dovrebbe montare &#xA;automaticamente il gruppo di volumi)&#xA;vgchange -ay vglab&#xA;&#xA;4. monta i volumi logici&#xA;mount -t ext4 -o user,noauto,rw  /dev/mapper/vglab-lvlab1 disk1&#xA;mount -t ext4 -o user,noauto,rw  /dev/mapper/vglab-lvlab2 disk2&#xA;mount -t ext4 -o user,noauto,rw  /dev/mapper/vglab-lvlab3 disk3&#xA;unmount&#xA;1. smonta i 3 dischi&#xA;umount disk1 disk2 disk3&#xA;&#xA;2. smonta il gruppo di volumi&#xA;vgchange -an vglab&#xA;&#xA;3. chiude il disco cifrato&#xA;cryptsetup close cipherdisk&#xA;&#xA;4. &#34;stacca&#34; il dispositivo fisico&#xA;losetup -d $DEV&#xA;&#xA;Questo è ciò che si farebbe normalmente quando si vuole il full disk encryption sul proprio pc. Ma con una piccola eccezione.&#xA;&#xA;In realtà ciò che viene cifrata è una partizione quasi completa del disco, perché almeno una piccola partizione, quella contenente il boot, /BOOT, deve essere in chiaro per consentire:&#xA;&#xA;a UEFI di avviare GRUB che conosce le partizioni,&#xA;GRUB provvederà all&#39;avvio del kernel,&#xA;l&#39;avvio del kernel con initramfs chiederà la password per sbloccare la partizione cifrata.&#xA;&#xA;La cifratura totale (che proprio totale non sarà perché /boot/efi deve rimanere in chiaro) eleva di molto la complessità del setup iniziale.&#xA;&#xA;Affidare a GRUB la gestione della cifratura potrebbe voler dire, oltre alla complessità della configurazione iniziale che dovrà far ricorso a moduli come cryptodisk, che bisognerà rinunciare ad Argon2 perché GRUB ancora non lo supporta pienamente e, a differenza del kernel, non ha sufficiente potenza per farlo lavorare come si deve.&#xA;&#xA;Un buon compromesso potrebbe essere il ricorso ad un dispositivo esterno, es. una pendrive, che contenga tutta la partizione /boot in chiaro, anche  l&#39;header del disco cifrato.&#xA;GRUB dovrà solo sapere dove si trovi il boot, il resto dell&#39;avvio viene affidato come di consueto al kernel.&#xA;Scenario 2&#xA;Rimanendo nell&#39;ambito dell&#39;esplorazione di device mapper e della cifratura di dispositivi esterni non avviabili, immaginiamo qualcosa di più estremo.&#xA;&#xA;Supponiamo di dover custodire un segreto in qualcosa che non sia un semplice vault cifrato.&#xA;&#xA;Per diminuire il rischio di compromettere un unico vault, decido di dividerlo in varie parti, come gli horcrux ma con 0 malignità. Ogni parte sarà cifrata con una sua chiave che affiderò ad una persona diversa, di mia fiducia. Solo io, titolare ultimo del segreto, avrò accesso ai dati e solo la mia chiave (come l&#39;Anello che li domina tutti) aprirà il vault.&#xA;&#xA;Supponiamo di avere 3 dispositivi fisici (che nel laboratoro saranno simulati da loop device come al solito) per altrettanti &#34;custodi&#34;, ognuno dei quali verrà cifrato con la chiave e con dei parametri da consegnare al &#34;custode&#34; specifico.&#xA;&#xA;I 3 dispositivi cifrati costituiranno un gruppo di volumi con un volume logico (dai requisiti posti non c&#39;è necessità di sfruttare la flessibilità di partizionamento dei volumi logici) che verrà cifrato con la mia chiave master.&#xA;&#xA;luks-lvm-luks&#xA;&#x9;&#xA;Ogni dispositivo, volume logico finale compreso, prima della cifratura, verrà inizializzato con del rumore casuale. Questo per impedire ad un&#39;analisi forense di risalire ad un qualunque pattern sul dispositivo raw.&#xA;&#xA;Caratteristiche di ogni cifratura:&#xA;&#xA;dispositivi inizializzati con rumore casuale&#xA;header detachable&#xA;offset custom&#xA;key-file&#xA;default di argon2id (controlla il tuo default con cryptsetup benchmark)&#xA;&#xA;Quando vorrò aprire il vault, sarà necessario che i 3 &#34;custodi&#34; aprano il loro &#34;pezzo&#34; e solo io potrò ricostruire e decifrare il volume con la mia chiave.&#xA;&#xA;L&#39;inzializzazione con rumore casuale dei dispositivo, tralasciando l&#39;uso di dd su /dev/urandom che sappiamo essere CPU-intensive, può essere fatta ricorrendo:&#xA;&#xA;ad openssl rand, come sappiamo da &#34;Come generare una password o un keyfile sicuri (Trilogia Della Password – 1 di 3&#34;&#xA;oppure usando furbescamente cryptsetup, con cui apriamo il dispositivo in modalità plain, senza header, e riempiendolo di zeri (da /dev/zero con dd) che, attraversando il motore di cifratura, verranno scritti sul dispositivo come dati casuali ad alta velocità.&#xA;&#xA;################################&#xA;Emulazione dei device fisici &#xA;################################&#xA;fallocate -l 512M cipherdisk1.img&#xA;fallocate -l 512M cipherdisk2.img&#xA;fallocate -l 512M cipherdisk3.img&#xA;&#xA;###########################&#xA;creazione dei 3 keyfile &#xA;###########################&#xA;keyfile del custode n° 1&#xA;dd if=/dev/urandom bs=1024 count=4 | \&#xA;    gpg --yes -o cipherdisk1.key.gpg -c \&#xA;        --s2k-mode 3 \&#xA;        --s2k-count 32505856 \&#xA;        --s2k-cipher-algo aes256 \&#xA;        --s2k-digest-algo sha512 \&#xA;        --force-mdc -&#xA;&#xA;keyfile del custode n° 2&#xA;dd if=/dev/urandom bs=1024 count=4 | \&#xA;    gpg --yes -o cipherdisk2.key.gpg -c \&#xA;        --s2k-mode 3 \&#xA;        --s2k-count 32505856 \&#xA;        --s2k-cipher-algo aes256 \&#xA;        --s2k-digest-algo sha512 \&#xA;        --force-mdc -&#xA;&#xA;keyfile del custode n° 3&#xA;dd if=/dev/urandom bs=1024 count=4 | \&#xA;    gpg --yes -o cipherdisk3.key.gpg -c \&#xA;        --s2k-mode 3 \&#xA;        --s2k-count 32505856 \&#xA;        --s2k-cipher-algo aes256 \&#xA;        --s2k-digest-algo sha512 \&#xA;        --force-mdc -&#xA;&#xA;keyfile master&#xA;dd if=/dev/urandom bs=1024 count=4 | \&#xA;    gpg --yes -o master.key.gpg -c \&#xA;        --s2k-mode 3 \&#xA;        --s2k-count 32505856 \&#xA;        --s2k-cipher-algo aes256 \&#xA;        --s2k-digest-algo sha512 \&#xA;        --force-mdc -&#xA;&#xA;##########################&#xA;cifratura dei 3 device &#xA;##########################&#xA;attach dispositivo&#xA;DEV1=$(losetup -Pf --show cipherdisk1.img)&#xA;&#xA;inizializzazione dev1 con rumore casuale&#xA;cryptsetup open --type plain ${DEV1} container --key-file /dev/urandom&#xA;dd if=/dev/zero of=/dev/mapper/container status=progress&#xA;cryptsetup close container&#xA;&#xA;cifratura dispositivo n° 1&#xA;gpg -d cipherdisk1.key.gpg | \&#xA;    cryptsetup luksFormat  \&#xA;        --type luks2 \&#xA;        --key-file - \&#xA;        --header header1.img \&#xA;        --offset 32768 \&#xA;        --hash sha512 \&#xA;        --key-size 512 \&#xA;        --cipher aes-xts-plain64 \&#xA;        ${DEV1}&#xA;&#xA;attach dispositivo&#xA;DEV2=$(losetup -Pf --show cipherdisk2.img)&#xA;&#xA;inizializzazione dev2 con rumore casuale&#xA;cryptsetup open --type plain ${DEV2} container --key-file /dev/urandom&#xA;dd if=/dev/zero of=/dev/mapper/container status=progress&#xA;cryptsetup close container&#xA;&#xA;cifratura dispositivo n° 2&#xA;gpg -d cipherdisk2.key.gpg | \&#xA;    cryptsetup luksFormat  \&#xA;        --type luks2 \&#xA;        --key-file - \&#xA;        --header header2.img \&#xA;        --offset 36864 \&#xA;        --hash sha512 \&#xA;        --key-size 512 \&#xA;        --cipher aes-xts-plain64 \&#xA;        ${DEV2}&#xA;&#xA;attach dispositivo&#xA;DEV3=$(losetup -Pf --show cipherdisk3.img)&#xA;&#xA;inizializzazione dev3 con rumore casuale&#xA;cryptsetup open --type plain ${DEV3} container --key-file /dev/urandom&#xA;dd if=/dev/zero of=/dev/mapper/container status=progress&#xA;cryptsetup close container&#xA;&#xA;cifratura dispositivo n° 3&#xA;gpg -d cipherdisk3.key.gpg | \&#xA;    cryptsetup luksFormat  \&#xA;        --type luks2 \&#xA;        --key-file - \&#xA;        --header header3.img \&#xA;        --offset 40960 \&#xA;        --hash sha512 \&#xA;        --key-size 512 \&#xA;        --cipher aes-xts-plain64 \&#xA;        ${DEV3}&#xA;&#xA;##############################&#xA;Creazione gruppo di volumi &#xA;##############################&#xA;&#34;apro&#34; il volume 1&#xA;gpg -d cipherdisk1.key.gpg | \&#xA;    cryptsetup open \&#xA;         --type luks2 \&#xA;         --header header1.img \&#xA;         --key-file - \&#xA;         ${DEV1} cipherdisk1&#xA;&#xA;&#34;apro&#34; il volume 2&#xA;gpg -d cipherdisk2.key.gpg | \&#xA;    cryptsetup open \&#xA;         --type luks2 \&#xA;         --header header2.img \&#xA;         --key-file - \&#xA;         ${DEV2} cipherdisk2&#xA;&#xA;&#34;apro&#34; il volume 3&#xA;gpg -d cipherdisk3.key.gpg | \&#xA;    cryptsetup open \&#xA;         --type luks2 \&#xA;         --header header3.img \&#xA;         --key-file - \&#xA;         ${DEV3} cipherdisk3&#xA;&#xA;Creo il mio gruppo di volumi con i 3 volumi &#34;fisici&#34;:&#xA;1. /dev/mapper/cipherdisk1&#xA;2. /dev/mapper/cipherdisk2&#xA;3. /dev/mapper/cipherdisk3&#xA;vgcreate vgmaster /dev/mapper/cipherdisk1  /dev/mapper/cipherdisk2  /dev/mapper/cipherdisk3&#xA;&#xA;creazione dell&#39;unico volume logico&#xA;lvcreate -n lvmaster vgmaster -l 100%FREE&#xA;&#xA;################################&#xA;cifratura e mount del master &#xA;################################&#xA;cifratura dispositivo master&#xA;dd if=/dev/urandom of=/dev/mapper/vgmaster-lvmaster bs=1M count=32 status=progress&#xA;gpg -d master.key.gpg | \&#xA;    cryptsetup luksFormat  \&#xA;        --type luks2 \&#xA;        --key-file - \&#xA;        --header headermaster.img \&#xA;&#x9;&#x9;--offset 65536 \&#xA;        --hash sha512 \&#xA;        --key-size 512 \&#xA;        --cipher aes-xts-plain64 \&#xA;        /dev/mapper/vgmaster-lvmaster&#xA;&#xA;apriamo il dispositivo master&#xA;gpg -d master.key.gpg | \&#xA;    cryptsetup open \&#xA;         --type luks2 \&#xA;         --header headermaster.img \&#xA;         --key-file - \&#xA;         /dev/mapper/vgmaster-lvmaster cipherdiskmaster&#xA;&#xA;e finalmente lo formattiamo&#xA;mkfs.ext4 /dev/mapper/cipherdiskmaster&#xA;&#xA;Test: montiamo il disco&#xA;mkdir -p /run/media/master/diskmaster&#xA;chown -R ${USER}:${USER} /run/media/master/diskmaster&#xA;mount -t auto /dev/mapper/cipherdiskmaster /run/media/master/diskmaster&#xA;&#xA;Infine chiudiamo tutto&#xA;umount /run/media/master/diskmaster&#xA;cryptsetup close cipherdiskmaster&#xA;vgchange -an vgmaster&#xA;cryptsetup close cipherdisk1&#xA;cryptsetup close cipherdisk2&#xA;cryptsetup close cipherdisk3&#xA;losetup -d ${DEV1} ${DEV2} ${DEV3}&#xA;Dopo aver appurato che tutto funzioni, consegno ad ogni &#34;custode&#34; dispositivo, keyfile e header.&#xA;&#xA;Se un attaccante dovesse entrare in possesso di uno o più dispositivi, troverebbe solo un mucchio di dati incomprensibili.&#xA;&#xA;Posto che riuscisse a decifrare il dispositivo, troverebbe un pezzo di un gruppo di volumi, cifrato e inutilizzabile.&#xA;&#xA;Il master a questo punto non dovrà fare altro che aprire e chiudere il vault, dopo aver riunito tutti i pezzi, come segue:&#xA;&#xA;Apertura del vault&#xA;Attach dei dispositivi&#xA;DEV1=$(losetup -Pf --show cipherdisk1.img)&#xA;DEV2=$(losetup -Pf --show cipherdisk2.img)&#xA;DEV3=$(losetup -Pf --show cipherdisk3.img)&#xA;&#xA;&#34;apro&#34; il volume 1&#xA;gpg -d cipherdisk1.key.gpg | \&#xA;    cryptsetup open \&#xA;         --type luks2 \&#xA;         --header header1.img \&#xA;         --key-file - \&#xA;         ${DEV1} cipherdisk1&#xA;&#xA;&#34;apro&#34; il volume 2&#xA;gpg -d cipherdisk2.key.gpg | \&#xA;    cryptsetup open \&#xA;         --type luks2 \&#xA;         --header header2.img \&#xA;         --key-file - \&#xA;         ${DEV2} cipherdisk2&#xA;&#xA;&#34;apro&#34; il volume 3&#xA;gpg -d cipherdisk3.key.gpg | \&#xA;    cryptsetup open \&#xA;         --type luks2 \&#xA;         --header header3.img \&#xA;         --key-file - \&#xA;         ${DEV3} cipherdisk3&#xA;&#xA;(facoltativo) apre il gruppo di volumi&#xA;vgchange -ay vgmaster&#xA;&#xA;&#34;apre&#34; il master volume&#xA;gpg -d master.key.gpg | \&#xA;    cryptsetup open \&#xA;         --type luks2 \&#xA;         --header headermaster.img \&#xA;         --key-file - \&#xA;         /dev/mapper/vgmaster-lvmaster cipherdiskmaster&#xA;&#xA;Monta il volume&#xA;mount -t auto /dev/mapper/cipherdiskmaster /run/media/master/diskmaster&#xA;Chiusura del vault&#xA;smonta il volume cifrato&#xA;umount /run/media/master/diskmaster&#xA;&#xA;chiusura del vault master&#xA;cryptsetup close cipherdiskmaster&#xA;&#xA;chiusura del gruppo di volumi&#xA;vgchange -an vgmaster&#xA;&#xA;chiusura dei singoli vault cifrati&#xA;cryptsetup close cipherdisk1&#xA;cryptsetup close cipherdisk2&#xA;cryptsetup close cipherdisk3&#xA;&#xA;deattach dei dispositivi&#xA;losetup -d ${DEV1} ${DEV2} ${DEV3}&#xA;&#xA;#cryptsetup #devicemapper #dmcrypt #gpg #loseup #luks #lvm #loopdevice #storage]]&gt;</description>
      <content:encoded><![CDATA[<p><img src="https://pixelfed.uno/storage/m/_v2/489827599091373610/fbedcc803-0ec729/JqVu1t3agiao/CSE9exMSdTVlv9adGCHXwSbkxvn9EPD0SPJNIsQo.jpg" alt="luks-lvm">
<em>(segue da <a href="https://noblogo.org/aytin/cenni-sulla-creazione-di-pool-di-storage-con-lvm/" rel="nofollow">“<strong>Cenni sulla creazione di pool di storage con LVM</strong>“</a>)</em>
...anche se è la base per una serie di sviluppi interessanti.</p>

<p>Come ormai sappiamo, <strong>device mapper</strong> è il framework del kernel Linux col quale mappare dispositivi a blocchi fisici su dispositivi a blocchi logici, che costituisce la base per fornire funzionalità ulteriori quali:
</p>
<ul><li>volumi logici</li>
<li>raid</li>
<li>cifratura (<strong>F</strong>ull <strong>D</strong>isk <strong>E</strong>ncryption)</li>
<li>snapshot di volumi</li></ul>

<h2 id="scenario-1">Scenario 1</h2>

<p>È molto comune per es. cifrare l&#39;intero disco e “affettarlo” con volumi logici in base alle proprie esigenze di partizionamento .</p>

<p>Il doppio vantaggio è dato da:</p>
<ol><li>offuscamento totale dello schema di partizionamento</li>
<li>estrema versatilità / flessibilità del partizionamento grazie ai volumi logici</li></ol>

<p>Come si agisce?</p>
<ol><li>si cifra il dispositivo fisico</li>
<li>si crea un gruppo di volumi avente come volume fisico il volume cifrato</li>
<li>si creano i volumi logici in base allo <strong>schema di partizionamento</strong> desiderato.</li></ol>

<p><img src="https://pixelfed.uno/storage/m/_v2/489827599091373610/fbedcc803-0ec729/e9tSS2DIxi6F/Bc8zWpFAR2KxQSK92F6I3Z6ocbA5E67bT3ziz1Ke.jpg" alt="luks-lvm"></p>

<h3 id="passo-1-inizializzazione">Passo 1 – Inizializzazione</h3>

<p>Simulo il mio dispositivo fisico ricorrendo ai <strong>loop device</strong>.</p>

<p>Il “disco” avrà una grandezza simbolica di 2 GiB, non avrà header detachable. L&#39;algoritmo di hash sarà sha512 e la chiave sarà da 512 bit. Il resto è il default di luks2 (argon2id come pbkdf, per maggiori dettagli vedi <code>cryptsetup --help</code>)</p>

<pre><code class="language-bash"># 1. Preparazione disco &#34;fisico&#34;
fallocate -l 2g cipher_disk.img

# 2. loop device che simula l&#39;attach del dispositivo
DEV=$(losetup -Pf --show cipher_disk.img)

# 3. Inizializzazione cifratura
cryptsetup luksFormat  \
    --type luks2 \
    --hash sha512 \
    --key-size 512 \
    $DEV
</code></pre>

<h3 id="passo-2">Passo 2</h3>

<p>Il passo successivo consiste nell&#39;apertura del dispositivo cifrato e nella definizione dello schema di partizionamento in volumi logici</p>

<pre><code class="language-bash"># 1. apertura del disco cifrato
cryptsetup open \
    --type luks2 \
    $DEV cipher_disk
</code></pre>

<p>Nell&#39;apertura, device mapper fa la sua prima magia.</p>

<p>Infatti, se osserviamo lo stato dei dispositivi, vedremo una situazione simile:</p>

<pre><code>lsblk
...
loop9                7:9    Kib0     2G  0 loop  
└─cipher_disk      252:3    0     2G  0 crypt 
...
</code></pre>

<p>Cio vuol dire che sopra il dispositivo fisico, <code>/dev/loop9</code>in questo caso, device mapper ha “poggiato” <strong>cipher_disk</strong> (<code>/dev/mapper/cipher_disk</code>).</p>

<p>Questo sarà il nostro volume fisico per definire gruppi di volume e, conseguentemente, i volumi logici.</p>

<pre><code class="language-bash"># 2. creazione gruppo di volumi
vgcreate vg_lab /dev/mapper/cipher_disk

# 3. creazione volumi logici
lvcreate -n lv_lab_1 vg_lab -L 700M
lvcreate -n lv_lab_2 vg_lab -L 600M
lvcreate -n lv_lab_3 vg_lab -l 100%FREE
</code></pre>

<p>La situazione dei dispositivi è ora questa:</p>

<pre><code>lsblk
...
loop9                7:9    0     2G  0 loop  
└─cipher_disk      252:3    0     2G  0 crypt
  ├─vg_lab-lv_lab_1 252:4    0   700M  0 lvm
  ├─vg_lab-lv_lab_2 252:5    0   600M  0 lvm
  └─vg_lab-lv_lab_3 252:6    0   728M  0 lvm
...
</code></pre>

<p>Sopra <code>/dev/loop9</code> c&#39;è <code>cipher_disk</code> (<code>/dev/mapper/cipher_disk</code>) e sopra di esso, i 3 volumi logici</p>
<ul><li><code>lv_lab_1</code> (<code>/dev/mapper/vg_lab-lv_lab_1</code>)</li>
<li><code>lv_lab_2</code> (<code>/dev/mapper/vg_lab-lv_lab_2</code>)</li>
<li><code>lv_lab_3</code> (<code>/dev/mapper/vg_lab-lv_lab_3</code>)</li></ul>

<p>Formatto e monto i volumi logici</p>

<pre><code class="language-bash"># 4. formattazione dei 3 volumi logici
mkfs.ext4 /dev/mapper/vg_lab-lv_lab_1
mkfs.ext4 /dev/mapper/vg_lab-lv_lab_2
mkfs.ext4 /dev/mapper/vg_lab-lv_lab_3

# 5. creo e preparo i punti di mmontaggio
mkdir disk_1 disk_2 disk_3
mount -t ext4 -o user,noauto,rw  /dev/mapper/vg_lab-lv_lab_1 disk_1
mount -t ext4 -o user,noauto,rw  /dev/mapper/vg_lab-lv_lab_2 disk_2
mount -t ext4 -o user,noauto,rw  /dev/mapper/vg_lab-lv_lab_3 disk_3
chown $USER disk_1 disk_2 disk_3

# 6. unmount di tutti i dispositivi
umount disk_1 disk_2 disk_3
vgchange -an vg_lab
cryptsetup close cipher_disk
losetup -d $DEV
</code></pre>

<h3 id="passo-3">Passo 3</h3>

<p>Infine, per completezza, gli script di mount e unmount del dispositivo.
<strong>mount</strong></p>

<pre><code class="language-bash"># 1. attach del dispositivo
DEV=$(losetup -Pf --show cipher_disk.img)

# 2. Apre il disco cifrato 
cryptsetup open \
    --type luks2 \
    $DEV cipher_disk

# 3. monta il gruppo di volume
# (opzionale. L&#39;apertura del disco cifrato dovrebbe montare 
# automaticamente il gruppo di volumi)
vgchange -ay vg_lab

# 4. monta i volumi logici
mount -t ext4 -o user,noauto,rw  /dev/mapper/vg_lab-lv_lab_1 disk_1
mount -t ext4 -o user,noauto,rw  /dev/mapper/vg_lab-lv_lab_2 disk_2
mount -t ext4 -o user,noauto,rw  /dev/mapper/vg_lab-lv_lab_3 disk_3
</code></pre>

<p><strong>unmount</strong></p>

<pre><code class="language-bash"># 1. smonta i 3 dischi
umount disk_1 disk_2 disk_3

# 2. smonta il gruppo di volumi
vgchange -an vg_lab

# 3. chiude il disco cifrato
cryptsetup close cipher_disk

# 4. &#34;stacca&#34; il dispositivo fisico
losetup -d $DEV
</code></pre>

<p>Questo è ciò che si farebbe normalmente quando si vuole il full disk encryption sul proprio pc. Ma con una piccola eccezione.</p>

<p>In realtà ciò che viene cifrata è una partizione quasi completa del disco, perché almeno una piccola partizione, quella contenente il boot, <code>/BOOT</code>, deve essere in chiaro per consentire:</p>
<ul><li>a UEFI di avviare GRUB che conosce le partizioni,</li>
<li>GRUB provvederà all&#39;avvio del kernel,</li>
<li>l&#39;avvio del kernel con initramfs chiederà la password per sbloccare la partizione cifrata.</li></ul>

<p>La cifratura totale (che proprio totale non sarà perché <code>/boot/efi</code> deve rimanere in chiaro) eleva di molto la complessità del setup iniziale.</p>

<p>Affidare a GRUB la gestione della cifratura potrebbe voler dire, oltre alla complessità della configurazione iniziale che dovrà far ricorso a moduli come <code>cryptodisk</code>, che bisognerà rinunciare ad Argon2 perché GRUB ancora non lo supporta pienamente e, a differenza del kernel, non ha sufficiente potenza per farlo lavorare come si deve.</p>

<p>Un buon compromesso potrebbe essere il ricorso ad un dispositivo esterno, es. una pendrive, che contenga tutta la partizione <code>/boot</code> in chiaro, anche  l&#39;header del disco cifrato.
GRUB dovrà solo sapere dove si trovi il boot, il resto dell&#39;avvio viene affidato come di consueto al kernel.</p>

<h2 id="scenario-2">Scenario 2</h2>

<p>Rimanendo nell&#39;ambito dell&#39;esplorazione di device mapper e della cifratura di dispositivi esterni non avviabili, immaginiamo qualcosa di più estremo.</p>

<p>Supponiamo di dover custodire un segreto in qualcosa che non sia un semplice vault cifrato.</p>

<p>Per diminuire il rischio di compromettere un unico vault, decido di dividerlo in varie parti, come gli horcrux ma con 0 malignità. Ogni parte sarà cifrata con una sua chiave che affiderò ad una persona diversa, di mia fiducia. Solo io, titolare ultimo del segreto, avrò accesso ai dati e solo la mia chiave (come l&#39;Anello che li domina tutti) aprirà il vault.</p>

<p>Supponiamo di avere 3 dispositivi fisici (che nel laboratoro saranno simulati da loop device come al solito) per altrettanti “custodi”, ognuno dei quali verrà cifrato con la chiave e con dei parametri da consegnare al “custode” specifico.</p>

<p>I 3 dispositivi cifrati costituiranno un gruppo di volumi con un volume logico (dai requisiti posti non c&#39;è necessità di sfruttare la flessibilità di partizionamento dei volumi logici) che verrà cifrato con la mia chiave master.</p>

<p><img src="https://pixelfed.uno/storage/m/_v2/489827599091373610/fbedcc803-0ec729/pnRafhfUlqts/Wl6xNlvrbnZJIjRVRbAIM0fmo6KZAnYoy1Wga0C2.jpg" alt="luks-lvm-luks"></p>

<p>Ogni dispositivo, volume logico finale compreso, prima della cifratura, verrà inizializzato con del rumore casuale. Questo per impedire ad un&#39;analisi forense di risalire ad un qualunque pattern sul dispositivo raw.</p>

<p>Caratteristiche di ogni cifratura:</p>
<ul><li>dispositivi inizializzati con rumore casuale</li>
<li>header detachable</li>
<li>offset custom</li>
<li>key-file</li>
<li>default di argon2id (controlla il tuo default con <code>cryptsetup benchmark</code>)</li></ul>

<p>Quando vorrò aprire il vault, sarà necessario che i 3 “custodi” aprano il loro “pezzo” e solo io potrò ricostruire e decifrare il volume con la mia chiave.</p>

<p>L&#39;inzializzazione con rumore casuale dei dispositivo, tralasciando l&#39;uso di <code>dd</code> su <code>/dev/urandom</code> che sappiamo essere CPU-intensive, può essere fatta ricorrendo:</p>
<ul><li>ad <code>openssl rand</code>, come sappiamo da <em><a href="https://noblogo.org/aytin/come-generare-una-password-o-un-keyfile-sicuri-trilogia-della-password-1-di/" rel="nofollow">“<strong>Come generare una password o un keyfile sicuri (Trilogia Della Password – 1 di 3</strong>“</a></em></li>
<li>oppure usando furbescamente <strong>cryptsetup</strong>, con cui apriamo il dispositivo in modalità plain, senza header, e riempiendolo di zeri (da <code>/dev/zero</code> con <code>dd</code>) che, attraversando il motore di cifratura, verranno scritti sul dispositivo come dati casuali ad alta velocità.</li></ul>

<pre><code class="language-bash">################################
# Emulazione dei device fisici #
################################
fallocate -l 512M cipher_disk_1.img
fallocate -l 512M cipher_disk_2.img
fallocate -l 512M cipher_disk_3.img



###########################
# creazione dei 3 keyfile #
###########################
# keyfile del custode n° 1
dd if=/dev/urandom bs=1024 count=4 | \
    gpg --yes -o cipher_disk_1.key.gpg -c \
        --s2k-mode 3 \
        --s2k-count 32505856 \
        --s2k-cipher-algo aes256 \
        --s2k-digest-algo sha512 \
        --force-mdc -

# keyfile del custode n° 2
dd if=/dev/urandom bs=1024 count=4 | \
    gpg --yes -o cipher_disk_2.key.gpg -c \
        --s2k-mode 3 \
        --s2k-count 32505856 \
        --s2k-cipher-algo aes256 \
        --s2k-digest-algo sha512 \
        --force-mdc -

# keyfile del custode n° 3
dd if=/dev/urandom bs=1024 count=4 | \
    gpg --yes -o cipher_disk_3.key.gpg -c \
        --s2k-mode 3 \
        --s2k-count 32505856 \
        --s2k-cipher-algo aes256 \
        --s2k-digest-algo sha512 \
        --force-mdc -

# keyfile master
dd if=/dev/urandom bs=1024 count=4 | \
    gpg --yes -o master.key.gpg -c \
        --s2k-mode 3 \
        --s2k-count 32505856 \
        --s2k-cipher-algo aes256 \
        --s2k-digest-algo sha512 \
        --force-mdc -



##########################
# cifratura dei 3 device #
##########################
# attach dispositivo
DEV_1=$(losetup -Pf --show cipher_disk_1.img)

# inizializzazione dev_1 con rumore casuale
cryptsetup open --type plain ${DEV_1} container --key-file /dev/urandom
dd if=/dev/zero of=/dev/mapper/container status=progress
cryptsetup close container

# cifratura dispositivo n° 1
gpg -d cipher_disk_1.key.gpg | \
    cryptsetup luksFormat  \
        --type luks2 \
        --key-file - \
        --header header_1.img \
        --offset 32768 \
        --hash sha512 \
        --key-size 512 \
        --cipher aes-xts-plain64 \
        ${DEV_1}

# attach dispositivo
DEV_2=$(losetup -Pf --show cipher_disk_2.img)

# inizializzazione dev_2 con rumore casuale
cryptsetup open --type plain ${DEV_2} container --key-file /dev/urandom
dd if=/dev/zero of=/dev/mapper/container status=progress
cryptsetup close container

# cifratura dispositivo n° 2
gpg -d cipher_disk_2.key.gpg | \
    cryptsetup luksFormat  \
        --type luks2 \
        --key-file - \
        --header header_2.img \
        --offset 36864 \
        --hash sha512 \
        --key-size 512 \
        --cipher aes-xts-plain64 \
        ${DEV_2}

# attach dispositivo
DEV_3=$(losetup -Pf --show cipher_disk_3.img)

# inizializzazione dev_3 con rumore casuale
cryptsetup open --type plain ${DEV_3} container --key-file /dev/urandom
dd if=/dev/zero of=/dev/mapper/container status=progress
cryptsetup close container

# cifratura dispositivo n° 3
gpg -d cipher_disk_3.key.gpg | \
    cryptsetup luksFormat  \
        --type luks2 \
        --key-file - \
        --header header_3.img \
        --offset 40960 \
        --hash sha512 \
        --key-size 512 \
        --cipher aes-xts-plain64 \
        ${DEV_3}



##############################
# Creazione gruppo di volumi #
##############################
# &#34;apro&#34; il volume 1
gpg -d cipher_disk_1.key.gpg | \
    cryptsetup open \
         --type luks2 \
         --header header_1.img \
         --key-file - \
         ${DEV_1} cipher_disk_1

# &#34;apro&#34; il volume 2
gpg -d cipher_disk_2.key.gpg | \
    cryptsetup open \
         --type luks2 \
         --header header_2.img \
         --key-file - \
         ${DEV_2} cipher_disk_2

# &#34;apro&#34; il volume 3
gpg -d cipher_disk_3.key.gpg | \
    cryptsetup open \
         --type luks2 \
         --header header_3.img \
         --key-file - \
         ${DEV_3} cipher_disk_3

# Creo il mio gruppo di volumi con i 3 volumi &#34;fisici&#34;:
# 1. /dev/mapper/cipher_disk_1
# 2. /dev/mapper/cipher_disk_2
# 3. /dev/mapper/cipher_disk_3
vgcreate vg_master /dev/mapper/cipher_disk_1  /dev/mapper/cipher_disk_2  /dev/mapper/cipher_disk_3

# creazione dell&#39;unico volume logico
lvcreate -n lv_master vg_master -l 100%FREE



################################
# cifratura e mount del master #
################################
# cifratura dispositivo master
dd if=/dev/urandom of=/dev/mapper/vg_master-lv_master bs=1M count=32 status=progress
gpg -d master.key.gpg | \
    cryptsetup luksFormat  \
        --type luks2 \
        --key-file - \
        --header header_master.img \
		--offset 65536 \
        --hash sha512 \
        --key-size 512 \
        --cipher aes-xts-plain64 \
        /dev/mapper/vg_master-lv_master

# apriamo il dispositivo master
gpg -d master.key.gpg | \
    cryptsetup open \
         --type luks2 \
         --header header_master.img \
         --key-file - \
         /dev/mapper/vg_master-lv_master cipher_disk_master

# e finalmente lo formattiamo
mkfs.ext4 /dev/mapper/cipher_disk_master

# Test: montiamo il disco
mkdir -p /run/media/master/disk_master
chown -R ${USER}:${USER} /run/media/master/disk_master
mount -t auto /dev/mapper/cipher_disk_master /run/media/master/disk_master

# Infine chiudiamo tutto
umount /run/media/master/disk_master
cryptsetup close cipher_disk_master
vgchange -an vg_master
cryptsetup close cipher_disk_1
cryptsetup close cipher_disk_2
cryptsetup close cipher_disk_3
losetup -d ${DEV_1} ${DEV_2} ${DEV_3}
</code></pre>

<p>Dopo aver appurato che tutto funzioni, consegno ad ogni “custode” dispositivo, keyfile e header.</p>

<p>Se un attaccante dovesse entrare in possesso di uno o più dispositivi, troverebbe solo un mucchio di dati incomprensibili.</p>

<p>Posto che riuscisse a decifrare il dispositivo, troverebbe un pezzo di un gruppo di volumi, cifrato e inutilizzabile.</p>

<p>Il master a questo punto non dovrà fare altro che aprire e chiudere il vault, dopo aver riunito tutti i pezzi, come segue:</p>

<p><strong>Apertura del vault</strong></p>

<pre><code class="language-bash"># Attach dei dispositivi
DEV_1=$(losetup -Pf --show cipher_disk_1.img)
DEV_2=$(losetup -Pf --show cipher_disk_2.img)
DEV_3=$(losetup -Pf --show cipher_disk_3.img)

# &#34;apro&#34; il volume 1
gpg -d cipher_disk_1.key.gpg | \
    cryptsetup open \
         --type luks2 \
         --header header_1.img \
         --key-file - \
         ${DEV_1} cipher_disk_1

# &#34;apro&#34; il volume 2
gpg -d cipher_disk_2.key.gpg | \
    cryptsetup open \
         --type luks2 \
         --header header_2.img \
         --key-file - \
         ${DEV_2} cipher_disk_2

# &#34;apro&#34; il volume 3
gpg -d cipher_disk_3.key.gpg | \
    cryptsetup open \
         --type luks2 \
         --header header_3.img \
         --key-file - \
         ${DEV_3} cipher_disk_3

# (facoltativo) apre il gruppo di volumi
vgchange -ay vg_master

# &#34;apre&#34; il master volume
gpg -d master.key.gpg | \
    cryptsetup open \
         --type luks2 \
         --header header_master.img \
         --key-file - \
         /dev/mapper/vg_master-lv_master cipher_disk_master

# Monta il volume
mount -t auto /dev/mapper/cipher_disk_master /run/media/master/disk_master
</code></pre>

<p><strong>Chiusura del vault</strong></p>

<pre><code class="language-bash"># smonta il volume cifrato
umount /run/media/master/disk_master

# chiusura del vault master
cryptsetup close cipher_disk_master

# chiusura del gruppo di volumi
vgchange -an vg_master

# chiusura dei singoli vault cifrati
cryptsetup close cipher_disk_1
cryptsetup close cipher_disk_2
cryptsetup close cipher_disk_3

# deattach dei dispositivi
losetup -d ${DEV_1} ${DEV_2} ${DEV_3}
</code></pre>

<p><a href="/aytin/tag:cryptsetup" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">cryptsetup</span></a> <a href="/aytin/tag:devicemapper" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devicemapper</span></a> <a href="/aytin/tag:dmcrypt" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">dmcrypt</span></a> <a href="/aytin/tag:gpg" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">gpg</span></a> <a href="/aytin/tag:loseup" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">loseup</span></a> <a href="/aytin/tag:luks" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">luks</span></a> <a href="/aytin/tag:lvm" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">lvm</span></a> <a href="/aytin/tag:loopdevice" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">loopdevice</span></a> <a href="/aytin/tag:storage" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">storage</span></a></p>
]]></content:encoded>
      <guid>https://noblogo.org/aytin/device-mapper-luks-lvm</guid>
      <pubDate>Mon, 23 Feb 2026 10:58:17 +0000</pubDate>
    </item>
    <item>
      <title>Archiviare le password in sicurezza con KDF/password-hashing (Trilogia Della Password - 3 di 3)</title>
      <link>https://noblogo.org/aytin/archiviare-le-password-in-sicurezza-con-kdf-password-hashing-trilogia-della</link>
      <description>&lt;![CDATA[password hashing&#xA;&#xA;Dopo aver capito come creare password inviolabili anche avendo a disposizione tutta l&#39;energia termica dell&#39;universo, pensiamo al modo migliore per archiviarle.&#xA;&#xA;Pensare di lasciare la password raw in un database, rappresenta un grosso rischio in virtù di un possibile attacco offline.&#xA;!--more--&#xA;&#xA;Rainbow Table&#xA;Come ti blocco la Rainbow Table: Il “Salt” (sale)&#xA;Il livello successivo: Il Pepper&#xA;Final step: L’hashing&#xA;  PBKDF2&#xA;  Bcrypt&#xA;  Scrypt&#xA;  yescrypt&#xA;  Argon2&#xA;Un piccolo esempio: hashing di una password con Argon2&#xA;Gestione del pepper&#xA;HSM&#xA;Key / Algo Rotation&#xA;  Caso A: Algo rotation&#xA;  Caso B: Pepper rotation&#xA;&#xA;Una prima linea di difesa consiste nel memorizzare il digest della password, una stringa alfanumerica univoca generata da un apposito algoritmo, così da lasciare nelle mani dell&#39;attaccante degli oggetti che, per la loro non invertibilità, non permettono di risalire alle password.&#xA;&#xA;La scelta dell&#39;algoritmo di hashing diventa critica al fine di scongiurare altri tipi di attacchi. Ad es. SHA-256, pur essendo ottimo e molto efficiente per il digest e la firma anche di file di grandi dimensioni, mostra il fianco, proprio in virtù della sua velocità, nel caso di attacchi con:&#xA;&#xA;brute-force: si calcola l’hash di password casuali fino a trovare una corrispondenza,&#xA;dizionario: un&#39;alternativa intelligente alla forza bruta. Si punta alle password più comuni, si calcola l&#39;hash e si controlla se c&#39;è corrispondenza.&#xA;Rainbow Table: l&#39;attacco al dizionario più insidioso di tutti&#xA;&#xA;Oltre al fatto che, la funzione di hashing , essendo deterministica, permette di capire chi sono gli utenti che hanno la stessa password, dal momento che avranno lo stesso digest.&#xA;Rainbow Table&#xA;Una Rainbow Table è un enorme dizionario pre-calcolato che contiene:&#xA;&#xA;Milioni di password comuni.&#xA;Il relativo hash corrispondente.&#xA;&#xA;Invece di calcolare l’entropia di ogni tentativo, l’attaccante ruba il database degli hash e fa un semplice “Cerca e Trova”. Se l’hash della tua password è nella tabella, la tua password è violata in millisecondi, indipendentemente da quanto fosse alta la sua entropia teorica.&#xA;&#xA;smallOss.: Una password totalmente casuale, generata da un CSPRNG affidabile, con un&#39;alta entropia (  120), rimarrebbe comunque inviolabile anche dalla rainbow table perché la probabilità che quella password si trovi nel dizionario, sarebbe equivalente ad indovinarla./small&#xA;Come ti blocco la Rainbow Table: Il “Salt” (sale)&#xA;Per rendere inutili le Rainbow Table, i sistemi sicuri utilizzano il Salt. Il salt è una stringa di dati casuali (generati da una sorgente d’entropia affidabile ovviamente) che viene aggiunta alla password prima di calcolarne il digest.&#xA;&#xA;In questo modo le rainbow table vengono vanificate perché gli hash precalcolati sulle password raccolte, mancando il salt, non valgono più. Anche se due utenti avessero la stessa password, avrebbero degli hash completamente diversi.&#xA;&#xA;Anche per questo motivo non è un problema che il salt sia pubblico, perché il suo obiettivo non è nascondere quello che è un pezzetto di password a tutti gli effetti, ma di impedire economie di scala degli attacchi perché, pur potendo disporre offline di un database di decine di milioni di utenti, gli hash della mia Rainbow Table (che può arrivare a pesare anche decine di GB) andrebbero tutti ricalcolati per ogni utente, con un costo computazionale e di archiviazione inimmaginabile.&#xA;&#xA;Per riassumere, gli ingredienti di base sono:&#xA;&#xA;una buona sorgente d’entropia: una fonte di casualità certificata per generare un salt unico;&#xA;entropia della password: sempre buona norma, ove possibile, come sappiamo ormai fare (https://noblogo.org/aytin/come-generare-una-password-o-un-keyfile-sicuri-trilogia-della-password-1-di). Evita attacchi brute-force o al dizionario in cui l’attaccante prova a indovinare;&#xA;salt: protegge la password dagli attacchi basati su database pre-calcolati (Rainbow Table).&#xA;&#xA;Il livello successivo: Il Pepper&#xA;È vero che col salt andiamo a complicare lo sfruttamento di un attacco offline ma possiamo fare di meglio.&#xA;&#xA;Il punto d&#39;attenzione è che il salt protegge le password di tutti gli utenti.&#xA;Ma un attaccante potrebbe non essere affatto interessato a violare ogni singolo utente (niente economia di scala) ma solo alcuni. E allora l&#39;attacco attraverso Ranbow Table potrebbe essere di nuovo praticabile.&#xA;&#xA;Ma gli informatici sono dei gran giocherelloni, si sa.&#xA;Visto che abbiamo già il &#34;sale&#34;, perché non finire aggiustando con un po’ di “pepe”? Detto, fatto!&#xA;&#xA;Il pepper, come il salt, è un&#39;altra password generata con gli stessi criteri del salt ma le analogie finiscono qua perché:&#xA;&#xA;a differenza del salt che si trova nel database, il pepper è separato da ques&#39;ultimo. L&#39;ideale sarebbe un HSM;&#xA;Il salt è visibile a tutti, attaccante compreso. Il pepper è segreto. Un eventuale data breach che permette all&#39;attaccante di disporre offline di tutto il database degli utenti, &#34;vedrà&#34; certamente gli eventuali salt ma sarà ignaro del fatto che gli mancherà sempre un pezzo di chiave;&#xA;il salt è diverso per ogni utente, il pepper, di solito, è unico;&#xA;il salt serve a rendere uniche la password degli utenti, il pepper protegge l&#39;intero database da attacchi offline.&#xA;&#xA;Il pepper è la chiave di un HMAC, o di un meccanismo di cifratura simmetrica, applicato al digest della password (che ricordo essere salt+password in realtà), che sarà ciò che verrà archiviato.&#xA;&#xA;Va detto che l&#39;uso del &#34;pepper&#34; complica ulteriormente lo scenario di archiviazione.&#xA;Nella stragrande maggioranza dei casi è sufficiente scegliere un buon algoritmo di password hashing (vedi paragrafo successivo) per scoraggiare gli attaccanti.&#xA;&#34;Pepare&#34; le password prevederebbe, come detto sopra, l&#39;uso di un HSM per es, e tutta una serie di riflessioni di contorno che evidenzierò più avanti.&#xA;Final step: L&#39;hashing&#xA;L&#39;ultimo punto da dettagliare è l&#39;hash della password.&#xA;&#xA;L&#39;hash crittografico, in uso in questi casi, deve soddisfare le seguenti proprietà:&#xA;&#xA;resistenza alla pre-immagine: dato un hash h, deve essere impossibile trovare una password p t.c. H(p) = h (non invertibilità della funzione hash)&#xA;resistenza alla pre-immagine secondaria: dato una password psub1/sub, deve essere impossibile trovare un&#39;altra password psub2/sub t.c. H(psub1/sub) = H(psub2/sub) (resistenza debole alle collisioni)&#xA;resistenza alle collisioni: è impossibile trovare due password diverse, psub1/sub e psub2/sub, t.c. H(psub1/sub) = H(psub2/sub) (resistenza forte alle collisioni)&#xA;effetto valanga: il cambio di un solo bit della password deve cambiare radicalmente l&#39;intero hash&#xA;&#xA;In un sistema moderno, l&#39;hash non può essere delegato a funzioni di tipo SHA perché nascono per altri compiti,&#xA;&#xA;SHA-2 e SHA-3 nascono per il digest veloce, per verificare l&#39;integrità di file anche molto grossi o firmare documenti. La loro eccellente velocità diventa il loro più grosso difetto quando si parla di password.&#xA;Negli scenari precedenti di attachi offline, l&#39;hacker che dispone di una grossa potenza di calcolo, può ricostituire velocemente le rainbow table per n utenti. Magari non di tutti ma di quelli attenzionati.&#xA;&#xA;Le funzioni di derivazione della chiave (KDF) come pbkdf2 e quelle ancora più estreme come B/Scrypt, Argon2, oltre che soddisfare tutti i punti precedentemente elencati tipici di funzioni di password hashing, sono progettate per essere computazionalmente pesantissime da calcolare perché il loro scopo non è il digest ma la protezione di un segreto contro il brute-force.&#xA;E mentre le vecchie KDF come pbdfk2 sono CPU bound, ma non GPU bound, le KDF più moderne come Bcrypt, Scrypt ma soprattutto Argon2, agiscono pesantemente su tempo, memoria e parallelismo e l&#39;attacco offline di cui sopra diventa impraticabile.&#xA;PBKDF2&#xA;È il decano delle KDF. Applica iterativamente una funzione pseudorandomica, come HMAC con uno SHA, con salt alla password.&#xA;Il conteggio delle iterazioni è un parametro configurabile.&#xA;&#xA;PBDKF2 è uno standard di lunga data ampiamente adottato. Se non ci sono necessità stringenti di sicurezza o requisiti legacy, è una buona scelta.&#xA;&#xA;Il fatto di essere solo CPU bound però non la rende la scelta ideale in scenari dove gli attaccanti possono attingere a risorse di calcolo considerevoli&#xA;Bcrypt&#xA;Basato su Blowfish, anche Bcrypt usa un hash crittografico sulla password con parametri il salt e un fattore di costo.&#xA;&#xA;Il fattore di costo aumenta esponenzialmente il numero di iterazioni per adattarsi all&#39;aumento di potenza di calcolo dell&#39;hardware.&#xA;&#xA;Bcrypt è stato progetto per essere lento e resistente a semplici attacchi di forzat bruta.&#xA;Tuttavia, il basso utilizzo di ram richiesto dal calcolo lo rendono poco resistente ad attacchi sferrati usando hardware specializzato.&#xA;&#xA;Bcrypt ha dalla sua una storia solidissima in ragione della quale da 20 anni a questa parte non sono state trovate vulnerabilità critiche nel suo design.&#xA;&#xA;Per questo motivo Bcrypt cifra le password di sistema di OpenBSD dal 1999, come pure ha cifrato quelle di tante distro Linux per anni, prima che passassero ad Argon2 o yescrypt (default di Fedora).&#xA;&#xA;Domina nei framework web (\[Python\] Django, \[Ruby\] Ruby on Rails, \[PHP\] Laravel), \[Java\] Spring, Node.js), nelle applicazioni (Ansible / Terraform, Docker), nel web (la cifratura in .htpasswd di Apache e Nginx) visto che la sua semplcitià di implementazione gli ha permesso di trovarsi praticamente in ogni linguaggio.&#xA;&#xA;È presente come alternativa anche nei password manager benché molti di essi abbiano spostato il default verso Argon2 o PBKDF2 per conformità agli standard FIPS.&#xA;&#xA;È molto semplice implementare e anche da usare perché bisogna agire solo sul fattore di costo (consigliato almeno 10-12, altrimenti diventa troppo vulnerabile ad attacchi sferrati attraverso la GPU)&#xA;Scrypt&#xA;Rilasciato nel 2009, Scrypt è stato il primo algoritmo a introdurre il concetto di Memory Hardness ed è stato progettato per rendere economicamente poco conveniente il ricorso ad hardware specializzato come gli ASIC o i FPGA e incidere pesantemente su CPU, ram e parallelismo.&#xA;&#xA;Il suo alveo principale sono state le cripto-valute, molte monete lo usano per il mining.&#xA;&#xA;Scrypt lo troviamo in quasi tutti i linguaggi di programmazione, in Tarsnap, servizio di baclup online creato dallo stesso autore di Scrypt, è stato usato da LastPass ed è presente come opzione in VeraCrypt per derivare la chiave dalla password.&#xA;Fino ad Android 9 era l&#39;algoritmo usato per la FDE del dispositvio (passato poi al FBE) .&#xA;Presente anche su FreeBSD come opzione per la cifratura delle password di sistema e come opzione su LUKS per la cifratura degli slot delle chiavi.&#xA;&#xA;Su Scrypt i parametri da configurare sono:&#xA;&#xA;Costro CPU/Memoria (N): un parametro che aumenta i costi computazionali di cpu e memoria&#xA;DImensione del blocco (R): influenza la larghezza di banda della memoria&#xA;Parallelizzazione (p): indica quanto deve incidere sul calcolo parallelo&#xA;&#xA;In questo modo riesce ad essere sia CPU bound che GPU bound che, a differenza di Bcrypt, lo rende resistente anche ad attacchi facenti uso di hardware specializzato..&#xA;&#xA;Di contro, in ambiente in cui siamo vincolati dalle risorse disponibili, la sua potenza diventa un fattore limitante.&#xA;Quasi paragonabile ad Argon2 in quanto a robustezza, il suo unico tallone d&#39;Achille è la permeabilità ad attacchi di tipo side-channel. &#xA;yescrypt&#xA;Piccola menzione per yescrypt, appartenente alla famiglia &#34;Scrypt&#34;, pensato per essere ancora più resistente di Scrypt agli attacchi GPU e FPGA ma con una gestione più intelligente delle risorse.&#xA;&#xA;Grazie alle sue peculiarità, di fatto, è diventato il successore spirituale di Bcrypt nei sistemi operativi gnu/linux dove, a cominciare da Fedora, passando per Debian, Ubuntu, Arch, Kali, è il default per la cifratura delle password di sistema in /etc/shadow.&#xA;&#xA;È talmente incardinato ormai nei sistemi operativi, che è la libreria libxcrypt di yescrypt a gestire la tipica funzione crypt() di C che è la base della crittografia su tutti i sistemi gnu/linux moderni.&#xA;&#xA;La sua robustezza unita alla gestione intelligente delle risorse lo rende un coltellino svizzero di riferimento utile per es. per versione custom di LUKS su sistemi embedded, che magari fanno uso di cpu meno recenti,  oppure come opzione per strumenti di backup specialistici&#xA;&#xA;Di fatto, sui sistemi operativi, yescrypt s&#39;è guadagnato un consenso amplissimo dovuto alla sua scalabilità, alla sua capacità di usare anche la ROM per rendere il cracking ancora più difficile e senza pesare sulla RAM e alla sua compatibilità potendosi inserire perfettamente nella storica funzione crypt() di C come detto prima.&#xA;&#xA;Se Argon2 è il vincitore accademico avendo vinto il Password Hashing Competition del 2015, yescrypt per la sua robustezza, efficienza e flessibilità si ritaglia un profilo di indispensabilità nei sistemi operativi,&#xA;Argon2&#xA;E veniamo al dominatore indiscusso di questa che non è una llista esaustiva di KDF.&#xA;&#xA;Argon2 è LO standard moderno per il password hashing raccomandato da OWASP e IETF.&#xA;&#xA;È il riferimento per praticamente ogni password manager: Bitwarden, KeppasXC, 1Password, a cui assegnanp la protezione della Master Password&#xA;&#xA;È la scelta principale per la cifratura degli hard disk anche con impostazioni molto aggressive, in ragione delle quali un ritardo di mezzo secondo (un tempo enorme se venisse scalato esponenzialmente) nell&#39;apertura di un HD è assolutamente accettabile.&#xA;È il default di LUKS2 (LUKS1 usava PBKDF2) e di VeraCrypt, con cui ha sostituito SHA-512.&#xA;&#xA;Come Bcrypt, è implementato estensivamente su praticamente ogni frameword web e backend, da PHP, Django (Python), Laravel fino a Node.js.&#xA;&#xA;Nei sistemi operativi, laddove yescrytpt domina nella gestione delle password utente, Argon2 è usato per compiti più critici.&#xA;Dal kernel Linux per gestire internamente le chiavi crittografiche o da macOS / iOS, dove algoritmi proprietari ispirati fortemente ad Argon2, proteggono i dati nel Secure Enclave.&#xA;&#xA;Argon2  setta 3 parametri principali per regolare la sua forza:&#xA;&#xA;t: iterazioni, quante volte vengono rimescolati i dati (default Bitwarden = 3)&#xA;m: memoria, quanta ram deve occupare il calcolo. Questa è la misura anti-GPU (default Bitwarden = 16 (64MB))&#xA;p: parallelismo, quanti core della cpu usare. Questa è la misura anti-CPU (default Bitwarden = 4)&#xA;&#xA;La variante id è anche resistente agli attacchi side-channel perché impediscono a un attaccante di capire la password osservando i tempi di accesso alla memoria.&#xA;&#xA;Un piccolo esempio: hashing di una password con Argon2&#xA;Il grosso vantaggio degli algoritmi di kdf è che sono naturalmente resilienti rispetto all&#39;evoluzione tecnologica che produce macchine con sempre maggiore potenza di calcolo.&#xA;Da pbkdf2 in poi, il salt implicito che invalida le rainbow table precalcolate e la possibilità di calibrare il key stretching in moda da agire intensivamente su ram e cpu, permettono all&#39;algoritmo di adeguarsi per conservare la sua robustezza.&#xA;&#xA;Mini-script per l&#39;hashing di una password fornita dall&#39;utente con argon2 settato al default di Bitwarden:&#xA;echo -n &#34;Password: &#34;; read -s PASSWORD&#xA;Genero un Salt casuale di 128 bit&#xA;SALT=$(openssl rand -base64 128)&#xA;PASSWORDHASH=$(echo &#34;${PASSWORD}&#34; | argon2 &#34;${SALT}&#34; -m 16 -t 3 -p 4 -id -e)&#xA;&#xA;PASSWORDHASH e SALT sono i dati che verranno archiviati e, poiché argon2 &#34;frulla&#34; la password con un salt, è praticamente impossibile risalire alla password originale.&#xA;&#xA;La verifica è tuttavia banale perché, avendo il salt e la password da verificare, si ricrea l&#39;hash con argon2 e si confronta con l&#39;hash memorizzato.&#xA;&#xA;Per maggior sicurezza salt e digest possono essere memorizzati in punti differenti. L&#39;importante è che possano essere recuperate a partire dall&#39;utente.&#xA;Gestione del pepper&#xA;Col pepper le cose cambiano un pochino perché:&#xA;&#xA;deve essere archiviato con tutte le paranoie possibili in un punto diverso dal database degli utenti&#xA;il key rotation del pepper non è banale&#xA;&#xA;Mini-script che mostra come applicare salt e pepper all&#39;hashing di una password:&#xA;L&#39;utente inserisce la password&#xA;echo -n &#34;Password: &#34;; read -s PASSWORD&#xA;&#xA;Genero un Salt casuale di 128 bit unico per ogni utente&#xA;SALT=$(openssl rand -base64 128)&#xA;&#xA;Anche PEPPER sarà qualcosa del tipo &#34;openssl rand -base64 128&#34;&#xA;e si troverà in un punto esterno al database degli utenti.&#xA;PEPPER=$(getpepperfromext)&#xA;&#xA;Digest della password+salt&#xA;PASSWORDHASH=$(echo &#34;${PASSWORD}&#34; | argon2 &#34;${SALT}&#34; -m 16 -t 3 -p 4 -id -e)&#xA;&#xA;HMAC del digest con PEPPER come chiave&#xA;PASSWORDPEPPER=$(echo &#34;${PASSWORDHASH}&#34; | openssl dgst -sha256 -hmac &#34;${PEPPER}&#34; -binary | base64)&#xA;HSM&#xA;Quella vista prima è una versione molto edulcorata di ciò che avviene nella realtà.&#xA;Il pepper, non può essere gestito con leggerezza visto che è un segreto che protegge non un singolo oggetto ma intere classi, come db di utenti.&#xA;&#xA;L&#39;apparato che gestisce chiavi di questo tipo e di questa importanza, deve essere robusto, praticamente inattaccabile, quasi completamente isolato dal resto dei sistemi a meno delle applicazioni, e solo di quelle, che hanno il permesso di richiedere una chiave,&#xA;&#xA;Apparati hardware specializzati che assolvono a tutte queste funzioni e anche di più, sono gli HSM (Hardware Security Module) che garantiscono il ciclo di vita delle chiavi, dalla generazione alla distruzione, includendo versionamento, rotazione e backup.&#xA;Sono concepiti per resistere anche a manipolazioni forzate che possono innescare un meccanismo di autodistruzione e, particolare rilevante, le operazioni crittografiche basate sulle chiavi protette vengono svolte dall&#39;hsm che consegna al client il risultato delle operazioni, non le chiavi. Nel nostro caso, l&#39;HSM dovrebbe restituirci l&#39;hmac del digest della password che gli inviamo.&#xA;Key / Algo Rotation&#xA;Cosa succede se cambio pepper o algoritmo (anche la sua configurazione)?&#xA;Non avendo disponibilità in alcun modo della password dovrò adottare una strategia ad-hoc.&#xA;Fra tutti gli scenari possibili, il miglior compromesso fra sicurezza e comodità secondo me, è quello basato sul wrapping.&#xA;&#xA;È necessario innanzitutto che vengano conservate le versioni delle chiavi per i servizi che le richiedono. E a questo dovrebbe pensarci l&#39;HSM, se ce n&#39;è uno o qualcosa di custom che abbia funzionalità analoghe.&#xA;Inoltre dovrebbero esserci dei flag che indichino quali sono gli utenti a cui sono state applicate le nuove configurazioni.&#xA;&#xA;Caso A: Algo rotation&#xA;Supponiamo che l&#39;algoritmo di hashing venga cambiato o vengano cambiate le sue configurazioni.&#xA;&#xA;Premessa:&#xA;Nel mio DB degli utenti, in corrispondenza di ogni utente, avrò:&#xA;&#xA;il digest della password &#34;pepato&#34;: HMAC ( pepper, HASH ( salt, password ) ) &#xA;il salt&#xA;&#xA;Il wrapping:&#xA;La strategia sarà quello di &#34;avvolgere&#34; la password di ogni utente col nuovo algoritmo, settare un qualche flag che mi indichi l&#39;operazione compiuta e archiviare il tutto.&#xA;&#xA;Imponiamo il nuovo algoritmo a tutti gli utenti &#34;imbustando&#34; il digest attuale (in questo caso &#39;HMAC in realtà, visto che abbiamo a che fare anche col pepper) con il nuovo digest HASH\NEW:&#xA;HASH\NEW ( salt\new, HMAC ( pepper, HASH ( salt, password ) ) ).&#xA;Per ogni utente averemo dunque:&#xA;&#x9;il nuovo digest al posto di quello vecchio,&#xA;&#x9;il nuovo salt &#xA;&#x9;il vecchio salt&#xA;Settiamo il flag del cambio algoritmo a true (o quello che è)&#xA;Quando l&#39;utente effettuerà il login con successo e il flag sarà a &#34;true&#34;, abbiamo la password che ci permetterà di eliminare il vecchio &#34;involucro&#34; e ripristinare l&#39;HMAC del nuovo digest: HMAC ( pepper, HASH\NEW ( salt\new, password ) ) e il flag ritornerà a &#34;false&#34;&#xA;&#xA;Considerazioni:&#xA;&#xA;La sicurezza non viene compromessa perché il digest di un digest, con KDF configurate a dovere, non comporta alcun rischio.&#xA;La fase di verifica è quella che si complica di più perché in base al valore del flag, dovrà essere effettuata in maniera differente.&#xA;   Se il flag è &#34;true&#34; (nella nostra convenzione), dopo il login devo avere gli elementi per calcolare il digest in questo modo: HASH\NEW ( salt\new, HMAC ( pepper, HASH ( salt, password ) ) ).&#xA;   Se il flag è a false, calcolerò al solito: HMAC ( pepper, HASH\NEW ( salt\new, password ) )&#xA;&#xA;Caso B: Pepper rotation&#xA;Supponiamo che a ruotare sia il pepper.&#xA;Procediamo sempre con il wrapping massivo su tutti gli utenti incapsulando il digest :&#xA;&#xA;HMAC ( pepper, HASH ( salt, password ) )&#xA;&#xA;con quello nuovo:&#xA;&#xA;HMAC ( pepper\new, HMAC ( pepper, HASH ( salt, password ) ) )&#xA;&#xA;mettendo il flag a &#34;true&#34;.&#xA;&#xA;Come prima, una volta che gli utenti cominceranno a fare il login, se il flag è &#34;true&#34; innanzitutto verificherò che:&#xA;&#xA;HMAC ( pepper\new, HMAC ( pepper, HASH ( salt, password ) ) ) &#xA;&#xA;sia uguale a ciò che è stato archiviato.&#xA;Se così fosse, ora che sono di nuovo in possesso della password, ripristinerò l&#39;HMAC con:&#xA;&#xA;HMAC ( pepper\new, HASH ( salt, password ) )_&#xA;&#xA;memorizzandolo al posto di quello vecchio e rimettendo il flag a false.&#xA;&#xA;Considerazioni:&#xA;La modifica massiva delle password degli utenti, stavolta passa dall&#39;HSM e potrebbe essere un problema perché un HSM è progettato per scoraggiare flooding di richieste.&#xA;&#xA;È vero che il pepper è sempre lo stesso per tutti gli utenti ma, come ricordavo prima, di solito un HSM non fornisce i suoi segreti ma solo i risultati crittografici delle loro applicazioni.&#xA;&#xA;#kdf #pbkdf2 #bcrypt #scrypt #yescrypt #argon2 #luks #cryptography #aes #sha #digest #RainbowTable #BruteForce #salt #pepper #entropy #hsm #hmac #hash]]&gt;</description>
      <content:encoded><![CDATA[<p><img src="https://pixelfed.uno/storage/m/_v2/489827599091373610/ffe7c43a6-a8b5f2/dnumO91UA8rS/L0t7UvdH6Z7pYPEvBDVb3aulMdNhW1WPBRIRKHMF.jpg" alt="password hashing"></p>

<p>Dopo aver capito come creare password inviolabili anche avendo a disposizione tutta l&#39;energia termica dell&#39;universo, pensiamo al modo migliore per archiviarle.</p>

<p>Pensare di lasciare la password raw in un database, rappresenta un grosso rischio in virtù di un possibile attacco offline.
</p>
<ul><li><a href="#rainbow-table" rel="nofollow">Rainbow Table</a></li>
<li><a href="#come-ti-blocco-la-rainbow-table-il-salt-sale" rel="nofollow">Come ti blocco la Rainbow Table: Il “Salt” (sale)</a></li>
<li><a href="#il-livello-successivo-il-pepper" rel="nofollow">Il livello successivo: Il Pepper</a></li>
<li><a href="#final-step-l-hashing" rel="nofollow">Final step: L’hashing</a>
<ul><li><a href="#pbkdf2" rel="nofollow">PBKDF2</a></li>
<li><a href="#bcrypt" rel="nofollow">Bcrypt</a></li>
<li><a href="#scrypt" rel="nofollow">Scrypt</a></li>
<li><a href="#yescrypt" rel="nofollow">yescrypt</a></li>
<li><a href="#argon2" rel="nofollow">Argon2</a></li></ul></li>
<li><a href="#un-piccolo-esempio-hashing-di-una-password-con-argon2" rel="nofollow">Un piccolo esempio: hashing di una password con Argon2</a></li>
<li><a href="#gestione-del-pepper" rel="nofollow">Gestione del pepper</a></li>
<li><a href="#hsm" rel="nofollow">HSM</a></li>
<li><a href="#key-algo-rotation" rel="nofollow">Key / Algo Rotation</a>
<ul><li><a href="#caso-a-algo-rotation" rel="nofollow">Caso A: Algo rotation</a></li>
<li><a href="#caso-b-pepper-rotation" rel="nofollow">Caso B: Pepper rotation</a></li></ul></li></ul>

<p>Una prima linea di difesa consiste nel memorizzare il <strong>digest della password</strong>, una stringa alfanumerica univoca generata da un apposito algoritmo, così da lasciare nelle mani dell&#39;attaccante degli oggetti che, per la loro non invertibilità, non permettono di risalire alle password.</p>

<p>La <strong>scelta</strong> dell&#39;algoritmo di hashing diventa critica al fine di scongiurare altri tipi di attacchi. Ad es. SHA-256, pur essendo ottimo e molto efficiente per il digest e la firma anche di file di grandi dimensioni, mostra il fianco, proprio in virtù della sua velocità, nel caso di attacchi con:</p>
<ul><li><strong>brute-force:</strong> si calcola l’hash di password casuali fino a trovare una corrispondenza,</li>
<li><strong>dizionario:</strong> un&#39;alternativa intelligente alla forza bruta. Si punta alle password più comuni, si calcola l&#39;hash e si controlla se c&#39;è corrispondenza.</li>
<li><strong>Rainbow Table:</strong> l&#39;attacco al dizionario più insidioso di tutti</li></ul>

<p>Oltre al fatto che, la funzione di hashing , essendo deterministica, permette di capire chi sono gli utenti che hanno la stessa password, dal momento che avranno lo stesso digest.</p>

<h2 id="rainbow-table">Rainbow Table</h2>

<p>Una <strong>Rainbow Table</strong> è un enorme dizionario pre-calcolato che contiene:</p>
<ul><li>Milioni di password comuni.</li>
<li>Il relativo hash corrispondente.</li></ul>

<p>Invece di calcolare l’entropia di ogni tentativo, l’attaccante ruba il database degli hash e fa un semplice “Cerca e Trova”. Se l’hash della tua password è nella tabella, la tua password è violata in millisecondi, indipendentemente da quanto fosse alta la sua entropia teorica.</p>

<p><small><strong>Oss.:</strong> Una password totalmente casuale, generata da un CSPRNG affidabile, con un&#39;alta entropia (&gt;120), rimarrebbe comunque inviolabile anche dalla rainbow table perché la probabilità che quella password si trovi nel dizionario, sarebbe equivalente ad indovinarla.</small></p>

<h2 id="come-ti-blocco-la-rainbow-table-il-salt-sale">Come ti blocco la Rainbow Table: Il “Salt” (sale)</h2>

<p>Per rendere inutili le Rainbow Table, i sistemi sicuri utilizzano il <strong>Salt</strong>. Il salt è una stringa di dati casuali (generati da una sorgente d’entropia affidabile ovviamente) che viene aggiunta alla password prima di calcolarne il digest.</p>

<p>In questo modo le rainbow table vengono vanificate perché gli hash precalcolati sulle password raccolte, mancando il salt, non valgono più. Anche se due utenti avessero la stessa password, avrebbero degli hash completamente diversi.</p>

<p>Anche per questo motivo non è un problema che il salt sia pubblico, perché il suo obiettivo non è nascondere quello che è un pezzetto di password a tutti gli effetti, ma di impedire <strong>economie di scala</strong> degli attacchi perché, pur potendo disporre offline di un database di decine di milioni di utenti, gli hash della mia Rainbow Table (che può arrivare a pesare anche decine di GB) <strong>andrebbero tutti ricalcolati per ogni utente</strong>, con un costo computazionale e di archiviazione inimmaginabile.</p>

<p>Per riassumere, gli ingredienti di base sono:</p>
<ol><li><strong>una buona sorgente d’entropia:</strong> una fonte di casualità certificata per generare un salt unico;</li>
<li><strong>entropia della password:</strong> sempre buona norma, ove possibile, come sappiamo ormai fare (<a href="https://noblogo.org/aytin/come-generare-una-password-o-un-keyfile-sicuri-trilogia-della-password-1-di" rel="nofollow">https://noblogo.org/aytin/come-generare-una-password-o-un-keyfile-sicuri-trilogia-della-password-1-di</a>). Evita attacchi brute-force o al dizionario in cui l’attaccante prova a indovinare;</li>
<li><strong>salt:</strong> protegge la password dagli attacchi basati su database pre-calcolati (Rainbow Table).</li></ol>

<h2 id="il-livello-successivo-il-pepper">Il livello successivo: Il Pepper</h2>

<p>È vero che col salt andiamo a complicare lo sfruttamento di un attacco offline ma possiamo fare di meglio.</p>

<p>Il punto d&#39;attenzione è che il salt protegge le password di <strong>tutti gli utenti</strong>.
Ma un attaccante potrebbe non essere affatto interessato a violare <strong>ogni singolo utente</strong> (niente economia di scala) ma solo alcuni. E allora l&#39;attacco attraverso Ranbow Table potrebbe essere di nuovo praticabile.</p>

<p>Ma gli informatici sono dei gran giocherelloni, si sa.
Visto che abbiamo già il “sale”, perché non finire aggiustando con un po’ di “pepe”? Detto, fatto!</p>

<p>Il <strong>pepper</strong>, come il salt, è un&#39;altra password generata con gli stessi criteri del salt ma le analogie finiscono qua perché:</p>
<ul><li>a differenza del <strong>salt</strong> che si trova nel database, il <strong>pepper</strong> è separato da ques&#39;ultimo. L&#39;ideale sarebbe un <strong>HSM</strong>;</li>
<li>Il <strong>salt</strong> è visibile a tutti, attaccante compreso. Il <strong>pepper</strong> è segreto. Un eventuale data breach che permette all&#39;attaccante di disporre offline di tutto il database degli utenti, “vedrà” certamente gli eventuali salt ma sarà ignaro del fatto che gli mancherà sempre un pezzo di chiave;</li>
<li>il <strong>salt</strong> è diverso per ogni utente, il <strong>pepper</strong>, di solito, è unico;</li>
<li>il <strong>salt</strong> serve a rendere uniche la password degli utenti, il <strong>pepper</strong> protegge l&#39;intero database da attacchi offline.</li></ul>

<p><strong>Il pepper è la chiave di un HMAC, o di un meccanismo di cifratura simmetrica, applicato al digest della password</strong> (che ricordo essere salt+password in realtà), che sarà ciò che verrà archiviato.</p>

<p>Va detto che l&#39;uso del “pepper” complica ulteriormente lo scenario di archiviazione.
Nella stragrande maggioranza dei casi è sufficiente scegliere un buon algoritmo di password hashing (vedi paragrafo successivo) per scoraggiare gli attaccanti.
“Pepare” le password prevederebbe, come detto sopra, l&#39;uso di un HSM per es, e tutta una serie di riflessioni di contorno che evidenzierò più avanti.</p>

<h2 id="final-step-l-hashing">Final step: L&#39;hashing</h2>

<p>L&#39;ultimo punto da dettagliare è l&#39;hash della password.</p>

<p>L&#39;<strong>hash crittografico</strong>, in uso in questi casi, deve soddisfare le seguenti proprietà:</p>
<ol><li><strong>resistenza alla pre-immagine:</strong> dato un hash <em>h</em>, deve essere impossibile trovare una password <em>p</em> t.c. <em>H(p) = h</em> (non invertibilità della funzione hash)</li>
<li><strong>resistenza alla pre-immagine secondaria:</strong> dato una password <em>p<sub>1</sub></em>, deve essere impossibile trovare un&#39;altra password <em>p<sub>2</sub></em> t.c. <em>H(p<sub>1</sub>) = H(p<sub>2</sub>)</em> (resistenza debole alle collisioni)</li>
<li><strong>resistenza alle collisioni:</strong> è impossibile trovare due password diverse, <em>p<sub>1</sub></em> e <em>p<sub>2</sub></em>, t.c. <em>H(p<sub>1</sub>) = H(p<sub>2</sub>)</em> (resistenza forte alle collisioni)</li>
<li><strong>effetto valanga:</strong> il cambio di un solo bit della password deve cambiare radicalmente l&#39;intero hash</li></ol>

<p>In un sistema moderno, l&#39;hash non può essere delegato a funzioni di tipo SHA perché nascono per altri compiti,</p>

<p><strong>SHA-2</strong> e <strong>SHA-3</strong> nascono per il digest veloce, per verificare l&#39;integrità di file anche molto grossi o firmare documenti. La loro eccellente velocità diventa il loro più grosso difetto quando si parla di password.
Negli scenari precedenti di attachi offline, l&#39;hacker che dispone di una grossa potenza di calcolo, può ricostituire velocemente le rainbow table per <em>n</em> utenti. Magari non di tutti ma di quelli attenzionati.</p>

<p>Le funzioni di derivazione della chiave (<strong>KDF</strong>) come <strong>pbkdf2</strong> e quelle ancora più estreme come <strong>B/Scrypt</strong>, <strong>Argon2</strong>, oltre che soddisfare tutti i punti precedentemente elencati tipici di funzioni di password hashing, sono progettate per essere computazionalmente pesantissime da calcolare perché il loro scopo non è il digest ma la protezione di un segreto contro il brute-force.
E mentre le vecchie KDF come <strong>pbdfk2</strong> sono CPU bound, ma non GPU bound, le KDF più moderne come <strong>Bcrypt</strong>, <strong>Scrypt</strong> ma soprattutto <strong>Argon2</strong>, agiscono pesantemente su tempo, memoria e parallelismo e l&#39;attacco offline di cui sopra diventa impraticabile.</p>

<h3 id="pbkdf2">PBKDF2</h3>

<p>È il decano delle KDF. Applica iterativamente una funzione pseudorandomica, come HMAC con uno SHA, con salt alla password.
Il conteggio delle iterazioni è un parametro configurabile.</p>

<p>PBDKF2 è uno standard di lunga data ampiamente adottato. Se non ci sono necessità stringenti di sicurezza o requisiti legacy, è una buona scelta.</p>

<p>Il fatto di essere solo CPU bound però non la rende la scelta ideale in scenari dove gli attaccanti possono attingere a risorse di calcolo considerevoli</p>

<h3 id="bcrypt">Bcrypt</h3>

<p>Basato su Blowfish, anche Bcrypt usa un hash crittografico sulla password con parametri il salt e un fattore di costo.</p>

<p>Il fattore di costo aumenta esponenzialmente il numero di iterazioni per adattarsi all&#39;aumento di potenza di calcolo dell&#39;hardware.</p>

<p>Bcrypt è stato progetto per essere lento e resistente a semplici attacchi di forzat bruta.
Tuttavia, il basso utilizzo di ram richiesto dal calcolo lo rendono poco resistente ad attacchi sferrati usando hardware specializzato.</p>

<p>Bcrypt ha dalla sua una storia solidissima in ragione della quale da 20 anni a questa parte non sono state trovate vulnerabilità critiche nel suo design.</p>

<p>Per questo motivo Bcrypt cifra le password di sistema di OpenBSD dal 1999, come pure ha cifrato quelle di tante distro Linux per anni, prima che passassero ad Argon2 o <strong>yescrypt</strong> (default di Fedora).</p>

<p>Domina nei framework web ([Python] Django, [Ruby] Ruby on Rails, [PHP] Laravel), [Java] Spring, Node.js), nelle applicazioni (Ansible / Terraform, Docker), nel web (la cifratura in .htpasswd di Apache e Nginx) visto che la sua semplcitià di implementazione gli ha permesso di trovarsi praticamente in ogni linguaggio.</p>

<p>È presente come alternativa anche nei password manager benché molti di essi abbiano spostato il default verso Argon2 o PBKDF2 per conformità agli standard FIPS.</p>

<p>È molto semplice implementare e anche da usare perché bisogna agire solo sul <strong>fattore di costo</strong> (consigliato almeno 10-12, altrimenti diventa troppo vulnerabile ad attacchi sferrati attraverso la GPU)</p>

<h3 id="scrypt">Scrypt</h3>

<p>Rilasciato nel 2009, Scrypt è stato il primo algoritmo a introdurre il concetto di <strong>Memory Hardness</strong> ed è stato progettato per rendere economicamente poco conveniente il ricorso ad hardware specializzato come gli ASIC o i FPGA e incidere pesantemente su CPU, ram e parallelismo.</p>

<p>Il suo alveo principale sono state le cripto-valute, molte monete lo usano per il mining.</p>

<p>Scrypt lo troviamo in quasi tutti i linguaggi di programmazione, in <strong>Tarsnap</strong>, servizio di baclup online creato dallo stesso autore di Scrypt, è stato usato da <strong>LastPass</strong> ed è presente come opzione in <strong>VeraCrypt</strong> per derivare la chiave dalla password.
Fino ad Android 9 era l&#39;algoritmo usato per la <strong>FDE</strong> del dispositvio (passato poi al <strong>FBE</strong>) .
Presente anche su FreeBSD come opzione per la cifratura delle password di sistema e come opzione su <strong>LUKS</strong> per la cifratura degli slot delle chiavi.</p>

<p>Su Scrypt i parametri da configurare sono:</p>
<ul><li><strong>Costro CPU/Memoria <em>(N)</em>:</strong> un parametro che aumenta i costi computazionali di cpu e memoria</li>
<li><strong>DImensione del blocco <em>®</em>:</strong> influenza la larghezza di banda della memoria</li>
<li><strong>Parallelizzazione <em>(p)</em>:</strong> indica quanto deve incidere sul calcolo parallelo</li></ul>

<p>In questo modo riesce ad essere sia CPU bound che GPU bound che, a differenza di Bcrypt, lo rende resistente anche ad attacchi facenti uso di hardware specializzato..</p>

<p>Di contro, in ambiente in cui siamo vincolati dalle risorse disponibili, la sua potenza diventa un fattore limitante.
Quasi paragonabile ad Argon2 in quanto a robustezza, il suo unico tallone d&#39;Achille è la permeabilità ad attacchi di tipo <strong>side-channel</strong>.</p>

<h3 id="yescrypt">yescrypt</h3>

<p>Piccola menzione per <strong>yescrypt</strong>, appartenente alla famiglia “Scrypt”, pensato per essere ancora più resistente di Scrypt agli attacchi GPU e FPGA ma con una gestione più intelligente delle risorse.</p>

<p>Grazie alle sue peculiarità, di fatto, è diventato il successore spirituale di Bcrypt nei sistemi operativi gnu/linux dove, a cominciare da Fedora, passando per Debian, Ubuntu, Arch, Kali, è il default per la cifratura delle password di sistema in <code>/etc/shadow</code>.</p>

<p>È talmente incardinato ormai nei sistemi operativi, che è la libreria <code>libxcrypt</code> di <strong>yescrypt</strong> a gestire la tipica funzione <code>crypt()</code> di C che è la base della crittografia su tutti i sistemi gnu/linux moderni.</p>

<p>La sua robustezza unita alla gestione intelligente delle risorse lo rende un coltellino svizzero di riferimento utile per es. per versione custom di LUKS su sistemi embedded, che magari fanno uso di cpu meno recenti,  oppure come opzione per strumenti di backup specialistici</p>

<p>Di fatto, sui sistemi operativi, <strong>yescrypt</strong> s&#39;è guadagnato un consenso amplissimo dovuto alla sua scalabilità, alla sua capacità di usare anche la ROM per rendere il cracking ancora più difficile e senza pesare sulla RAM e alla sua compatibilità potendosi inserire perfettamente nella storica funzione <code>crypt()</code> di C come detto prima.</p>

<p>Se Argon2 è il vincitore accademico avendo vinto il Password Hashing Competition del 2015, <strong>yescrypt</strong> per la sua robustezza, efficienza e flessibilità si ritaglia un profilo di indispensabilità nei sistemi operativi,</p>

<h3 id="argon2">Argon2</h3>

<p>E veniamo al dominatore indiscusso di questa che non è una llista esaustiva di KDF.</p>

<p>Argon2 è LO standard moderno per il password hashing raccomandato da OWASP e IETF.</p>

<p>È il riferimento per praticamente ogni password manager: <strong>Bitwarden</strong>, <strong>KeppasXC</strong>, <strong>1Password</strong>, a cui assegnanp la protezione della <strong>Master Password</strong></p>

<p>È la scelta principale per la cifratura degli hard disk anche con impostazioni molto aggressive, in ragione delle quali un ritardo di mezzo secondo (un tempo enorme se venisse scalato esponenzialmente) nell&#39;apertura di un HD è assolutamente accettabile.
È il default di <strong>LUKS2</strong> (LUKS1 usava PBKDF2) e di <strong>VeraCrypt</strong>, con cui ha sostituito SHA-512.</p>

<p>Come Bcrypt, è implementato estensivamente su praticamente ogni frameword web e backend, da PHP, Django (Python), Laravel fino a Node.js.</p>

<p>Nei sistemi operativi, laddove <strong>yescrytpt</strong> domina nella gestione delle password utente, <strong>Argon2</strong> è usato per compiti più critici.
Dal kernel Linux per gestire internamente le chiavi crittografiche o da macOS / iOS, dove algoritmi proprietari ispirati fortemente ad Argon2, proteggono i dati nel Secure Enclave.</p>

<p>Argon2  setta 3 parametri principali per regolare la sua forza:</p>
<ul><li><strong>t: iterazioni</strong>, quante volte vengono rimescolati i dati <em>(default Bitwarden = 3)</em></li>
<li><strong>m: memoria</strong>, quanta ram deve occupare il calcolo. Questa è la misura anti-GPU <em>(default Bitwarden = 16 (64MB))</em></li>
<li><strong>p: parallelismo</strong>, quanti core della cpu usare. Questa è la misura anti-CPU <em>(default Bitwarden = 4)</em></li></ul>

<p>La variante <em>id</em> è anche resistente agli <strong>attacchi side-channel</strong> perché impediscono a un attaccante di capire la password osservando i tempi di accesso alla memoria.</p>

<h2 id="un-piccolo-esempio-hashing-di-una-password-con-argon2">Un piccolo esempio: hashing di una password con Argon2</h2>

<p>Il grosso vantaggio degli algoritmi di kdf è che sono naturalmente resilienti rispetto all&#39;evoluzione tecnologica che produce macchine con sempre maggiore potenza di calcolo.
Da pbkdf2 in poi, il salt implicito che invalida le rainbow table precalcolate e la possibilità di calibrare il key stretching in moda da agire intensivamente su ram e cpu, permettono all&#39;algoritmo di adeguarsi per conservare la sua robustezza.</p>

<p>Mini-script per l&#39;hashing di una password fornita dall&#39;utente con argon2 settato al default di Bitwarden:</p>

<pre><code class="language-bash">echo -n &#34;Password: &#34;; read -s PASSWORD
# Genero un Salt casuale di 128 bit
SALT=$(openssl rand -base64 128)
PASSWORD_HASH=$(echo &#34;${PASSWORD}&#34; | argon2 &#34;${SALT}&#34; -m 16 -t 3 -p 4 -id -e)
</code></pre>

<p><code>PASSWORD_HASH</code> e <code>SALT</code> sono i dati che verranno archiviati e, poiché argon2 “frulla” la password con un salt, è praticamente impossibile risalire alla password originale.</p>

<p>La verifica è tuttavia banale perché, avendo il salt e la password da verificare, si ricrea l&#39;hash con argon2 e si confronta con l&#39;hash memorizzato.</p>

<p>Per maggior sicurezza salt e digest possono essere memorizzati in punti differenti. L&#39;importante è che possano essere recuperate a partire dall&#39;utente.</p>

<h2 id="gestione-del-pepper">Gestione del pepper</h2>

<p>Col pepper le cose cambiano un pochino perché:</p>
<ul><li>deve essere archiviato con tutte le paranoie possibili in un punto diverso dal database degli utenti</li>
<li>il key rotation del pepper non è banale</li></ul>

<p>Mini-script che mostra come applicare salt e pepper all&#39;hashing di una password:</p>

<pre><code class="language-bash"># L&#39;utente inserisce la password
echo -n &#34;Password: &#34;; read -s PASSWORD

# Genero un Salt casuale di 128 bit unico per ogni utente
SALT=$(openssl rand -base64 128)

# Anche PEPPER sarà qualcosa del tipo &#34;openssl rand -base64 128&#34;
# e si troverà in un punto esterno al database degli utenti.
PEPPER=$(get_pepper_from_ext)

# Digest della password+salt
PASSWORD_HASH=$(echo &#34;${PASSWORD}&#34; | argon2 &#34;${SALT}&#34; -m 16 -t 3 -p 4 -id -e)

# HMAC del digest con PEPPER come chiave
PASSWORD_PEPPER=$(echo &#34;${PASSWORD_HASH}&#34; | openssl dgst -sha256 -hmac &#34;${PEPPER}&#34; -binary | base64)
</code></pre>

<h2 id="hsm">HSM</h2>

<p>Quella vista prima è una versione molto edulcorata di ciò che avviene nella realtà.
Il pepper, non può essere gestito con leggerezza visto che è un segreto che protegge non un singolo oggetto ma intere classi, come db di utenti.</p>

<p>L&#39;apparato che gestisce chiavi di questo tipo e di questa importanza, deve essere robusto, praticamente inattaccabile, quasi completamente isolato dal resto dei sistemi a meno delle applicazioni, e solo di quelle, che hanno il permesso di richiedere una chiave,</p>

<p>Apparati hardware specializzati che assolvono a tutte queste funzioni e anche di più, sono gli <strong>HSM</strong> (<strong>H</strong>ardware <strong>S</strong>ecurity <strong>M</strong>odule) che garantiscono il ciclo di vita delle chiavi, dalla generazione alla distruzione, includendo versionamento, rotazione e backup.
Sono concepiti per resistere anche a manipolazioni forzate che possono innescare un meccanismo di autodistruzione e, particolare rilevante, <strong>le operazioni crittografiche basate sulle chiavi protette vengono svolte dall&#39;hsm che consegna al client il risultato delle operazioni, non le chiavi</strong>. Nel nostro caso, l&#39;HSM dovrebbe restituirci l&#39;hmac del digest della password che gli inviamo.</p>

<h2 id="key-algo-rotation">Key / Algo Rotation</h2>

<p>Cosa succede se cambio pepper o algoritmo (anche la sua configurazione)?
Non avendo disponibilità in alcun modo della password dovrò adottare una strategia ad-hoc.
Fra tutti gli scenari possibili, il miglior compromesso fra sicurezza e comodità secondo me, è quello basato sul wrapping.</p>

<p>È necessario innanzitutto che vengano conservate le versioni delle chiavi per i servizi che le richiedono. E a questo dovrebbe pensarci l&#39;HSM, se ce n&#39;è uno o qualcosa di custom che abbia funzionalità analoghe.
Inoltre dovrebbero esserci dei flag che indichino quali sono gli utenti a cui sono state applicate le nuove configurazioni.</p>

<h3 id="caso-a-algo-rotation">Caso A: Algo rotation</h3>

<p>Supponiamo che <strong>l&#39;algoritmo di hashing venga cambiato</strong> o vengano cambiate le sue configurazioni.</p>

<p><strong>Premessa:</strong>
Nel mio DB degli utenti, in corrispondenza di ogni utente, avrò:</p>
<ol><li>il digest della password “pepato”: <em>HMAC ( pepper, HASH ( salt, password ) )</em></li>
<li>il salt</li></ol>

<p><strong>Il wrapping:</strong>
La strategia sarà quello di “avvolgere” la password di ogni utente col nuovo algoritmo, settare un qualche flag che mi indichi l&#39;operazione compiuta e archiviare il tutto.</p>
<ol><li>Imponiamo il nuovo algoritmo a tutti gli utenti “imbustando” il digest attuale (in questo caso &#39;HMAC in realtà, visto che abbiamo a che fare anche col pepper) con il nuovo digest <em><strong>HASH_NEW</strong></em>:
<em><strong>HASH_NEW</strong> ( <strong>salt_new</strong>, HMAC ( pepper, HASH ( salt, password ) ) )</em>.</li>
<li>Per ogni utente averemo dunque:
<ol><li>il nuovo digest al posto di quello vecchio,</li>
<li>il nuovo salt</li>
<li>il vecchio salt</li></ol></li>
<li>Settiamo il flag del cambio algoritmo a <em>true</em> (o quello che è)</li>
<li>Quando l&#39;utente effettuerà il login con successo e il flag sarà a “<em>true</em>”, abbiamo la password che ci permetterà di eliminare il vecchio “involucro” e ripristinare l&#39;HMAC del nuovo digest: <em>HMAC ( pepper, <strong>HASH_NEW</strong> ( <strong>salt_new</strong>, password ) )</em> e il flag ritornerà a “<em>false</em>“</li></ol>

<p><strong>Considerazioni:</strong></p>
<ul><li>La sicurezza non viene compromessa perché il digest di un digest, con KDF configurate a dovere, non comporta alcun rischio.</li>
<li>La fase di verifica è quella che si complica di più perché in base al valore del flag, dovrà essere effettuata in maniera differente.
<ul><li>Se il flag è “<em>true</em>” (nella nostra convenzione), dopo il login devo avere gli elementi per calcolare il digest in questo modo: <em><strong>HASH_NEW</strong> ( <strong>salt_new</strong>, HMAC ( pepper, HASH ( salt, password ) ) )</em>.</li>
<li>Se il flag è a <em>false</em>, calcolerò al solito: <em>HMAC ( pepper, <strong>HASH_NEW</strong> ( <strong>salt_new</strong>, password ) )</em></li></ul></li></ul>

<h3 id="caso-b-pepper-rotation">Caso B: Pepper rotation</h3>

<p>Supponiamo che a ruotare sia il pepper.
Procediamo sempre con il wrapping massivo su tutti gli utenti incapsulando il digest :</p>

<p><em>HMAC ( pepper, HASH ( salt, password ) )</em></p>

<p>con quello nuovo:</p>

<p><em>HMAC ( <strong>pepper_new</strong>, HMAC ( pepper, HASH ( salt, password ) ) )</em></p>

<p>mettendo il flag a “<em>true</em>”.</p>

<p>Come prima, una volta che gli utenti cominceranno a fare il login, se il flag è “<em>true</em>” innanzitutto verificherò che:</p>

<p><em>HMAC ( <strong>pepper_new</strong>, HMAC ( pepper, HASH ( salt, password ) ) )</em></p>

<p>sia uguale a ciò che è stato archiviato.
Se così fosse, ora che sono di nuovo in possesso della password, ripristinerò l&#39;HMAC con:</p>

<p><em>HMAC ( <strong>pepper_new</strong>, HASH ( salt, password ) )</em></p>

<p>memorizzandolo al posto di quello vecchio e rimettendo il flag a false.</p>

<p><strong>Considerazioni:</strong>
La modifica massiva delle password degli utenti, stavolta passa dall&#39;HSM e potrebbe essere un problema perché un HSM è progettato per scoraggiare flooding di richieste.</p>

<p>È vero che il pepper è sempre lo stesso per tutti gli utenti ma, come ricordavo prima, di solito un HSM non fornisce i suoi segreti ma solo <strong>i risultati crittografici delle loro applicazioni.</strong></p>

<p><a href="/aytin/tag:kdf" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">kdf</span></a> <a href="/aytin/tag:pbkdf2" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">pbkdf2</span></a> <a href="/aytin/tag:bcrypt" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">bcrypt</span></a> <a href="/aytin/tag:scrypt" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">scrypt</span></a> <a href="/aytin/tag:yescrypt" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">yescrypt</span></a> <a href="/aytin/tag:argon2" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">argon2</span></a> <a href="/aytin/tag:luks" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">luks</span></a> <a href="/aytin/tag:cryptography" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">cryptography</span></a> <a href="/aytin/tag:aes" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">aes</span></a> <a href="/aytin/tag:sha" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">sha</span></a> <a href="/aytin/tag:digest" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">digest</span></a> <a href="/aytin/tag:RainbowTable" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">RainbowTable</span></a> <a href="/aytin/tag:BruteForce" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">BruteForce</span></a> <a href="/aytin/tag:salt" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">salt</span></a> <a href="/aytin/tag:pepper" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">pepper</span></a> <a href="/aytin/tag:entropy" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">entropy</span></a> <a href="/aytin/tag:hsm" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">hsm</span></a> <a href="/aytin/tag:hmac" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">hmac</span></a> <a href="/aytin/tag:hash" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">hash</span></a></p>
]]></content:encoded>
      <guid>https://noblogo.org/aytin/archiviare-le-password-in-sicurezza-con-kdf-password-hashing-trilogia-della</guid>
      <pubDate>Thu, 29 Jan 2026 17:49:50 +0000</pubDate>
    </item>
    <item>
      <title>Come creare un file container cifrato</title>
      <link>https://noblogo.org/aytin/come-creare-un-file-container</link>
      <description>&lt;![CDATA[cartella con lucchetto&#xA;smallia href=&#34;https://www.freepik.com/free-photo/yellow-folder-with-security-key954598.htm#query=file%20lock&amp;position=1&amp;fromview=keyword&amp;track=ais&amp;uuid=7a92782d-c3bf-42db-ac2c-6c63c46f9edd&#34;Image by d3images/a/i on Freepik/small&#xA;&#xA;Introduzione a cryptsetup + LUKS&#xA;Supponiamo di voler creare una piccola cassaforte digitale come faremmo con Veracrypt. Ma senza Veracrypt.&#xA;&#xA;Ciò sarà possibile grazie a dm-crypt.&#xA;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 &#34;poggiare&#34; dm-crypt sopra ogni possibile mapping dei dispositivi e quindi può cifrare partizioni, volumi raid o volumi logici.&#xA;!--more--&#xA;Introduzione a cryptsetup + LUKS&#xA;  Inizializzazione&#xA;  Apertura&#xA;  Chiusura&#xA;Come creare il file vault&#xA;Header detachable e keyfile&#xA;Partizionare un &#34;volume&#34; cifrato&#xA;&#xA;\EDIT 02/03/2026\]: Riscrittura del par. 3 - &#34;Header detachable e keyfile&#34;, alla luce degli approfondimenti fatti con &#34;[Device Mapper: Luks + LVM&#34; e la &#34;Trilogia delle password - Vol. 1,2 e 3&#34;&#xA;&#xA;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.&#xA;&#xA;La configurazione della cifratura avviene di solito (non è l’unico modo ma è lo standard de facto) con l’utility cryptsetup + LUKS&#xA;&#xA;Come avviene di base la cifratura di un dispositivo?&#xA;&#xA;Le operazioni principali di cryptsetup (con estensioni LUKS) sono:&#xA;&#xA;luksFormat: stabilisce le modalità di cifratura (consigliato il default)&#xA;luksOpen: (deprecato. Si usa open \-\-type luks2) attiva la cifratura sul device associandolo (grazie a device mapper) ad un dispositivo a blocchi virtuale&#xA;luksClose (deprecato. Si usa close \-\-type luks2): disattiva la cifratura&#xA;&#xA;Inizializzazione&#xA;&#xA;attacco il dispositivo fisico, viene creata la entry per /dev/\id\dispositivo\a\blocchi\fisico\&#xA;cifratura del dispositivo a blocchi /dev/\id\dispositivo\a\blocchi\fisico\ con LUKS (cryptsetup luksFormat)&#xA;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.&#xA;Infine, partiziono e/o formatto nella maniera canonica il dispositivo a blocchi virtuale (cifrato) /dev/mapper/\nome\fittizio\.&#xA;&#xA;Apertura&#xA;&#xA;attacco il dispositivo, viene creata la entry per /dev/\id\dispositivo\a\blocchi\fisico\&#xA;attivo il dispositivo a blocchi virtuale che eroga le funzionalità di cifratura (cryptsetup open) e che creerà l’occorrenza sotto /dev/mapper/\nome\fittizio\&#xA;faccio il mount del dispositivo a blocchi virtuale /dev/mapper/ su un punto di montaggio che è una cartella che creerò per l’occasione&#xA;&#xA;Chiusura&#xA;&#xA;faccio l’unmount del punto di montaggio&#xA;chiudo il dispositivo a blocchi virtuale (cryptsetup close) /dev/mapper/\nome\fittizio\&#xA;stacco il dispositivo fisico&#xA;&#xA;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.&#xA;È veramente semplicesmalla id=&#34;linknota1&#34; title=&#34;vai alla nota 1&#34; href=&#34;#nota1&#34;supstrong [1] /strong/sup/a/small.&#xA;&#xA;Come creare il file vault&#xA;Divido le operazioni in 3 fasi:&#xA;&#xA;Init: fase di inizializzazione del &#34;dispositivo&#34; (il file). Andrà fatta solo la prima volta.&#xA;Open e Close: sono le operazioni che farò ogniqualvolta dovrò usare il container.&#xA;&#xA;Gli oggetti su cui andrà a lavorare sono:&#xA;&#xA;discocifrato.img: il file container cifrato.&#xA;discocifrato: il nome con cui discocifrato.img (o meglio, la sua rappresentazione come loop device visibile con losetup) viene mappato da device mapper.&#xA;disco\in\chiaro: il punto di montaggio.&#xA;&#xA;Init&#xA;creazione di una cartella che sarà il nostro punto di mount&#xA;mkdir discoinchiaro&#xA; &#xA;creazione di un file vuoto di 2 GiB&#xA;sudo fallocate -l 2g discocifrato.img&#xA; &#xA;preparazione cifratura LUKS &#xA;sudo cryptsetup luksFormat \&#xA;     --type luks2 \&#xA;     --hash=sha512 \&#xA;     --key-size=512 \&#xA;     discocifrato.img&#xA; &#xA;creazione dispositivo a blocchi virtuale che eroga le funzionalità di cifratura&#xA;sudo cryptsetup open \&#xA;     --type luks2 \&#xA;     discocifrato.img discocifrato&#xA; &#xA;formattazione&#xA;sudo mkfs.ext4 /dev/mapper/discocifrato&#xA;Open&#xA;creazione dispositivo a blocchi virtuale&#xA;sudo cryptsetup open \&#xA;     --type luks2 &#xA;     discocifrato.img discocifrato&#xA; &#xA;monta il dispositivo nel punto di montaggio&#xA;sudo mount -t ext4 -o defaults /dev/mapper/discocifrato discoinchiaro&#xA;Close&#xA;smonta il dispositivo&#xA;sudo umount discoinchiaro&#xA; &#xA;chiudo il dispositivo a blocchi virtuale staccandolo dai loop device&#xA;sudo cryptsetup close discocifrato&#xA;Header detachable e keyfile&#xA;Alziamo il livello di paranoia aggiungendo una complessità ulteriore. Creeremo il file vault nel seguente modo:&#xA;&#xA;il vault sarà inizializzato con rumore casuale&#xA;header detachable&#xA;offset custom&#xA;keyfile invece che password&#xA;default di argon2id (controlla il tuo default con cryptsetup benchmark)&#xA;&#xA;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.&#xA;&#xA;La presenza di rumore casuale su tutto il vault nella fase di inizializzazione impedirà qualunque tentativo di risalire alla tipologia di informazioni memorizzate, l&#39;assenza di zeri impedirà di capire quanti sono i dati (cifrati) memorizzati.&#xA;&#xA;L&#39;offset custom aggiunge un ulteriore tassello perchè nasconde il punto in cui inizia il payload.&#xA;&#xA;Un keyfile di dati binari casuali è infinitamente più robusto di una password  e può esser conservato separatamente insieme all&#39;header. La cifratura simmetrica del keyfile aggiunge ulteriore protezione, fatta magari usando una passphrase ottenuta col metodo Diceware.&#xA;&#xA;Infine la cifratura di default di luks2, argon2id, che è notoriamente sia CPU bound che GPU bound.&#xA;&#xA;Creo il keyfile protetto da una cifratura simmetrica di gpgsmalla id=&#34;linknota2&#34; title=&#34;vai alla nota 2&#34; href=&#34;#nota2&#34;supstrong [2] /strong/sup/small/a:&#xA;Creazione di un keyfile di 4KiB basata sulla pseudocasualità di /dev/urandom&#xA;creazione del keyfile&#xA;dd if=/dev/urandom bs=1024 count=4 | \&#xA;    gpg --yes -o discocifrato.key.gpg -c \&#xA;        --s2k-mode 3 \&#xA;        --s2k-count 32505856 \&#xA;        --s2k-cipher-algo aes256 \&#xA;        --s2k-digest-algo sha512 \&#xA;        --force-mdc -&#xA;Inizializzo il vault con dati casuali e poi lo formatto creando l&#39;header detachable del volume cifrato fornendo il keyfile invece della passphrase classica:&#xA;inizializzazione vault con dati casuali&#xA;Apro il vault in modalità &#39;plain&#39; senza header&#xA;sudo cryptsetup open \&#xA;    --type plain \&#xA;    --key-file /dev/urandom \&#xA;    --cipher aes-xts-plain64 \&#xA;    --key-size 512 \ &#xA;    discocifrato.img container&#xA;&#xA;scrivo degli zeri a cui, attraverso il motore di cifratura,&#xA;corrisponderanno dati casuali sul vault.&#xA;dd if=/dev/zero of=/dev/mapper/container status=progress&#xA;cryptsetup close container&#xA;&#xA;Formattazione vault&#xA;gpg -d discocifrato.key.gpg | \&#xA;    sudo cryptsetup luksFormat \&#xA;        --type luks2 \&#xA;        --key-file - \&#xA;        --header header.img \&#xA;        --offset 32768 \&#xA;        --hash sha512 \&#xA;        --key-size 512 \&#xA;        --cipher aes-xts-plain64 \&#xA;        discocifrato.img&#xA;Ed è così che l’apertura del volume cifrato è ora vincolata al possesso dell’header e del keyfile:&#xA;attivazione dispositivo a blocchi virtuale passando header e keyfile protetto da gpg&#xA;gpg -d discocifrato.key.gpg | \&#xA;    sudo cryptsetup open \&#xA;        --type luks2 \&#xA;&#x9;--header header.img \&#xA;&#x9;--key-file - \&#xA;&#x9;discocifrato.img discocifrato&#xA;Provando ad aprire container cifrato senza fornire l’header (non tanto il keyfile che è una passphrase evoluta) succede questo:&#xA;sudo cryptsetup open \&#xA;    --type luks2 \&#xA;    discocifrato.img discocifrato&#xA;Il dispositivo discocifrato.img non è un dispositivo LUKS valido.&#xA;Infine, un&#39;annotazione doverosa.&#xA;&#xA;Visto che il livello di paranoia abbiamo detto essere elevato, sarebbe consigliabile prestare una certa attenzione alla scelta dei nomi.&#xA;&#xA;Quelli usati finora avevano uno scopo &#34;didattico&#34;, che chiarisse il loro scopo  con un nome parlante.&#xA;&#xA;i nomi dei file relativi a header e keyfile sarebbe bene non fossero qualcosa del tpo &#34;header.img&#34; o &#34;discocifratokey.gpg&#34; che rivelano troppo esplicitamente la presenza di un disco cifrato o addirittura dello stesso LUKS. Meglio usare nomi più decontestualizzati, che so... &#34;sys-module-virtio.bin&#34; per l&#39;header e &#34;boot-vmlinuz-06.blob&#34; per il keyfile e via dicendo.&#xA;Partizionare un &#34;volume&#34; cifrato&#xA;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…&#xA;&#xA;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.&#xA;Init&#xA;mkdir discoinchiaro&#xA;sudo fallocate -l 2g discocifrato.img&#xA;sudo cryptsetup luksFormat --hash=sha512 --key-size=512 discocifrato.img&#xA;sudo cryptsetup open --type luks2 discocifrato.img discocifrato&#xA; &#xA;Creo 1 partizione primaria da 600 MiB, una estesa da 1400 MiB&#xA;contenente 2 partizioni logiche da 600 MiB e 829 MiB (mancano i&#xA;16 MiB dell&#39;intestazione luks e i 3 MiB delle intestazioni delle&#xA;partizioni primarie ed estese per un totale di 2GiB tondi tondi)&#xA;sudo fdisk /dev/mapper/discocifrato&#xA;n,,,,+600M,n,e,,,,n,,+600M,n,,,w&#xA; &#xA;il partizionamento potrebbe richiedere l&#39;esecuzione di partprobe&#xA;affinché il kernel carichi la tabella delle partizioni aggiornata&#xA;sudo partprobe /dev/mapper/discocifrato&#xA;&#xA;stato delle partizioni&#xA;sudo fdisk -l /dev/mapper/discocifrato&#xA;Disk /dev/mapper/discocifrato: 1,98 GiB, 2130706432 bytes, 520192 sectors&#xA;Units: sectors of 1 * 4096 = 4096 bytes&#xA;Sector size (logical/physical): 4096 bytes / 4096 bytes&#xA;I/O size (minimum/optimal): 4096 bytes / 4096 bytes&#xA;Disklabel type: dos&#xA;Disk identifier: 0x6d65a06f&#xA;&#xA;Device                     Boot  Start    End Sectors  Size Id Type&#xA;/dev/mapper/discocifrato1         256 153855  153600  600M 83 Linux&#xA;/dev/mapper/discocifrato2      153856 520191  366336  1,4G  5 Extended&#xA;/dev/mapper/discocifrato5      154112 307711  153600  600M 83 Linux&#xA;/dev/mapper/discocifrato6      307968 520191  212224  829M 83 Linux&#xA;&#xA;procedo con la formattazione&#xA;sudo mkfs.ext4 /dev/mapper/discocifrato1 #formatto la partizione&#xA;sudo mkfs.ext4 /dev/mapper/discocifrato5 #formatto la partizione&#xA;sudo mkfs.ext4 /dev/mapper/discocifrato6 #formatto la partizione&#xA;Rispetto all’init di prima, invece che formattare subito il device &#34;fisico&#34;, l’ho partizionato. Esattamente come avrei fatto con un dispositivo realmente fisico.&#xA;Open&#xA;sudo cryptsetup open --type luks2 discocifrato.img discocifrato&#xA; &#xA;di nuovo, potrebbe essere necessario a meno di non riavviare&#xA;sudo partprobe /dev/mapper/discocifrato&#xA; &#xA;mount delle partizioni&#xA;sudo mount -t ext4 -o defaults /dev/mapper/discocifrato1 discoinchiaro1&#xA;sudo mount -t ext4 -o defaults /dev/mapper/discocifrato5 discoinchiaro2&#xA;sudo mount -t ext4 -o defaults /dev/mapper/discocifrato6 discoinchiaro3&#xA;Close&#xA;sudo umount discoinchiaro1&#xA;sudo umount discoinchiaro2&#xA;sudo umount discoinchiaro3&#xA;sudo cryptsetup close discocifrato1&#xA;sudo cryptsetup close discocifrato2&#xA;sudo cryptsetup close discocifrato5&#xA;sudo cryptsetup close discocifrato6&#xA;sudo cryptsetup close discocifrato&#xA;&#xA;Note:&#xA;small&#xA;&#xA;cryptsetup --open luks2 e cryptsetup --close luks2, a id=&#34;nota1&#34;/aquando applicate direttamente al file container, includono implicitamente la parte di accoppiamento al loop device.&#xA;    In altre parole,&#xA;    &#xA;        cryptsetup open --type luks2 discocifrato.img discocifrato&#xA;        equivale a &#xA;        cryptsetup open --type luks2 $(losetup -Pf --show discocifrato.img) discocifrato&#xA;        e&#xA;        cryptsetup close --type luks2 discocifrato&#xA;        equivale aa href=&#34;#linknota1&#34; title=&#34;torna su&#34;supb [↵] /b/sup/a&#xA;        cryptsetup close --type luks2 discocifrato&#xA;    losetup -D&#xA;    a id=&#34;nota2&#34;/aSe si incorre in questo errore usando gpg:&#xA;&#xA;        gpg: cancelled by user&#xA;    gpg: error creating passphrase: Operation cancelled&#xA;    gpg: symmetric encryption of &#39;[stdin]&#39; failed: Operation cancelled&#xA;        vuol dire che la variabile d’ambiente GPGTTY non ha lo stesso valore del comando tty. Per ovviare basta settare correttamente la variabile:&#xA;        GPGTTY=$(tty)&#xA;    export GPGTTY&#xA;        come suggerito dalla documentazione GnuPG a href=&#34;#linknota2&#34; title=&#34;torna su&#34;supb [↵] /b/sup/a&#xA;&#xA;/small&#xA;&#xA;#cryptsetup #devicemapper #dmcrypt #gpg #loseup #luks #lvm #loopdevice #storage]]&gt;</description>
      <content:encoded><![CDATA[<p><img src="https://cyberdynesystem.files.wordpress.com/2024/05/yellow-folder-with-security-key.png" alt="cartella con lucchetto">
<small><i><a href="https://www.freepik.com/free-photo/yellow-folder-with-security-key_954598.htm#query=file%20lock&amp;position=1&amp;from_view=keyword&amp;track=ais&amp;uuid=7a92782d-c3bf-42db-ac2c-6c63c46f9edd" rel="nofollow">Image by d3images</a></i> on Freepik</small></p>

<h2 id="introduzione-a-cryptsetup-luks">Introduzione a cryptsetup + LUKS</h2>

<p>Supponiamo di voler creare una piccola cassaforte digitale come faremmo con Veracrypt. Ma senza Veracrypt.</p>

<p>Ciò sarà possibile grazie a <strong>dm-crypt</strong>.
dm-crypt è un modulo del kernel che usa il framework <strong>device mapper</strong> 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. <a href="#introduzione-a-cryptsetup-luks" rel="nofollow">Introduzione a cryptsetup + LUKS</a>
  2. <a href="#inizializzazione" rel="nofollow">Inizializzazione</a>
  2. <a href="#apertura" rel="nofollow">Apertura</a>
  2. <a href="#chiusura" rel="nofollow">Chiusura</a>
1. <a href="#come-creare-il-file-vault" rel="nofollow">Come creare il file vault</a>
1. <a href="#header-detachable-e-keyfile" rel="nofollow">Header detachable e keyfile</a>
1. <a href="#partizionare-un-volume-cifrato" rel="nofollow">Partizionare un “volume” cifrato</a></p>

<p><strong>[EDIT 02/03/2026]: Riscrittura del par. 3 – “Header detachable e keyfile”, alla luce degli approfondimenti fatti con “<a href="https://noblogo.org/aytin/device-mapper-luks-lvm" rel="nofollow">Device Mapper: Luks + LVM</a>” e la “Trilogia delle password – Vol. <a href="https://noblogo.org/aytin/come-generare-una-password-o-un-keyfile-sicuri-trilogia-della-password-1-di" rel="nofollow">1</a>,<a href="https://noblogo.org/aytin/come-valutare-la-resistenza-di-una-password-trilogia-della-password-2-di-3" rel="nofollow">2</a> e <a href="https://noblogo.org/aytin/archiviare-le-password-in-sicurezza-con-kdf-password-hashing-trilogia-della" rel="nofollow">3</a>“</strong></p>

<p>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.</p>

<p>La configurazione della cifratura avviene di solito (non è l’unico modo ma è lo standard de facto) con l’utility <strong>cryptsetup + LUKS</strong></p>

<p>Come avviene di base la cifratura di un dispositivo?</p>

<p>Le operazioni principali di cryptsetup (con estensioni LUKS) sono:</p>
<ul><li><strong>luksFormat</strong>: stabilisce le modalità di cifratura (consigliato il default)</li>
<li><strong>luksOpen</strong>: (deprecato. Si usa <strong>open --type luks2</strong>) attiva la cifratura sul device associandolo (grazie a device mapper) ad un dispositivo a blocchi virtuale</li>
<li><strong>luksClose</strong> (deprecato. Si usa <strong>close --type luks2</strong>): disattiva la cifratura</li></ul>

<h3 id="inizializzazione">Inizializzazione</h3>
<ul><li>attacco il dispositivo fisico, viene creata la entry per <em>/dev/&lt;id_dispositivo_a_blocchi_fisico&gt;</em></li>
<li>cifratura del dispositivo a blocchi <em>/dev/&lt;id_dispositivo_a_blocchi_fisico&gt;</em> con LUKS (<strong>cryptsetup luksFormat</strong>)</li>
<li>al di sopra del dispositivo a blocchi fisico, viene creato un dispositivo a blocchi virtuale, che troverò sotto <em>/dev/mapper/&lt;nome_fittizio&gt;</em>, che eroga le funzionalità di cifratura (<strong>cryptsetup open</strong>) al dispositivo a blocchi fisico sottostante.</li>
<li>Infine, partiziono e/o formatto nella maniera canonica il dispositivo a blocchi virtuale (cifrato) <em>/dev/mapper/&lt;nome_fittizio&gt;</em>.</li></ul>

<h3 id="apertura">Apertura</h3>
<ul><li>attacco il dispositivo, viene creata la entry per <em>/dev/&lt;id_dispositivo_a_blocchi_fisico&gt;</em></li>
<li>attivo il dispositivo a blocchi virtuale che eroga le funzionalità di cifratura (<strong>cryptsetup open</strong>) e che creerà l’occorrenza sotto <em>/dev/mapper/&lt;nome_fittizio&gt;</em></li>
<li>faccio il mount del dispositivo a blocchi virtuale <em>/dev/mapper/</em> su un punto di montaggio che è una cartella che creerò per l’occasione</li></ul>

<h3 id="chiusura">Chiusura</h3>
<ul><li>faccio l’unmount del punto di montaggio</li>
<li>chiudo il dispositivo a blocchi virtuale (<strong>cryptsetup close</strong>) <em>/dev/mapper/&lt;nome_fittizio&gt;</em></li>
<li>stacco il dispositivo fisico</li></ul>

<p>Fermo restando che questi restano i passaggi generali per qualunque dispositivo fisico, per creare un <strong>file container cifrato</strong> come farebbe Veracrypt, basta che cryptsetup <strong>formatti un file</strong> invece che un dispositivo a blocchi e automaticamente assocerà il file al primo loop device disponibile.
È veramente semplice<small><a id="link_nota_1" title="vai alla nota 1" href="#nota_1" rel="nofollow"><sup><strong> [1] </strong></sup></a></small>.</p>

<h2 id="come-creare-il-file-vault">Come creare il file vault</h2>

<p>Divido le operazioni in 3 fasi:</p>
<ul><li><strong>Init</strong>: fase di inizializzazione del “dispositivo” (il file). Andrà fatta solo la prima volta.</li>
<li><strong>Open</strong> e <strong>Close</strong>: sono le operazioni che farò ogniqualvolta dovrò usare il container.</li></ul>

<p>Gli oggetti su cui andrà a lavorare sono:</p>
<ul><li><strong>disco_cifrato.img</strong>: il file container cifrato.</li>
<li><strong>disco_cifrato</strong>: il nome con cui disco_cifrato.img (o meglio, la sua rappresentazione come loop device visibile con <strong>losetup</strong>) viene mappato da device mapper.</li>
<li><strong>disco_in_chiaro</strong>: il punto di montaggio.</li></ul>

<pre><code class="language-bash">## 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
</code></pre>

<pre><code class="language-bash">## 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
</code></pre>

<pre><code class="language-bash">## Close
# smonta il dispositivo
sudo umount disco_in_chiaro
 
# chiudo il dispositivo a blocchi virtuale staccandolo dai loop device
sudo cryptsetup close disco_cifrato
</code></pre>

<h2 id="header-detachable-e-keyfile">Header detachable e keyfile</h2>

<p>Alziamo il livello di paranoia aggiungendo una complessità ulteriore. Creeremo il file vault nel seguente modo:</p>
<ol><li>il vault sarà inizializzato con rumore casuale</li>
<li>header detachable</li>
<li>offset custom</li>
<li>keyfile invece che password</li>
<li>default di argon2id (controlla il tuo default con cryptsetup benchmark)</li></ol>

<p>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.</p>

<p>La presenza di rumore casuale su tutto il vault nella fase di inizializzazione impedirà qualunque tentativo di risalire alla tipologia di informazioni memorizzate, l&#39;assenza di zeri impedirà di capire quanti sono i dati (cifrati) memorizzati.</p>

<p>L&#39;offset custom aggiunge un ulteriore tassello perchè nasconde il punto in cui inizia il payload.</p>

<p>Un keyfile di dati binari casuali è infinitamente più robusto di una password  e può esser conservato separatamente insieme all&#39;header. La cifratura simmetrica del keyfile aggiunge ulteriore protezione, fatta magari usando una passphrase ottenuta col <strong><a href="https://noblogo.org/aytin/come-valutare-la-resistenza-di-una-password-trilogia-della-password-2-di-3#metodo-diceware" rel="nofollow">metodo Diceware</a>.</strong></p>

<p>Infine la cifratura di default di luks2, argon2id, che è notoriamente sia CPU bound che GPU bound.</p>

<p>Creo il keyfile protetto da una cifratura simmetrica di gpg<small><a id="link_nota_2" title="vai alla nota 2" href="#nota_2" rel="nofollow"><sup><strong> [2] </strong></sup></small></a>:</p>

<pre><code class="language-bash">## 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-mode 3 \
        --s2k-count 32505856 \
        --s2k-cipher-algo aes256 \
        --s2k-digest-algo sha512 \
        --force-mdc -
</code></pre>

<p>Inizializzo il vault con dati casuali e poi lo formatto creando l&#39;header detachable del volume cifrato fornendo il keyfile invece della passphrase classica:</p>

<pre><code class="language-bash"># inizializzazione vault con dati casuali
# Apro il vault in modalità &#39;plain&#39; senza header
sudo cryptsetup open \
    --type plain \
    --key-file /dev/urandom \
    --cipher aes-xts-plain64 \
    --key-size 512 \ 
    disco_cifrato.img container

# scrivo degli zeri a cui, attraverso il motore di cifratura,
# corrisponderanno dati casuali sul vault.
dd if=/dev/zero of=/dev/mapper/container status=progress
cryptsetup close container

# Formattazione vault
gpg -d disco_cifrato.key.gpg | \
    sudo cryptsetup luksFormat \
        --type luks2 \
        --key-file - \
        --header header.img \
        --offset 32768 \
        --hash sha512 \
        --key-size 512 \
        --cipher aes-xts-plain64 \
        disco_cifrato.img
</code></pre>

<p>Ed è così che l’apertura del volume cifrato è ora <strong>vincolata al possesso dell’header e del keyfile</strong>:</p>

<pre><code class="language-bash"># 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
</code></pre>

<p>Provando ad aprire container cifrato <strong>senza fornire l’header</strong> (non tanto il keyfile che è una passphrase evoluta) succede questo:</p>

<pre><code class="language-bash">sudo cryptsetup open \
    --type luks2 \
    disco_cifrato.img disco_cifrato
Il dispositivo disco_cifrato.img non è un dispositivo LUKS valido.
</code></pre>

<p>Infine, un&#39;annotazione doverosa.</p>

<p>Visto che il livello di paranoia abbiamo detto essere elevato, sarebbe consigliabile prestare una certa attenzione alla scelta dei nomi.</p>

<p>Quelli usati finora avevano uno scopo “didattico”, che chiarisse il loro scopo  con un nome parlante.</p>

<p>i nomi dei file relativi a header e keyfile sarebbe bene non fossero qualcosa del tpo “header.img” o “discocifrato_key.gpg” che rivelano troppo esplicitamente la presenza di un disco cifrato o addirittura dello stesso LUKS. Meglio usare nomi più decontestualizzati, che so... “<code>sys-module-virtio.bin</code>” per l&#39;header e “<code>boot-vmlinuz-06.blob</code>” per il keyfile e via dicendo.</p>

<h2 id="partizionare-un-volume-cifrato">Partizionare un “volume” cifrato</h2>

<p>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…</p>

<p>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.</p>

<pre><code class="language-bash">## 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&#39;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&#39;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
</code></pre>

<p>Rispetto all’init di prima, invece che formattare subito il device “fisico”, l’ho partizionato. Esattamente come avrei fatto con un dispositivo realmente fisico.</p>

<pre><code class="language-bash">## 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
</code></pre>

<pre><code class="language-bash">## 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
</code></pre>

<p><strong>Note:</strong>
<small></p>
<ol><li><p><code>cryptsetup --open luks2</code> e <code>cryptsetup --close luks2</code>, <a id="nota_1"></a>quando applicate direttamente al file container, includono implicitamente la parte di accoppiamento al loop device.
In altre parole,</p>

<pre><code class="language-bash">cryptsetup open --type luks2 disco_cifrato.img disco_cifrato
</code></pre>

<p>equivale a</p>

<pre><code class="language-bash">cryptsetup open --type luks2 $(losetup -Pf --show disco_cifrato.img) disco_cifrato
</code></pre>

<p>e</p>

<pre><code class="language-bash">cryptsetup close --type luks2 disco_cifrato
</code></pre>

<p>equivale a<a href="#link_nota_1" title="torna su" rel="nofollow"><sup><b> [↵] </b></sup></a></p>

<pre><code class="language-bash">cryptsetup close --type luks2 disco_cifrato
losetup -D
</code></pre></li>

<li><p><a id="nota_2"></a>Se si incorre in questo errore usando gpg:</p>

<pre><code class="language-bash">gpg: cancelled by user
gpg: error creating passphrase: Operation cancelled
gpg: symmetric encryption of &#39;[stdin]&#39; failed: Operation cancelled
</code></pre>

<p>vuol dire che la variabile d’ambiente <code>GPG_TTY</code> non ha lo stesso valore del comando tty. Per ovviare basta settare correttamente la variabile:</p>

<pre><code class="language-bash">GPG_TTY=$(tty)
export GPG_TTY
</code></pre>

<p>come suggerito dalla <a href="https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html#Invoking-GPG_002dAGENT" rel="nofollow">documentazione GnuPG</a> <a href="#link_nota_2" title="torna su" rel="nofollow"><sup><b> [↵] </b></sup></a></p></li></ol>

<p></small></p>

<p><a href="/aytin/tag:cryptsetup" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">cryptsetup</span></a> <a href="/aytin/tag:devicemapper" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devicemapper</span></a> <a href="/aytin/tag:dmcrypt" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">dmcrypt</span></a> <a href="/aytin/tag:gpg" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">gpg</span></a> <a href="/aytin/tag:loseup" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">loseup</span></a> <a href="/aytin/tag:luks" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">luks</span></a> <a href="/aytin/tag:lvm" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">lvm</span></a> <a href="/aytin/tag:loopdevice" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">loopdevice</span></a> <a href="/aytin/tag:storage" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">storage</span></a></p>
]]></content:encoded>
      <guid>https://noblogo.org/aytin/come-creare-un-file-container</guid>
      <pubDate>Fri, 10 May 2024 21:32:57 +0000</pubDate>
    </item>
  </channel>
</rss>