Installation des clés et déchiffrement par le TPM
Tout est maintenant en place, et nous allons pouvoir installer nos clés dans l'UEFI, puis démarrer sur notre système nouvellement installé.
Installation des clés
La mise en place initiale des clés se fait dans l'interface UEFI, il faut donc placer les clés à
installer (les trois fichiers .auth généré précédemment) dans un emplacement accessible depuis
l'UEFI. Le plus simple est en général de les copier à la racine de la partition EFI. Dans certains
cas il est également possible d'utiliser un support amovible comme une clé USB. Il faut ensuite
redémarrer la machine, et entrer dans le menu de configuration EFI. Cela peut se faire soit en
appuyant sur une touche au démarrage (souvent Suppr ou F2, parfois F1 ou d'autre encore), soit
directement depuis un système déjà démarré en utilisant la commande suivante :
systemctl reboot --firmware
Le procédé exact d'installation des clés, et en particulier les libellés utilisés dans les menus, varient selon les constructeurs. Mais la procédure générale est toujours la même : il faut d'abord supprimer la Platform Key existante, pour pouvoir ensuite installer nos clés. Un système sans PK est dit en setup mode, l'option à utiliser dans le menu UEFI peut donc être nommée de deux manières : soit elle parle de setup mode (ou quelque chose d'approchant), soit elle parle de supprimer la clé de plateforme (ou parfois l'ensemble des clés). Selon que l'UEFI offre seulement une option de suppression de l'ensemble des clés, ou des options séparées par type de clés, il peut être nécessaire d'effacer aussi les KEK et db avant d'ajouter les vôtres, en particulier si vous ne souhaitez pas conserver l'ensemble des clés préinstallées.
Une fois la PK supprimée, on peut installer nos clés. Il existe en général un menu qui permet de les importer depuis la partition EFI. Dès que nous aurons installé notre propre PK, le système repassera en mode de fonctionnement ordinaire, et l'installation des autres clés (KEK et db) pourrait, dans certains cas, ne plus être possible (même si, en principe, nos clés devraient être acceptées, puisqu'elles sont signées). Il est donc plus prudent d'installer la PK en dernier.
Le système devrait maintenant être en capacité de démarrer. Toutefois, la clé de déchiffrement du disque n'est pas encore enrollée dans le TPM, le déchiffrement va donc nécessiter la saisie de la phrase de passe LUKS.
Configuration du déchiffrement par le TPM au démarrage
Pour ne plus avoir à saisir la phrase de passe à chaque démarrage, nous allons enregistrer une seconde clé LUKS qui sera stockée dans le TPM. Le TPM possède plusieurs registres nommés PCR, pour Platform Configuration Registers, et numérotés de 0 à 23. Les valeurs contenues dans ces registres sont des hash sha1 et/ou sha256 de différents paramètres mesurés au démarrage comme le code exécutable de l'UEFI ou l'état secure-boot. La clé LUKS sera stockée dans le TPM sous une forme chiffrée par certains de ces registres, et ne pourra ainsi être restituée que si les registres PCR choisis ont les mêmes valeurs que lors de son enregistrement. L'utilisation de chacun de ces registres (pour certains cas l'usage est spécifique à Linux) peut être consulté sur le wiki d'Arch.
Nous utiliserons ici les PCR 0, 2 et 7. D'autres combinaisons sont possibles, par exemple en incluant les n°1 et 3, on verrouille non seulement la version de l'UEFI, mais aussi sa configuration. L'essentiel est de bien inclure le n°7 qui correspond à l'état secure-boot, ce qui inclus non seulement la liste des clés (PK, KEK et db) installées, mais également la clé particulière utilisée pour signer l'OS démarré. Puisque nous avons signé notre noyau avec notre propre clé privée, qui n'est partagée avec personne d'autre, nous avons donc la garantie que notre partition LUKS ne pourra être déchiffrée sans phrase de passe que lors du démarrage de notre UKI, signé par notre clé. En cas de démarrage, par exemple, depuis une image live publiée par une distribution, donc via shim, signé par la clé de Microsoft, la valeur du PCR n°7 sera différente, et le TPM ne fournira pas la clé LUKS. Les opérations qui suivent ne doivent donc être réalisées qu'après avoir réussi à démarrer sur l'UKI signée par notre clé secure-boot, sans quoi les valeurs des PCR utilisées ne seraient pas les bonnes.
Création de la clé et enregistrement dans le TPM
Pour commencer, nous allons créer une nouvelle clé que nous allons stocker dans un premier temps dans un fichier. Elle est convertie en notation hexadecimal car l'outil que nous utiliserons ensuite pour l'enregistrer dans le TPM n'accepte pas les données binaires. Cette clé doit rester secrète, on restreint donc immédiatement les droits au seul propriétaire :
dd if=/dev/random bs=64 count=1 | xxd -p -c64 | tr -d '\n' > /root/cle-luks
chmod 0400 /root/cle-luks
Nous ajoutons ensuite notre nouvelle clé à notre partition LUKS (cette commande demandera la saisie de la phrase de passe existante) :
cryptsetup luksAddKey --pbkdf-force-iterations 4 --pbkdf-memory 32 /dev/nvme0n1p2 /root/cle-luks
Les paramètres --pbkdf-* concernent la fonction dérivation de
clé : une phrase de passe
n'est souvent pas assez longue pour servir directement de clé, LUKS utilise donc un algorithme qui
permet de calculer, à partir de la phrase de passe, une clé suffisemment longue. Cette fonction est
paramètrable, pour pouvoir ajuster le bon compromis entre sécurité et rapidité, en fonction des
capacités du matériel. Ici nous avons créé une clé dans un fichier, avec une taille suffisante. On
peut donc configurer la fonction de dérivation avec une complexité minimale, pour gagner en rapidité
de déchiffrement.
On enregistre maintenant cette nouvelle clé dans le TPM avec la commande tpm2-initramfs-tool,
fournie par le paquet du même nom. C'est là que nous spécifions les numéros des registres PCR à
utiliser :
tpm2-initramfs-tool seal --data $(cat /root/cle-luks) --pcrs 0,2,7
Obtention de la clé pour déchiffrement au démarrage
La clé étant enregistré dans le TPM, nous allons pouvoir la récupérer pour déchiffrer le disque sans saisir la phrase de passe lors du démarrage. Cette récupération est susceptible d'échouer, par exemple en cas de mise à jour de l'UEFI, induisant un changement de valeur d'un des PCR ; il faut donc, en cas d'échec, que le système demande la phrase de passe, pour permettre de démarrer le système et de ré-enregistrer la clé avec les nouvelles valeurs, avec la commande ci-dessus. Ce processus devra se dérouler à l'exécution de l'UKI chargée depuis la partition EFI, nous allons donc placer tout ce qui est nécessaire dans l'initrd, qui est inclus dans l'UKI.
On commence par créer le script suivant, dans le fichier /etc/initramfs-tools/tpm2-cryptsetup :
#!/bin/sh
[ "$CRYPTTAB_TRIED" -lt "1" ] && exec tpm2-initramfs-tool unseal --pcrs 0,2,7
/usr/bin/askpass "Phrase de passe pour $CRYPTTAB_SOURCE ($CRYPTTAB_NAME) : "
Et on le rend exécutable :
chmod +x /etc/initramfs-tools/tpm2-cryptsetup
Il faut maintenant faire en sorte que ce script, ainsi que les exécutables dont il a besoin, soient
inclus dans l'initrd. Nous allons pour cela créer un hook dans
/etc/initramfs-tools/hooks/tpm2-initramfs-tool :
#!/bin/sh
PREREQ=""
prereqs()
{
echo "$PREREQ"
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
. /usr/share/initramfs-tools/hook-functions
copy_exec /usr/lib/x86_64-linux-gnu/libtss2-tcti-device.so.0
copy_exec /usr/bin/tpm2-initramfs-tool
copy_exec /usr/lib/cryptsetup/askpass /usr/bin
copy_exec /etc/initramfs-tools/tpm2-cryptsetup
copy_exec /usr/bin/sha256sum
Les cinq dernières lignes copient les fichiers nécessaires. Tout ce qui précède est un entête
nécessaire aux hooks initramfs, comme cela est indiqué dans la page de manuel d'initramfs-tools
(section Header). Ce script doit lui aussi être rendu exécutable :
chmod +x /etc/initramfs-tools/hooks/tpm2-initramfs-tool
Il nous reste à mettre à jour le fichier /etc/crypttab pour indiquer le script à utiliser pour le
déchiffrement (le seul changement par rapport à la version précédente est l'ajout de l'option
keyscript en fin de ligne) :
# <target name> <source device> <key file> <options>
nvme0n1p2_crypt /dev/nvme0n1p2 none luks,discard,keyscript=/etc/initramfs-tools/tpm2-cryptsetup
Vérification du système de fichier racine
Ce type d'installation est potentiellement vulnérable à une attaque, décrite dans cet article (en anglais), qui consiste à remplacer le volume LUKS par un volume similaire, contrôlé par l'attaquant et contenant un processus d'init malveillant qui pourra alors obtenir la clé depuis le TPM, puisqu'aucun PCR n'aura été altéré. Pour s'en prémunir, il faut s'assurer que le système de fichier déchiffré est bien le nôtre, avant de transférer le contrôle au processus d'init qu'il contient.
La solution présentée ici est basée sur ma propre compréhension de l'article cité ci-dessus, et elle est assez différente de ce qui y est suggéré (une solution qui authentifie la partition chiffrée à partir de sa clé, via le PRC 15 du TPM, et dont la mise en œuvre est basée sur l'utilisation de systemd dans l'initrd, ce qui n'est pas le cas ici).
N'ayant pris connaissance de ce problème qu'à la toute fin de la rédaction de ce guide, j'ai opté pour une solution qui changerait le moins possible ce qui était déjà rédigé (et déjà en place sur ma propre installation). Il s'agit donc d'une proposition personnelle, sans aucune garantie.
Pour vérifier l'intégrité du système de fichier, nous allons utiliser une somme de contrôle d'un fichier secret, présent uniquement dans notre volume chiffré, et stable dans le temps. La clé privée secure-boot spécifique à notre machine que nous avons déjà mise en place correspond à ces critères, c'est donc elle que nous allons vérifier avec un script écrit sur le modèle suivant :
#!/bin/sh
PREREQ=""
prereqs()
{
echo "$PREREQ"
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
. /scripts/functions
attendu='<valeur de la somme de contrôle>'
mesure=$(echo "<une valeur aléatoire de salage>" \
| cat - $rootmnt/chemin/vers/la/clé/privée.key \
| sha256sum \
| cut -d ' ' -f 1 \
| tr -d '\n')
if [ "$mesure" != "$attendu" ]
then
panic "Système de fichier racine invalide"
fi
Les valeurs entre < et > sont bien sûr à adapter, ainsi que le chemin vers la clé privée. Le
salage permet d'éviter une attaque par collision sur la somme de contrôle, par exemple au moyen de
rainbow tables, cf. l'article
Wikipédia pour plus de détails. La valeur
attendu est à déterminée en utilisant la commande de mesure, une fois la valeur de salage fixée.
Attention, la fonction panic arrête le processus de démarrage. En cas d'erreur dans le script, on
risque donc de rendre le système impossible à démarrer. Il faudra alors démarrer sur un support live
et déchiffrer manuellement le disque (en saississant la phrase de passe) pour tout débloquer. Il est
donc plus prudent de remplacer, dans un premier temps, l'appel à panic par un simple affichage,
et de ne le rétablir qu'une fois qu'on s'est assuré que tout fonctionne bien.
Ce script est à placer dans le répertoire /etc/initramfs-tools/scripts/local-bottom/ (en le
rendant exécutable, comme d'habitude) ce qui permet son exécution après le montage du système de
fichier racine, mais avant l'exécution du processus d'init qui s'y trouve.
Il ne reste plus qu'à forcer la regénération de l'initrd :
update-initramfs -u -k all
Au redémarrage, la partition LUKS devrait désormais être déchiffrée automatiquement, sans avoir à saisir la phrase de passe.