Keystore soutenu par le matériel

La disponibilité d'un environnement d'exécution fiable dans un système sur puce (SoC) offre aux appareils Android la possibilité de fournir des services de sécurité solides et soutenus par le matériel au système d'exploitation Android, aux services de plate-forme et même aux applications tierces. Les développeurs à la recherche d'extensions spécifiques à Android doivent se rendre sur android.security.keystore .

Avant Android 6.0, Android disposait déjà d'une simple API de services de cryptage basée sur le matériel, fournie par les versions 0.2 et 0.3 de Keymaster Hardware Abstraction Layer (HAL). Keystore a fourni des opérations de signature et de vérification numériques, ainsi que la génération et l'importation de paires de clés de signature asymétriques. Ceci est déjà implémenté sur de nombreux appareils, mais il existe de nombreux objectifs de sécurité qui ne peuvent pas être facilement atteints avec une seule API de signature. Keystore dans Android 6.0 a étendu l'API Keystore pour fournir une gamme plus large de fonctionnalités.

Dans Android 6.0, Keystore a ajouté des primitives cryptographiques symétriques , AES et HMAC, et un système de contrôle d'accès pour les clés matérielles. Les contrôles d'accès sont spécifiés lors de la génération de la clé et appliqués pendant toute la durée de vie de la clé. Les clés peuvent être restreintes pour être utilisables uniquement après que l'utilisateur a été authentifié, et uniquement à des fins spécifiées ou avec des paramètres cryptographiques spécifiés. Pour plus d'informations, consultez les pages Balises d'autorisation et Fonctions .

En plus d'élargir la gamme de primitives cryptographiques, Keystore dans Android 6.0 a ajouté ce qui suit :

  • Un schéma de contrôle d'utilisation pour permettre de limiter l'utilisation des clés, afin d'atténuer le risque de compromission de la sécurité en raison d'une mauvaise utilisation des clés
  • Un schéma de contrôle d'accès pour permettre la restriction des clés à des utilisateurs, des clients et une plage de temps définis

Dans Android 7.0, Keymaster 2 a ajouté la prise en charge de l'attestation de clé et de la liaison de version. L'attestation de clé fournit des certificats de clé publique qui contiennent une description détaillée de la clé et de ses contrôles d'accès, afin de rendre vérifiable à distance l'existence de la clé dans le matériel sécurisé et sa configuration.

La liaison de version lie les clés au système d'exploitation et à la version du niveau de correctif. Cela garantit qu'un attaquant qui découvre une faiblesse dans une ancienne version du système ou du logiciel TEE ne peut pas restaurer un appareil vers la version vulnérable et utiliser les clés créées avec la version la plus récente. De plus, lorsqu'une clé avec une version et un niveau de correctif donnés est utilisée sur un appareil qui a été mis à niveau vers une version ou un niveau de correctif plus récent, la clé est mise à niveau avant de pouvoir être utilisée et la version précédente de la clé est invalidée. Au fur et à mesure que l'appareil est mis à niveau, les clés "cliquent" vers l'avant avec l'appareil, mais toute réversion de l'appareil vers une version précédente rend les clés inutilisables.

Dans Android 8.0, Keymaster 3 est passé de l'ancienne couche d'abstraction matérielle (HAL) à structure C à l'interface HAL C++ générée à partir d'une définition dans le nouveau langage de définition d'interface matérielle (HIDL). Dans le cadre du changement, de nombreux types d'arguments ont changé, bien que les types et les méthodes aient une correspondance biunivoque avec les anciens types et les méthodes de structure HAL. Voir la page Fonctions pour plus de détails.

En plus de cette révision d'interface, Android 8.0 a étendu la fonction d'attestation de Keymaster 2 pour prendre en charge l' attestation d'identité . L'attestation d'ID fournit un mécanisme limité et facultatif pour attester fortement des identifiants matériels, tels que le numéro de série de l'appareil, le nom du produit et l'ID du téléphone (IMEI / MEID). Pour implémenter cet ajout, Android 8.0 a modifié le schéma d'attestation ASN.1 pour ajouter l'attestation d'ID. Les implémentations Keymaster doivent trouver un moyen sécurisé de récupérer les éléments de données pertinents, ainsi que de définir un mécanisme pour désactiver la fonctionnalité de manière sécurisée et permanente.

Dans Android 9, les mises à jour comprenaient :

  • Mise à jour vers Keymaster 4
  • Prise en charge des éléments sécurisés intégrés
  • Prise en charge de l'importation de clé sécurisée
  • Prise en charge du cryptage 3DES
  • Modifications de la liaison de version afin que boot.img et system.img aient des versions définies séparément pour permettre des mises à jour indépendantes

Glossaire

Voici un aperçu rapide des composants Keystore et de leurs relations.

AndroidKeystore est l'API et le composant Android Framework utilisés par les applications pour accéder à la fonctionnalité Keystore. Il est implémenté en tant qu'extension des API standard de l'architecture de chiffrement Java et se compose de code Java qui s'exécute dans l'espace de processus propre à l'application. AndroidKeystore répond aux demandes d'application pour le comportement Keystore en les transmettant au démon keystore.

Le démon keystore est un démon du système Android qui permet d'accéder à toutes les fonctionnalités du Keystore via une API Binder . Il est responsable du stockage des "blobs de clés", qui contiennent le matériel de clé secrète réel, crypté afin que Keystore puisse les stocker mais pas les utiliser ou les révéler.

keymasterd est un serveur HIDL qui donne accès au Keymaster TA. (Ce nom n'est pas normalisé et est à des fins conceptuelles.)

Keymaster TA (application de confiance) est le logiciel s'exécutant dans un contexte sécurisé, le plus souvent en TrustZone sur un SoC ARM, qui assure l'ensemble des opérations sécurisées du Keystore, a accès à la matière première des clés, valide toutes les conditions de contrôle d'accès sur les clés , etc.

LockSettingsService est le composant du système Android responsable de l'authentification de l'utilisateur, à la fois par mot de passe et par empreinte digitale. Cela ne fait pas partie de Keystore, mais est pertinent car de nombreuses opérations de clé Keystore nécessitent une authentification de l'utilisateur. LockSettingsService interagit avec le Gatekeeper TA et le Fingerprint TA pour obtenir des jetons d'authentification, qu'il fournit au démon de magasin de clés, et qui sont finalement consommés par l'application Keymaster TA.

Gatekeeper TA (application de confiance) est un autre composant exécuté dans le contexte sécurisé, qui est chargé d'authentifier les mots de passe des utilisateurs et de générer des jetons d'authentification utilisés pour prouver au Keymaster TA qu'une authentification a été effectuée pour un utilisateur particulier à un moment donné.

Fingerprint TA (application de confiance) est un autre composant exécuté dans le contexte sécurisé qui est chargé d'authentifier les empreintes digitales des utilisateurs et de générer des jetons d'authentification utilisés pour prouver au Keymaster TA qu'une authentification a été effectuée pour un utilisateur particulier à un moment donné.

Architecture

L'API Android Keystore et le Keymaster HAL sous-jacent fournissent un ensemble basique mais adéquat de primitives cryptographiques pour permettre la mise en œuvre de protocoles utilisant des clés à accès contrôlé et sauvegardées sur matériel.

Keymaster HAL est une bibliothèque chargeable dynamiquement fournie par l'OEM et utilisée par le service Keystore pour fournir des services cryptographiques basés sur le matériel. Pour assurer la sécurité, les implémentations HAL n'effectuent aucune opération sensible dans l'espace utilisateur, ni même dans l'espace noyau. Les opérations sensibles sont déléguées à un processeur sécurisé accessible via une interface du noyau. L'architecture résultante ressemble à ceci :

Accès à Keymaster

Figure 1. Accès à Keymaster

Dans un appareil Android, le "client" de Keymaster HAL se compose de plusieurs couches (par exemple, application, framework, démon Keystore), mais cela peut être ignoré pour les besoins de ce document. Cela signifie que l'API Keymaster HAL décrite est de bas niveau, utilisée par les composants internes à la plate-forme et non exposée aux développeurs d'applications. L'API de niveau supérieur est décrite sur le site Android Developer .

L'objectif de Keymaster HAL n'est pas d'implémenter les algorithmes sensibles à la sécurité, mais uniquement de rassembler et de désorganiser les requêtes vers le monde sécurisé. Le format de fil est défini par l'implémentation.

Compatibilité avec les versions précédentes

Le Keymaster 1 HAL est totalement incompatible avec les HAL précédemment publiés, par exemple Keymaster 0.2 et 0.3. Pour faciliter l'interopérabilité sur les appareils exécutant Android 5.0 et versions antérieures lancées avec les anciens Keymaster HAL, Keystore fournit un adaptateur qui implémente le Keymaster 1 HAL avec des appels à la bibliothèque matérielle existante. Le résultat ne peut pas fournir la gamme complète de fonctionnalités dans le Keymaster 1 HAL. En particulier, il ne prend en charge que les algorithmes RSA et ECDSA, et toute l'application de l'autorisation de clé est effectuée par l'adaptateur, dans le monde non sécurisé.

Keymaster 2 a encore simplifié l'interface HAL en supprimant les méthodes get_supported_* et en permettant à la méthode finish() d'accepter les entrées. Cela réduit le nombre d'allers-retours vers le TEE dans les cas où l'entrée est disponible en une seule fois, et simplifie la mise en œuvre du décryptage AEAD.

Dans Android 8.0, Keymaster 3 est passé de l'ancien style C-structure HAL à l'interface C++ HAL générée à partir d'une définition dans le nouveau langage de définition d'interface matérielle (HIDL). Une implémentation HAL de style nouveau est créée en sous-classant la classe IKeymasterDevice générée et en implémentant les méthodes virtuelles pures. Dans le cadre du changement, de nombreux types d'arguments ont changé, bien que les types et les méthodes aient une correspondance un à un avec les anciens types et les méthodes de structure HAL.

Présentation de l'HIDL

Le langage de définition d'interface matérielle (HIDL) fournit un mécanisme indépendant du langage d'implémentation pour spécifier les interfaces matérielles. L'outillage HIDL prend actuellement en charge la génération d'interfaces C++ et Java. On s'attend à ce que la plupart des implémenteurs de l'environnement d'exécution de confiance (TEE) trouvent les outils C++ plus pratiques, donc ce document ne traite que de la représentation C++.

Les interfaces HIDL consistent en un ensemble de méthodes, exprimées comme suit :

  methodName(INPUT ARGUMENTS) generates (RESULT ARGUMENTS);

Il existe différents types prédéfinis et les HAL peuvent définir de nouveaux types d'énumération et de structure. Pour plus de détails sur HIDL, consultez la section Référence .

Un exemple de méthode de Keymaster 3 IKeymasterDevice.hal est :

generateKey(vec<KeyParameter> keyParams)
        generates(ErrorCode error, vec<uint8_t> keyBlob,
                  KeyCharacteristics keyCharacteristics);

C'est l'équivalent de ce qui suit du keymaster2 HAL :

keymaster_error_t (*generate_key)(
        const struct keymaster2_device* dev,
        const keymaster_key_param_set_t* params,
        keymaster_key_blob_t* key_blob,
        keymaster_key_characteristics_t* characteristics);

Dans la version HIDL, l'argument dev est supprimé, car il est implicite. L'argument params n'est plus une structure contenant un pointeur référençant un tableau d'objets key_parameter_t , mais un vec (vecteur) contenant des objets KeyParameter . Les valeurs de retour sont répertoriées dans la clause " generates ", y compris un vecteur de valeurs uint8_t pour le blob de clé.

La méthode virtuelle C++ générée par le compilateur HIDL est :

Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
                         generateKey_cb _hidl_cb) override;

generateKey_cb est un pointeur de fonction défini comme :

std::function<void(ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
                   const KeyCharacteristics& keyCharacteristics)>

Autrement dit, generateKey_cb est une fonction qui prend les valeurs de retour répertoriées dans la clause generate. La classe d'implémentation HAL remplace cette méthode generateKey et appelle le pointeur de fonction generateKey_cb pour renvoyer le résultat de l'opération à l'appelant. Notez que l'appel du pointeur de fonction est synchrone . L'appelant appelle generateKey et generateKey appelle le pointeur de fonction fourni, qui s'exécute jusqu'à la fin, rendant le contrôle à l'implémentation de generateKey , qui revient ensuite à l'appelant.

Pour un exemple détaillé, voir l'implémentation par défaut dans hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp . L'implémentation par défaut offre une rétrocompatibilité pour les périphériques avec l'ancien style keymaster0, keymaster1 ou keymaster2 HALS.

Contrôle d'accès

La règle la plus élémentaire du contrôle d'accès Keystore est que chaque application a son propre espace de noms. Mais pour chaque règle il y a une exception. Keystore a des cartes codées en dur qui permettent à certains composants du système d'accéder à certains autres espaces de noms. Il s'agit d'un instrument très brutal dans la mesure où il donne à un composant un contrôle total sur un autre espace de noms. Et puis il y a la question des composants du fournisseur en tant que clients de Keystore. Nous n'avons actuellement aucun moyen d'établir un espace de noms pour les composants du fournisseur, par exemple, le demandeur WPA.

Afin de prendre en charge les composants des fournisseurs et de généraliser le contrôle d'accès sans exceptions codées en dur, Keystore 2.0 introduit des domaines et des espaces de noms SELinux.

Domaines de magasin de clés

Avec les domaines Keystore, nous pouvons dissocier les espaces de noms des UID. Les clients accédant à une clé dans Keystore doivent spécifier le domaine, l'espace de noms et l'alias auxquels ils souhaitent accéder. Sur la base de ce tuple et de l'identité de l'appelant, nous pouvons déterminer à quelle clé l'appelant souhaite accéder et s'il dispose des autorisations appropriées.

Nous introduisons cinq paramètres de domaine qui régissent l'accès aux clés. Ils contrôlent la sémantique du paramètre d'espace de noms du descripteur de clé et la façon dont le contrôle d'accès est effectué.

  • DOMAIN_APP : le domaine de l'application couvre le comportement hérité. Le Java Keystore SPI utilise ce domaine par défaut. Lorsque ce domaine est utilisé, l'argument d'espace de noms est ignoré et l'UID de l'appelant est utilisé à la place. L'accès à ce domaine est contrôlé par l'étiquette Keystore de la classe keystore_key dans la politique SELinux.
  • DOMAIN_SELINUX : Ce domaine indique que l'espace de noms a une étiquette dans la politique SELinux. Le paramètre d'espace de noms est recherché et traduit dans un contexte cible, et une vérification des autorisations est effectuée pour le contexte SELinux appelant pour la classe keystore_key . Lorsque l'autorisation a été établie pour l'opération donnée, le tuple complet est utilisé pour la recherche de clé.
  • DOMAIN_GRANT : le domaine d'octroi indique que le paramètre d'espace de noms est un identifiant d'octroi. Le paramètre alias est ignoré. Les vérifications SELinux sont effectuées lors de la création de la subvention. Un contrôle d'accès supplémentaire vérifie uniquement si l'UID de l'appelant correspond à l'UID des bénéficiaires de l'octroi demandé.
  • DOMAIN_KEY_ID : ce domaine indique que le paramètre d'espace de noms est un identifiant de clé unique. La clé elle-même peut avoir été créée avec DOMAIN_APP ou DOMAIN_SELINUX . La vérification des autorisations est effectuée après que le domain et l'espace de namespace ont été chargés à partir de la base de données de clés de la même manière que si le blob était chargé par le domaine, l'espace de noms et le tuple d'alias. La raison d'être du domaine d'identifiant de clé est la continuité. Lors de l'accès à une clé par alias, les appels ultérieurs peuvent fonctionner sur des clés différentes, car une nouvelle clé peut avoir été générée ou importée et liée à cet alias. L'identifiant de clé, cependant, ne change jamais. Ainsi, lors de l'utilisation d'une clé par ID de clé après qu'elle a été chargée à partir de la base de données Keystore en utilisant l'alias une fois, on peut être certain qu'il s'agit de la même clé tant que l'ID de clé existe toujours. Cette fonctionnalité n'est pas exposée aux développeurs d'applications. Au lieu de cela, il est utilisé dans le SPI Android Keystore pour fournir une expérience plus cohérente, même lorsqu'il est utilisé simultanément de manière non sécurisée.
  • DOMAIN_BLOB : Le domaine blob indique que l'appelant gère lui-même le blob. Ceci est utilisé pour les clients qui doivent accéder au Keystore avant le montage de la partition de données. Le blob de clé est inclus dans le champ blob du descripteur de clé.

En utilisant le domaine SELinux, nous pouvons donner aux composants du fournisseur l'accès à des espaces de noms Keystore très spécifiques qui peuvent être partagés par des composants système tels que la boîte de dialogue des paramètres.

Politique SELinux pour keystore_key

Les étiquettes d'espace de noms sont configurées à l'aide du fichier keystore2_key_context .
Chaque ligne de ces fichiers associe un identifiant d'espace de noms numérique à une étiquette SELinux. Par exemple,

# wifi_key is a keystore2_key namespace intended to be used by wpa supplicant and
# Settings to share keystore keys.
102            u:object_r:wifi_key:s0

Après avoir configuré un nouvel espace de noms de clé de cette manière, nous pouvons lui donner accès en ajoutant une stratégie appropriée. Par exemple, pour permettre à wpa_supplicant d'obtenir et d'utiliser des clés dans le nouvel espace de noms, nous ajouterions la ligne suivante à hal_wifi_supplicant.te :

allow hal_wifi_supplicant wifi_key:keystore2_key { get, use };

Après avoir configuré le nouvel espace de noms, AndroidKeyStore peut être utilisé presque comme d'habitude. La seule différence est que l'ID d'espace de noms doit être spécifié. Pour charger et importer des clés depuis et vers Keystore, l'identifiant de l'espace de noms est spécifié à l'aide de AndroidKeyStoreLoadStoreParameter . Par exemple,

import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
import java.security.KeyStore;

KeyStore keystore = KeyStore.getInstance("AndroidKeyStore");
keystore.load(new AndroidKeyStoreLoadStoreParameter(102));

Pour générer une clé dans un espace de noms donné, l'identifiant de l'espace de noms doit être donné à l'aide KeyGenParameterSpec.Builder#setNamespace():

import android.security.keystore.KeyGenParameterSpec;
KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder();
specBuilder.setNamespace(102);

Les fichiers de contexte suivants peuvent être utilisés pour configurer les espaces de noms Keystore 2.0 SELinux. Chaque partition a une plage réservée différente de 10 000 identifiants d'espace de noms pour éviter les collisions.

Cloison Intervalle Fichiers de configuration
Système 0 ... 9 999
/system/etc/selinux/keystore2_key_contexts, /plat_keystore2_key_contexts
Système étendu 10 000 ... 19 999
/system_ext/etc/selinux/system_ext_keystore2_key_contexts, /system_ext_keystore2_key_contexts
Produit 20 000 ... 29 999
/product/etc/selinux/product_keystore2_key_contexts, /product_keystore2_key_contexts
Vendeur 30 000 ... 39 999
/vendor/etc/selinux/vendor_keystore2_key_contexts, /vendor_keystore2_key_contexts

Le client demande la clé en demandant le domaine SELinux et l'espace de noms virtuel souhaité, dans ce cas "wifi_key" , par son identifiant numérique.

Au-dessus de cela, les espaces de noms suivants ont été définis. S'ils remplacent des règles spéciales, le tableau suivant indique l'UID auquel ils correspondaient.

ID d'espace de noms Libellé SEPolicy UID La description
0 su_key N / A Clé super utilisateur. Utilisé uniquement pour les tests sur les versions userdebug et eng. Non pertinent pour les builds utilisateur.
1 shell_key N / A Espace de noms disponible pour le shell. Principalement utilisé pour les tests, mais peut également être utilisé sur les versions utilisateur à partir de la ligne de commande.
100 vold_key N / A Destiné à être utilisé par vold.
101 odsing_key N / A Utilisé par le démon de signature sur l'appareil.
102 clé_wifi AID_WIFI(1010) Utilisé par le système Wifi d'Android, y compris wpa_supplicant.
120 resume_on_reboot_key AID_SYSTEM(1000) Utilisé par le serveur système d'Android pour prendre en charge la reprise au redémarrage.

Vecteurs d'accès

La classe SELinux keystore_key a un peu vieilli et certaines des autorisations, telles que verify ou sign , ont perdu leur sens. Voici le nouvel ensemble d'autorisations, keystore2_key , que Keystore 2.0 appliquera.

Autorisation Sens
delete Vérifié lors de la suppression des clés de Keystore.
get_info Coché lorsque les métadonnées d'une clé sont demandées.
grant L'appelant a besoin de cette autorisation pour créer un octroi à la clé dans le contexte cible.
manage_blob L'appelant peut utiliser DOMAIN_BLOB sur l'espace de noms SELinux donné, gérant ainsi les blobs par lui-même. Ceci est particulièrement utile pour vold.
rebind Cette autorisation contrôle si un alias peut être lié à une nouvelle clé. Ceci est requis pour l'insertion et implique que la clé précédemment liée sera supprimée. Il s'agit essentiellement d'une autorisation d'insertion, mais elle capture mieux la sémantique du keystore.
req_forced_op Les clients disposant de cette autorisation peuvent créer des opérations non élagables, et la création d'opérations n'échoue jamais à moins que tous les emplacements d'opération ne soient occupés par des opérations non élagables.
update Requis pour mettre à jour le sous-composant d'une clé.
use Coché lors de la création d'une opération Keymint qui utilise le matériel de clé, par exemple, pour la signature, en/déchiffrement.
use_dev_id Requis lors de la génération d'informations d'identification de l'appareil, telles que l'attestation d'identification de l'appareil.

De plus, nous avons divisé un ensemble d'autorisations de magasin de clés non spécifiques à la clé dans le keystore2 de la classe de sécurité SELinux :

Autorisation Sens
add_auth Requis par le fournisseur d'authentification tel que Gatekeeper ou BiometricsManager pour l'ajout de jetons d'authentification.
clear_ns Anciennement clear_uid, cette autorisation permet à un non propriétaire d'un espace de noms de supprimer toutes les clés de cet espace de noms.
list Requis par le système pour énumérer les clés selon diverses propriétés, telles que la propriété ou la délimitation de l'authentification. Cette autorisation n'est pas requise par les appelants qui énumèrent leurs propres espaces de noms. Ceci est couvert par la permission get_info .
lock Cette autorisation permet de verrouiller Keystore, c'est-à-dire d'expulser la clé principale, de sorte que les clés liées à l'authentification deviennent inutilisables et impossibles à créer.
reset Cette autorisation permet de réinitialiser Keystore aux paramètres d'usine par défaut, en supprimant toutes les clés qui ne sont pas vitales pour le fonctionnement du système d'exploitation Android.
unlock Cette autorisation est requise pour tenter de déverrouiller la clé principale pour les clés liées à l'authentification.