Clés enveloppées dans du matériel

Comme la plupart des logiciels de chiffrement de disques et de fichiers, le chiffrement du stockage d'Android repose traditionnellement sur les clés de chiffrement brutes présentes dans la mémoire système afin que le chiffrement puisse être effectué. Même lorsque le chiffrement est effectué par un matériel dédié plutôt que par un logiciel, le logiciel doit généralement gérer les clés de chiffrement brutes.

Cela n'est généralement pas considéré comme un problème car les clés ne seront pas présentes lors d'une attaque hors ligne, qui est le principal type d'attaque contre lequel le chiffrement du stockage est censé protéger. Cependant, il existe un souhait de fournir une protection accrue contre d'autres types d'attaques, telles que les attaques par démarrage à froid et les attaques en ligne dans lesquelles un attaquant pourrait fuir 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 matérielles , où la prise en charge matérielle est présente. Les clés enveloppées dans le matériel sont des clés de stockage qui ne sont connues sous forme brute que par le matériel dédié ; le logiciel voit et fonctionne uniquement avec ces clés sous forme enveloppée (cryptée). Ce matériel doit être capable de générer et d'importer des clés de stockage, d'encapsuler les 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 en ligne et de renvoyer une sous-clé distincte au logiciel.

Remarque : Un moteur de chiffrement en ligne (ou matériel de chiffrement en ligne ) fait référence au matériel qui chiffre/déchiffre les données lorsqu'elles sont en route 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 cryptographiques définies par la spécification JEDEC correspondante.

Conception

Cette section présente la conception de la fonctionnalité de clés enveloppées dans le matériel, y compris la prise en charge matérielle requise pour celle-ci. Cette discussion se concentre 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 serait de les conserver uniquement dans les emplacements de clés d’un moteur de chiffrement en ligne. Cependant, cette approche se heurte à certains problèmes :

  • Le nombre de clés de chiffrement peut dépasser le nombre d'emplacements de clés.
  • Les moteurs de chiffrement en ligne ne peuvent être utilisés que pour chiffrer/déchiffrer des blocs complets de données sur le disque. Cependant, dans le cas de FBE, le logiciel doit encore être capable d'effectuer d'autres travaux cryptographiques tels que le cryptage des noms de fichiers et la déduction des identifiants de clé. Le logiciel aurait toujours besoin d'accéder aux clés FBE brutes pour effectuer cet autre travail.

Pour éviter ces problèmes, les clés de stockage sont plutôt transformées en clés enveloppées dans le matériel , qui ne peuvent être déballé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 un moteur de chiffrement en ligne.

Hiérarchie des clés

Les clés peuvent être dérivées d'autres clés à l'aide d'un KDF (fonction de dérivation de clé) tel que HKDF , ce qui donne lieu à une hiérarchie de clés .

Le diagramme suivant représente une hiérarchie de clés typique pour FBE lorsque les clés encapsulées matériellement ne sont pas utilisées :

Hiérarchie des clés FBE (standard)
Figure 1. Hiérarchie des clés FBE (standard)

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 particulier. (Dans le noyau, cette clé est appelée clé principale fscrypt .) À partir de cette clé, le noyau dérive les sous-clés suivantes :

  • L'identifiant de la clé. Ceci n'est pas utilisé pour le cryptage, mais plutôt comme valeur utilisée pour identifier la clé avec laquelle un fichier ou un répertoire particulier est protégé.
  • La clé de chiffrement du contenu du fichier
  • La clé de cryptage des noms de fichiers

En revanche, le diagramme suivant décrit la hiérarchie des clés pour FBE lorsque des clés encapsulées matériellement sont utilisées :

Hiérarchie des clés FBE (avec clé enveloppée dans le matériel)
Figure 2. Hiérarchie des clés FBE (avec clé enveloppée dans le matériel)

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 cryptés. Cependant, cette clé est désormais sous forme éphémère et, pour pouvoir être utilisée, elle doit être transmise à un matériel dédié. Ce matériel doit implémenter deux interfaces qui acceptent une clé enveloppée de manière éphémère :

  • Une interface pour dériver inline_encryption_key et la programmer directement dans un emplacement de clé du moteur de chiffrement en ligne. 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'opération blk_crypto_ll_ops::keyslot_program , qui doit être implémentée par le pilote de stockage.
  • Une interface pour dériver et renvoyer sw_secret (« secret logiciel » -- également appelé « secret brut » à certains endroits), qui est la clé que Linux utilise pour dériver les sous-clés pour tout autre chose que le cryptage du contenu des fichiers. Dans les noyaux communs Android, cette interface correspond à l'opération blk_crypto_ll_ops::derive_sw_secret , qui doit être implémentée par le pilote de stockage.

Pour dériver inline_encryption_key et sw_secret de la clé de stockage brute, le matériel doit utiliser un KDF cryptographiquement fort. Ce KDF doit suivre les meilleures pratiques de cryptographie ; il doit avoir une force de sécurité d'au moins 256 bits, c'est-à-dire suffisamment pour tout algorithme utilisé ultérieurement. Il doit également utiliser une étiquette distincte, un contexte et/ou une chaîne d'informations spécifique à l'application lors de la dérivation de chaque type de sous-clé afin de 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 des clés n'est pas nécessaire, car la clé de stockage brute est déjà une clé uniformément aléatoire.

Techniquement, tout KDF répondant aux exigences de sécurité pourrait être utilisé. Cependant, à des fins de test, il est nécessaire de réimplémenter le même KDF dans le code de test. Actuellement, un KDF a été examiné et mis en œuvre ; il peut être trouvé dans le code source de vts_kernel_encryption_test . Il est recommandé que le matériel utilise ce KDF, qui utilise NIST SP 800-108 « KDF en mode compteur » avec AES-256-CMAC comme PRF. Notez que pour être compatible, toutes les parties de l'algorithme doivent être identiques, y compris le choix des contextes KDF et des étiquettes pour chaque sous-clé.

Emballage des clés

Pour répondre aux objectifs de sécurité des clés encapsulées matériellement, deux types d'encapsulation de clé sont définis :

  • Wrapping éphémère : le matériel crypte la clé brute à l'aide d'une clé générée aléatoirement à chaque démarrage et qui n'est pas directement exposée à l'extérieur du matériel.
  • Wrapping à 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 à l'extérieur du matériel.

Toutes les clés transmises au noyau Linux pour déverrouiller le stockage sont enveloppées de manière éphémère. Cela garantit que si un attaquant parvient à extraire une clé en cours d'utilisation de la mémoire système, cette clé sera alors inutilisable non seulement hors de l'appareil, mais également sur l'appareil après un redémarrage.

Dans le même temps, Android doit toujours être capable de stocker une version crypté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. Cependant, 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 appareil, même si elles sont extraites au moment du démarrage. C’est pour cette raison que le concept d’emballage à long terme est défini.

Pour prendre en charge la gestion des clés enveloppées de ces deux manières différentes, le matériel doit implémenter les interfaces suivantes :

  • Interfaces pour générer et importer des clés de stockage, en les renvoyant sous une forme encapsulée à long terme. Ces interfaces sont accessibles indirectement via KeyMint et correspondent à la balise TAG_STORAGE_KEY KeyMint. La capacité « générer » est utilisée par vold pour générer de nouvelles clés de stockage destinées à être utilisées par Android, tandis que la capacité « importer » est utilisée par vts_kernel_encryption_test pour importer des clés de test.
  • Une interface pour convertir une clé de stockage encapsulée à long terme en une clé de stockage encapsulée de manière éphémère. Cela correspond à la méthode convertStorageKeyToEphemeral KeyMint. Cette méthode est utilisée à la fois par vold et vts_kernel_encryption_test afin de 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 que AES-256-GCM avec des IV aléatoires.

Modifications logicielles requises

AOSP dispose déjà d'un cadre de base pour prendre en charge les clés matérielles. Cela inclut la prise en charge des 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 .

Cependant, certains changements spécifiques à la mise en œuvre sont nécessaires.

Modifications de KeyMint

L'implémentation KeyMint de l'appareil doit être modifiée pour prendre en charge TAG_STORAGE_KEY et implémenter la méthode convertStorageKeyToEphemeral .

Dans Keymaster, exportKey a été utilisé à la place de convertStorageKeyToEphemeral .

Modifications du noyau Linux

Le pilote du noyau Linux pour le moteur de chiffrement en ligne du périphérique doit être modifié pour prendre en charge les clés encapsulées matériellement.

Pour les noyaux android14 et supérieurs, définissez BLK_CRYPTO_KEY_TYPE_HW_WRAPPED dans blk_crypto_profile::key_types_supported , créez blk_crypto_ll_ops::keyslot_program et blk_crypto_ll_ops::keyslot_evict prenant en charge la programmation/l'expulsion des clés enveloppées dans le matériel et implémentez blk_crypto_ll_ops::derive_sw_secret .

Pour les noyaux android12 et android13 , définissez BLK_CRYPTO_FEATURE_WRAPPED_KEYS dans blk_keyslot_manager::features , créez blk_ksm_ll_ops::keyslot_program et blk_ksm_ll_ops::keyslot_evict prenant en charge la programmation/l'expulsion des clés enveloppées dans le matériel et implémentez blk_ksm_ll_ops::derive_raw_secret .

Pour les noyaux android11 , définissez BLK_CRYPTO_FEATURE_WRAPPED_KEYS dans keyslot_manager::features , faites en sorte que keyslot_mgmt_ll_ops::keyslot_program et keyslot_mgmt_ll_ops::keyslot_evict prennent en charge la programmation/l'expulsion des clés enveloppées dans le matériel et implémentez keyslot_mgmt_ll_ops::derive_raw_secret .

Essai

Bien que le chiffrement avec des clés matérielles soit plus difficile à tester que le chiffrement avec des clés standard, il est toujours possible de le tester en important une clé de test et en réimplémentant la dérivation de clé effectuée par le matériel. Ceci 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és enveloppées dans le matériel (par exemple FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy et DmDefaultKeyTest.TestHwWrappedKey ) n'ont pas été ignorés en raison de la prise en charge des clés enveloppées dans le matériel non détectées, car les résultats des tests seront toujours « transmis » dans ce cas.

Activation

Une fois que la prise en charge matérielle des clés de l'appareil fonctionne correctement, vous pouvez apporter les modifications suivantes au fichier fstab de l'appareil pour qu'Android l'utilise pour le cryptage FBE et des métadonnées :

  • FBE : ajoutez l'indicateur wrappedkey_v0 au paramètre fileencryption . Par exemple, utilisez fileencryption=::inlinecrypt_optimized+wrappedkey_v0 . Pour plus de détails, consultez la documentation FBE .
  • Cryptage des métadonnées : ajoutez l'indicateur wrappedkey_v0 au paramètre metadata_encryption . Par exemple, utilisez metadata_encryption=:wrappedkey_v0 . Pour plus de détails, consultez la documentation sur le chiffrement des métadonnées .