Keystore basé sur 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 du matériel au système d'exploitation Android, aux services de plate-forme et même aux applications tierces. Les développeurs recherchant des extensions spécifiques à Android doivent se rendre sur android.security.keystore .

Avant Android 6.0, Android disposait déjà d'une API de services de chiffrement simple, basée sur le matériel, fournie par les versions 0.2 et 0.3 de la couche d'abstraction matérielle Keymaster (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 de nombreux objectifs de sécurité ne peuvent pas être facilement atteints avec une seule API de signature. Keystore dans Android 6.0 a étendu l'API Keystore pour offrir une gamme plus large de fonctionnalités.

Dans Android 6.0, Keystore a ajouté des primitives cryptographiques symétriques , AES et HMAC, ainsi qu'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 et fonctions d’autorisation.

En plus d'élargir la gamme de primitives cryptographiques, Keystore dans Android 6.0 a ajouté les éléments suivants :

  • Un système de contrôle d'utilisation pour permettre de limiter l'utilisation des clés, afin d'atténuer le risque de compromission de sécurité 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 spécifiés et une plage de temps définie

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 l'existence de la clé dans un matériel sécurisé et sa configuration vérifiables à distance.

La liaison de version lie les clés au système d’exploitation et à la version du 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 de la mise à niveau de l'appareil, les touches avancent avec l'appareil, mais tout retour de l'appareil à 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 C++ HAL générée à partir d'une définition dans le nouveau langage de définition d'interface matérielle (HIDL). Dans le cadre de ce 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 de l'interface, Android 8.0 a étendu la fonctionnalité d'attestation de Keymaster 2 pour prendre en charge l'attestation d'identité . L'attestation d'identité fournit un mécanisme limité et facultatif pour attester fortement les identifiants matériels, tels que le numéro de série de l'appareil, le nom du produit et l'identifiant 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 une attestation d'identification. Les implémentations de Keymaster doivent trouver un moyen sécurisé de récupérer les éléments de données pertinents, ainsi que définir un mécanisme permettant de désactiver la fonctionnalité de manière sécurisée et permanente.

Sous Android 9, les mises à jour incluaient :

  • Mise à jour vers Keymaster 4
  • Prise en charge des éléments sécurisés intégrés
  • Prise en charge de l'importation sécurisée de clés
  • Prise en charge du cryptage 3DES
  • Modifications apportées à 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 aux fonctionnalités Keystore. Il est implémenté en tant qu'extension des API standard de l'architecture de cryptographie Java et se compose de code Java qui s'exécute dans le propre espace de processus de l'application. AndroidKeystore répond aux demandes d'application concernant le comportement du Keystore en les transmettant au démon keystore.

Le démon keystore est un démon système Android qui permet d'accéder à toutes les fonctionnalités 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 permet d'accéder au Keymaster TA. (Ce nom n'est pas standardisé et est destiné à des fins conceptuelles.)

Keymaster TA (application de confiance) est le logiciel exécuté dans un contexte sécurisé, le plus souvent dans TrustZone sur un SoC ARM, qui fournit toutes les opérations Keystore sécurisées, a accès au matériel de clé brut, 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 des utilisateurs, à 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 Gatekeeper TA et Fingerprint TA pour obtenir des jetons d'authentification, qu'il fournit au démon keystore et qui sont finalement consommés par l'application Keymaster TA.

Gatekeeper TA (application de confiance) est un autre composant fonctionnant dans un contexte sécurisé, 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 fonctionnant dans le contexte sécurisé qui est responsable de l'authentification des empreintes digitales des utilisateurs et de la génération de 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 matérielles à contrôle d'accès.

Keymaster HAL est une bibliothèque OEM chargeable dynamiquement, utilisée par le service Keystore pour fournir des services cryptographiques basés sur le matériel. Pour assurer la sécurité des choses, 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

Au sein d'un appareil Android, le « client » du 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 de la plate-forme et non exposée aux développeurs d'applications. L'API de niveau supérieur est décrite sur le site des développeurs Android .

Le but de Keymaster HAL n'est pas d'implémenter des 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 HAL Keymaster 1 est totalement incompatible avec les HAL publiés précédemment, 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és avec les anciens HAL Keymaster, Keystore fournit un adaptateur qui implémente le HAL Keymaster 1 avec des appels à la bibliothèque matérielle existante. Le résultat ne peut pas fournir la gamme complète de fonctionnalités du Keymaster 1 HAL. En particulier, il ne prend en charge que les algorithmes RSA et ECDSA, et toutes les opérations d'autorisation de clé sont effectuées par l'adaptateur, dans un 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 de structure C 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 d'un nouveau style 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 de ce 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.

HIDL en aperçu

Le langage HIDL (Hardware Interface Definition Language) fournit un mécanisme d'implémentation indépendant du langage pour spécifier les interfaces matérielles. Les outils HIDL prennent actuellement en charge la génération d'interfaces C++ et Java. On s'attend à ce que la plupart des implémenteurs de Trusted Execution Environment (TEE) trouvent les outils C++ plus pratiques, c'est pourquoi ce document traite uniquement de la représentation C++.

Les interfaces HIDL se composent d'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 énumérés et structurés. 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 HAL keymaster2 :

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, renvoyant le contrôle à l'implémentation generateKey , qui revient ensuite à l'appelant.

Pour un exemple détaillé, consultez l'implémentation par défaut dans hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp . L’implémentation par défaut offre une compatibilité descendante pour les appareils dotés de l’ancien système HALS keymaster0, keymaster1 ou keymaster2.

Contrôle d'accès

La règle la plus fondamentale du contrôle d’accès Keystore est que chaque application possède son propre espace de noms. Mais à chaque règle il y a une exception. Keystore contient 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 des fournisseurs 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 s'adapter aux 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 du 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 la manière dont les clés sont accessibles. Ils contrôlent la sémantique du paramètre d'espace de noms du descripteur de clé et la manière dont le contrôle d'accès est effectué.

  • DOMAIN_APP : le domaine de l'application couvre le comportement hérité. Le SPI Java Keystore 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 de subvention indique que le paramètre d'espace de noms est un identifiant de subvention. 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 du bénéficiaire de la subvention demandée.
  • 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 une fois que le domain et l' namespace ont été chargés à partir de la base de données de clés de la même manière que si l'objet blob était chargé par le domaine, l'espace de noms et le tuple d'alias. La justification du domaine d'identification clé est la continuité. Lors de l'accès à une clé par alias, les appels ultérieurs peuvent opérer 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. Cependant, l'identifiant de la clé ne change jamais. Ainsi, lorsque vous utilisez une clé par identifiant de clé après qu'elle ait été chargée une fois à partir de la base de données Keystore en utilisant l'alias, vous pouvez être certain qu'il s'agit de la même clé tant que l'identifiant 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 offrir une expérience plus cohérente, même lorsqu'il est utilisé simultanément de manière dangereuse.
  • 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 que la partition de données ne soit montée. 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 les composants du 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 mappe 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 ainsi configuré un nouvel espace de noms de clé, nous pouvons y donner accès en ajoutant une politique 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 de l'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 de 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 dispose d'une plage réservée différente de 10 000 identifiants d'espace de noms pour éviter les collisions.

Cloison Gamme 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
Fournisseur 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. Si elles remplacent des règles spéciales, le tableau suivant indique l'UID auquel elles correspondaient.

ID d'espace de noms Étiquette de stratégie SE UID Description
0 su_key N / A Clé super utilisateur. Utilisé uniquement pour les tests sur les versions userdebug et eng. Non pertinent sur les builds utilisateur.
1 clé_shell N / A Espace de noms disponible pour le shell. Principalement utilisé pour les tests, mais peut également être utilisé sur les builds utilisateur à partir de la ligne de commande.
100 clé_vol N / A Destiné à être utilisé par vold.
101 clé_odsing 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 CV_on_reboot_key AID_SYSTEM(1000) Utilisé par le serveur système d'Android pour prendre en charge la reprise au redémarrage.

Accéder aux vecteurs

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

Autorisation Signification
delete Coché 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 une autorisation pour la clé dans le contexte cible.
manage_blob L'appelant peut utiliser DOMAIN_BLOB sur l'espace de noms SELinux donné, gérant ainsi lui-même les blobs. Ceci est particulièrement utile pour vold.
rebind Cette autorisation contrôle si un alias peut être renvoyé vers 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 magasin de clés.
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 Nécessaire 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, le chiffrement/le déchiffrement.
use_dev_id Obligatoire 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 à une clé dans la classe de sécurité SELinux keystore2 :

Autorisation Signification
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 par diverses propriétés, telles que la propriété ou le caractère limité de l'authentification. Cette autorisation n'est pas requise par les appelants énumérant leurs propres espaces de noms. Ceci est couvert par l'autorisation 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 au 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.