Clés encapsulées dans le matériel

Comme la plupart des logiciels de chiffrement de disque et de fichier, le chiffrement de 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 toujours 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 sont pas présentes lors d'une attaque hors connexion, qui est le principal type d'attaque contre lequel le chiffrement de stockage est censé protéger. Toutefois, il est souhaitable de renforcer la protection 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 provoquer une fuite de 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 matériellement, lorsque la prise en charge matérielle est disponible. Les clés encapsulées dans le matériel sont des clés de stockage qui ne sont connues sous forme brute que du 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, de les encapsuler sous des formes éphémères et à long terme, d'obtenir 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 cryptographie en ligne (ou matériel de chiffrement en ligne) désigne un matériel qui chiffre/déchiffre les données lorsqu'elles sont en transit vers ou depuis l'appareil de stockage. Il s'agit généralement d'un contrôleur hôte UFS ou eMMC qui implémente les extensions de cryptographie définies par la spécification JEDEC correspondante.

Conception

Cette section présente la conception de la fonctionnalité de clés encapsulées matériellement, y compris l'assistance 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.

Pour éviter d'avoir besoin des clés de chiffrement brutes dans la mémoire système, vous pouvez les conserver uniquement dans les emplacements de clés d'un moteur de cryptographie intégré. Cependant, cette approche rencontre quelques 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 de stockage (généralement UFS ou eMMC) est réinitialisé. La réinitialisation du contrôleur de stockage est une procédure de récupération d'erreur standard qui est exécutée si certains types d'erreurs de stockage se produisent. Ces erreurs peuvent se produire à tout moment. Par conséquent, lorsque la cryptographie intégrée est utilisée, le système d'exploitation doit toujours être prêt à reprogrammer les emplacements de clés sans intervention de l'utilisateur.
  • Les moteurs de cryptographie intégrés ne peuvent être utilisés que pour chiffrer/déchiffrer des blocs de données entiers sur le disque. Toutefois, dans le cas du FBE, le logiciel doit toujours pouvoir effectuer d'autres tâches cryptographiques, telles que le chiffrement des noms de fichiers et la dérivation d'identifiants de clé. Le logiciel aurait toujours besoin d'accéder aux clés FBE brutes pour effectuer cette autre tâche.

Pour éviter ces problèmes, les clés de stockage sont transformées en clés encapsulées matériellement, 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 un moteur de cryptographie 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, key derivation function) telle que HKDF, ce qui donne une hiérarchie de clés.

Le schéma suivant illustre une hiérarchie de clés typique pour le FBE lorsque les clés encapsulées matériellement ne sont pas utilisées:

Hiérarchie des clés FBE (standard)
Image 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:

  • Identifiant de clé. Il ne s'agit pas d'un chiffrement, mais d'une valeur utilisée pour 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

À l'inverse, le schéma suivant illustre la hiérarchie des clés pour le FBE lorsque des clés encapsulées matériellement sont utilisées:

Hiérarchie des clés FBE (avec clé encapsulée matériellement)
Figure 2 : Hiérarchie des clés FBE (avec clé encapsulée matérielle)

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 des fichiers 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. Cependant, cette clé est maintenant encapsulée de manière éphémère. Pour être utilisée, elle doit être transmise à du matériel dédié. Ce matériel doit implémenter deux interfaces qui utilisent une clé encapsulé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 cryptographie intégré. Cela permet de chiffrer/déchiffrer le contenu des fichiers sans que le logiciel n'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 permettant de dériver et de renvoyer sw_secret ("secret logiciel", également appelé "secret brut" à certains endroits), qui est la clé utilisée par Linux pour dériver les sous-clés pour tout autre chose que le chiffrement 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 à partir de la clé de stockage brute, le matériel doit utiliser un KDF cryptographiquement sécurisé. Ce KDF doit respecter les bonnes pratiques de cryptographie. Il doit avoir une sécurité d'au moins 256 bits, ce qui est suffisant pour tout algorithme utilisé ultérieurement. Elle doit également utiliser un libellé distinct, un contexte et une chaîne d'informations spécifiques à l'application lors de la dérivation de chaque type de sous-clé afin de garantir que les sous-clés résultantes sont isolées de manière cryptographique, c'est-à-dire que la connaissance de l'une d'entre 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, n'importe quel KDF répondant aux exigences de sécurité peut être utilisé. Toutefois, à des fins de test, il est nécessaire de réimplémenter le même KDF dans le code de test. Actuellement, un fichier KDF a été examiné et implémenté. Vous le trouverez dans le code source de vts_kernel_encryption_test. Il est recommandé que le matériel utilise ce KDF, qui utilise le NIST SP 800-108 "KDF en mode compteur" avec AES-256-CMAC comme PRF. Notez que, pour être compatibles, toutes les parties de l'algorithme doivent être identiques, y compris le choix des contextes et des étiquettes KDF pour chaque sous-clé.

Encapsulation de clé

Pour atteindre les objectifs de sécurité des clés encapsulées matériellement, deux types d'encapsulation de clés 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. Ainsi, si un pirate informatique est en mesure d'extraire une clé en cours d'utilisation de la mémoire système, cette clé sera inutilisable non seulement hors de l'appareil, mais également sur l'appareil après un redémarrage.

En même temps, Android doit toujours pouvoir stocker une version chiffrée des clés sur le disque afin qu'elles puissent être déverrouillées. Les clés brutes conviennent à 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 en dehors de l'appareil, même si elles sont extraites au démarrage. Pour cette raison, le concept d'encapsulation à long terme est défini.

Pour permettre la gestion des clés encapsulées de ces deux façons 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. Ces interfaces sont accessibles indirectement via KeyMint et correspondent à la balise KeyMint TAG_STORAGE_KEY. La fonctionnalité "générer" est utilisée par vold pour générer de nouvelles clés de stockage à utiliser par Android, tandis que la fonctionnalité "importer" est utilisée par vts_kernel_encryption_test pour importer des clés de test.
  • Interface permettant de convertir une clé de stockage encapsulée à long terme en clé de stockage encapsulée de manière éphémère. Cela correspond à la méthode KeyMint convertStorageKeyToEphemeral. Cette méthode est utilisée à la fois par vold et vts_kernel_encryption_test pour déverrouiller l'espace de stockage.

L'algorithme d'encapsulation de clé est un détail d'implémentation, mais il doit utiliser un AEAD puissant tel qu'AES-256-GCM avec des IV aléatoires.

Modifications logicielles requises

AOSP dispose déjà d'un framework de base pour prendre en charge les clés encapsulées matériellement. Cela inclut la compatibilité avec les composants de l'espace utilisateur tels que vold, ainsi que la compatibilité du noyau Linux avec blk-crypto, fscrypt et dm-default-key.

Cependant, certaines modifications spécifiques à l'implémentation sont nécessaires.

Modifications apportées à 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 était utilisé à la place de convertStorageKeyToEphemeral.

Modifications du noyau Linux

Le pilote du noyau Linux pour le moteur de cryptographie intégré de l'appareil 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, faites en sorte que blk_crypto_ll_ops::keyslot_program et blk_crypto_ll_ops::keyslot_evict prennent en charge la programmation/l'éviction de clés encapsulé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, faites en sorte que blk_ksm_ll_ops::keyslot_program et blk_ksm_ll_ops::keyslot_evict prennent en charge la programmation/l'éviction des clés encapsulées matériellement, 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'éviction des clés encapsulées matériellement, et implémentez keyslot_mgmt_ll_ops::derive_raw_secret.

Tests

Bien que le chiffrement avec des clés encapsulées matériellement soit plus difficile à tester que le chiffrement avec des clés standards, 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 la commande suivante :

atest -v vts_kernel_encryption_test

Lisez le journal de test et vérifiez que les scénarios de test de clés encapsulées dans le matériel (par exemple, FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy et DmDefaultKeyTest.TestHwWrappedKey) n'ont pas été ignorés, car les clés encapsulées dans le matériel ne sont pas détectées, car les résultats des tests sont toujours "réussi" dans ce cas.

Activer les touches

Une fois que la prise en charge des clés encapsulées matériellement de l'appareil fonctionne correctement, vous pouvez apporter les modifications suivantes au fichier fstab de l'appareil pour qu'Android l'utilise pour le chiffrement 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 en savoir plus, consultez la documentation FBE.
  • Chiffrement des métadonnées: ajoutez l'option wrappedkey_v0 au paramètre metadata_encryption. Par exemple, utilisez metadata_encryption=:wrappedkey_v0. Pour en savoir plus, consultez la documentation sur le chiffrement des métadonnées.