Android 4.4 et versions ultérieures sont compatibles avec le démarrage validé via la fonctionnalité de noyau device-mapper-verity (dm-verity) facultative, qui fournit une vérification transparente de l'intégrité des appareils de bloc. dm-verity permet d'éviter les rootkits persistants qui peuvent conserver les droits d'administration et compromettre les appareils. Cette fonctionnalité permet aux utilisateurs Android de s'assurer, au démarrage d'un appareil, qu'il est dans le même état que lors de sa dernière utilisation.
Les applications potentiellement dangereuses (PHA) disposant de droits d'accès racine peuvent se cacher des programmes de détection et se masquer de toute autre manière. Le logiciel d'enracinement peut le faire, car il est souvent plus privilégié que les détecteurs, ce qui lui permet de "mentir" aux programmes de détection.
La fonctionnalité dm-verity vous permet d'examiner un appareil de bloc, la couche de stockage sous-jacente du système de fichiers, et de déterminer s'il correspond à sa configuration attendue. Pour ce faire, il utilise un arbre de hachage cryptographique. Pour chaque bloc (généralement 4 ko), il existe un hachage SHA256.
Étant donné que les valeurs de hachage sont stockées dans un arbre de pages, seul le hachage racine de premier niveau doit être approuvé pour vérifier le reste de l'arbre. La possibilité de modifier l'un des blocs équivaut à casser le hachage cryptographique. Consultez le schéma suivant pour voir une représentation de cette structure.

Figure 1 : Table de hachage dm-verity
Une clé publique est incluse dans la partition de démarrage, qui doit être validée en externe par le fabricant de l'appareil. Cette clé permet de vérifier la signature de ce hachage et de confirmer que la partition système de l'appareil est protégée et inchangée.
Conditions de fonctionnement
La protection dm-verity réside dans le noyau. Par conséquent, si un logiciel d'enracinement compromet le système avant le démarrage du kernel, il conserve cet accès. Pour atténuer ce risque, la plupart des fabricants vérifient le noyau à l'aide d'une clé gravée sur l'appareil. Cette clé ne peut pas être modifiée une fois que l'appareil quitte l'usine.
Les fabricants utilisent cette clé pour valider la signature sur le bootloader de premier niveau, qui à son tour valide la signature sur les niveaux suivants, le bootloader de l'application et finalement le noyau. Chaque fabricant souhaitant profiter du démarrage validé doit disposer d'une méthode permettant de vérifier l'intégrité du noyau. En supposant que le noyau a été validé, il peut examiner un périphérique de bloc et le valider lorsqu'il est installé.
Pour vérifier un appareil de bloc, vous pouvez hacher directement son contenu et le comparer à une valeur stockée. Toutefois, tenter de valider un appareil de bloc entier peut prendre un certain temps et consommer une grande partie de l'alimentation de l'appareil. Le démarrage des appareils prenait beaucoup de temps, et leur batterie était considérablement déchargée avant leur utilisation.
À la place, dm-verity vérifie les blocs individuellement et uniquement lorsqu'ils sont consultés. Lors de la lecture en mémoire, le bloc est haché en parallèle. Le hachage est ensuite validé dans l'arborescence. Étant donné que la lecture du bloc est une opération très coûteuse, la latence introduite par cette validation au niveau du bloc est relativement nominale.
En cas d'échec de la validation, l'appareil génère une erreur d'E/S indiquant que le bloc ne peut pas être lu. Il semble que le système de fichiers a été corrompu, comme prévu.
Les applications peuvent choisir de continuer sans les données résultantes, par exemple lorsque ces résultats ne sont pas nécessaires à la fonction principale de l'application. Toutefois, si l'application ne peut pas continuer sans les données, elle échoue.
Correction d'erreurs par anticipation
Android 7.0 et les versions ultérieures améliorent la robustesse de dm-verity avec la correction d'erreurs par anticipation (FEC). L'implémentation AOSP commence par le code de correction d'erreur Reed-Solomon commun et applique une technique appelée entrelacement pour réduire les frais généraux d'espace et augmenter le nombre de blocs corrompus pouvant être récupérés. Pour en savoir plus sur la FEC, consultez la section Verified Boot strictement appliqué avec correction d'erreurs.Implémentation
Résumé
- Générez une image système ext4.
- Générez un arbre de hachage pour cette image.
- Créez une table dm-verity pour cet arbre de hachage.
- Signez cette table dm-verity pour générer une signature de table.
- Groupez la signature de la table et la table dm-verity dans les métadonnées Verity.
- Concatenate l'image système, les métadonnées de validation et l'arborescence de hachage.
Pour obtenir une description détaillée de l'arborescence de hachage et du tableau dm-verity, consultez The Chromium Projects - Verified Boot (Projets Chromium : démarrage validé).
Générer l'arborescence de hachage
Comme indiqué dans l'introduction, l'arborescence de hachage est un élément essentiel de dm-verity. L'outil cryptsetup génère un arbre de hachage pour vous. Vous pouvez également utiliser un autre élément compatible défini ici:
<your block device name> <your block device name> <block size> <block size> <image size in blocks> <image size in blocks + 8> <root hash> <salt>
Pour former le hachage, l'image système est divisée à la couche 0 en blocs de 4 ko, chacun étant associé à un hachage SHA256. La couche 1 est constituée en ne joignant que ces hachages SHA256 en blocs de 4 ko, ce qui donne une image beaucoup plus petite. La couche 2 est formée de manière identique, avec les hachages SHA256 de la couche 1.
Cette opération est répétée jusqu'à ce que les hachages SHA256 de la couche précédente puissent tenir dans un seul bloc. Lorsque vous obtenez le SHA-256 de ce bloc, vous disposez du hachage racine de l'arborescence.
La taille de l'arborescence de hachage (et l'espace disque utilisé en conséquence) varie en fonction de la taille de la partition validée. En pratique, la taille des arbres de hachage tend à être faible, souvent inférieure à 30 Mo.
Si un bloc d'une couche n'est pas complètement rempli naturellement par les hachages de la couche précédente, vous devez le remplir de zéros pour obtenir les 4 ko attendus. Cela vous permet de savoir que l'arborescence de hachage n'a pas été supprimée et qu'elle est plutôt complétée par des données vides.
Pour générer l'arborescence de hachage, concaténez les hachages de la couche 2 sur ceux de la couche 1, ceux de la couche 3 sur ceux de la couche 2, etc. Écrivez tout cela sur le disque. Notez que cela ne fait pas référence à la couche 0 du hachage racine.
Pour récapituler, l'algorithme général de construction de l'arbre de hachage est le suivant:
- Choisissez un sel aléatoire (encodage hexadécimal).
- Déssparsez votre image système en blocs de 4 ko.
- Pour chaque bloc, obtenez son hachage SHA256 (salé).
- Concaténez ces hachages pour former un niveau
- Ajoutez des zéros au niveau pour atteindre une limite de bloc de 4 ko.
- Concatenate le niveau à votre arbre de hachage.
- Répétez les étapes 2 à 6 en utilisant le niveau précédent comme source du suivant jusqu'à ce qu'il ne reste qu'un seul hachage.
Le résultat est un hachage unique, qui est votre hachage racine. Cela et votre sel sont utilisés lors de la création de votre table de mappage dm-verity.
Créer la table de mappage dm-verity
Créez la table de mappage dm-verity, qui identifie le périphérique de bloc (ou cible) pour le noyau et l'emplacement de l'arborescence de hachage (qui est la même valeur). Ce mappage est utilisé pour la génération et le démarrage de fstab
. Le tableau identifie également la taille des blocs et hash_start, l'emplacement de départ de l'arborescence de hachage (plus précisément, son numéro de bloc à partir du début de l'image).
Pour obtenir une description détaillée des champs de la table de mappage des cibles de validation, consultez cryptsetup.
Signer le tableau dm-verity
Signez la table dm-verity pour générer une signature de table. Lors de la validation d'une partition, la signature de la table est validée en premier. Cela se fait à l'aide d'une clé sur votre image de démarrage à un emplacement fixe. Les clés sont généralement incluses dans les systèmes de compilation des fabricants pour être automatiquement incluses sur les appareils à un emplacement fixe.
Pour valider la partition avec cette signature et cette combinaison de clés:
- Ajoutez une clé RSA-2048 au format compatible avec libmincrypt à la partition
/boot
à l'emplacement/verity_key
. Identifiez l'emplacement de la clé utilisée pour valider l'arborescence de hachage. - Dans le fichier fstab de l'entrée concernée, ajoutez
verify
aux indicateursfs_mgr
.
Regrouper la signature de la table dans les métadonnées
Regroupez la signature de la table et la table dm-verity dans les métadonnées Verity. L'ensemble du bloc de métadonnées est versionné afin qu'il puisse être étendu, par exemple pour ajouter un deuxième type de signature ou modifier un ordre.
Pour vérification, un nombre magique est associé à chaque ensemble de métadonnées de table qui permet de l'identifier. Étant donné que la longueur est incluse dans l'en-tête de l'image système ext4, cela permet de rechercher les métadonnées sans connaître le contenu des données elles-mêmes.
Cela permet de vérifier que vous n'avez pas choisi de valider une partition non validée. Si tel est le cas, l'absence de ce nombre magique interrompt le processus de validation. Ce nombre ressemble à 0xb001b001
.
Les valeurs d'octets au format hexadécimal sont les suivantes:
- premier octet = b0
- Deuxième octet = 01
- Troisième octet = b0
- Quatrième octet = 01
Le diagramme suivant illustre la répartition des métadonnées de validation:
<magic number>|<version>|<signature>|<table length>|<table>|<padding> \-------------------------------------------------------------------/ \----------------------------------------------------------/ | | | | 32K block content
Ce tableau décrit ces champs de métadonnées.
Tableau 1. Vérifier les champs de métadonnées
Champ | Objectif | Taille | Valeur |
---|---|---|---|
nombre magique | utilisé par fs_mgr comme vérification de validité | 4 octets | 0xb001b001 |
version | utilisé pour versionner le bloc de métadonnées | 4 octets | actuellement 0 |
signature | la signature de la table au format PKCS1.5 rempli | 256 octets | |
longueur de la table | longueur de la table dm-verity en octets | 4 octets | |
table | le tableau dm-verity décrit précédemment | octets de la longueur de la table | |
padding | Cette structure est remplie de zéros pour atteindre une longueur de 32 ko. | 0 |
Optimiser dm-verity
Pour obtenir les meilleures performances possibles avec dm-verity, procédez comme suit:
- Dans le kernel, activez NEON SHA-2 pour ARMv7 et les extensions SHA-2 pour ARMv8.
- Testez différents paramètres de prélecture et de prefetch_cluster pour trouver la meilleure configuration pour votre appareil.