Dans les articles précédents, nous avons examiné le périphérique matériel TrustZone et le fonctionnement du mécanisme Secure Monitor. Aujourd'hui, nous allons nous concentrer sur le système d'exploitation de confiance (TEE) et ses applications. Et si la dernière fois, il y avait des choses de bas niveau, maintenant tout sera à un niveau très élevé - au niveau du système d'exploitation.
Qu'est-ce que TEE?
Qu'est-ce que TEE? Il s'agit de l'environnement d'exécution de confiance (Trusted Execution Environment), en premier lieu - c'est l'environnement d'exécution des programmes. Nous le décrivons en termes de fonction et de propriétés, mais pas dans le sens de la programmation, mais dans un sens philosophique.
Par exemple, un train longue distance, un train et un taxi ont l'une des fonctions les plus importantes - pour transporter des personnes. Mais selon leurs propriétés, ils diffèrent, par exemple: un train circule entre les villes, un train électrique - hors ville, et un taxi - principalement en ville. Train et train pour les billets, taxi - no. Et ainsi de suite.
La fonction TEE est de stocker en toute sécurité certaines données pour nous et de lancer des applications pour nous. Nous voulons transmettre des commandes TEE: lancer telle ou telle application, prendre telle ou telle donnée et faire ceci et cela avec eux. Dans le même temps, nous ne pouvons pas voir le code de l'application, ainsi que les données. Nous n'obtiendrons que le résultat. L'interaction avec TEE est très similaire à RPC.
Cette fonction est idéale pour diverses cryptographies, par exemple pour la signature électronique: les clés sont stockées dans TEE, et nous demandons à TEE de signer les données transmises avec une clé stockée dans TEE. Nous obtenons le résultat, mais n'avons pas accès à la clé.
TEE a un certain nombre de propriétés, mais les principales sont: a) nous faisons confiance à sa mise en œuvre, et b) il est séparé de manière fiable du système d'exploitation principal de l'appareil, protégé, il est difficile de le casser ou de le casser. Il existe d'autres propriétés, mais nous l'appelons un système d'exploitation de confiance pour cela. Propriété b) la chose la plus importante est que le TEE est séparé et difficile à casser, c'est-à-dire qu'il est protégé.
Si vous regardez TEE à travers le prisme des fonctions et des propriétés, il devient clair que TEE ne concerne même pas TrustZone. TrustZone est l'un des moyens de séparer TEE du système d'exploitation principal (invité).
Options de mise en œuvre de TEE
Si les principales propriétés de TEE sont qu'il est séparé et difficile à casser, nous pouvons proposer différentes options pour implémenter TEE:
- Utilisez TrustZone - nous obtenons la séparation de TEE et du système d'exploitation principal au sein du même cœur de processeur.
- Exécutez TEE sur un noyau séparé du système sur une puce et communiquez avec lui via une interface matérielle. Certains processeurs spécialisés ont des cœurs de confiance distincts pour exécuter TEE, mais vous ne pouvez pas les acheter dans le magasin, hélas. Mais vous pouvez prendre un cristal dual-core, par exemple, Cortex-A + Cortex-M0 / M4 et l'exécuter sur Cortex-M TEE.
- Exécutez TEE sur une puce distincte et établissez une connexion sécurisée avec lui via une interface externe, par exemple SPI ou SMbus. Pour protéger la communication, utilisez des méthodes cryptographiques.
Cette méthode est utilisée lorsque vous établissez une connexion avec une carte à puce, telle qu'une carte de paiement en plastique puce sur puce. Dans un sens, TEE est exécuté dans la puce, car à notre demande, il effectue très en toute confiance des transactions financières, stocke des données, etc.
La même méthode est utilisée dans le TPM (Trusted Platform Module) de l'architecture PC moderne.
Nous ne parlerons que de l'implémentation de TEE dans TrustZone, car il s'agit d'une version très courante de l'implémentation de TEE. Mais une grande partie de ce qui précède s'appliquera au TEE en général.
TEE as OS
Dans les articles précédents, nous avons toujours appelé TEE un système d'exploitation de confiance et déclaré qu'il ressemblait beaucoup à de vrais systèmes d'exploitation.
Sans prétendre être général, nous disons que la majorité des TEE ont:
- applications et processus: TEE peut télécharger des applications et les exécuter;
- séparation de la mémoire du processus et du noyau: utilisée par la MMU pour protéger l'espace de la mémoire du processus et pour protéger la mémoire centrale du TEE;
- fils, interactions de processus;
- stockage de données.
Vous pouvez proposer des versions plus tronquées de TEE, par exemple, sans chargement d'application dynamique, sans interaction de processus, sans threads, mais les applications elles-mêmes, le stockage de données et la séparation de la mémoire de processus et de l'espace du noyau resteront.
Texte masquéUn exemple d'un TEE tronqué peut être vu maintenant dans le projet ARM Trusted Firmware-M pour la nouvelle génération de microcontrôleurs Cortex-M sur la plate-forme ARMv8-M. Il s'agit d'un TEE allégé, il existe désormais un support pour les microcontrôleurs sur les cœurs Cortex-M23 et Cortex-M33. Il s'agit de microcontrôleurs basés sur flash, à peu près équivalents à Cortex-M0 et Cortex-M3, mais avec prise en charge de TrustZone. Ils ont peu de RAM, le programme s'exécute principalement à partir de Flash, et donc dans TEE il n'y a pas de chargement dynamique des programmes. À l'heure actuelle, TF-M est également monofil.
Interface logicielle TEE
Pour interagir avec d'autres composants logiciels, TEE dispose d'une API:
- TEE fournit une API pour les programmes via les appels système (appel du superviseur, commande SVC);
- TEE fournit l'API pour Normal World via des appels à Secure Monitor (commande SMC).
Grâce aux appels système, les programmes enregistrent les données et appellent les fonctions du système d'exploitation. Comme tout système d'exploitation décent, TEE essaie d'abstraire des programmes du matériel à un degré ou à un autre.
Par exemple, les résumés Linux fonctionnent avec des fichiers via des appels ouverts, lus, écrits et fermés - toutes les fonctions stdio relèvent essentiellement des appels du système d'exploitation. Et TEE permet également à ses applications de travailler avec des données stockées via des appels qui stockent et chargent de manière abstraite des objets (blocs de données) dans le stockage. TEE peut également fournir certaines fonctions cryptographiques au niveau du système, etc.
Il existe un ensemble de spécifications
GlobalPlatform pour TEE, elles décrivent les API, les exigences, les scénarios d'utilisation, etc.
Les principales API TEE de ses programmes sont décrites dans la spécification TEE Internal Core API. Il décrit les fonctions de stockage de données, les fonctions cryptographiques, etc. Et l '«API client TEE» décrit comment appeler des applications depuis Normal World.
Si votre TEE implémente ces API, l'écriture d'une application sera assez simple. Grâce à une API, la portabilité des programmes est également implémentée.
Différences entre TEE et OS standard
Les deux principales différences entre TEE et Linux et d'autres systèmes d'exploitation courants que nous connaissons sont:
- TEE effectue des actions non pas sur la commande de l'utilisateur, mais sur la commande de Normal World;
- TEE dans TrustZone n'a pas son propre planificateur.
Dans un système d'exploitation normal, l'utilisateur génère une entrée - entre des commandes, clique sur les icônes et le système d'exploitation traite cette entrée, la transfère aux programmes et les programmes la traitent. Dans la version serveur, l'entrée ne provient pas de l'utilisateur, mais de certains clients, très probablement via le réseau. Mais l'OS, cependant, agit sur la base d'une entrée externe.
TEE ne traite pas les données externes et ne les transfère pas aux applications. Au lieu de cela, il traite les commandes et les données transmises depuis Normal World via l'API client TEE, et c'est presque tout. Il s'avère que TEE agit pour le système d'exploitation comme une bibliothèque avec une interface RPC, dont les fonctions sont appelées. Après avoir traité les fonctions, TEE peut ne rien faire.
La deuxième différence découle de la première. Le TEE de Trustee partage le temps CPU avec Normal World et est appelé comme bibliothèque. TEE n'alloue pas constamment de temps processeur pour lui-même, il passe autant de temps qu'il le faut pour terminer la demande, puis transfère le contrôle à Normal World. Et si c'est le cas, elle ne devrait pas avoir son propre planificateur - elle a besoin d'un planificateur de système d'exploitation invité.
Le planificateur principal du système d'exploitation transfère indirectement le contrôle à TEE:
- le planificateur définit la tâche à accomplir;
- la tâche appelle l'appel système du noyau;
- un appel système appelle TEE, si nécessaire;
- TEE travaille aussi longtemps que nécessaire pour compléter la demande et renvoie le contrôle à Normal World.
Applications TEE
Les applications exécutées sur TEE sont appelées trustlets - similaires aux applets qui s'exécutent sur des cartes à puce.
Citation de Wikipedia:
Applet (Eng. Applet from application - application and -let - diminutive suffix) est un composant logiciel non autonome travaillant dans le contexte d'une autre application complète, conçu pour une tâche étroite et n'ayant pas de valeur indépendamment de l'application de base.
Trustlet est une applet de confiance. Il s'agit d'un programme pour TEE, comme nous l'avons déjà découvert, il communique avec TEE via des appels système, il a un cycle de vie, etc.
Mais quand même, le nom indique qu'il s'agit d'un composant non autonome. Ici, l'indépendance s'exprime dans le fait que le trustlet fera des appels depuis Normal World, puis se déconnectera avec TEE. S'il tourne dans une boucle infinie, le cœur du processeur cessera de fonctionner comme un système d'exploitation et tout finira par se bloquer. Mais le programme pour un système d'exploitation normal peut tourner dans une boucle sans fin et le mien pour compter certaines tâches, c'est tout à fait normal pour le programme. À cet égard, il est indépendant du trustlet.
Le trustlet doit avoir une sorte d'identifiant pour que Normal World puisse l'appeler. Il est habituel de donner des trustlets sous forme d'UUID - identifiants uniques.
Cycle de vie du Trustlet
Considérez comment le trastlet est lancé et les commandes sont exécutées.
Il serait logique de charger le trustlet en mémoire et de commencer à travailler, mais dans l'API client GlobalPlatform TEE pour démarrer le trustlet, vous devez créer un contexte et établir une session avec le trustlet.
La création d'un contexte est l'établissement d'une connexion entre le monde normal et TEE. Dans ce cas, la spécification GlobalPlatform suppose que le périphérique peut avoir plusieurs TEE et, au moment de la création du contexte, vous pouvez choisir le TEE à contacter.
Dans l'API client GlobalPlatform TEE, une fonction est fournie pour cela:
TEEC_Result TEEC_InitializeContext (const char * name, TEEC_Context * context)
Cette fonction est appelée depuis l'application Normal World. Ici, le nom indique le TEE sélectionnable. Si nous voulons TEE par défaut ou si nous sommes sûrs que nous n'avons qu'un seul TEE, nous substituons NULL. En contexte, le contexte créé est enregistré.
Après avoir créé le contexte, vous devez établir une session avec l'approbation. Ici, l'UUID du trustlet nous est utile. Pour ce faire, la fonction est appelée:
TEEC_Result TEEC_OpenSession (
Contexte TEEC_Context *, session TEEC_Session *,
const TEEC_UUID * destination, uint32_t connectionMethod,
const void * connectionData, opération TEEC_Operation *,
uint32_t * returnOrigin)
Une session équivaut à travailler avec une instance de programme dans un système d'exploitation normal: il peut y avoir de nombreuses instances du même programme dans le système d'exploitation, et elles fonctionneront indépendamment. Mais il existe de nombreuses sessions dans TEE, et essentiellement, ce sont des connexions à des instances uniques du trustlet en mémoire. Dans ce cas, la zone de code sera probablement la même, mappée via MMU à la mémoire de différents processus. Mais chaque processus aura sa propre zone de données, permettant aux instances de fonctionner indépendamment. Tout comme sur Linux.
Lorsque TEEC_OpenSession est appelé, le contexte et l'UUID de l'approbation de destination sont transmis en entrée. La session établie sera enregistrée dans «session». Certains paramètres ci-après ne seront pas pris en compte, ils ne sont pas si importants pour la compréhension.
Au moment de la création de la session, le trustlet peut être chargé en mémoire. C'est ce qui se passe avec les applications du système d'exploitation. En gros TEE, l'éditeur de liens est responsable de cela, il télécharge l'image binaire du trustlet, c'est un tel fichier ELF signé. S'il s'agit d'un petit TEE, le trustlet doit déjà être chargé en mémoire - il peut être lié statiquement ou, pour les microcontrôleurs flash, écrit dans la mémoire flash à l'adresse spécifiée.
Supposons que nous avons un grand TEE et que nous devons charger le trustlet en mémoire. D'où vient-il? En principe, TEE au moment du chargement a besoin d'un objet avec un certain UUID, et le mécanisme pour obtenir cet objet peut être n'importe lequel:
- l'objet peut déjà être en mémoire;
- l'objet peut être placé statiquement dans la mémoire flash (pour les microcontrôleurs flash);
- l'objet peut être lié statiquement à TEE - pour les trustlets système;
- enfin, vous pouvez télécharger le fichier en RAM à partir du système de fichiers, ou même sur le réseau.
Demandez-vous plus tard, comment ce TEE télécharge-t-il les données à partir d'un système de fichiers ou sur un réseau? !!!
Après avoir téléchargé l'image du trustlet, sa signature numérique est vérifiée. Un système de certificats est utilisé et TEE vérifiera que l'approbation est signée par une partie à laquelle TEE fait confiance. Ceci est très important car il élimine la possibilité de télécharger un trustlet usurpé avec certains logiciels malveillants.
Lorsque l'image du trustlet est reçue et que la signature est vérifiée, TEE crée l'espace d'adressage pour l'instance de trustlet dans la MMU, et l'éditeur de liens charge la zone de code en mémoire, la mappe à l'espace d'adressage du trustlet et initialise la zone de données. Le résultat est une instance entièrement initialisée du trustlet pour travailler avec l'application appelante spécifique - il s'agit de la création de la session.
Une fois la session créée, le trustlet est en pleine préparation et peut exécuter les demandes de l'application appelante. Afin d'appeler les fonctions de trustlet à partir du système d'exploitation, la fonction est utilisée:
TEEC_Result TEEC_InvokeCommand (
Session TEEC_Session *,
uint32_t commandID,
Opération TEEC_Operation *,
uint32_t * returnOrigin)
Ici, «session» indique notre session, c'est-à-dire l'instance TEE et l'instance de trustlet avec lesquelles nous travaillons.
"CommandID" indique la fonction appelée du trustlet. Il s'agit de la fonction de trustlet, pas de la fonction TEE. Tous les soucis de TEE sont de démarrer le trustlet et d'envoyer des commandes, et les numéros de commande à attribuer pour communiquer avec le trustlet dépendent de vous, il n'y a pas de règle ou de liste globale de fonctions.
Si vous devez passer des paramètres à la fonction appelée, ils sont passés par l'opération - il s'agit d'un pointeur vers la structure TEEC_Operation. Nous n'entrerons pas trop dans les détails maintenant, notez simplement que cette structure contient jusqu'à 4 paramètres de fonction (type TEEC_Parameter). Les paramètres peuvent être un simple TEEC_Value ou un pointeur vers la mémoire. Les paramètres ont également une typification dans le sens: TEEC_VALUE_INPUT (entrée), TEEC_VALUE_OUTPUT (sortie) ou TEEC_VALUE_INOUT (bidirectionnel).
Si nous passons un pointeur sur la structure TEEC_Operation, nous devons d'abord l'initialiser: définir toutes les valeurs et directions. Une fois l'appel terminé, nous pouvons vérifier les valeurs renvoyées dans cette structure (pour TEEC_VALUE_OUTPUT et TEEC_VALUE_INOUT).
Pendant la session, nous pouvons appeler les fonctions du trustlet autant de fois que nécessaire. À la fin du travail, vous devrez terminer la session et libérer le contexte en appelant TEEC_CloseSession et TEEC_FinalizeContext.
Tout cela fait très penser au RPC, non? En principe, toutes les opérations avec TEE sont conçues comme RPC, et grâce à cela, vous pouvez travailler avec une variété d'implémentations TEE: dans TrustZone, dans un noyau séparé, dans une puce distincte.
Suppliant
Ci-dessus, nous nous sommes demandé: comment TEE télécharge-t-il les données à partir d'un système de fichiers ou sur un réseau?
Si vous y réfléchissez, TEE lui-même n'a pas accès au système de fichiers du système d'exploitation. Autrement dit, TEE implémenté dans TrustZone pourrait avoir un tel accès, mais il devrait ensuite le partager avec Normal World, et ce n'est pas si simple. Par exemple, Linux fonctionne constamment avec le système de fichiers, et son état actuel est uniquement dans la mémoire du noyau Linux, et non sur le disque. Si TEE veut intervenir et travailler avec le système de fichiers en parallèle, ce sera très difficile. Avec le partage réseau identique.
En outre, TEE est un système d'exploitation plutôt petit et il ne serait pas rentable d'implémenter des pilotes de bas niveau pour travailler avec des médias, avec un contrôleur réseau et prendre en charge une pile réseau ou un pilote FS. De plus, cela augmente considérablement la surface d'attaque - il y aurait une chance de casser TEE en glissant un inode inhabituel sur ext2 ou quelque chose comme ça. Nous n'en voulons pas.
Par conséquent, lorsque le système d'exploitation démarre, le soi-disant Supplicant est chargé - un programme assistant. Il est toujours connecté à TEE et TEE l'utilise pour accéder aux ressources Normal World.
Par conséquent, si TEE souhaite télécharger l'image du trustlet à partir du système de fichiers, il appelle Supplicant:
TEE: Qu'en est-il d'un objet avec un tel UUID?
Suppliant: (Charge un objet à partir du système de fichiers) Désolé, monsieur!Bien sûr, la sécurité de ces appels doit être vérifiée. Dans ce cas, nous vérifions la signature dans le trustlet et ne prenons presque aucun risque - soit la signature est correcte et le trustlet fonctionne, soit la signature est incorrecte. Autrement dit, nous le risquons - il peut ne pas y avoir de trustlet, Supplicant ne peut pas être lancé, mais c'est une autre partie du modèle de menace.
Bibliothèque d'espace utilisateur
L'interface du programme (appels à TEEC_OpenSession, etc.) est implémentée à l'aide d'une bibliothèque qui transmet un appel du niveau application à TEE.
Lors de l'implémentation de TEE dans TrustZone, pour cela, la bibliothèque doit d'abord transférer l'appel au niveau du noyau OS, car seul le noyau OS peut appeler SMC (Secure Monitor Call).
Dans le bundle Linux + OP-TEE, la bibliothèque de l'espace utilisateur est libteec. Il traduit les appels de l'API client GlobalPlatform TEE vers le pilote du noyau via les opérations ioctl sur le fichier du périphérique: lorsque le système d'exploitation démarre, le module du noyau (pilote) est chargé, le pilote crée le fichier du périphérique. En ouvrant le fichier de périphérique avec libteec, le programme utilisateur peut fonctionner avec l'API client TEE.
Autrement dit, cette conception fonctionne:
Application> libteec> fichier de périphérique> pilote du noyau> SMC> TEE> trust.
Un exemple de trustlet
Voici comment cela fonctionne dans une application réelle:

Ici, le trustlet est utilisé pour signer électroniquement des documents. Un programme de Linux appelle le trustlet, dans ce but un contexte TEE est créé, une session avec le trustlet, les données pour la signature sont transmises et la signature électronique est retournée.
Conclusion
Dans cet article, nous avons compris ce que sont les TEE et les trustlets. Nous avons rencontré l'API TEE et appris comment les trustlets sont appelés.
Nous avons délibérément laissé de côté beaucoup de choses, telles que l'utilisation de la mémoire partagée et l'écriture de trastlets, car l'article ne prétend pas être un guide exhaustif.
Si vous êtes intéressé par le sujet du TEE, continuez à étudier par vous-même: vous pouvez commencer par étudier les spécifications de GlobalPlatform ou en explorant OP-TEE. Vous pouvez également nous envoyer un curriculum vitae marqué «TrustZone».