dm-verity implementieren

Android 4.4 und höher unterstützt den verifizierten Bootmodus durch die optionale dm-verity-Kernel-Funktion (device-mapper-verity), die transparente Integritätsprüfung blockierter Geräte. dm-verity hilft, persistente Rootkits zu verhindern die Root-Berechtigungen haben und Geräte kompromittieren kann. Diese Funktion hilft Android-Nutzern, beim Starten eines Geräts sicherzugehen, dass es sich im selben Zustand befindet wie bei der letzten Verwendung.

Potenziell schädliche Anwendungen (Potentially Harmful Applications, PHAs) mit Root-Berechtigungen können sich vor Erkennungsprogrammen verstecken und sich anderweitig tarnen. Die Rooting-Software kann dies, weil sie oft mehr Berechtigungen hat als die Erkennungsprogramme, sodass sie den Erkennungsprogrammen „lügen“ kann.

Mit der dm-verity-Funktion können Sie ein Blockgerät, die zugrunde liegende Speicherschicht des Dateisystems, prüfen und feststellen, ob es der erwarteten Konfiguration entspricht. Dazu wird ein kryptografischer Hashbaum verwendet. Für jeden Block (in der Regel 4 KB) gibt es einen SHA256-Hash.

Da die Hash-Werte in einem Seitenbaum gespeichert sind, muss nur der Hashwert der obersten Ebene („Stamm“) für die Überprüfung des restlichen Baums vertrauenswürdig sein. Die Möglichkeit, einen der Blöcke zu ändern, würde dem Brechen des kryptografischen Hashes entsprechen. Das folgende Diagramm zeigt diese Struktur.

dm-verity-hash-table

Abbildung 1: dm-verity-Hashtabelle

Auf der Bootpartition ist ein öffentlicher Schlüssel enthalten, der vom Gerätehersteller extern verifiziert werden muss. Mit diesem Schlüssel wird die Signatur für diesen Hashwert überprüft und bestätigt, dass die Systempartition des Geräts geschützt und unverändert ist.

Betrieb

Der dm-verity-Schutz befindet sich im Kernel. Wenn also Rooting-Software das System manipuliert, bevor der Kernel gestartet wird, behält sie diesen Zugriff. Um dieses Risiko zu minimieren, verifizieren die meisten Hersteller den Kernel mit einem Schlüssel, der in das Gerät eingebrannt ist. Dieser Schlüssel kann in fabrikneuem Zustand nicht mehr geändert werden.

Hersteller verwenden diesen Schlüssel, um die Signatur auf der ersten Ebene zu verifizieren. Bootloader, der wiederum die Signatur auf nachfolgenden Ebenen überprüft, den App-Bootloader und schließlich den Kernel. Jeder Hersteller, der profitieren Sie von bestätigten boot sollte eine Methode zum Prüfen der Kernel-Integrität vorhanden sein. Angenommen, der Kernel wurde überprüft, kann er ein Blockgerät prüfen und bestätigen, dass es bereitgestellt wurde.

Eine Möglichkeit, ein Blockgerät zu verifizieren, besteht darin, seinen Inhalt direkt zu hashen in einen gespeicherten Wert. Die Bestätigung eines komplett blockierten Geräts kann jedoch länger dauern und einen großen Teil des Stromverbrauchs eines Geräts verbrauchen. Das Starten der Geräte dauerte sehr lange und der Akku war vor der Verwendung bereits stark entladen.

Stattdessen überprüft dm-verity Blockierungen einzeln und nur dann, wenn jeder Block Zugriff haben. Beim Einlesen in den Arbeitsspeicher wird der Block parallel gehasht. Der Hashwert und den Baum überprüft. Und da das Lesen des Blocks so teuer ist, ist die durch diese Block-Level-Überprüfung verursachte Latenz vergleichsweise nominal.

Wenn die Überprüfung fehlschlägt, generiert das Gerät einen I/O-Fehler, der angibt, dass der Block nicht gelesen werden kann. Offenbar ist das Dateisystem beschädigt, zu erwarten war.

Apps können ohne die resultierenden Daten fortfahren, z. B. Diese Ergebnisse sind nicht für die Hauptfunktion der App erforderlich. Wenn die App jedoch ohne die Daten nicht fortgesetzt werden kann, schlägt sie fehl.

Vorwärtsfehlerkorrektur

Ab Android 7.0 wird die Robustheit von dm-verity durch die Vorwärtsfehlerkorrektur (Forward Error Correction, FEC) verbessert. Die AOSP-Implementierung beginnt mit der allgemeinen Reed-Solomon-Fehlerkorrekturcode und wendet einen Technik namens Verschränkung, um den Raum-Overhead zu reduzieren und die Anzahl der beschädigten Blöcke, die wiederhergestellt werden können. Weitere Informationen zu FEC finden Sie unter Streng erzwungener Verified Boot mit Fehlerkorrektur.

Implementierung

Zusammenfassung

  1. Generieren Sie ein ext4-System-Image.
  2. Erstellen Sie einen Hash-Baum für dieses Bild.
  3. Erstellen Sie eine dm-verity-Tabelle für diesen Hash-Baum.
  4. Signieren Sie diese dm-verity-Tabelle, um eine Tabelle zu erstellen. Signatur.
  5. Bündeln Sie die Tabellensignatur und die dm-verity-Tabelle in Verity-Metadaten.
  6. Verketten Sie das System-Image, die Versionsmetadaten und den Hash-Baum.

Eine detaillierte Beschreibung des Hashbaums und der dm-verity-Tabelle finden Sie unter The Chromium Projects – Verified Boot (in englischer Sprache).

Hash-Struktur generieren

Wie in der Einführung beschrieben, ist der Hash-Baum ein integraler Bestandteil von dm-verity. Das Tool cryptsetup generiert einen Hash-Baum für Sie. Alternativ wird hier eine kompatible definiert:

<your block device name> <your block device name> <block size> <block size> <image size in blocks> <image size in blocks + 8> <root hash> <salt>

Zur Bildung des Hash-Werts wird das System-Image bei Ebene 0 in 4.000 Blöcke aufgeteilt, die jeweils SHA256-Hash zugewiesen ist. Ebene 1 wird gebildet, indem nur diese SHA256-Hashes zusammengeführt werden in 4.000 Blöcke, was zu einem viel kleineren Bild führt. Ebene 2 wird gebildet identisch, mit den SHA256-Hashes der Ebene 1.

Dies geschieht, bis die SHA256-Hashes der vorherigen Ebene in ein einzelnes blockieren. Wenn Sie den SHA256 dieses Blocks erhalten, haben Sie den Stamm-Hash des Baums.

Die Größe des Hashbaums (und die entsprechende Laufwerksspeichernutzung) variiert mit der Größe der geprüften Partition. In der Praxis ist die Größe von Hash-Bäumen klein, häufig kleiner als 30 MB.

Wenn ein Block in einer Ebene nicht vollständig durch die Hashes der vorherigen Ebene gefüllt ist, sollten Sie ihn mit Nullen auffüllen, um die erwarteten 4 KB zu erreichen. So können Sie feststellen, dass der Hash-Baum nicht entfernt wurde, sondern stattdessen mit leeren Daten ausgefüllt ist.

Um den Hash-Baum zu generieren, verketten Sie die Layer-2-Hashes mit denen für die Ebene. 1, Ebene 3 die Hashes auf Ebene 2 usw. Schreiben Sie alles auf die Festplatte. Beachten Sie, dass sich dies nicht auf die Ebene 0 des Root-Hashs bezieht.

Zur Wiederholung: Der allgemeine Algorithmus zum Erstellen des Hashbaums sieht so aus:

  1. Wählen Sie nach dem Zufallsprinzip einen Salt aus (Hexadezimalcodierung).
  2. Teilen Sie Ihr System-Image in 4k-Blöcke auf.
  3. Rufen Sie für jeden Block seinen (salted) SHA256-Hash ab.
  4. Diese Hashes verketten, um eine Ebene zu bilden
  5. Füllen Sie das Level mit Nullen bis zu einer Blockgrenze von 4K.
  6. Verketten Sie die Ebene mit Ihrem Hash-Baum.
  7. Wiederholen Sie die Schritte 2 bis 6, wobei Sie die vorherige Ebene als Quelle für die nächste verwenden, bis Sie nur noch einen einzelnen Hash haben.

Das Ergebnis ist ein einzelner Hash, der als Root-Hash bezeichnet wird. Das und dein Salz werden beim Erstellen der dm-verity-Zuordnungstabelle verwendet.

dm-verity-Zuordnungstabelle erstellen

Erstellen Sie die dm-verity-Zuordnungstabelle, in der das zu blockierende Gerät (oder Ziel) identifiziert wird. für den Kernel und die Position des Hash-Baums (gleicher Wert). Diese Zuordnung wird für die fstab-Generierung und das Booten verwendet. Die Tabelle enthält auch die Größe der Blöcke und „hash_start“, die Startposition des Hash-Baums (insbesondere die Blocknummer am Anfang des Bildes).

Unter cryptsetup finden Sie eine eine ausführliche Beschreibung der Felder der Tabelle für die Zuordnung der Ziele.

dm-verity-Tabelle signieren

Signieren Sie die dm-verity-Tabelle, um eine Tabellensignatur zu erstellen. Bei der Überprüfung einer Partition wird zuerst die Tabellensignatur validiert. Dies erfolgt anhand eines Schlüssels an einem festen Ort speichern. Schlüssel sind in der Regel in den Build-Systemen der Hersteller enthalten, um sie automatisch auf Geräten an einem festen Standort einzubinden.

So überprüfen Sie die Partition mit dieser Signatur-/Schlüsselkombination:

  1. Fügen Sie dem Partition /boot um /verity_key. Geben Sie den Speicherort des Schlüssels an, der zum Verifizieren des Hashbaums verwendet wird.
  2. Fügen Sie in der fstab für den entsprechenden Eintrag den Flags fs_mgr die Zeichenfolge verify hinzu.

Tabellensignatur in Metadaten einbinden

Bündeln Sie die Tabellensignatur und die dm-verity-Tabelle in Verity-Metadaten. Das gesamte der Metadatenblock versioniert ist, sodass er erweitert werden kann, etwa um eine Signatur oder die Reihenfolge ändern.

Zur Fehlerprüfung ist jeder Tabelle eine Magische Zahl zugewiesen, die zur Identifizierung der Tabelle dient. Da die Länge im Header des ext4-System-Images enthalten ist, können Sie nach den Metadaten suchen, ohne den Inhalt der Daten zu kennen.

Dadurch wird sichergestellt, dass Sie die Prüfung einer nicht bestätigten Partition nicht ausgewählt haben. Wenn ja, wenn diese magische Zahl fehlt, um den Bestätigungsprozess zu stoppen. Diese Zahl ähnelt 0xb001b001.

Die Byte-Werte im Hexadezimalformat sind:

  • Erstes Byte = b0
  • zweites Byte = 01
  • drittes Byte = b0
  • viertes Byte = 01

Das folgende Diagramm zeigt eine Aufschlüsselung der Verity-Metadaten:

<magic number>|<version>|<signature>|<table length>|<table>|<padding>
\-------------------------------------------------------------------/
\----------------------------------------------------------/   |
                            |                                  |
                            |                                 32K
                       block content

In dieser Tabelle werden diese Metadatenfelder beschrieben.

Tabelle 1 Felder für Verity-Metadaten

Feld Zweck Größe Wert
Magische Zahl von fs_mgr als Integritätsprüfung verwendet 4 Byte 0xb001b001
Version zum Versionieren des Metadatenblocks 4 Byte aktuell 0
Signatur die Signatur der Tabelle im PKCS1.5-ausgeglichenen Format 256 Byte
Tabellenlänge die Länge der dm-verity-Tabelle in Byte 4 Byte
Tisch die oben beschriebene dm-verity-Tabelle Bytes für die Tabellenlänge
padding Diese Struktur wird auf 32.000 Byte aufgefüllt. 0

dm-verity optimieren

Um die beste Leistung mit dm-verity zu erzielen, sollten Sie Folgendes tun:

  • Aktivieren Sie im Kernel NEON SHA-2 für ARMv7 und SHA-2 Erweiterungen für ARMv8.
  • Testen Sie verschiedene Einstellungen für die Vorab-Lesefunktion und prefetch_cluster, um die beste Konfiguration für Ihr Gerät zu finden.