Android 4.4 et versions ultérieures prennent en charge le démarrage vérifié via la fonctionnalité facultative du noyau device-mapper-verity (dm-verity), qui fournit une vérification transparente de l'intégrité des périphériques de bloc. dm-verity aide à empêcher les rootkits persistants qui peuvent conserver les privilèges root et compromettre les appareils. Cette fonctionnalité permet aux utilisateurs d'Android de s'assurer qu'au démarrage d'un appareil, il est dans le même état que lors de sa dernière utilisation.
Les applications potentiellement nuisibles (PHA) avec des privilèges root peuvent se cacher des programmes de détection et se masquer autrement. Le logiciel d'enracinement peut le faire car il est souvent plus privilégié que les détecteurs, ce qui permet au logiciel de "mentir" aux programmes de détection.
La fonction dm-verity vous permet d'examiner un périphérique bloc, la couche de stockage sous-jacente du système de fichiers, et de déterminer s'il correspond à la configuration attendue. Pour ce faire, il utilise un arbre de hachage cryptographique. Pour chaque bloc (généralement 4k), il existe un hachage SHA256.
Étant donné que les valeurs de hachage sont stockées dans une arborescence de pages, seul le hachage "racine" de niveau supérieur doit être approuvé pour vérifier le reste de l'arborescence. La possibilité de modifier l'un des blocs équivaudrait à casser le hachage cryptographique. Voir le schéma suivant pour une représentation de cette structure.

Figure 1. Table de hachage dm-verity
Une clé publique est incluse sur la partition de démarrage, qui doit être vérifiée en externe par le fabricant de l'appareil. Cette clé est utilisée pour vérifier la signature de ce hachage et confirmer que la partition système de l'appareil est protégée et inchangée.
Opération
La protection dm-verity réside dans le noyau. Donc, si le logiciel d'enracinement compromet le système avant que le noyau n'apparaisse, il conservera cet accès. Pour atténuer ce risque, la plupart des fabricants vérifient le noyau à l'aide d'une clé gravée dans l'appareil. Cette clé n'est pas modifiable une fois que l'appareil quitte l'usine.
Les fabricants utilisent cette clé pour vérifier la signature sur le chargeur de démarrage de premier niveau, qui à son tour vérifie la signature sur les niveaux suivants, le chargeur de démarrage de l'application et éventuellement le noyau. Chaque fabricant souhaitant profiter du démarrage vérifié devrait avoir une méthode pour vérifier l'intégrité du noyau. En supposant que le noyau a été vérifié, le noyau peut examiner un périphérique bloc et le vérifier lorsqu'il est monté.
Une façon de vérifier un périphérique bloc consiste à hacher directement son contenu et à le comparer à une valeur stockée. Cependant, tenter de vérifier un périphérique de bloc entier peut prendre une période prolongée et consommer une grande partie de l'énergie d'un périphérique. Les appareils mettraient de longues périodes à démarrer et seraient ensuite considérablement drainés avant utilisation.
Au lieu de cela, dm-verity vérifie les blocs individuellement et uniquement lors de l'accès à chacun d'eux. Lorsqu'il est lu en mémoire, le bloc est haché en parallèle. Le hachage est ensuite vérifié dans l'arborescence. Et comme la lecture du bloc est une opération si coûteuse, la latence introduite par cette vérification au niveau du bloc est relativement nominale.
Si la vérification échoue, le dispositif génère une erreur d'E/S indiquant que le bloc ne peut pas être lu. Il apparaîtra comme si le système de fichiers a été corrompu, comme prévu.
Les applications peuvent choisir de poursuivre sans les données résultantes, par exemple lorsque ces résultats ne sont pas nécessaires à la fonction principale de l'application. Cependant, si l'application ne peut pas continuer sans les données, elle échouera.
Correction d'erreur directe
Android 7.0 et supérieur améliore la robustesse de dm-verity avec la correction d'erreur directe (FEC). L'implémentation AOSP commence par le code correcteur d'erreurs Reed-Solomon commun et applique une technique appelée entrelacement pour réduire la surcharge d'espace et augmenter le nombre de blocs corrompus pouvant être récupérés. Pour plus de détails sur FEC, consultez Démarrage vérifié strictement appliqué avec correction d'erreur .Mise en œuvre
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 produire une signature de table.
- Regroupez la signature de table et la table dm-verity dans les métadonnées verity.
- Concaténez l'image système, les métadonnées Verity et l'arbre de hachage.
VoirThe Chromium Projects - Verified Boot pour une description détaillée de l'arbre de hachage et de la table dm-verity.
Génération de l'arbre de hachage
Comme décrit dans l'introduction, l'arbre de hachage fait partie intégrante de dm-verity. L'outil cryptsetup générera un arbre de hachage pour vous. Alternativement, un compatible est 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 au niveau de la couche 0 en blocs de 4k, chacun étant affecté d'un hachage SHA256. La couche 1 est formée en joignant uniquement ces hachages SHA256 en blocs de 4k, 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.
Ceci est fait jusqu'à ce que les hachages SHA256 de la couche précédente puissent tenir dans un seul bloc. Lorsque vous obtenez le SHA256 de ce bloc, vous avez le hachage racine de l'arbre.
La taille de l'arbre de hachage (et l'utilisation de l'espace disque correspondant) varie avec la taille de la partition vérifiée. En pratique, la taille des arbres de hachage a tendance à être petite, souvent inférieure à 30 Mo.
Si vous avez un bloc dans un calque qui n'est pas complètement rempli naturellement par les hachages du calque précédent, vous devez le remplir de zéros pour obtenir le 4k attendu. Cela vous permet de savoir que l'arbre de hachage n'a pas été supprimé et qu'il est à la place complété avec des données vides.
Pour générer l'arbre de hachage, concaténez les hachages de la couche 2 sur ceux de la couche 1, les hachages de la couche 3 sur ceux de la couche 2, et ainsi de suite. É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 pour construire l'arbre de hachage est le suivant :
- Choisissez un sel aléatoire (codage hexadécimal).
- Décomposez votre image système en blocs de 4k.
- Pour chaque bloc, obtenez son hachage SHA256 (salé).
- Concaténer ces hachages pour former un niveau
- Complétez le niveau avec des 0 jusqu'à une limite de bloc de 4k.
- Concaténer le niveau à votre arbre de hachage.
- Répétez les étapes 2 à 6 en utilisant le niveau précédent comme source pour le suivant jusqu'à ce que vous n'ayez qu'un seul hachage.
Le résultat est un hachage unique, qui est votre hachage racine. Ceci et votre sel sont utilisés lors de la construction de votre table de mappage dm-verity.
Construire la table de mappage dm-verity
Construisez la table de mappage dm-verity, qui identifie le périphérique bloc (ou cible) pour le noyau et l'emplacement de l'arbre 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 le hash_start, l'emplacement de départ de l'arbre de hachage (en particulier, son numéro de bloc depuis le début de l'image).
Voir cryptsetup pour une description détaillée des champs de la table de mappage cible Verity.
Signature de la table dm-verity
Signez la table dm-verity pour produire une signature de table. Lors de la vérification d'une partition, la signature de la table est validée en premier. Cela se fait par rapport à une clé sur votre image de démarrage dans un emplacement fixe. Les clés sont généralement incluses dans les systèmes de construction des fabricants pour une inclusion automatique sur les appareils dans un emplacement fixe.
Pour vérifier la partition avec cette combinaison de signature et de clé :
- Ajoutez une clé RSA-2048 au format compatible libmincrypt à la partition
/boot
dans/verity_key
. Identifiez l'emplacement de la clé utilisée pour vérifier l'arbre de hachage. - Dans le fstab de l'entrée appropriée, ajoutez
verify
aux indicateursfs_mgr
.
Regroupement de la signature de table dans les métadonnées
Regroupez la signature de 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érifier l'intégrité, un nombre magique est associé à chaque ensemble de métadonnées de table qui aide à identifier la table. É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 garantit que vous n'avez pas choisi de vérifier une partition non vérifiée. Si tel est le cas, l'absence de ce nombre magique interrompra le processus de vérification. Ce nombre ressemble à :
0xb001b001
Les valeurs d'octets en hexadécimal sont :
- premier octet = b0
- deuxième octet = 01
- troisième octet = b0
- quatrième octet = 01
Le schéma suivant illustre la répartition des métadonnées Verity :
<magic number>|<version>|<signature>|<table length>|<table>|<padding> \-------------------------------------------------------------------/ \----------------------------------------------------------/ | | | | 32K block content
Et ce tableau décrit ces champs de métadonnées.
Tableau 1. Champs de métadonnées Verity
Champ | But | Taille | Valeur |
---|---|---|---|
nombre magique | utilisé par fs_mgr comme contrôle d'intégrité | 4 octets | 0xb001b001 |
version | utilisé pour versionner le bloc de métadonnées | 4 octets | actuellement 0 |
signature | la signature de la table sous forme PKCS1.5 rembourrée | 256 octets | |
longueur du tableau | la longueur de la table dm-verity en octets | 4 octets | |
tableau | la table dm-verity décrite précédemment | octets de longueur de table | |
rembourrage | cette structure est 0-rembourrée à 32k de longueur | 0 |
Optimisation de dm-verity
Pour obtenir les meilleures performances de dm-verity, vous devez :
- Dans le noyau, activez NEON SHA-2 pour ARMv7 et les extensions SHA-2 pour ARMv8.
- Expérimentez avec différents paramètres de lecture anticipée et de prefetch_cluster pour trouver la meilleure configuration pour votre appareil.