In früheren Artikeln haben wir uns mit dem TrustZone-Hardwaregerät und der Funktionsweise des Secure Monitor-Mechanismus befasst. Heute konzentrieren wir uns auf das vertrauenswürdige Betriebssystem (TEE) und seine Anwendungen. Und wenn es beim letzten Mal ziemlich niedrige Dinge gab, wird jetzt alles auf einem sehr hohen Niveau sein - auf der Ebene des Betriebssystems.
Was ist TEE?
Was ist TEE? Dies ist in erster Linie die vertrauenswürdige Ausführungsumgebung (Trusted Execution Environment) - dies ist die Ausführungsumgebung von Programmen. Wir beschreiben es in Bezug auf Funktion und Eigenschaften, aber nicht im Sinne der Programmierung, sondern im philosophischen Sinne.
Zum Beispiel haben ein Fernzug, ein Zug und ein Taxi eine der wichtigsten Funktionen - den Transport von Menschen. Aber je nach ihren Eigenschaften unterscheiden sie sich zum Beispiel: Ein Zug fährt zwischen Städten, ein elektrischer Zug - außerhalb der Stadt und ein Taxi - hauptsächlich in der Stadt. Zug und Zug für Fahrkarten, Taxi - Nr. Usw.
Die TEE-Funktion besteht darin, einige Daten sicher für uns zu speichern und Anwendungen für uns zu starten. Wir wollen TEE-Befehle übertragen: Starten Sie diese und jene Anwendung, nehmen Sie diese und jene Daten und tun Sie dies und das mit ihnen. Gleichzeitig können wir den Anwendungscode sowie die Daten nicht sehen. Wir werden nur das Ergebnis bekommen. Die Interaktion mit TEE ist RPC sehr ähnlich.
Diese Funktion ist ideal für verschiedene Kryptografien, beispielsweise für die elektronische Signatur: Schlüssel werden in TEE gespeichert, und wir bitten TEE, die übertragenen Daten mit einem in TEE gespeicherten Schlüssel zu signieren. Wir erhalten das Ergebnis, haben aber keinen Zugriff auf den Schlüssel.
TEE hat eine Reihe von Eigenschaften, aber die wichtigsten sind: a) Wir vertrauen auf seine Implementierung und b) es ist zuverlässig vom Hauptbetriebssystem des Geräts getrennt, geschützt, es ist schwer zu brechen oder zu brechen. Es gibt andere Eigenschaften, aber wir nennen es ein vertrauenswürdiges Betriebssystem für genau das. Eigenschaft b) Das Wichtigste ist, dass TEE getrennt und schwer zu brechen ist, das heißt, es ist geschützt.
Wenn Sie TEE durch das Prisma von Funktionen und Eigenschaften betrachten, wird klar, dass es bei TEE nicht einmal um TrustZone geht. TrustZone ist eine der Möglichkeiten, TEE vom Hauptbetriebssystem (Gastbetriebssystem) zu trennen.
TEE-Implementierungsoptionen
Wenn die Haupteigenschaften von TEE darin bestehen, dass es getrennt und schwer zu brechen ist, können wir verschiedene Optionen für die Implementierung von TEE finden:
- Verwenden Sie TrustZone - wir erhalten die Trennung von TEE und dem Hauptbetriebssystem innerhalb desselben Prozessorkerns.
- Führen Sie TEE auf einem separaten Kern innerhalb des Systems auf einem Chip aus und kommunizieren Sie mit ihm über eine Hardwareschnittstelle. Einige spezialisierte Prozessoren verfügen über separate vertrauenswürdige Kerne, um TEE auszuführen, aber Sie können sie leider nicht im Geschäft kaufen. Sie können jedoch einen Doppelkernkristall, z. B. Cortex-A + Cortex-M0 / M4, auf Cortex-M TEE ausführen.
- Führen Sie TEE in einem separaten Chip aus und stellen Sie über eine externe Schnittstelle, z. B. SPI oder SMbus, eine sichere Verbindung her. Verwenden Sie zum Schutz der Kommunikation kryptografische Methoden.
Diese Methode wird verwendet, wenn Sie eine Verbindung mit einer Smartcard herstellen, z. B. einer Chip-on-Chip-Kunststoff-Zahlungskarte. In gewisser Weise wird TEE im Chip ausgeführt, da es auf unsere Anfrage sehr sicher Finanztransaktionen durchführt, Daten speichert usw.
Die gleiche Methode wird in TPM (Trusted Platform Module) der modernen PC-Architektur verwendet.
Wir werden nur über die Implementierung von TEE in TrustZone sprechen, da dies eine sehr häufige Version der Implementierung von TEE ist. Viele der oben genannten Punkte gelten jedoch allgemein für TEE.
TEE als OS
In früheren Artikeln haben wir TEE immer als vertrauenswürdiges Betriebssystem bezeichnet und gesagt, dass es realen Betriebssystemen sehr ähnlich ist.
Ohne vorzugeben, allgemein zu sein, sagen wir, dass der Großteil der TEE:
- Anwendungen und Prozesse: TEE kann Anwendungen herunterladen und ausführen.
- Trennung von Prozess- und Kernelspeicher: Wird von der MMU verwendet, um den Prozessspeicherplatz und den TEE-Kernspeicher zu schützen.
- Threads, Prozessinteraktionen;
- Datenspeicherung.
Sie können mehr abgeschnittene Versionen von TEE entwickeln, z. B. ohne dynamisches Laden von Anwendungen, ohne Prozessinteraktion, ohne Threads, aber die Anwendungen selbst, die Datenspeicherung und die Trennung von Prozessspeicher und Kernelspeicher bleiben erhalten.
Versteckter TextEin Beispiel für ein abgeschnittenes TEE ist jetzt im ARM Trusted Firmware-M-Projekt für die neue Generation von Cortex-M-Mikrocontrollern auf der ARMv8-M-Plattform zu sehen. Dies ist ein abgespecktes TEE. Jetzt werden Mikrocontroller auf den Cortex-M23- und Cortex-M33-Kernen unterstützt. Hierbei handelt es sich um Flash-basierte Mikrocontroller, die in etwa Cortex-M0 und Cortex-M3 entsprechen, jedoch TrustZone-Unterstützung bieten. Sie haben wenig RAM, das Programm läuft hauptsächlich über Flash und daher gibt es in TEE kein dynamisches Laden von Programmen. Im Moment ist TF-M auch Single-Threaded.
TEE-Softwareschnittstelle
Für die Interaktion mit anderen Softwarekomponenten verfügt TEE über eine API:
- TEE bietet eine API für Programme über Systemaufrufe (Supervisor Call, SVC-Befehl).
- TEE stellt die API für Normal World durch Aufrufe von Secure Monitor (SMC-Befehl) bereit.
Durch Systemaufrufe speichern Programme Daten und rufen Betriebssystemfunktionen auf. Wie jedes anständige Betriebssystem versucht TEE, Programme von der Hardware bis zu dem einen oder anderen Grad zu abstrahieren.
Beispielsweise arbeiten Linux-Abstracts mit Dateien durch Öffnen, Lesen, Schreiben und Schließen von Aufrufen - alle stdio-Funktionen fallen grundsätzlich auf die Systemaufrufe des Betriebssystems. TEE ermöglicht seinen Anwendungen auch die Arbeit mit gespeicherten Daten durch Aufrufe, mit denen Objekte (Datenblöcke) abstrakt gespeichert und in den Speicher geladen werden. TEE kann auch einige kryptografische Funktionen auf Systemebene usw. bereitstellen.
Es gibt eine Reihe von
GlobalPlatform- Spezifikationen für TEE, die APIs, Anforderungen, Verwendungsszenarien usw. beschreiben.
Die Kern-TEE-APIs für seine Programme sind in der internen Kern-API-Spezifikation von TEE beschrieben. Es beschreibt Datenspeicherfunktionen, kryptografische Funktionen usw. Und die „TEE Client API“ beschreibt das Aufrufen von Anwendungen aus Normal World.
Wenn Ihr TEE diese APIs implementiert, ist das Schreiben einer Anwendung dafür recht einfach. Dank einer API wird auch die Portabilität von Programmen implementiert.
Unterschiede zwischen TEE und regulärem Betriebssystem
Die beiden Hauptunterschiede zwischen TEE und Linux und anderen uns bekannten Betriebssystemen sind:
- TEE führt Aktionen nicht auf Befehl des Benutzers aus, sondern auf Befehl von Normal World.
- TEE in TrustZone hat keinen eigenen Scheduler.
In einem normalen Betriebssystem generiert der Benutzer einige Eingaben - gibt Befehle ein, klickt auf die Symbole, und das Betriebssystem verarbeitet diese Eingaben, überträgt sie an Programme und Programme verarbeiten sie. In der Serverversion stammt die Eingabe nicht vom Benutzer, sondern von bestimmten Clients, höchstwahrscheinlich über das Netzwerk. Das Betriebssystem handelt jedoch auf der Grundlage externer Eingaben.
TEE verarbeitet keine externen Daten und überträgt sie nicht an Anwendungen. Stattdessen verarbeitet es die Befehle und Daten, die von Normal World über die TEE-Client-API übertragen werden, und das ist fast alles. Es stellt sich heraus, dass TEE für das Betriebssystem als eine Bibliothek mit einer RPC-Schnittstelle fungiert, deren Funktionen aufgerufen werden. Nach der Verarbeitung der Funktionen kann TEE nichts tun.
Der zweite Unterschied ergibt sich aus dem ersten. Das TEE des Treuhänders teilt die CPU-Zeit mit Normal World und wird als Bibliothek aufgerufen. TEE weist sich nicht ständig Prozessorzeit zu, sondern verbringt so viel Zeit wie nötig, um die Anforderung abzuschließen, und überträgt dann die Kontrolle an Normal World. Und wenn ja, dann sollte sie keinen eigenen Scheduler haben - sie braucht einen Scheduler für das Gastbetriebssystem.
Der Haupt-OS-Scheduler überträgt die Kontrolle indirekt an TEE:
- Der Scheduler legt die zu erledigende Aufgabe fest.
- Die Task ruft den Kernel-Systemaufruf auf.
- ein Systemaufruf ruft bei Bedarf TEE auf;
- TEE arbeitet so lange wie nötig, um die Anforderung abzuschließen, und gibt die Kontrolle an Normal World zurück.
TEE-Anwendungen
Anwendungen, die auf TEE ausgeführt werden, werden als Trustlets bezeichnet - ähnlich wie Applets, die auf Smartcards ausgeführt werden.
Zitat aus Wikipedia:
Applet (engl. Applet aus Anwendung - Anwendung und -let - Diminutivsuffix) ist eine nicht eigenständige Softwarekomponente, die im Kontext einer anderen, vollgewichteten Anwendung arbeitet, für eine enge Aufgabe konzipiert ist und keinen Wert isoliert von der Basisanwendung hat.
Trustlet ist ein vertrauenswürdiges Applet. Dies ist ein Programm für TEE, wie wir bereits herausgefunden haben, es kommuniziert mit TEE über Systemaufrufe, es hat einen Lebenszyklus usw.
Der Name weist jedoch darauf hin, dass es sich um eine nicht eigenständige Komponente handelt. Hier drückt sich die Unabhängigkeit darin aus, dass das Trustlet Anrufe von Normal World tätigt und dann zusammen mit TEE die Verbindung trennt. Wenn es sich in einer Endlosschleife dreht, funktioniert der Prozessorkern nicht mehr als Betriebssystem, und irgendwann hängt alles. Aber das Programm für ein reguläres Betriebssystem kann sich in einer Endlosschleife drehen und meine, um einige Aufgaben zu zählen. Dies ist für das Programm völlig normal. In dieser Hinsicht ist es unabhängig vom Trustlet.
Das Trustlet muss eine Art Kennung haben, damit Normal World es aufrufen kann. Es ist üblich, Trustlets als UUIDs anzugeben - eindeutige Bezeichner.
Trustlet-Lebenszyklus
Überlegen Sie, wie das Trastlet gestartet und die Befehle ausgeführt werden.
Es wäre logisch, das Trustlet in den Speicher zu laden und mit der Arbeit zu beginnen. In der GlobalPlatform TEE Client-API müssen Sie jedoch zum Starten des Trustlets einen Kontext erstellen und eine Sitzung mit dem Trustlet einrichten.
Das Erstellen eines Kontexts ist das Herstellen einer Verbindung zwischen Normal World und TEE. In diesem Fall wird in der GlobalPlatform-Spezifikation davon ausgegangen, dass das Gerät mehrere TEEs haben kann. Zum Zeitpunkt der Erstellung des Kontexts können Sie auswählen, an welches TEE Sie sich wenden möchten.
In der GlobalPlatform TEE Client API wird hierfür eine Funktion bereitgestellt:
TEEC_Result TEEC_InitializeContext (const char * name, TEEC_Context * context)
Diese Funktion wird von der Normal World-Anwendung aufgerufen. Hier gibt der Name das auswählbare TEE an. Wenn wir standardmäßig TEE möchten oder sicher sind, dass wir nur ein TEE haben, ersetzen wir NULL. Im Kontext wird der erstellte Kontext gespeichert.
Nach dem Erstellen des Kontexts müssen Sie eine Sitzung mit der Vertrauensstellung einrichten. Hier ist die UUID des Trustlets für uns nützlich. Dazu heißt die Funktion:
TEEC_Result TEEC_OpenSession (
TEEC_Context * -Kontext, TEEC_Session * -Sitzung,
const TEEC_UUID * Ziel, uint32_t connectionMethod,
const void * connectionData, TEEC_Operation * operation,
uint32_t * returnOrigin)
Eine Sitzung entspricht der Arbeit mit einer Programminstanz in einem regulären Betriebssystem: Es können viele Instanzen desselben Programms im Betriebssystem vorhanden sein, die unabhängig voneinander arbeiten. Es gibt jedoch viele Sitzungen in TEE, und im Wesentlichen handelt es sich dabei um Verbindungen zu eindeutigen Instanzen des Trustlets im Speicher. In diesem Fall ist der Codebereich höchstwahrscheinlich derselbe, der über die MMU dem Speicher verschiedener Prozesse zugeordnet wird. Jeder Prozess verfügt jedoch über einen eigenen Datenbereich, sodass Instanzen unabhängig voneinander arbeiten können. Genau wie unter Linux.
Beim Aufruf von TEEC_OpenSession werden der Kontext und die UUID der Zielvertrauensstellung als Eingabe übertragen. Die eingerichtete Sitzung wird in "Sitzung" gespeichert. Einige Parameter, die wir im Folgenden nicht berücksichtigen werden, sind für das Verständnis nicht so wichtig.
Zum Zeitpunkt der Erstellung der Sitzung kann das Trustlet in den Speicher geladen werden. Dies geschieht mit Anwendungen auf dem Betriebssystem. Bei großen TEE ist der Linker dafür verantwortlich, er lädt das Binärbild des Trustlets herunter, dies ist eine solche signierte ELF-Datei. Wenn es sich um ein kleines TEE handelt, sollte das Trustlet bereits in den Speicher geladen sein. Es kann statisch verknüpft oder bei Flash-Mikrocontrollern unter der angegebenen Adresse in den Flash-Speicher geschrieben werden.
Nehmen wir an, wir haben ein großes TEE und müssen das Trustlet in den Speicher laden. Woher kommt er? Im Prinzip benötigt TEE zum Zeitpunkt des Ladens ein Objekt mit einer bestimmten UUID, und der Mechanismus zum Erhalten dieses Objekts kann folgender sein:
- Das Objekt befindet sich möglicherweise bereits im Speicher.
- Das Objekt kann statisch im Flash-Speicher abgelegt werden (für Flash-Mikrocontroller).
- Das Objekt kann statisch mit TEE verknüpft werden - für System-Trustlets.
- Schließlich können Sie die Datei vom Dateisystem oder sogar über das Netzwerk in den RAM herunterladen.
Fragen Sie sich später, wie dieses TEE Daten aus einem Dateisystem oder über ein Netzwerk herunterlädt? !!!
Nach dem Herunterladen des Image des Trustlets wird seine digitale Signatur überprüft. Es wird ein Zertifikatsystem verwendet, und TEE überprüft, ob die Vertrauensstellung von einer Partei unterzeichnet wurde, der TEE vertraut. Dies ist sehr wichtig, da dadurch die Möglichkeit ausgeschlossen wird, ein gefälschtes Trustlet mit Malware herunterzuladen.
Wenn das Trustlet-Image empfangen und die Signatur überprüft wurde, erstellt TEE den Adressraum für die Trustlet-Instanz in der MMU, und der Linker lädt den Codebereich in den Speicher, ordnet ihn dem Adressraum des Trustlets zu und initialisiert den Datenbereich. Das Ergebnis ist eine vollständig initialisierte Instanz des Trustlets für die Arbeit mit der spezifischen aufrufenden Anwendung. Dies ist die Erstellung der Sitzung.
Nachdem die Sitzung erstellt wurde, ist das Trustlet vollständig bereit und kann Anforderungen von der aufrufenden Anwendung ausführen. Um die Trustlet-Funktionen vom Betriebssystem aus aufzurufen, wird die folgende Funktion verwendet:
TEEC_Result TEEC_InvokeCommand (
TEEC_Session * Sitzung,
uint32_t commandID,
TEEC_Operation * Operation,
uint32_t * returnOrigin)
Hier gibt "Sitzung" unsere Sitzung an, dh die TEE-Instanz und die Trustlet-Instanz, mit der wir arbeiten.
"CommandID" gibt die aufgerufene Funktion des Trustlets an. Dies ist die Trustlet-Funktion, nicht die TEE-Funktion. Alles, was TEE interessiert, ist das Starten des Trustlets und das Senden von Befehlen. Welche Befehls-ID-Nummern für die Kommunikation mit dem Trustlet zugewiesen werden müssen, liegt bei Ihnen. Es gibt keine Regel oder globale Liste von Funktionen.
Wenn Sie Parameter an die aufgerufene Funktion übergeben müssen, werden diese durch die Operation übergeben - dies ist ein Zeiger auf die Struktur TEEC_Operation. Wir werden jetzt nicht zu sehr in die Tiefe gehen. Beachten Sie nur, dass diese Struktur bis zu 4 Funktionsparameter enthält (Typ TEEC_Parameter). Parameter können ein einfacher TEEC_Value oder ein Zeiger auf den Speicher sein. Die Parameter haben auch eine Typisierung in der Richtung: TEEC_VALUE_INPUT (Eingabe), TEEC_VALUE_OUTPUT (Ausgabe) oder TEEC_VALUE_INOUT (bidirektional).
Wenn wir einen Zeiger auf die TEEC_Operation-Struktur übergeben, müssen wir ihn zuerst initialisieren: Setzen Sie alle Werte und Richtungen. Nach Abschluss des Aufrufs können wir die zurückgegebenen Werte in dieser Struktur überprüfen (für TEEC_VALUE_OUTPUT und TEEC_VALUE_INOUT).
Während der Sitzung können wir die Trustlet-Funktionen so oft aufrufen, wie wir benötigen. Am Ende der Arbeit müssen Sie die Sitzung beenden und den Kontext freigeben, indem Sie TEEC_CloseSession und TEEC_FinalizeContext aufrufen.
Das alles erinnert sehr an RPC, oder? Im Prinzip sind alle Operationen mit TEE als RPC konzipiert. Dank dieser Funktion können Sie mit einer Vielzahl von TEE-Implementierungen arbeiten: in TrustZone in einem separaten Kern, in einem separaten Chip.
Supplicant
Oben haben wir uns gefragt: Wie lädt TEE Daten aus einem Dateisystem oder über ein Netzwerk herunter?
Wenn Sie darüber nachdenken, hat TEE selbst keinen Zugriff auf das Betriebssystem-Dateisystem. Das heißt, in TrustZone implementiertes TEE könnte einen solchen Zugriff haben, müsste es dann aber mit Normal World teilen, und dies ist nicht so einfach. Beispielsweise arbeitet Linux ständig mit dem Dateisystem, und sein aktueller Status befindet sich nur im Linux-Kernelspeicher und nicht auf der Festplatte. Wenn TEE eingreifen und parallel mit dem Dateisystem arbeiten möchte, ist dies sehr schwierig. Mit Netzwerkfreigabe das gleiche.
Darüber hinaus ist TEE ein eher kleines Betriebssystem, und es wäre unrentabel, Treiber auf niedriger Ebene für die Arbeit mit Medien, einen Netzwerkcontroller und die Unterstützung eines Netzwerkstapels oder FS-Treibers zu implementieren. Darüber hinaus erhöht dies die Angriffsfläche erheblich - es besteht die Möglichkeit, TEE zu knacken, indem eine ungewöhnliche Inode auf ext2 oder ähnliches verschoben wird. Das wollen wir nicht.
Daher wird beim Start des Betriebssystems der sogenannte Supplicant geladen - ein Hilfsprogramm. Es ist immer mit TEE verbunden und TEE verwendet es, um auf Ressourcen der normalen Welt zuzugreifen.
Wenn TEE das Trustlet-Image aus dem Dateisystem herunterladen möchte, ruft es Supplicant auf:
TEE: Was ist mit einem Objekt mit einer solchen UUID?
Supplicant: (Lädt ein Objekt aus dem Dateisystem) Entschuldigung, Sir!Natürlich sollten solche Anrufe auf Sicherheit überprüft werden. In diesem Fall überprüfen wir die Signatur im Trustlet und gehen fast kein Risiko ein - entweder ist die Signatur korrekt und das Trustlet funktioniert oder die Signatur ist falsch. Das heißt, wir riskieren es - es gibt möglicherweise kein Trustlet, Supplicant wird möglicherweise nicht gestartet, aber dies ist ein weiterer Teil des Bedrohungsmodells.
Userspace-Bibliothek
Die Programmschnittstelle (Aufrufe von TEEC_OpenSession usw.) wird mithilfe einer Bibliothek implementiert, die einen Aufruf von der Anwendungsebene an TEE überträgt.
Bei der Implementierung von TEE in TrustZone muss die Bibliothek dazu zunächst den Aufruf an die OS-Kernel-Ebene weiterleiten, da nur der OS-Kernel SMC (Secure Monitor Call) aufrufen kann.
Im Linux + OP-TEE-Bundle lautet die Userspace-Bibliothek libteec. Es übersetzt Aufrufe der GlobalPlatform TEE Client API über ioctl-Operationen an der Gerätedatei an den Kerneltreiber: Wenn das Betriebssystem gestartet wird, wird das Kernelmodul (Treiber) geladen, der Treiber erstellt die Gerätedatei. Durch Öffnen der Gerätedatei mit libteec kann das Anwenderprogramm mit der TEE-Client-API arbeiten.
Das heißt, dieses Design funktioniert:
Anwendung> libteec> Gerätedatei> Kerneltreiber> SMC> TEE> Vertrauen.
Ein Beispiel für ein Trustlet
So funktioniert es in einer realen Anwendung:

Hier wird das Trustlet zum elektronischen Signieren von Dokumenten verwendet. Ein Programm von Linux ruft das Trustlet auf. Zu diesem Zweck wird ein TEE-Kontext erstellt, eine Sitzung mit dem Trustlet erstellt, Daten zum Signieren übertragen und die elektronische Signatur zurückgegeben.
Fazit
In diesem Artikel haben wir herausgefunden, was TEE und Trustlets sind. Wir haben uns mit der TEE-API getroffen und erfahren, wie Trustlets aufgerufen werden.
Wir haben bewusst viele Dinge beiseite gelassen, z. B. die Verwendung von Shared Memory und das Schreiben von Trastlets, da der Artikel keine erschöpfende Anleitung darstellt.
Wenn Sie sich für das Thema TEE interessieren, lernen Sie weiter auf eigene Faust: Sie können zunächst die GlobalPlatform-Spezifikationen studieren oder OP-TEE erkunden. «TrustZone».