Comme la plupart des logiciels de chiffrement de disque et de fichiers, le chiffrement du stockage d'Android repose traditionnellement sur la présence des clés de chiffrement brutes dans la mémoire système pour que le chiffrement puisse être effectué. Même lorsque le chiffrement est effectué par du matériel dédié plutôt que par un logiciel, le logiciel doit généralement gérer les clés de chiffrement brutes.
Traditionnellement, cela n'est pas considéré comme un problème, car les clés ne sont pas présentes lors d'une attaque hors connexion, qui est le principal type d'attaque contre lequel le chiffrement du stockage est destiné à protéger. Cependant, il existe un désir de fournir une protection accrue contre d'autres types d'attaques, telles que les attaques par démarrage à froid et les attaques en ligne où un pirate informatique peut être en mesure de fuiter la mémoire système sans compromettre complètement l'appareil.
Pour résoudre ce problème, Android 11 a introduit la prise en charge des clés encapsulées dans le matériel, lorsque la prise en charge matérielle est présente. Les clés encapsulées dans le matériel sont des clés de stockage connues sous forme brute uniquement par le matériel dédié. Les logiciels ne voient et n'utilisent ces clés que sous forme encapsulée (chiffrée). Ce matériel doit être capable de générer et d'importer des clés de stockage, d'encapsuler des clés de stockage sous des formes éphémères et à long terme, de dériver des sous-clés, de programmer directement une sous-clé dans un moteur de chiffrement intégré et de renvoyer une sous-clé distincte au logiciel.
Remarque : Un moteur de chiffrement intégré (ou matériel de chiffrement intégré) fait référence à un matériel qui chiffre/déchiffre les données lors de leur transfert vers/depuis le périphérique de stockage. Il s'agit généralement d'un contrôleur hôte UFS ou eMMC qui implémente les extensions de chiffrement définies par la spécification JEDEC correspondante.
Conception
Cette section présente la conception de la fonctionnalité des clés encapsulées dans le matériel, y compris la prise en charge matérielle requise. Cette discussion porte sur le chiffrement basé sur les fichiers (FBE), mais la solution s'applique également au chiffrement des métadonnées.
Une façon d'éviter d'avoir besoin des clés de chiffrement brutes dans la mémoire système consiste à les conserver uniquement dans les emplacements de clés d'un moteur de chiffrement intégré. Toutefois, cette approche pose certains problèmes :
- Le nombre de clés de chiffrement peut dépasser le nombre d'emplacements de clés.
- Les moteurs de chiffrement intégrés perdent généralement le contenu de leurs emplacements de clés si le contrôleur hôte de stockage est réinitialisé. La réinitialisation du contrôleur hôte de stockage est une procédure standard de récupération d'erreur qui est exécutée si certains types d'erreurs de stockage se produisent, et ces erreurs peuvent se produire à tout moment. Par conséquent, lorsque le chiffrement intégré est utilisé, le système d'exploitation doit toujours être prêt à reprogrammer les emplacements de clés sans intervention de l'utilisateur.
- Les moteurs de chiffrement intégrés ne peuvent être utilisés que pour chiffrer/déchiffrer des blocs de données complets sur le disque. Toutefois, dans le cas du FBE, le logiciel doit toujours être en mesure d'effectuer d'autres tâches de chiffrement, telles que le chiffrement des noms de fichiers et la dérivation des identifiants de clés. Le logiciel aurait toujours besoin d'accéder aux clés FBE brutes pour effectuer ces autres tâches.
Pour éviter ces problèmes, les clés de stockage sont plutôt transformées en clés encapsulées dans le matériel, qui ne peuvent être désencapsulées et utilisées que par du matériel dédié. Cela permet de prendre en charge un nombre illimité de clés. De plus, la hiérarchie des clés est modifiée et partiellement déplacée vers ce matériel, ce qui permet de renvoyer une sous-clé au logiciel pour les tâches qui ne peuvent pas utiliser de moteur de chiffrement intégré.
Hiérarchie des clés
Les clés peuvent être dérivées d'autres clés à l'aide d'une fonction de dérivation de clé (KDF) telle que HKDF, ce qui donne lieu à une hiérarchie de clés.
Le schéma suivant illustre une hiérarchie de clés typique pour le FBE lorsque des clés encapsulées dans le matériel ne sont pas utilisées :
La clé de classe FBE est la clé de chiffrement brute qu'Android transmet au noyau Linux pour déverrouiller un ensemble particulier de répertoires chiffrés, tels que le stockage chiffré par identifiants pour un utilisateur Android spécifique. (Dans le noyau, cette clé est appelée clé principale fscrypt.) À partir de cette clé, le noyau dérive les sous-clés suivantes :
- Identifiant de clé. Il n'est pas utilisé pour le chiffrement, mais plutôt comme une valeur permettant d'identifier la clé avec laquelle un fichier ou un répertoire particulier est protégé.
- Clé de chiffrement du contenu du fichier
- Clé de chiffrement des noms de fichiers
En revanche, le schéma suivant illustre la hiérarchie des clés pour le FBE lorsque des clés encapsulées dans le matériel sont utilisées :
Par rapport au cas précédent, un niveau supplémentaire a été ajouté à la hiérarchie des clés, et la clé de chiffrement du contenu du fichier a été déplacée. Le nœud racine représente toujours la clé qu'Android transmet à Linux pour déverrouiller un ensemble de répertoires chiffrés. Toutefois, cette clé est désormais sous forme encapsulée de manière éphémère et, pour être utilisée, elle doit être transmise à du matériel dédié. Ce matériel doit implémenter deux interfaces qui prennent une clé encapsulée de manière éphémère :
- Une interface pour dériver
inline_encryption_keyet directement la programmer dans un emplacement de clés du moteur de chiffrement intégré. Cela permet de chiffrer/déchiffrer le contenu du fichier sans que le logiciel ait accès à la clé brute. Dans les noyaux communs Android, cette interface correspond à l'blk_crypto_ll_ops::keyslot_programopération, qui doit être implémentée par le pilote de stockage. - Une interface pour dériver et renvoyer
sw_secret("secret logiciel" -- précédemment appelé "secret brut"), qui est la clé que Linux utilise pour dériver les sous-clés pour tout ce qui n'est pas le chiffrement du contenu du fichier. Dans les noyaux communs Android, cette interface correspond à l'blk_crypto_ll_ops::derive_sw_secretopération, qui doit être implémentée par le pilote de stockage.
Pour dériver inline_encryption_key et sw_secret à partir de la clé de stockage brute, le matériel doit utiliser une KDF cryptographiquement forte. Cette KDF doit suivre les bonnes pratiques de cryptographie. Elle doit avoir une force de sécurité d'au moins 256 bits, ce qui est suffisant pour tout algorithme utilisé ultérieurement. Elle doit également utiliser un libellé et un contexte distincts lors de la dérivation de chaque type de sous-clé pour garantir que les sous-clés résultantes sont cryptographiquement isolées, c'est-à-dire que la connaissance de l'une d'elles n'en révèle aucune autre. L'étirement de clé n'est pas nécessaire, car la clé de stockage brute est déjà une clé aléatoire uniforme.
Techniquement, n'importe quelle KDF répondant aux exigences de sécurité peut être utilisée.
Toutefois, à des fins de test, vts_kernel_encryption_test
implémente la même KDF dans un logiciel pour reproduire le texte chiffré sur disque
et vérifier qu'il est correct. Pour faciliter les tests et s'assurer qu'une KDF sécurisée et déjà examinée est utilisée, nous vous recommandons d'implémenter la KDF par défaut que le test vérifie. Pour le matériel qui utilise une KDF différente,
consultez Tester les clés encapsulées pour savoir comment configurer le test
en conséquence.
Encapsulation de clé
Pour atteindre les objectifs de sécurité des clés encapsulées dans le matériel, deux types d'encapsulation de clé sont définis :
- Encapsulation éphémère : le matériel chiffre la clé brute à l'aide d'une clé générée de manière aléatoire à chaque démarrage et qui n'est pas directement exposée en dehors du matériel.
- Encapsulation à long terme : le matériel chiffre la clé brute à l'aide d'une clé unique et persistante intégrée au matériel qui n'est pas directement exposée en dehors du matériel.
Toutes les clés transmises au noyau Linux pour déverrouiller le stockage sont encapsulées de manière éphémère. Cela garantit que si un pirate informatique parvient à extraire une clé en cours d'utilisation de la mémoire système, cette clé est inutilisable non seulement hors de l'appareil, mais également sur l'appareil après un redémarrage.
En même temps, Android doit toujours être en mesure de stocker une version chiffrée des clés sur le disque afin qu'elles puissent être déverrouillées en premier lieu. Les clés brutes fonctionneraient à cet effet. Toutefois, il est souhaitable que les clés brutes ne soient jamais présentes dans la mémoire système afin qu'elles ne puissent jamais être extraites pour être utilisées hors de l'appareil, même si elles sont extraites au moment du démarrage. C'est pourquoi le concept d'encapsulation à long terme est défini.
Pour prendre en charge la gestion des clés encapsulées de ces deux manières différentes, le matériel doit implémenter les interfaces suivantes :
- Interfaces permettant de générer et d'importer des clés de stockage, en les renvoyant sous forme encapsulée à long terme. L'interface generate est utilisée par
voldpour générer de nouvelles clés de stockage à utiliser par Android. L'interface import est utilisée parvts_kernel_encryption_testpour importer des clés de test. - Une interface permettant de convertir une clé de stockage encapsulée à long terme en une
clé de stockage encapsulée de manière éphémère. Cette interface est utilisée par
voldetvts_kernel_encryption_testpour déverrouiller le stockage.
L'algorithme d'encapsulation de clé est un détail d'implémentation, mais il doit utiliser un AEAD fort tel qu'AES-256-GCM avec des IV aléatoires.
Modifications logicielles requises
AOSP dispose déjà d'un framework de base pour la prise en charge des clés encapsulées dans le matériel. Cela
inclut la prise en charge dans les composants de l'espace utilisateur tels que vold, ainsi
que la prise en charge du noyau Linux dans blk-crypto, fscrypt et
dm-default-key.
Modifications du noyau Linux
Le pilote du noyau Linux pour le contrôleur de stockage de l'appareil avec prise en charge du chiffrement intégré doit être modifié pour prendre en charge les clés encapsulées dans le matériel.
Pour les noyaux android17 et versions ultérieures :
- Définissez
BLK_CRYPTO_KEY_TYPE_HW_WRAPPEDdansblk_crypto_profile::key_types_supported. - Faites en sorte que
blk_crypto_ll_ops::keyslot_programprenne en charge la programmation des clés encapsulées dans le matériel. - Faites en sorte que
blk_crypto_ll_ops::keyslot_evictprenne en charge l'éviction des clés encapsulées dans le matériel. - Implémentez
blk_crypto_ll_ops::derive_sw_secret,blk_crypto_ll_ops::import_key,blk_crypto_ll_ops::generate_key, etblk_crypto_ll_ops::prepare_key.
Pour les noyaux android14, android15 et android16 :
- Définissez
BLK_CRYPTO_KEY_TYPE_HW_WRAPPEDdansblk_crypto_profile::key_types_supported. - Faites en sorte que
blk_crypto_ll_ops::keyslot_programprenne en charge la programmation des clés encapsulées dans le matériel. - Faites en sorte que
blk_crypto_ll_ops::keyslot_evictprenne en charge l'éviction des clés encapsulées dans le matériel. - Implémentez
blk_crypto_ll_ops::derive_sw_secret.
Pour les noyaux android12 et android13 :
- Définissez
BLK_CRYPTO_FEATURE_WRAPPED_KEYSdansblk_keyslot_manager::features. - Faites en sorte que
blk_ksm_ll_ops::keyslot_programprenne en charge la programmation des clés encapsulées dans le matériel. - Faites en sorte que
blk_ksm_ll_ops::keyslot_evictprenne en charge l'éviction des clés encapsulées dans le matériel. - Implémentez
blk_ksm_ll_ops::derive_raw_secret.
Pour les noyaux android11 :
- Définissez
BLK_CRYPTO_FEATURE_WRAPPED_KEYSdanskeyslot_manager::features. - Faites en sorte que
keyslot_mgmt_ll_ops::keyslot_programprenne en charge la programmation des clés encapsulées dans le matériel. - Faites en sorte que
keyslot_mgmt_ll_ops::keyslot_evictprenne en charge l'éviction des clés encapsulées dans le matériel. - Implémentez
keyslot_mgmt_ll_ops::derive_raw_secret.
Modifications KeyMint (héritées)
Dans la version actuelle des clés encapsulées dans le matériel (wrappedkey), la génération, l'importation et la préparation des clés encapsulées dans le matériel utilisent les ioctls du noyau Linux BLKCRYPTOGENERATEKEY, BLKCRYPTOIMPORTKEY et BLKCRYPTOPREPAREKEY. Ces ioctls correspondent à des méthodes dans struct blk_crypto_ll_ops. Le pilote de stockage implémente ces méthodes et communique avec le matériel d'encapsulation de clé pour effectuer l'opération demandée. Pour en savoir plus sur ces
ioctls, consultez la
documentation du noyau Linux..
Ces ioctls ont été ajoutés dans Linux 6.16. Sur les appareils qui n'ont pas été lancés avec la solution basée sur ioctl, une autre solution utilisant Android KeyMint (ou précédemment KeyMaster) est utilisée. L'ancienne solution (wrappedkey_v0) n'est pas compatible avec le noyau Linux principal ni avec la solution actuelle. L'ancienne solution utilise les fonctionnalités KeyMint suivantes :
- Prise en charge de
TAG_STORAGE_KEY, à la fois pour la génération et importation de clés. - Prise en charge de la méthode
convertStorageKeyToEphemeral.
Cette fonctionnalité KeyMint n'est nécessaire que sur les appareils qui utilisent l'ancienne solution, correspondant à wrappedkey_v0 dans le fichier fstab.
Les appareils qui utilisent la solution actuelle, correspondant à wrappedkey dans le fichier fstab, n'ont pas besoin d'implémenter cette fonctionnalité KeyMint.
Tester les clés encapsulées
Bien que le chiffrement avec des clés encapsulées dans le matériel soit plus difficile à tester que le chiffrement avec des clés brutes, il est toujours possible de tester en important une clé de test et en réimplémentant la dérivation de clé effectuée par le matériel. Cela est implémenté
dans vts_kernel_encryption_test. Pour exécuter ce test, exécutez :
atest -v vts_kernel_encryption_test
Lisez le journal de test et vérifiez que les cas de test de clé encapsulée dans le matériel (par exemple, FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy et DmDefaultKeyTest.TestHwWrappedKey) n'ont pas été ignorés, car la prise en charge des clés encapsulées dans le matériel n'a pas été détectée, car les résultats du test sont toujours "réussis" dans ce cas.
Par défaut, vts_kernel_encryption_test suppose que le matériel implémente une KDF qu'il appelle kdf1. Cette
KDF appartient à la famille de KDF en mode compteur de NIST
SP 800-108 et utilise AES-256-CMAC comme fonction pseudo-aléatoire. Pour en savoir plus
sur CMAC, consultez la spécification
CMAC. La KDF utilise des contextes et des libellés spécifiques lors de la dérivation de chaque sous-clé. Le matériel doit implémenter cette KDF, y compris le choix exact du contexte, du libellé et du format de la chaîne d'entrée fixe lors de la dérivation de chaque sous-clé.
Toutefois, vts_kernel_encryption_test implémente également des KDF supplémentaires kdf2 à kdf4. Elles sont aussi sécurisées que kdf1 et ne diffèrent que par le choix des contextes, des libellés et du format de la chaîne d'entrée fixe. Elles n'existent que pour s'adapter à différents matériels.
Pour les appareils qui utilisent une KDF différente, définissez la propriété système ro.crypto.hw_wrapped_keys.kdf dans PRODUCT_VENDOR_PROPERTIES sur le nom de la KDF tel que défini dans le code source du test. Cela permet à vts_kernel_encryption_test de rechercher cette KDF au lieu de kdf1. Par exemple, pour sélectionner kdf2, utilisez :
PRODUCT_VENDOR_PROPERTIES += ro.crypto.hw_wrapped_keys.kdf=kdf2
Pour les appareils qui utilisent une KDF non compatible avec le test, ajoutez également une implémentation de cette KDF au test et donnez-lui un nom unique.
Activer les clés encapsulées
Lorsque la prise en charge des clés encapsulées dans le matériel de l'appareil fonctionne correctement, apportez les modifications suivantes au fichier fstab de l'appareil pour qu'Android l'utilise pour le FBE et le chiffrement des métadonnées :
- FBE : ajoutez l'indicateur
wrappedkey(ouwrappedkey_v0pour l'ancienne version) au paramètrefileencryption. Par exemple, utilisezfileencryption=::inlinecrypt_optimized+wrappedkey. Pour en savoir plus, consultez la documentation FBE. - Chiffrement des métadonnées : ajoutez l'indicateur
wrappedkey(ouwrappedkey_v0pour l'ancienne version) aumetadata_encryptionparamètre. Par exemple, utilisezmetadata_encryption=:wrappedkey. Pour en savoir plus, consultez la documentation sur le chiffrement des métadonnées.
Dans chaque cas, il existe deux versions de l'indicateur :
wrappedkey, compatible avec Android 17 et versions ultérieures, active la version actuelle des clés encapsulées dans le matériel. Cette version est compatible avec le noyau Linux principal.wrappedkey_v0, compatible avec Android 11 et versions ultérieures, active l'ancienne version des clés encapsulées dans le matériel. Cette version n'est pas compatible avec le noyau Linux principal. Elle proxy certaines opérations via KeyMint et utilise un format sur disque non standard. Pour en savoir plus, consultez Modifications KeyMint (héritées).
Sur les appareils lancés avec Android 17 ou une version ultérieure, préférez wrappedkey.
Sur les appareils déjà lancés avec wrappedkey_v0, continuez à utiliser wrappedkey_v0 pour la rétrocompatibilité.