Implémentation de dm-verity

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é aide les utilisateurs d'Android à s'assurer que lors du démarrage d'un appareil, il est dans le même état que lors de sa dernière utilisation.

Les applications potentiellement dangereuses (PHA) dotées des privilèges root peuvent se cacher des programmes de détection et se masquer autrement. Le logiciel de root 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 fonctionnalité 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 à sa 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 n’importe lequel des blocs équivaudrait à casser le hachage cryptographique. Voir le diagramme suivant pour une représentation de cette structure.

table de hachage dm-verity

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 du périphérique. Cette clé est utilisée pour vérifier la signature de ce hachage et confirmer que la partition système du périphérique est protégée et inchangée.

Opération

La protection dm-verity réside dans le noyau. Ainsi, si le logiciel de root 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 le périphérique. 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, sur le chargeur de démarrage de l'application et éventuellement sur le noyau. Chaque fabricant souhaitant bénéficier du démarrage vérifié doit disposer d'une méthode permettant de 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 au fur et à mesure de son montage.

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 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, puis seraient considérablement épuisé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 très coûteuse, la latence introduite par cette vérification au niveau du bloc est relativement minime.

Si la vérification échoue, l'appareil génère une erreur d'E/S indiquant que le bloc ne peut pas être lu. Il semblera que le système de fichiers ait é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. Cependant, si l'application ne peut pas continuer sans les données, elle échouera.

Correction d'erreur directe

Android 7.0 et versions ultérieures améliorent la robustesse de DM-Verity avec la correction d'erreur directe (FEC). L'implémentation AOSP commence par le code de correction d'erreurs commun Reed-Solomon 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é

  1. Générez une image système ext4.
  2. Générez un arbre de hachage pour cette image.
  3. Créez une table dm-verity pour cet arbre de hachage.
  4. Signez cette table dm-verity pour produire une signature de table.
  5. Regroupez la signature de la table et la table dm-verity dans les métadonnées Verity.
  6. Concaténez l'image système, les métadonnées Verity et l'arbre de hachage.

Voir The Chromium Projects - Verified Boot pour une description détaillée de l'arbre de hachage et de la table dm-verity.

Générer 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 4 000 blocs, chacun étant affecté d'un hachage SHA256. La couche 1 est formée en joignant uniquement ces hachages SHA256 en blocs de 4 000 k, 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 effectué 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'arborescence.

La taille de l'arborescence de hachage (et l'utilisation correspondante de l'espace disque) varie en fonction de 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 complété par 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, la couche 3 les hachages 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 :

  1. Choisissez un sel aléatoire (codage hexadécimal).
  2. Réparez votre image système en blocs de 4 000 k.
  3. Pour chaque bloc, obtenez son hachage SHA256 (salé).
  4. Concaténez ces hachages pour former un niveau
  5. Remplissez le niveau avec des 0 jusqu'à une limite de bloc de 4k.
  6. Concaténez le niveau à votre arbre de hachage.
  7. Répétez les étapes 2 à 6 en utilisant le niveau précédent comme source du suivant jusqu'à ce que vous n'ayez qu'un seul hachage.

Le résultat est un seul hachage, 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

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 le hash_start, l'emplacement de départ de l'arbre de hachage (plus précisément, 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 de 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 situés à un emplacement fixe.

Pour vérifier la partition avec cette signature et cette combinaison de clés :

  1. Ajoutez une clé RSA-2048 au format compatible avec libmincrypt à la partition /boot dans /verity_key . Identifiez l'emplacement de la clé utilisée pour vérifier l'arborescence de hachage.
  2. Dans le fstab de l'entrée concernée, ajoutez verify aux indicateurs fs_mgr .

Regrouper la signature de la table dans des 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 de pouvoir être étendu, par exemple pour ajouter un deuxième type de signature ou modifier un certain ordre.

À titre de contrôle d'intégrité, un nombre magique est associé à chaque ensemble de métadonnées de table qui permet d'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 numéro magique interrompra le processus de vérification. Ce numéro 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 diagramme 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 du tableau sous forme complétée PKCS1.5 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 longueur de la table octets
rembourrage cette structure est complétée de 0 à 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 prefetch_cluster pour trouver la meilleure configuration pour votre appareil.