La disponibilité d'un environnement d'exécution sécurisé dans un système sur une puce (SoC) permet aux appareils Android de fournir des services de sécurité performants et intégrés au matériel à l'OS Android, aux services de plate-forme et même aux applications tierces. Les développeurs à la recherche des extensions spécifiques à Android doivent accéder à android.security.keystore.
Avant Android 6.0, Android disposait déjà d'une API de services de cryptographie simple, basée sur le matériel, fournie par les versions 0.2 et 0.3 de la couche d'abstraction matérielle (HAL) Keymaster. Le keystore fournissait des opérations de signature et de validation numériques, ainsi que la génération et l'importation de paires de clés de signature asymétriques. Cette fonctionnalité est déjà implémentée 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 fournir 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 des accès pour les clés basées sur du matériel. Les contrôles d'accès sont spécifiés lors de la génération de la clé et appliqués pendant toute sa durée de vie. Les clés peuvent être limitées à une utilisation uniquement après l'authentification de l'utilisateur, et uniquement à des fins spécifiées ou avec des paramètres cryptographiques spécifiés. Pour en savoir plus, consultez la page Tags d'autorisation.
En plus d'étendre la gamme de primitives cryptographiques, Keystore dans Android 6.0 a ajouté les éléments suivants:
- Un schéma de contrôle de l'utilisation permettant de limiter l'utilisation des clés afin de réduire le risque de compromission de la sécurité en raison d'une utilisation abusive des clés
- Un schéma de contrôle des accès permettant de limiter les clés à des utilisateurs, des clients et une période 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 contenant 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 du matériel sécurisé et sa configuration vérifiables à distance.
La liaison de version lie les clés au système d'exploitation et au niveau du correctif. Cela garantit qu'un pirate informatique qui découvre une faille dans une ancienne version du système ou du logiciel TEE ne peut pas faire revenir un appareil à la version vulnérable et utiliser les clés créées avec la version 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écents, la clé est mise à niveau avant de pouvoir être utilisée, et la version précédente de la clé est invalidée. Lorsque l'appareil est mis à niveau, les clés "s'enclenchent" 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) de 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 de ce changement, de nombreux types d'arguments ont changé, bien que les types et les méthodes correspondent de manière individuelle aux anciens types et aux méthodes de struct HAL.
En plus de cette révision d'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 permettant d'attester de manière forte des identifiants matériels, tels que le numéro de série de l'appareil, le nom du produit et l'ID de téléphone (IMEI / MEID). Pour implémenter cette addition, Android 8.0 a modifié le schéma d'attestation ASN.1 afin d'ajouter l'attestation d'identité. Les implémentations Keymaster doivent trouver un moyen sécurisé de récupérer les éléments de données pertinents, ainsi qu'un mécanisme permettant de désactiver de manière sécurisée et permanente la fonctionnalité.
Dans Android 9, les mises à jour incluaient les éléments suivants:
- Mise à jour vers Keymaster 4
- Compatibilité avec les éléments sécurisés intégrés
- Prise en charge de l'importation de clés sécurisée
- Compatibilité avec le chiffrement 3DES
- Modifications apportées à la liaison de version afin que les versions de boot.img et system.img soient définies séparément pour permettre des mises à jour indépendantes
Glossaire
Voici un bref aperçu 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 Java Cryptography Architecture standards et se compose de code Java exécuté dans l'espace de processus de l'application. AndroidKeystore
répond aux requêtes d'application concernant le comportement du keystore en les transmettant au daemon du keystore.
Le daemon keystore est un daemon système Android qui permet d'accéder à toutes les fonctionnalités de Keystore avec une API Binder. Il est chargé de stocker des "blobs de clé", qui contiennent le matériel de clé secrète, chiffré afin que Keystore puisse les stocker, mais pas les utiliser ni les révéler.
keymasterd est un serveur HIDL qui permet d'accéder au TA Keymaster. (Ce nom n'est pas standardisé et est à des fins conceptuelles.)
Keymaster TA (application fiable) est le logiciel exécuté dans un contexte sécurisé, le plus souvent dans TrustZone sur un SoC ARM. Il 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 des 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. Il 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 TA Gatekeeper et le TA Fingerprint pour obtenir des jetons d'authentification, qu'il fournit au daemon du keystore, qui sont finalement consommés par l'application TA Keymaster.
La TA Gatekeeper (application fiable) 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 à la TA Keymaster qu'une authentification a été effectuée pour un utilisateur particulier à un moment donné.
La TA empreinte digitale (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 à la TA Keymaster qu'une authentification a été effectuée pour un utilisateur particulier à un moment donné.
Architecture
L'API Android Keystore et le HAL Keymaster sous-jacent fournissent un ensemble de primitives cryptographiques de base, mais adapté, pour permettre l'implémentation de protocoles à l'aide de clés matérielles contrôlées par accès.
Le HAL Keymaster est une bibliothèque fournie par l'OEM et pouvant être chargée dynamiquement utilisée par le service Keystore pour fournir des services cryptographiques avec support 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 du noyau. Les opérations sensibles sont déléguées à un processeur sécurisé accessible via une interface de kernel. L'architecture obtenue se présente comme suit:
![Accès à Keymaster](https://source.android.google.cn/static/docs/security/images/access-to-keymaster.png?authuser=2&hl=fr)
Figure 1 : Accès à Keymaster
Sur un appareil Android, le "client" du HAL Keymaster se compose de plusieurs couches (application, framework, daemon Keystore, par exemple), mais cela peut être ignoré aux fins de ce document. Cela signifie que l'API HAL Keymaster 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 pour les développeurs Android.
L'objectif du HAL Keymaster n'est pas d'implémenter les algorithmes sensibles à la sécurité, mais uniquement de marshaller et de désmarshaller les requêtes vers l'environnement sécurisé. Le format de transmission est défini par l'implémentation.
Compatibilité avec les versions précédentes
Le HAL Keymaster 1 est complètement 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 équipés d'Android 5.0 ou version antérieure 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 l'ensemble des fonctionnalités de la couche HAL Keymaster 1. En particulier, il n'est compatible qu'avec les algorithmes RSA et ECDSA, et l'ensemble de l'application de l'autorisation des clés est effectué par l'adaptateur, dans l'environnement non sécurisé.
Keymaster 2 a encore simplifié l'interface HAL en supprimant les méthodes get_supported_*
et en autorisant la méthode finish()
à 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 l'implémentation du déchiffrement AEAD.
Dans Android 8.0, Keymaster 3 est passé de l'ancienne structure C HAL à 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, Hardware Interface Definition Language). Une implémentation HAL de 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 correspondent de manière individuelle aux anciens types et aux méthodes de struct HAL.
Présentation de HIDL
Le langage de définition d'interface matérielle (HIDL) fournit un mécanisme d'implémentation indépendant du langage pour spécifier les interfaces matérielles. L'outil HIDL permet actuellement de générer des interfaces C++ et Java. Nous nous attendons à ce que la plupart des implémentateurs d'environnements d'exécution sécurisés (TEE) trouvent les outils C++ plus pratiques. Par conséquent, cette page ne traite que 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 de structure. Pour en savoir plus sur HIDL, consultez la section de référence.
Voici un exemple de méthode de la IKeymasterDevice.hal
Keymaster 3:
generateKey(vec<KeyParameter> keyParams) generates(ErrorCode error, vec<uint8_t> keyBlob, KeyCharacteristics keyCharacteristics);
Il s'agit de l'équivalent de ce qui suit dans le 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 struct contenant un pointeur référençant un tableau d'objets key_parameter_t
, mais un vec
(vecteur) contenant des objets KeyParameter
. Les valeurs renvoyées sont listé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 la suivante:
Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams, generateKey_cb _hidl_cb) override;
Où generateKey_cb
est un pointeur de fonction défini comme suit:
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 renvoyées listé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, redonnant le contrôle à l'implémentation generateKey
, qui revient ensuite à l'appelant.
Pour obtenir 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 assure la rétrocompatibilité des appareils avec les HAL keymaster0, keymaster1 ou keymaster2 obsolètes.
Contrôle des accès
La règle la plus élémentaire du contrôle des accès au Keystore est que chaque application dispose de son propre espace de noms. Mais il existe une exception à chaque règle. Keystore contient des mappages codés en dur qui permettent à certains composants système d'accéder à d'autres espaces de noms. Il s'agit d'un outil très directif, car il donne à un composant un contrôle total sur un autre espace de noms. Il y a aussi la question des composants du fournisseur en tant que clients du Keystore. Pour le moment, nous n'avons aucun moyen d'établir un espace de noms pour les composants du fournisseur, par exemple le demandeur WPA.
Pour prendre en charge les composants du fournisseur et généraliser le contrôle des accès sans exceptions codées en dur, Keystore 2.0 introduit des domaines et des espaces de noms SELinux.
Domaines de keystore
Avec les domaines Keystore, nous pouvons dissocier les espaces de noms des UID. Les clients qui accèdent à 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 manière dont le contrôle des accès est effectué.
DOMAIN_APP
: le domaine de l'application couvre l'ancien comportement. 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 le libellé Keystore de la classekeystore_key
dans la stratégie SELinux.DOMAIN_SELINUX
: ce domaine indique que l'espace de noms possède un libellé dans la règle SELinux. Le paramètre d'espace de noms est recherché et traduit en contexte cible, et une vérification des autorisations est effectuée pour le contexte SELinux appelant pour la classekeystore_key
. Une fois l'autorisation établie pour l'opération donnée, le tuple complet est utilisé pour la recherche de clé.DOMAIN_GRANT
: le domaine d'attribution indique que le paramètre d'espace de noms est un identifiant d'attribution. Le paramètre d'alias est ignoré. Des vérifications SELinux sont effectuées lors de la création de l'autorisation. Un autre contrôle d'accès vérifie uniquement si l'UID de l'appelant correspond à l'UID des bénéficiaires de l'autorisation demandée.DOMAIN_KEY_ID
: ce domaine indique que le paramètre d'espace de noms est un ID de clé unique. La clé elle-même peut avoir été créée avecDOMAIN_APP
ouDOMAIN_SELINUX
. La vérification des autorisations est effectuée une fois quedomain
etnamespace
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 tuple de domaine, d'espace de noms et d'alias. La raison d'être du domaine d'ID de clé est la continuité. Lorsque vous accédez à une clé par alias, les appels suivants peuvent fonctionner sur différentes clés, car une nouvelle clé peut avoir été générée ou importée et liée à cet alias. L'ID de clé, en revanche, ne change jamais. Par conséquent, lorsque vous utilisez une clé par identifiant de clé après l'avoir chargée à partir de la base de données Keystore à l'aide de l'alias une fois, 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. Il est plutôt 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 non sécurisée.DOMAIN_BLOB
: le domaine de blob indique que l'appelant gère le blob lui-même. Il 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 champblob
du descripteur de clé.
À l'aide du domaine SELinux, nous pouvons accorder aux composants du fournisseur un accès à des espaces de noms Keystore très spécifiques qui peuvent être partagés par les composants système tels que la boîte de dialogue des paramètres.
Règle SELinux pour keystore_key
Les libellés d'espace de noms sont configurés à l'aide du fichier keystore2_key_context
.
Chaque ligne de ces fichiers met en correspondance un ID d'espace de noms numérique avec un libellé 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és de cette manière, nous pouvons y accéder en ajoutant une stratégie appropriée. Par exemple, pour autoriser wpa_supplicant
à obtenir et à 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 };
Une fois le nouvel espace de noms configuré, 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 le keystore, l'ID 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'ID de l'espace de noms doit être fourni à 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 SELinux Keystore 2.0. Chaque partition dispose d'une plage réservée différente de 10 000 ID d'espace de noms pour éviter les collisions.
Partition | Plage | 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 ID numérique.
En outre, 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 auparavant.
ID de l'espace de noms | Libellé SEPolicy | UID | Description |
---|---|---|---|
0 | su_key | N/A | Clé super-utilisateur. Utilisé uniquement pour les tests sur les builds userdebug et eng. Non pertinent pour les builds utilisateur. |
1 | shell_key | N/A | Espace de noms disponible pour le shell. Utilisé principalement pour les tests, mais peut également être utilisé sur les builds 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 daemon de signature sur l'appareil. |
102 | wifi_key | AID_WIFI(1010) | Utilisé par le sous-système Wi-Fi d'Android, y compris wpa_supplicant. |
120 | resume_on_reboot_key | AID_SYSTEM(1000) | Utilisé par le serveur système d'Android pour permettre la reprise au redémarrage. |
Vecteurs d'accès
La classe SELinux keystore_key
a beaucoup vieilli, et certaines des autorisations, telles que verify
ou sign
, ont perdu leur signification. Voici le nouvel ensemble d'autorisations, keystore2_key
, que Keystore 2.0 applique.
Autorisation | Signification |
---|---|
delete
|
Coché lors de la suppression de clés du keystore. |
get_info
|
Vérifié 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é, ce qui lui permet de gérer les blobs lui-même. Cela est particulièrement utile pour les cas de violence. |
rebind
|
Cette autorisation détermine si un alias peut être réaffecté à une nouvelle touche. Cela est nécessaire pour l'insertion et implique que la clé précédemment liée est supprimée. Il s'agit 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 élaguables, et la création d'opérations ne échoue jamais, sauf si tous les emplacements d'opération sont occupés par des opérations non élaguables. |
update
|
Obligatoire 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 et le déchiffrement. |
use_dev_id
|
Obligatoire lors de la génération d'informations d'identification de l'appareil, telles qu'une attestation d'ID d'appareil. |
De plus, nous avons dissocié un ensemble d'autorisations de keystore non spécifiques aux clés dans la classe de sécurité SELinux keystore2
:
Autorisation | Signification |
---|---|
add_auth
|
Obligatoire pour le fournisseur d'authentification tel que Gatekeeper ou BiometricsManager pour ajouter des 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
|
Obligatoire pour le système afin d'énumérer les clés en fonction de diverses propriétés, telles que la propriété ou la limite d'authentification. Cette autorisation n'est pas requise par les appelants qui énumèrent leurs propres espaces de noms. Cette opération est couverte par l'autorisation get_info . |
lock
|
Cette autorisation permet de verrouiller Keystore, c'est-à-dire d'éjecter la clé principale, de sorte que les clés liées à l'authentification deviennent inutilisables et non créables. |
reset
|
Cette autorisation permet de réinitialiser Keystore aux paramètres d'usine, en supprimant toutes les clés qui ne sont pas essentielles au fonctionnement de l'OS Android. |
unlock
|
Cette autorisation est requise pour tenter de déverrouiller la clé principale pour les clés liées à l'authentification. |