Hardware-verpackte Schlüssel

Wie bei den meisten Softwarelösungen zur Laufwerk- und Dateiverschlüsselung setzt auch die Speicherverschlüsselung von Android traditionell darauf, dass sich die Rohverschlüsselungsschlüssel im Systemspeicher befinden, damit die Verschlüsselung durchgeführt werden kann. Auch wenn die Verschlüsselung nicht durch Software, sondern durch spezielle Hardware durchgeführt wird, müssen die Rohverschlüsselungsschlüssel in der Regel weiterhin von Software verwaltet werden.

Das wird traditionell nicht als Problem angesehen, da die Schlüssel bei einem Offlineangriff nicht vorhanden sind. Dies ist die Hauptart von Angriffen, vor denen die Speicherverschlüsselung schützen soll. Es besteht jedoch der Wunsch, einen besseren Schutz vor anderen Arten von Angriffen wie Cold-Boot-Angriffen und Onlineangriffen zu bieten, bei denen ein Angreifer möglicherweise Systemspeicher auslesen kann, ohne das Gerät vollständig zu schädigen.

Um dieses Problem zu lösen, wurde in Android 11 die Unterstützung für hardwareverpackte Schlüssel eingeführt, sofern Hardware-Unterstützung vorhanden ist. Hardware-verpackte Schlüssel sind Speicherschlüssel, die nur in Rohform für spezielle Hardware bekannt sind. Software sieht und arbeitet nur mit diesen Schlüsseln in verpackter (verschlüsselter) Form. Diese Hardware muss Speicherschlüssel generieren und importieren, Speicherschlüssel in sitzungsspezifische und langfristige Formen verpacken, Unterschlüssel ableiten, einen Unterschlüssel direkt in eine Inline-Krypto-Engine programmieren und einen separaten Unterschlüssel an die Software zurückgeben können.

Hinweis:Eine Inline-Krypto-Engine (oder Inline-Verschlüsselungshardware) bezieht sich auf Hardware, die Daten verschlüsselt/entschlüsselt, während sie auf dem Weg zum/vom Speichergerät sind. In der Regel ist dies ein UFS- oder eMMC-Hostcontroller, der die Krypto-Erweiterungen implementiert, die in der entsprechenden JEDEC-Spezifikation definiert sind.

Design

In diesem Abschnitt wird die Funktion der hardwareverpackten Schlüssel beschrieben, einschließlich der erforderlichen Hardwareunterstützung. In diesem Artikel geht es hauptsächlich um die dateibasierte Verschlüsselung (File-Based Encryption, FBE). Die Lösung gilt aber auch für die Verschlüsselung von Metadaten.

Eine Möglichkeit, die Notwendigkeit von Rohverschlüsselungsschlüsseln im Systemspeicher zu vermeiden, besteht darin, sie nur in den Schlüsselslots einer Inline-Krypto-Engine zu speichern. Bei diesem Ansatz gibt es jedoch einige Probleme:

  • Die Anzahl der Verschlüsselungsschlüssel kann die Anzahl der Schlüsselslots überschreiten.
  • Bei Inline-Krypto-Engines geht der Inhalt der Schlüsselslots in der Regel verloren, wenn der Speichercontroller (normalerweise UFS oder eMMC) zurückgesetzt wird. Das Zurücksetzen des Speichercontrollers ist ein standardmäßiges Verfahren zur Fehlerbehebung, das ausgeführt wird, wenn bestimmte Arten von Speicherfehlern auftreten. Solche Fehler können jederzeit auftreten. Daher muss das Betriebssystem bei der Verwendung von Inline-Kryptografie immer bereit sein, die Schlüsselslots ohne Nutzereingriff neu zu programmieren.
  • Inline-Krypto-Engines können nur zum Verschlüsseln/Entschlüsseln ganzer Datenblöcke auf dem Laufwerk verwendet werden. Bei der FBE muss die Software jedoch weiterhin andere kryptografische Aufgaben ausführen können, z. B. die Verschlüsselung von Dateinamen und die Ableitung von Schlüssel-IDs. Software benötigt weiterhin Zugriff auf die Roh-FBE-Schlüssel, um diese anderen Aufgaben ausführen zu können.

Um diese Probleme zu vermeiden, werden die Speicherschlüssel stattdessen in hardwareverpackte Schlüssel umgewandelt, die nur von spezieller Hardware entpackt und verwendet werden können. So kann eine unbegrenzte Anzahl von Schlüsseln unterstützt werden. Außerdem wird die Schlüsselhierarchie geändert und teilweise auf diese Hardware verschoben. So kann ein Unterschlüssel für Aufgaben, für die keine Inline-Krypto-Engine verwendet werden kann, an die Software zurückgegeben werden.

Schlüsselhierarchie

Schlüssel können mithilfe einer Schlüsselableitungsfunktion (Key Derivation Function, KDF) wie HKDF aus anderen Schlüsseln abgeleitet werden, was zu einer Schlüsselhierarchie führt.

Das folgende Diagramm zeigt eine typische Schlüsselhierarchie für die FBE, wenn keine hardwareverpackten Schlüssel verwendet werden:

FBE-Schlüsselhierarchie (Standard)
Abbildung 1. FBE-Schlüsselhierarchie (Standard)

Der FBE-Klassenschlüssel ist der Rohverschlüsselungsschlüssel, den Android an den Linux-Kernel weitergibt, um eine bestimmte Gruppe verschlüsselter Verzeichnisse zu entsperren, z. B. den mit Anmeldedaten verschlüsselten Speicher für einen bestimmten Android-Nutzer. Im Kernel wird dieser Schlüssel als fscrypt-Masterschlüssel bezeichnet. Aus diesem Schlüssel leitet der Kernel die folgenden Unterschlüssel ab:

  • Die Schlüsselkennung. Dieser Wert wird nicht für die Verschlüsselung verwendet, sondern dient dazu, den Schlüssel zu identifizieren, mit dem eine bestimmte Datei oder ein bestimmtes Verzeichnis geschützt ist.
  • Der Verschlüsselungsschlüssel für den Dateiinhalt
  • Der Verschlüsselungsschlüssel für Dateinamen

Im folgenden Diagramm ist dagegen die Schlüsselhierarchie für die FBE dargestellt, wenn hardwareverpackte Schlüssel verwendet werden:

FBE-Schlüsselhierarchie (mit hardwaregewickeltem Schlüssel)
Abbildung 2. FBE-Schlüsselhierarchie (mit hardwareverpacktem Schlüssel)

Im Vergleich zum vorherigen Fall wurde der Schlüsselhierarchie eine zusätzliche Ebene hinzugefügt und der Verschlüsselungsschlüssel für den Dateiinhalt wurde verschoben. Der Stammknoten stellt weiterhin den Schlüssel dar, den Android an Linux weitergibt, um eine Reihe verschlüsselter Verzeichnisse zu entsperren. Dieser Schlüssel ist jetzt jedoch in einer verpackten Form und muss an spezielle Hardware übergeben werden, um verwendet werden zu können. Diese Hardware muss zwei Schnittstellen implementieren, die einen kurzlebig verpackten Schlüssel akzeptieren:

  • Eine Schnittstelle, um inline_encryption_key abzuleiten und direkt in einen Schlüsselschlitz der Inline-Krypto-Engine zu programmieren. So können Dateiinhalte verschlüsselt/entschlüsselt werden, ohne dass Software Zugriff auf den Rohschlüssel hat. In den gemeinsamen Android-Kerneln entspricht diese Schnittstelle dem Vorgang blk_crypto_ll_ops::keyslot_program, der vom Speichertreiber implementiert werden muss.
  • Eine Schnittstelle zum Ableiten und Zurückgeben von sw_secret („Software Secret“, an manchen Stellen auch „Raw Secret“ genannt), dem Schlüssel, mit dem Linux die Unterschlüssel für alles andere als die Verschlüsselung des Dateiinhalts ableitet. In den gemeinsamen Android-Kerneln entspricht diese Schnittstelle dem Vorgang blk_crypto_ll_ops::derive_sw_secret, der vom Speichertreiber implementiert werden muss.

Um inline_encryption_key und sw_secret aus dem Rohspeicherschlüssel abzuleiten, muss die Hardware einen kryptografisch starken KDF verwenden. Diese KDF muss den Best Practices für Kryptografie entsprechen und eine Sicherheitsstärke von mindestens 256 Bit haben, was für jeden später verwendeten Algorithmus ausreicht. Außerdem muss beim Ableiten der einzelnen Unterschlüssel ein eindeutiges Label, ein eindeutiger Kontext und ein app-spezifischer Informationsstring verwendet werden, um sicherzustellen, dass die resultierenden Unterschlüssel kryptografisch isoliert sind, d. h., dass das Wissen um einen von ihnen keine anderen preisgibt. Eine Schlüsselstreckung ist nicht erforderlich, da der Rohspeicherschlüssel bereits ein gleichmäßig zufälliger Schlüssel ist.

Technisch gesehen kann jeder KDF verwendet werden, der die Sicherheitsanforderungen erfüllt. Zu Testzwecken muss dieselbe KDF jedoch im Testcode neu implementiert werden. Derzeit wurde ein KDF geprüft und implementiert. Es befindet sich im Quellcode für vts_kernel_encryption_test. Für die Hardware wird dieser KDF empfohlen, der NIST SP 800-108 „KDF in Counter Mode“ mit AES-256-CMAC als PRF verwendet. Hinweis: Für die Kompatibilität müssen alle Teile des Algorithmus identisch sein, einschließlich der Auswahl der KDF-Kontexte und Labels für jeden Unterschlüssel.

Key-Wrapping

Um die Sicherheitsziele von hardwareverpackten Schlüsseln zu erreichen, sind zwei Arten der Schlüsselverschlüsselung definiert:

  • Sitzungsspezifisches Wrapping: Die Hardware verschlüsselt den Rohschlüssel mit einem Schlüssel, der bei jedem Starten des Geräts zufällig generiert wird und nicht direkt außerhalb der Hardware freigegeben wird.
  • Langfristige Verschlüsselung: Die Hardware verschlüsselt den Rohschlüssel mit einem eindeutigen, persistenten Schlüssel, der in die Hardware integriert ist und nicht direkt außerhalb der Hardware freigegeben wird.

Alle Schlüssel, die zum Entsperren des Speichers an den Linux-Kernel übergeben werden, sind sitzungsbasiert verpackt. So wird sichergestellt, dass ein Angreifer, der einen verwendeten Schlüssel aus dem Systemspeicher extrahiert, diesen Schlüssel nicht nur außerhalb des Geräts, sondern auch auf dem Gerät nach einem Neustart nicht verwenden kann.

Gleichzeitig muss Android weiterhin eine verschlüsselte Version der Schlüssel auf dem Laufwerk speichern können, damit sie überhaupt entsperrt werden können. Die Rohschlüssel eignen sich dafür. Es ist jedoch wünschenswert, dass die Rohschlüssel nicht im Systemspeicher vorhanden sind, damit sie nicht extrahiert und außerhalb des Geräts verwendet werden können, auch wenn sie beim Start extrahiert werden. Aus diesem Grund wurde das Konzept des langfristigen Wrappers definiert.

Damit die Verwaltung von Schlüsseln auf diese beiden Arten unterstützt werden kann, muss die Hardware die folgenden Schnittstellen implementieren:

  • Schnittstellen zum Generieren und Importieren von Speicherschlüsseln, die in verpackter Form für die Langzeitspeicherung zurückgegeben werden. Auf diese Schnittstellen wird indirekt über KeyMint zugegriffen und sie entsprechen dem TAG_STORAGE_KEY KeyMint-Tag. Die Funktion „generieren“ wird von vold verwendet, um neue Speicherschlüssel für die Verwendung unter Android zu generieren. Die Funktion „importieren“ wird von vts_kernel_encryption_test verwendet, um Testschlüssel zu importieren.
  • Eine Schnittstelle zum Umwandeln eines langfristig verpackten Speicherschlüssels in einen sitzungsspezifischen verpackten Speicherschlüssel. Dies entspricht der convertStorageKeyToEphemeral-KeyMint-Methode. Diese Methode wird sowohl von vold als auch von vts_kernel_encryption_test verwendet, um den Speicher zu entsperren.

Der Schlüsselverschlüsselungsalgorithmus ist ein Implementierungsdetail, sollte aber einen starken AEAD wie AES-256-GCM mit zufälligen IV verwenden.

Erforderliche Softwareänderungen

AOSP bietet bereits ein grundlegendes Framework für die Unterstützung von hardwareverpackten Schlüsseln. Dazu gehören die Unterstützung in Userspace-Komponenten wie vold sowie die Linux-Kernel-Unterstützung in blk-crypto, fscrypt und dm-default-key.

Es sind jedoch einige implementierungsspezifische Änderungen erforderlich.

Änderungen bei KeyMint

Die KeyMint-Implementierung des Geräts muss so geändert werden, dass TAG_STORAGE_KEY unterstützt und die Methode convertStorageKeyToEphemeral implementiert wird.

In Keymaster wurde exportKey anstelle von convertStorageKeyToEphemeral verwendet.

Änderungen am Linux-Kernel

Der Linux-Kernel-Treiber für die Inline-Krypto-Engine des Geräts muss so geändert werden, dass er hardwareverpackte Schlüssel unterstützt.

Für Kernel ab android14: Legen Sie BLK_CRYPTO_KEY_TYPE_HW_WRAPPED in blk_crypto_profile::key_types_supported fest, sorgen Sie dafür, dass blk_crypto_ll_ops::keyslot_program und blk_crypto_ll_ops::keyslot_evict die Programmierung/Auslagerung von hardwareverpackten Schlüsseln unterstützen, und implementieren Sie blk_crypto_ll_ops::derive_sw_secret.

Für android12- und android13-Kernel muss BLK_CRYPTO_FEATURE_WRAPPED_KEYS in blk_keyslot_manager::features festgelegt sein. blk_ksm_ll_ops::keyslot_program und blk_ksm_ll_ops::keyslot_evict müssen das Programmieren und Entfernen von hardwareverpackten Schlüsseln unterstützen und blk_ksm_ll_ops::derive_raw_secret muss implementiert sein.

Für android11-Kernel müssen Sie BLK_CRYPTO_FEATURE_WRAPPED_KEYS in keyslot_manager::features festlegen, keyslot_mgmt_ll_ops::keyslot_program und keyslot_mgmt_ll_ops::keyslot_evict so konfigurieren, dass sie das Programmieren und Entfernen von hardwareverpackten Schlüsseln unterstützen, und keyslot_mgmt_ll_ops::derive_raw_secret implementieren.

Testen

Die Verschlüsselung mit hardwareverpackten Schlüsseln ist zwar schwieriger zu testen als die Verschlüsselung mit Standardschlüsseln, aber es ist trotzdem möglich, sie zu testen, indem Sie einen Testschlüssel importieren und die Schlüsselableitung der Hardware neu implementieren. Dies ist in vts_kernel_encryption_test implementiert. Führen Sie zum Ausführen dieses Tests Folgendes aus:

atest -v vts_kernel_encryption_test

Lesen Sie das Testprotokoll und prüfen Sie, ob die Testfälle für hardwareverpackte Schlüssel (z. B. FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy und DmDefaultKeyTest.TestHwWrappedKey) nicht übersprungen wurden, weil die Unterstützung für hardwareverpackte Schlüssel nicht erkannt wurde, da die Testergebnisse in diesem Fall weiterhin als „bestanden“ gelten.

Tasten aktivieren

Sobald die Unterstützung für hardwareverpackte Schlüssel des Geräts ordnungsgemäß funktioniert, können Sie die folgenden Änderungen an der fstab-Datei des Geräts vornehmen, damit Android sie für die FBE- und Metadatenverschlüsselung verwendet:

  • FBE: Fügen Sie dem Parameter fileencryption das Flag wrappedkey_v0 hinzu. Verwenden Sie zum Beispiel fileencryption=::inlinecrypt_optimized+wrappedkey_v0. Weitere Informationen finden Sie in der FBE-Dokumentation.
  • Verschlüsselung von Metadaten: Fügen Sie dem Parameter metadata_encryption das Flag wrappedkey_v0 hinzu. Verwenden Sie zum Beispiel metadata_encryption=:wrappedkey_v0. Weitere Informationen finden Sie in der Dokumentation zur Verschlüsselung von Metadaten.