Wie die meisten Festplatten- und Dateiverschlüsselungsprogramme ist die Speicherverschlüsselung von Android traditionell darauf angewiesen, dass die rohen Verschlüsselungsschlüssel im Systemspeicher vorhanden sind, damit die Verschlüsselung durchgeführt werden kann. Selbst wenn die Verschlüsselung eher durch dedizierte Hardware als durch Software durchgeführt wird, muss die Software im Allgemeinen immer noch die rohen Verschlüsselungsschlüssel verwalten.
Dies wird traditionell nicht als Problem angesehen, da die Schlüssel während eines Offline-Angriffs nicht vorhanden sind, was die Hauptangriffsart ist, vor der die Speicherverschlüsselung schützen soll. Es besteht jedoch der Wunsch, einen erhöhten Schutz gegen andere Arten von Angriffen bereitzustellen, wie z. B. Kaltstartangriffe und Online-Angriffe, bei denen ein Angreifer in der Lage sein könnte, Systemspeicher zu verlieren, ohne das Gerät vollständig zu gefährden.
Um dieses Problem zu lösen, hat Android 11 die Unterstützung für Hardware-verpackte Schlüssel eingeführt, wo Hardware-Unterstützung vorhanden ist. Hardwareverpackte Schlüssel sind Speicherschlüssel, die dedizierter Hardware nur in Rohform bekannt sind; Software sieht und arbeitet nur mit diesen Schlüsseln in verpackter (verschlüsselter) Form. Diese Hardware muss in der Lage sein, Speicherschlüssel zu generieren und zu importieren, Speicherschlüssel in flüchtige und langfristige Formen zu verpacken, Unterschlüssel abzuleiten, einen Unterschlüssel direkt in eine Inline-Krypto-Engine zu programmieren und einen separaten Unterschlüssel an die Software zurückzugeben.
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-Host-Controller, der die Krypto-Erweiterungen implementiert, die durch die entsprechende JEDEC-Spezifikation definiert sind.
Design
In diesem Abschnitt wird das Design der Funktion für mit Hardware umschlossene Schlüssel vorgestellt, einschließlich der dafür erforderlichen Hardwareunterstützung. Diese Diskussion konzentriert sich auf die dateibasierte Verschlüsselung (FBE), aber die Lösung gilt auch für die Metadatenverschlüsselung .
Eine Möglichkeit zu vermeiden, dass die rohen Verschlüsselungsschlüssel im Systemspeicher benötigt werden, wäre, sie nur in den Schlüsselschlitzen einer Inline-Krypto-Engine aufzubewahren. Dieser Ansatz stößt jedoch auf einige Probleme:
- Die Anzahl der Verschlüsselungsschlüssel kann die Anzahl der Schlüsselschlitze überschreiten.
- Inline-Krypto-Engines können nur zum Verschlüsseln/Entschlüsseln vollständiger Datenblöcke auf der Festplatte verwendet werden. Im Fall von FBE muss die Software jedoch noch in der Lage sein, andere kryptografische Aufgaben wie die Verschlüsselung von Dateinamen und das Ableiten von Schlüsselkennungen auszuführen. Die Software benötigt weiterhin Zugriff auf die rohen FBE-Schlüssel, um diese andere Arbeit zu erledigen.
Um diese Probleme zu vermeiden, werden die Speicherschlüssel stattdessen in Hardware-verpackte Schlüssel umgewandelt, die nur von dedizierter Hardware entpackt und verwendet werden können. Dadurch kann eine unbegrenzte Anzahl von Schlüsseln unterstützt werden. Darüber hinaus wird die Schlüsselhierarchie modifiziert und teilweise auf diese Hardware verschoben, wodurch ein Unterschlüssel an Software für Aufgaben zurückgegeben werden kann, die keine Inline-Krypto-Engine verwenden können.
Schlüsselhierarchie
Schlüssel können mithilfe einer KDF (Schlüsselableitungsfunktion) wie HKDF von anderen Schlüsseln abgeleitet werden, was zu einer Schlüsselhierarchie führt.
Das folgende Diagramm zeigt eine typische Schlüsselhierarchie für FBE, wenn keine in Hardware verpackten Schlüssel verwendet werden:
Der FBE-Klassenschlüssel ist der rohe Verschlüsselungsschlüssel, den Android an den Linux-Kernel übergibt, um einen bestimmten Satz verschlüsselter Verzeichnisse zu entsperren, z. B. den mit Anmeldeinformationen verschlüsselten Speicher für einen bestimmten Android-Benutzer. (Im Kernel wird dieser Schlüssel als fscrypt-Hauptschlüssel bezeichnet.) Aus diesem Schlüssel leitet der Kernel die folgenden Unterschlüssel ab:
- Die Schlüsselkennung. Dies wird nicht zur Verschlüsselung verwendet, sondern ist ein Wert, der verwendet wird, um 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 Dateinamen-Verschlüsselungsschlüssel
Im Gegensatz dazu zeigt das folgende Diagramm die Schlüsselhierarchie für FBE, wenn in Hardware verpackte Schlüssel verwendet werden:
Im Vergleich zum früheren Fall wurde der Schlüsselhierarchie eine zusätzliche Ebene hinzugefügt, und der Verschlüsselungsschlüssel für den Dateiinhalt wurde verschoben. Der Root-Knoten stellt immer noch den Schlüssel dar, den Android an Linux übergibt, um einen Satz verschlüsselter Verzeichnisse zu entsperren. Jetzt liegt dieser Schlüssel jedoch in kurzlebiger Form vor, und um verwendet werden zu können, muss er an dedizierte Hardware übergeben werden. Diese Hardware muss zwei Schnittstellen implementieren, die einen flüchtig verpackten Schlüssel akzeptieren:
- Eine Schnittstelle, um
inline_encryption_key
abzuleiten und direkt in einen Keyslot der Inline-Krypto-Engine zu programmieren. Dadurch können Dateiinhalte verschlüsselt/entschlüsselt werden, ohne dass Software Zugriff auf den Rohschlüssel hat. In den allgemeinen Android-Kerneln entspricht diese Schnittstelle der Operationblk_crypto_ll_ops::keyslot_program
, die vom Speichertreiber implementiert werden muss. - Eine Schnittstelle zum Ableiten und Zurückgeben
sw_secret
("Softwaregeheimnis" - an manchen Stellen auch als "rohes Geheimnis" bezeichnet), das der Schlüssel ist, den Linux verwendet, um die Unterschlüssel für alles andere als die Verschlüsselung von Dateiinhalten abzuleiten. In den allgemeinen Android-Kerneln entspricht diese Schnittstelle der Operationblk_crypto_ll_ops::derive_sw_secret
, die vom Speichertreiber implementiert werden muss.
Um inline_encryption_key
und sw_secret
aus dem rohen Speicherschlüssel abzuleiten, muss die Hardware ein kryptografisch starkes KDF verwenden. Dieses KDF muss den Best Practices für Kryptografie folgen; es muss eine Sicherheitsstärke von mindestens 256 Bit haben, also genug für jeden später verwendeten Algorithmus. Es muss auch eine eindeutige Bezeichnung, einen Kontext und/oder eine anwendungsspezifische Informationszeichenfolge verwenden, wenn es jeden Typ von Unterschlüssel herleitet, um zu garantieren, dass die resultierenden Unterschlüssel kryptografisch isoliert sind, dh die Kenntnis von einem von ihnen offenbart keinen anderen. Eine Schlüsselstreckung ist nicht erforderlich, da der Rohspeicherschlüssel bereits ein einheitlich zufälliger Schlüssel ist.
Technisch gesehen könnte jedes KDF verwendet werden, das die Sicherheitsanforderungen erfüllt. Zu Testzwecken ist es jedoch erforderlich, dasselbe KDF erneut in Testcode zu implementieren. Derzeit wurde ein KDF überprüft und implementiert; es kann im Quellcode für vts_kernel_encryption_test
. Es wird empfohlen, dass die Hardware dieses KDF verwendet, das NIST SP 800-108 „KDF im Zählermodus“ mit AES-256-CMAC als PRF verwendet. Beachten Sie, dass alle Teile des Algorithmus identisch sein müssen, um kompatibel zu sein, einschließlich der Auswahl von KDF-Kontexten und Bezeichnungen für jeden Unterschlüssel.
Schlüsselverpackung
Um die Sicherheitsziele von in Hardware verpackten Schlüsseln zu erfüllen, werden zwei Arten von Key Wrapping definiert:
- Ephemeral Wrapping : Die Hardware verschlüsselt den Rohschlüssel mit einem Schlüssel, der bei jedem Start zufällig generiert wird und außerhalb der Hardware nicht direkt offengelegt wird.
- Langfristige Verpackung : Die Hardware verschlüsselt den Rohschlüssel mit einem eindeutigen, dauerhaften Schlüssel, der in die Hardware integriert ist und außerhalb der Hardware nicht direkt offengelegt wird.
Alle Schlüssel, die zum Entsperren des Speichers an den Linux-Kernel übergeben werden, sind kurzlebig verpackt. Dadurch wird sichergestellt, dass, wenn ein Angreifer einen in Gebrauch befindlichen Schlüssel aus dem Systemspeicher extrahieren kann, dieser Schlüssel nicht nur außerhalb des Geräts, sondern auch nach einem Neustart auf dem Gerät unbrauchbar ist.
Gleichzeitig muss Android weiterhin in der Lage sein, eine verschlüsselte Version der Schlüssel auf der Festplatte zu speichern, damit sie überhaupt entsperrt werden können. Die rohen Schlüssel würden für diesen Zweck funktionieren. Es ist jedoch wünschenswert, dass die Rohschlüssel niemals im Systemspeicher vorhanden sind, damit sie niemals extrahiert werden können, um außerhalb des Geräts verwendet zu werden, selbst wenn sie beim Booten extrahiert werden. Aus diesem Grund wird der Begriff der Langzeitverpackung definiert.
Um die Verwaltung von Schlüsseln zu unterstützen, die auf diese beiden unterschiedlichen Arten verpackt sind, muss die Hardware die folgenden Schnittstellen implementieren:
- Schnittstellen zum Generieren und Importieren von Speicherschlüsseln, die in langfristig verpackter Form zurückgegeben werden. Auf diese Schnittstellen wird indirekt über KeyMint zugegriffen, und sie entsprechen dem
TAG_STORAGE_KEY
-Tag TAG_STORAGE_KEY. Die Fähigkeit „generate“ wird vonvold
verwendet, um neue Speicherschlüssel zur Verwendung durch Android zu generieren, während die Fähigkeit „import“ vonvts_kernel_encryption_test
verwendet wird, um Testschlüssel zu importieren. - Eine Schnittstelle zum Konvertieren eines langfristig verpackten Speicherschlüssels in einen flüchtig verpackten Speicherschlüssel. Dies entspricht der Methode
convertStorageKeyToEphemeral
KeyMint. Diese Methode wird sowohl vonvold
als auch vonvts_kernel_encryption_test
verwendet, um den Speicher zu entsperren.
Der Key-Wrapping-Algorithmus ist ein Implementierungsdetail, sollte jedoch ein starkes AEAD wie AES-256-GCM mit zufälligen IVs verwenden.
Softwareänderungen erforderlich
AOSP verfügt bereits über ein Grundgerüst zur Unterstützung von in Hardware verpackten Schlüsseln. Dazu gehört die Unterstützung in Userspace-Komponenten wie vold
, sowie die Linux-Kernel-Unterstützung in blk-crypto , fscrypt und dm-default-key .
Allerdings sind einige implementierungsspezifische Änderungen erforderlich.
KeyMint-Änderungen
Die KeyMint-Implementierung des Geräts muss geändert werden, um convertStorageKeyToEphemeral
zu unterstützen und die Methode TAG_STORAGE_KEY
zu implementieren.
In Keymaster wurde exportKey
anstelle von convertStorageKeyToEphemeral
verwendet.
Linux-Kernel-Änderungen
Der Linux-Kernel-Treiber für die Inline-Krypto-Engine des Geräts muss geändert werden, um hardwareverpackte Schlüssel zu unterstützen.
Setzen Sie für android14
und höhere Kernel BLK_CRYPTO_KEY_TYPE_HW_WRAPPED
in blk_crypto_profile::key_types_supported
, sorgen Sie dafür, dass blk_crypto_ll_ops::keyslot_program
und blk_crypto_ll_ops::keyslot_evict
das Programmieren/Entfernen von in Hardware verpackten Schlüsseln unterstützen, und implementieren blk_crypto_ll_ops::derive_sw_secret
.
Setzen Sie für android12
und android13
Kernel BLK_CRYPTO_FEATURE_WRAPPED_KEYS
in blk_keyslot_manager::features
, sorgen Sie dafür, dass blk_ksm_ll_ops::keyslot_program
und blk_ksm_ll_ops::keyslot_evict
das Programmieren/Entfernen von Hardware-verpackten Schlüsseln unterstützen, und implementieren blk_ksm_ll_ops::derive_raw_secret
.
Setzen Sie für android11
Kernel BLK_CRYPTO_FEATURE_WRAPPED_KEYS
in keyslot_manager::features
, sorgen Sie dafür, dass keyslot_mgmt_ll_ops::keyslot_program
und keyslot_mgmt_ll_ops::keyslot_evict
das Programmieren/Entfernen von in Hardware verpackten Schlüsseln unterstützen, und implementieren keyslot_mgmt_ll_ops::derive_raw_secret
.
Testen
Obwohl die Verschlüsselung mit in Hardware verpackten Schlüsseln schwieriger zu testen ist als die Verschlüsselung mit Standardschlüsseln, ist es dennoch möglich, sie zu testen, indem Sie einen Testschlüssel importieren und die Schlüsselableitung erneut implementieren, die die Hardware durchführt. Dies ist in vts_kernel_encryption_test
implementiert. Um diesen Test auszuführen, führen Sie Folgendes aus:
atest -v vts_kernel_encryption_test
Lesen Sie das Testprotokoll und vergewissern Sie sich, dass die Testfälle für mit Hardware umschlossene Schlüssel (z. B. FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy
und DmDefaultKeyTest.TestHwWrappedKey
) nicht übersprungen wurden, da die Unterstützung für umschlossene Schlüssel mit Hardware nicht erkannt wurde, da die Testergebnisse weiterhin „übergeben“ werden dieser Fall.
Aktivieren
Sobald die Hardware-Wrapped Key-Unterstützung 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:
wrappedkey_v0
Sie das Flag wrappedkey_v0 zumfileencryption
Parameter hinzu. Verwenden Sie beispielsweisefileencryption=::inlinecrypt_optimized+wrappedkey_v0
. Weitere Einzelheiten finden Sie in der FBE-Dokumentation . - Metadatenverschlüsselung: Fügen Sie das Flag
wrappedkey_v0
zum Parametermetadata_encryption
hinzu. Verwenden Sie beispielsweisemetadata_encryption=:wrappedkey_v0
. Weitere Einzelheiten finden Sie in der Dokumentation zur Metadatenverschlüsselung .