Seit Android 12 ist das ART-Modul (Android Runtime) ein Mainline-Modul. Wenn Sie das Modul aktualisieren, müssen Sie möglicherweise die Ahead-of-Time-Kompilierungsartefakte der Bootclasspath-JAR-Dateien und des Systemservers neu erstellen. Da diese Artefakte sicherheitsrelevant sind, verwendet Android 12 die Funktion „On-Device-Signatur“, um Manipulationen dieser Artefakte zu verhindern. Auf dieser Seite wird die On-Device-Signaturarchitektur und ihre Interaktionen mit anderen Android-Sicherheitsfunktionen beschrieben.
Grobkonzept
Die On-Device-Signatur hat zwei Hauptkomponenten:
odrefresh
ist Teil des ART-Mainline-Moduls. Er ist für die Generierung der Laufzeitartefakte verantwortlich. Es prüft vorhandene Artefakte anhand der installierten Version des ART-Moduls, der Bootclasspath-JAR-Dateien und der Systemserver-JAR-Dateien, um festzustellen, ob sie auf dem neuesten Stand sind oder neu generiert werden müssen. Wenn sie neu generiert werden müssen, werden sie vonodrefresh
generiert und gespeichert.odsign
ist ein Binärprogramm, das zur Android-Plattform gehört. Es wird während des frühen Startvorgangs ausgeführt, direkt nachdem die/data
-Partition bereitgestellt wurde. Seine Hauptaufgabe besteht darin,odrefresh
aufzurufen, um zu prüfen, ob Artefakte generiert oder aktualisiert werden müssen. Für alle neuen oder aktualisierten Artefakte, die vonodrefresh
generiert werden, berechnetodsign
eine Hash-Funktion. Das Ergebnis einer solchen Hash-Berechnung wird als Datei-Digest bezeichnet. Bei bereits vorhandenen Artefakten prüftodsign
, ob die Digests der vorhandenen Artefakte mit den Digests übereinstimmen, dieodsign
zuvor berechnet hat. So wird sichergestellt, dass die Artefakte nicht manipuliert wurden.
Bei Fehlern, z. B. wenn der Digest für eine Datei nicht übereinstimmt, löschen odrefresh
und odsign
alle vorhandenen Artefakte auf /data
und versuchen, sie neu zu generieren. Wenn das fehlschlägt, wechselt das System zum JIT-Modus.
odrefresh
und odsign
sind durch dm-verity
geschützt und Teil der verifizierten Boot-Kette von Android.
Berechnung von Datei-Digests mit fs-verity
fs-verity ist eine Funktion des Linux-Kernels, die eine Merkle-Baum-basierte Überprüfung von Dateidaten durchführt. Wenn Sie „fs-verity“ für eine Datei aktivieren, erstellt das Dateisystem einen Merkle-Baum über die Daten der Datei mit SHA-256-Hashes, speichert ihn an einem versteckten Speicherort neben der Datei und kennzeichnet die Datei als schreibgeschützt. „fs-verity“ prüft die Daten der Datei bei Bedarf automatisch anhand des Merkle-Baums, während sie gelesen werden. „fs-verity“ stellt den Stamm-Hash des Merkle-Baums als Wert namens fs-verity-Datei-Digest zur Verfügung und sorgt dafür, dass alle aus der Datei gelesenen Daten mit diesem Datei-Digest übereinstimmen.
odsign
verwendet fs-verity, um die Bootleistung zu verbessern, indem die kryptografische Authentifizierung von auf dem Gerät kompilierten Artefakten beim Start optimiert wird. Wenn ein Artefakt generiert wird, aktiviert odsign
die Dateisystemüberprüfung darauf. Wenn odsign
ein Artefakt überprüft, wird der Datei-Digest von fs-verity anstelle des vollständigen Datei-Hashs überprüft. Dadurch müssen die vollständigen Daten des Artefakts nicht mehr beim Start gelesen und gehasht werden. Artefaktdaten werden stattdessen bei Bedarf von fs-verity blockweise gehasht.
Auf Geräten, deren Kernel fs-verity nicht unterstützt, odsign
greift auf die Berechnung von Datei-Digests im Userspace zurück. odsign
verwendet denselben Merkle-Baum-basierten Hash-Algorithmus wie fs-verity. Die Digests sind also in beiden Fällen identisch. fs-verity ist auf allen Geräten erforderlich, die mit Android 11 oder höher ausgeliefert wurden.
Speichern der Datei-Digests
odsign
speichert die Datei-Digests der Artefakte in einer separaten Datei namens odsign.info
. Damit odsign.info
nicht manipuliert wird, wird odsign.info
mit einem Signaturschlüssel signiert, der wichtige Sicherheitseigenschaften hat. Insbesondere kann der Schlüssel nur während des frühen Startvorgangs generiert und verwendet werden, zu dem nur vertrauenswürdiger Code ausgeführt wird. Weitere Informationen finden Sie unter Vertrauenswürdige Signaturschlüssel.
Überprüfung von Datei-Digests
Wenn odrefresh
bei jedem Start feststellt, dass die vorhandenen Artefakte auf dem neuesten Stand sind, prüft odsign
, ob die Dateien seit ihrer Erstellung nicht manipuliert wurden. odsign
prüft dazu die Datei-Digests. Zuerst wird die Signatur von odsign.info
überprüft. Wenn die Signatur gültig ist, überprüft odsign
, ob der Hashwert jeder Datei mit dem entsprechenden Hashwert in odsign.info
übereinstimmt.
Vertrauenswürdige Signaturschlüssel
In Android 12 wird eine neue Schlüsselspeicherfunktion namens „Boot-Phasenschlüssel“ eingeführt, die die folgenden Sicherheitsbedenken angeht:
- Was verhindert, dass ein Angreifer unseren Signaturschlüssel verwendet, um seine eigene Version von
odsign.info
zu signieren? - Was verhindert, dass ein Angreifer seinen eigenen Signaturschlüssel generiert und damit seine eigene Version von
odsign.info
signiert?
Boot-Phasenschlüssel unterteilen den Boot-Zyklus von Android in Ebenen und binden die Erstellung und Verwendung eines Schlüssels kryptografisch an eine bestimmte Ebene. odsign
erstellt seinen Signaturschlüssel in einer frühen Phase, wenn nur vertrauenswürdiger Code ausgeführt wird, der durch dm-verity
geschützt ist.
Die Ebenen der Bootphase sind von 0 bis zur magischen Zahl 1000000000 nummeriert. Während des Bootvorgangs von Android können Sie die Bootebene erhöhen, indem Sie eine Systemeigenschaft über init.rc
festlegen. Im folgenden Code wird beispielsweise die Bootebene auf 10 festgelegt:
setprop keystore.boot_level 10
Clients von Keystore können Schlüssel erstellen, die mit einer bestimmten Bootebene verknüpft sind. Wenn Sie beispielsweise einen Schlüssel für die Bootebene 10 erstellen, kann dieser Schlüssel nur verwendet werden, wenn sich das Gerät in der Bootebene 10 befindet.
odsign
verwendet die Bootebene 30 und der erstellte Signaturschlüssel ist mit dieser Bootebene verknüpft. Bevor ein Schlüssel zum Signieren von Artefakten verwendet wird, prüft odsign
, ob der Schlüssel mit Bootebene 30 verknüpft ist.
Dadurch werden die beiden in diesem Abschnitt beschriebenen Angriffe verhindert:
- Angreifer können den generierten Schlüssel nicht verwenden, da die Boot-Ebene bis zum Zeitpunkt, zu dem ein Angreifer schädlichen Code ausführen kann, auf über 30 angestiegen ist und der Keystore Vorgänge ablehnt, bei denen der Schlüssel verwendet wird.
- Angreifer können keinen neuen Schlüssel erstellen, da die Boot-Ebene bis zum Zeitpunkt, zu dem ein Angreifer schädlichen Code ausführen kann, auf über 30 angestiegen ist und der Keystore keinen neuen Schlüssel mit dieser Boot-Ebene erstellt. Wenn ein Angreifer einen neuen Schlüssel erstellt, der nicht an die Bootebene 30 gebunden ist, wird er von
odsign
abgelehnt.
Der Schlüsselspeicher sorgt dafür, dass die Bootebene ordnungsgemäß erzwungen wird. In den folgenden Abschnitten wird genauer beschrieben, wie das für verschiedene Keymaster-Versionen funktioniert.
Keymaster 4.0-Implementierung
Verschiedene Versionen von Keymaster verarbeiten die Implementierung von Boot-Phasenschlüsseln unterschiedlich. Auf Geräten mit einem Keymaster 4.0-TEE/Strongbox erfolgt die Implementierung durch Keymaster so:
- Beim ersten Start erstellt der Schlüsselspeicher einen symmetrischen Schlüssel K0, für den das
MAX_USES_PER_BOOT
-Tag auf1
festgelegt ist. Das bedeutet, dass der Schlüssel nur einmal pro Start verwendet werden kann. - Wenn während des Starts die Bootebene erhöht wird, kann mithilfe einer HKDF-Funktion ein neuer Schlüssel für diese Bootebene aus K0 generiert werden:
Ki+i=HKDF(Ki, "some_fixed_string")
. Wenn Sie beispielsweise von der Bootebene 0 zur Bootebene 10 wechseln, wird der HKDF zehnmal aufgerufen, um K10 aus K0 abzuleiten. Wenn sich die Bootebene ändert, wird der Schlüssel für die vorherige Bootebene aus dem Arbeitsspeicher gelöscht und die Schlüssel, die mit vorherigen Bootebenen verknüpft sind, sind nicht mehr verfügbar.
Schlüssel K0 ist ein
MAX_USES_PER_BOOT=1
-Schlüssel. Das bedeutet, dass dieser Schlüssel auch später im Bootvorgang nicht verwendet werden kann, da immer mindestens eine Bootebenenübergang (zur letzten Bootebene) erfolgt.
Wenn ein Keystore-Client wie odsign
anfordert, dass ein Schlüssel auf Bootebene i
erstellt wird, wird sein Blob mit dem Schlüssel Ki
verschlüsselt. Da Ki
nach der Bootebene i
nicht mehr verfügbar ist, kann dieser Schlüssel in späteren Bootphasen nicht erstellt oder entschlüsselt werden.
Implementierung von Keymaster 4.1 und KeyMint 1.0
Die Implementierungen von Keymaster 4.1 und KeyMint 1.0 stimmen weitgehend mit der Implementierung von Keymaster 4.0 überein. Der Hauptunterschied besteht darin, dass K0 kein MAX_USES_PER_BOOT
-Schlüssel, sondern ein EARLY_BOOT_ONLY
-Schlüssel ist, der in Keymaster 4.1 eingeführt wurde. Ein EARLY_BOOT_ONLY
-Schlüssel kann nur in den frühen Phasen des Starts verwendet werden, wenn kein nicht vertrauenswürdiger Code ausgeführt wird. Dies bietet eine zusätzliche Schutzebene: Bei der Keymaster 4.0-Implementierung kann ein Angreifer, der das Dateisystem und SELinux manipuliert, die Keystore-Datenbank ändern, um einen eigenen MAX_USES_PER_BOOT=1
-Schlüssel zum Signieren von Artefakten zu erstellen. Ein solcher Angriff ist mit den Implementierungen von Keymaster 4.1 und KeyMint 1.0 unmöglich, da EARLY_BOOT_ONLY
-Schlüssel nur während des frühen Bootens erstellt werden können.
Öffentliche Komponente vertrauenswürdiger Signaturschlüssel
odsign
ruft die öffentliche Schlüsselkomponente des Signaturschlüssels aus dem Keystore ab.
Der Keystore ruft diesen öffentlichen Schlüssel jedoch nicht aus dem TEE/SE ab, in dem sich der entsprechende private Schlüssel befindet. Stattdessen ruft er den öffentlichen Schlüssel aus seiner eigenen Datenbank auf dem Laufwerk ab. Das bedeutet, dass ein Angreifer, der das Dateisystem manipuliert, die Keystore-Datenbank so ändern könnte, dass sie einen öffentlichen Schlüssel enthält, der Teil eines öffentlichen/privaten Schlüsselpaars ist, das er kontrolliert.
Um diesen Angriff zu verhindern, erstellt odsign
einen zusätzlichen HMAC-Schlüssel mit derselben Bootebene wie der Signaturschlüssel. Beim Erstellen des Signaturschlüssels verwendet odsign
dann diesen HMAC-Schlüssel, um eine Signatur des öffentlichen Schlüssels zu erstellen und auf der Festplatte zu speichern. Bei nachfolgenden Starts wird beim Abrufen des öffentlichen Schlüssels des Signaturschlüssels der HMAC-Schlüssel verwendet, um zu überprüfen, ob die Signatur auf dem Laufwerk mit der Signatur des abgerufenen öffentlichen Schlüssels übereinstimmt. Wenn sie übereinstimmen, ist der öffentliche Schlüssel vertrauenswürdig, da der HMAC-Schlüssel nur in den frühen Boot-Ebenen verwendet werden kann und daher nicht von einem Angreifer erstellt werden kann.