TrustZone: sistema operativo de confianza y sus aplicaciones

En artículos anteriores, analizamos el dispositivo de hardware TrustZone y el funcionamiento del mecanismo Secure Monitor. Hoy nos centraremos en el sistema operativo de confianza (TEE) y sus aplicaciones. Y si la última vez hubo cosas de nivel bastante bajo, ahora todo estará en un nivel muy alto, en el nivel del sistema operativo.

¿Qué es la ETE?


¿Qué es la ETE? Este es el entorno de ejecución de confianza (Trusted Execution Environment), en primer lugar, este es el entorno de ejecución de los programas. Lo describimos en términos de función y propiedades, pero no en el sentido de la programación, sino en un sentido filosófico.

Por ejemplo, un tren de larga distancia, un tren y un taxi tienen una de las funciones más importantes: transportar personas. Pero según sus propiedades, difieren, por ejemplo: un tren lleva entre ciudades, un tren eléctrico, fuera de la ciudad, y un taxi, principalmente en la ciudad. Tren y tren para boletos, taxi - no. Y así sucesivamente.

La función TEE es almacenar de forma segura algunos datos para nosotros y lanzar aplicaciones para nosotros. Queremos transmitir comandos TEE: iniciar tal o cual aplicación, tomar tal y tal información y hacer esto y aquello con ellos. Al mismo tiempo, no podemos ver el código de la aplicación ni los datos. Solo obtendremos el resultado. La interacción con TEE es muy similar a RPC.

Esta función es ideal para varias criptografías, por ejemplo, para la firma electrónica: las claves se almacenan en TEE, y le pedimos a TEE que firme los datos transmitidos con una clave almacenada en TEE. Obtenemos el resultado, pero no tenemos acceso a la clave.

TEE tiene una serie de propiedades, pero las principales son: a) confiamos en su implementación, yb) está separado de manera confiable del sistema operativo principal del dispositivo, protegido, es difícil de romper o romper. Hay otras propiedades, pero lo llamamos un sistema operativo confiable para eso. Propiedad b) lo más importante es que TEE está separado y es difícil de romper, es decir, está protegido.

Si observa TEE a través del prisma de funciones y propiedades, queda claro que TEE ni siquiera se trata de TrustZone. TrustZone es una de las formas de separar TEE del sistema operativo principal (invitado).

Opciones de implementación de TEE


Si las principales propiedades de TEE son que es independiente y difícil de romper, entonces podemos encontrar diferentes opciones para implementar TEE:

  • Use TrustZone: obtenemos la separación de TEE y el sistema operativo principal dentro del mismo núcleo del procesador.
  • Ejecute TEE en un núcleo separado dentro del sistema en un chip y comuníquese con él a través de una interfaz de hardware. Algunos procesadores especializados tienen núcleos de confianza separados para ejecutar TEE, pero, por desgracia, no puede comprarlos en la tienda. Pero puede tomar un cristal de doble núcleo, por ejemplo, Cortex-A + Cortex-M0 / M4 y ejecutarlo en Cortex-M TEE.
  • Ejecute TEE en un chip separado y establezca una conexión segura con él a través de una interfaz externa, por ejemplo, SPI o SMbus. Para proteger la comunicación, use métodos criptográficos.
    Este método se utiliza cuando establece una conexión con una tarjeta inteligente, como una tarjeta de pago de plástico chip-on-chip. En cierto sentido, TEE se ejecuta en el chip, porque a petición nuestra, realiza transacciones financieras con confianza, almacena datos, etc.
    El mismo método se utiliza en TPM (Trusted Platform Module) de la arquitectura moderna de PC.

Solo hablaremos sobre la implementación de TEE en TrustZone, porque esta es una versión muy común de la implementación de TEE. Pero gran parte de lo anterior se aplicará a TEE en general.

TEE como OS


En artículos anteriores, siempre llamamos a TEE un sistema operativo confiable y decimos que es muy parecido a los sistemas operativos reales.

Sin pretender ser general, decimos que la mayor parte de TEE tiene:
  • aplicaciones y procesos: TEE puede descargar aplicaciones y ejecutarlas;
  • separación del proceso y la memoria del núcleo: utilizada por la MMU para proteger el espacio de la memoria del proceso y para proteger la memoria central del TEE;
  • hilos, interacciones de proceso;
  • almacenamiento de datos

Puede crear versiones más truncadas de TEE, por ejemplo, sin carga dinámica de aplicaciones, sin interacción de procesos, sin hilos, pero las aplicaciones mismas, el almacenamiento de datos y la separación de la memoria de procesos y el espacio del kernel permanecerán.
Texto oculto
Un ejemplo de un TEE truncado se puede ver ahora en el proyecto ARM Trusted Firmware-M para la nueva generación de microcontroladores Cortex-M en la plataforma ARMv8-M. Este es un TEE simplificado, ahora hay soporte para microcontroladores en los núcleos Cortex-M23 y Cortex-M33. Estos son microcontroladores basados ​​en flash, aproximadamente equivalentes a Cortex-M0 y Cortex-M3, pero con soporte de TrustZone. Tienen poca RAM, el programa se ejecuta principalmente desde Flash y, por lo tanto, en TEE no hay carga dinámica de programas. Por el momento, TF-M también tiene un solo subproceso.

Interfaz de software TEE


Para interactuar con otros componentes de software, TEE tiene una API:

  • TEE proporciona una API para programas a través de llamadas al sistema (Supervisor Call, comando SVC);
  • TEE proporciona la API para Mundo Normal a través de llamadas a Secure Monitor (comando SMC).

A través de las llamadas al sistema, los programas guardan datos y llaman a las funciones del sistema operativo. Al igual que cualquier sistema operativo decente, TEE intenta abstraer programas del hardware en un grado u otro.
Por ejemplo, los resúmenes de Linux funcionan con archivos a través de llamadas abiertas, de lectura, de escritura, cerradas: todas las funciones estándar básicamente se incluyen en las llamadas del sistema operativo. Y TEE también permite que sus aplicaciones trabajen con datos almacenados a través de llamadas que almacenan y cargan objetos (bloques de datos) de forma abstracta en el almacenamiento. TEE también puede proporcionar algunas funciones criptográficas a nivel del sistema, etc.

Existe un conjunto de especificaciones de GlobalPlatform para TEE, que describen API, requisitos, escenarios de uso, etc.
Las API principales de TEE para sus programas se describen en la Especificación interna de la API interna de TEE. Describe funciones de almacenamiento de datos, funciones criptográficas, etc. Y la "API de cliente TEE" describe cómo llamar a aplicaciones desde Normal World.

Si su TEE implementa estas API, escribir una aplicación para él será bastante fácil. Gracias a una API, también se implementa la portabilidad de los programas.

Diferencias entre TEE y SO normal


Las dos diferencias principales entre TEE y Linux y otros sistemas operativos comunes que nos son familiares son:

  1. TEE realiza acciones no en el comando del usuario, sino en el comando del Mundo Normal;
  2. TEE en TrustZone no tiene su propio planificador.

En un sistema operativo normal, el usuario genera alguna entrada: ingresa comandos, hace clic en los iconos y el sistema operativo procesa esta entrada, la transfiere a los programas y los programas la procesan. En la versión del servidor, la entrada no proviene del usuario, sino de ciertos clientes, probablemente a través de la red. Pero el sistema operativo, sin embargo, actúa sobre la base de una entrada externa.

TEE no procesa datos externos ni los transfiere a aplicaciones. En cambio, procesa los comandos y los datos transmitidos desde Normal World a través de la API del cliente TEE, y eso es casi todo. Resulta que TEE actúa para el sistema operativo como una biblioteca con una interfaz RPC, cuyas funciones se llaman. Después de procesar las funciones, TEE no puede hacer nada.

La segunda diferencia se sigue de la primera. El TEE del administrador comparte el tiempo de CPU con Normal World y se llama como una biblioteca. TEE no asigna constantemente el tiempo del procesador por sí mismo, dedica todo el tiempo que sea necesario para completar la solicitud y luego transfiere el control al Mundo Normal. Y si es así, entonces no debería tener su propio programador, necesita un programador invitado del sistema operativo.

El programador principal del sistema operativo transfiere el control a TEE indirectamente:

  • el planificador establece la tarea que se completará;
  • la tarea llama a la llamada del sistema kernel;
  • una llamada al sistema llama a TEE, si es necesario;
  • TEE funciona todo el tiempo que sea necesario para completar la solicitud y devuelve el control a Mundo Normal.

Aplicaciones TEE


Las aplicaciones que se ejecutan en TEE se denominan trustlets, similares a los applets que se ejecutan en tarjetas inteligentes.
Cita de Wikipedia:
Applet (Eng. Applet de la aplicación - aplicación y -let - sufijo diminutivo) es un componente de software no autónomo que funciona en el contexto de otra aplicación ponderada, diseñada para una tarea estrecha y que no tiene un valor aislado de la aplicación base.

Trustlet es un applet de confianza. Este es un programa para TEE, como ya hemos descubierto, se comunica con TEE a través de llamadas al sistema, tiene un ciclo de vida, etc.

Pero aún así, el nombre indica que es un componente no autónomo. Aquí, la independencia se expresa en el hecho de que el trustlet hará llamadas desde Mundo Normal y luego se desconectará junto con TEE. Si gira en un bucle infinito, el núcleo del procesador dejará de funcionar como un sistema operativo y todo se bloqueará eventualmente. Pero el programa para un sistema operativo normal puede girar en un bucle sin fin y el mío para contar algunas tareas, esto es completamente normal para el programa. En este sentido, es independiente del trustlet.

El trustlet debe tener algún tipo de identificador para que Normal World pueda llamarlo. Es habitual dar los trustlets como UUID, identificadores únicos.

Ciclo de vida de Trustlet


Considere cómo se inicia el trastlet y se ejecutan los comandos.

Sería lógico cargar el trustlet en la memoria y comenzar a trabajar, pero en la API de cliente de GlobalPlatform TEE, para iniciar el trustlet, debe crear un contexto y establecer una sesión con el trustlet.

Crear un contexto es el establecimiento de una conexión entre Normal World y TEE. En este caso, la especificación GlobalPlatform supone que el dispositivo puede tener varios TEE y, al momento de crear el contexto, puede elegir con qué TEE contactar.

En la API de cliente de GlobalPlatform TEE, se proporciona una función para esto:

 TEEC_Result TEEC_InitializeContext (const char * name, TEEC_Context * context)

Esta función se llama desde la aplicación Mundo Normal. Aquí el nombre indica el TEE seleccionable. Si queremos TEE por defecto o estamos seguros de que solo tenemos un TEE, sustituimos NULL. En contexto, el contexto creado se guarda.

Después de crear el contexto, debe establecer una sesión con la confianza. Aquí el UUID del trustlet es útil para nosotros. Para hacer esto, la función se llama:

 TEEC_Result TEEC_OpenSession (
	 TEEC_Context * context, TEEC_Session * session,
	 const TEEC_UUID * destino, uint32_t connectionMethod,
	 const void * connectionData, operación TEEC_Operation *,
	 uint32_t * returnOrigin)

Una sesión es equivalente a trabajar con una instancia de programa en un sistema operativo normal: puede haber muchas instancias del mismo programa en el sistema operativo y funcionarán de forma independiente. Pero hay muchas sesiones en TEE, y en esencia, estas son conexiones a instancias únicas del trustlet en la memoria. En este caso, el área de código probablemente será la misma, asignada a través de MMU a la memoria de diferentes procesos. Pero cada proceso tendrá su propia área de datos, permitiendo que las instancias trabajen independientemente. Al igual que en Linux.

Cuando se llama a TEEC_OpenSession, el contexto y el UUID de la confianza de destino se transmiten como entrada. La sesión establecida se guardará en "sesión". Algunos parámetros de aquí en adelante no los consideraremos, no son tan importantes para su comprensión.

En el momento en que se crea la sesión, el trustlet se puede cargar en la memoria. Esto es lo que sucede con las aplicaciones en el sistema operativo. En TEE grande, el enlazador es responsable de esto, descarga la imagen binaria del trustlet, este es un archivo ELF firmado. Si se trata de un TEE pequeño, el trustlet ya debería estar cargado en la memoria; puede estar vinculado estáticamente o, para microcontroladores flash, escrito en la memoria flash en la dirección especificada.

Supongamos que tenemos un TEE grande y necesitamos cargar el trustlet en la memoria. ¿De donde viene? En principio, la TEE en el momento de la carga necesita un objeto con un determinado UUID, y el mecanismo para obtener este objeto puede ser cualquiera:

  • el objeto ya puede estar en la memoria;
  • el objeto puede colocarse estáticamente en la memoria flash (para microcontroladores flash);
  • el objeto se puede vincular estáticamente con TEE, para los trustlets del sistema;
  • finalmente, puede descargar el archivo a la RAM desde el sistema de archivos, o incluso a través de la red.

Pregúntese más tarde, ¿cómo descarga este TEE datos de un sistema de archivos o de una red?

Después de descargar la imagen del trustlet, se verifica su firma digital. Se utiliza un sistema de certificado y TEE verificará que la confianza esté firmada por una parte en la que TEE confía. Esto es muy importante porque elimina la posibilidad de descargar un trustlet falso con algún malware.

Cuando se recibe la imagen del trustlet y se verifica la firma, TEE crea el espacio de direcciones para la instancia de trustlet en la MMU, y el vinculador carga el área de código en la memoria, la asigna al espacio de direcciones del trustlet e inicializa el área de datos. El resultado es una instancia completamente inicializada del trustlet para trabajar con la aplicación de llamada específica: esta es la creación de la sesión.

Después de crear la sesión, el trustlet está en plena preparación y puede ejecutar solicitudes desde la aplicación que realiza la llamada. Para llamar a las funciones de confianza desde el sistema operativo, se utiliza la función:

 TEEC_Result TEEC_InvokeCommand (
	 Sesión de TEEC_Session *,
	 uint32_t commandID,
	 Operación TEEC_Operation *,
	 uint32_t * returnOrigin) 

Aquí, "sesión" indica nuestra sesión, es decir, la instancia de TEE y la instancia de trustlet con la que estamos trabajando.

"CommandID" indica la función llamada del trustlet. Esta es la función trustlet, no la función TEE. Todo lo que le importa a TEE es iniciar el trustlet y enviar comandos, y qué números de ID de comando asignar para comunicarse con el trustlet depende de usted, no existe una regla o una lista global de funciones.

Si necesita pasar parámetros a la función llamada, se pasan a través de la operación; este es un puntero a la estructura TEEC_Operation. No entraremos en demasiada profundidad ahora, solo observe que esta estructura contiene hasta 4 parámetros de función (tipo TEEC_Parameter). Los parámetros pueden ser un simple TEEC_Value o un puntero a la memoria. Los parámetros también tienen tipificación en la dirección: TEEC_VALUE_INPUT (entrada), TEEC_VALUE_OUTPUT (salida) o TEEC_VALUE_INOUT (bidireccional).

Si pasamos un puntero a la estructura TEEC_Operation, primero debemos inicializarlo: establecer todos los valores y direcciones. Al finalizar la llamada, podemos verificar los valores devueltos en esta estructura (para TEEC_VALUE_OUTPUT y TEEC_VALUE_INOUT).

Durante la sesión, podemos llamar a las funciones de confianza tantas veces como sea necesario. Al final del trabajo, deberá finalizar la sesión y liberar el contexto llamando a TEEC_CloseSession y TEEC_FinalizeContext.

Todo esto recuerda mucho a RPC, ¿verdad? En principio, todas las operaciones con TEE están diseñadas como RPC, y gracias a esto, puede trabajar con una variedad de implementaciones de TEE: en TrustZone, en un núcleo separado, en un chip separado.

Suplicante


Arriba, nos preguntamos: ¿cómo descarga TEE datos de un sistema de archivos o de una red?
Si lo piensa, TEE en sí no tiene acceso al sistema de archivos del sistema operativo. Es decir, TEE implementado en TrustZone podría tener dicho acceso, pero luego tendría que compartirlo con Normal World, y esto no es tan simple. Por ejemplo, Linux trabaja constantemente con el sistema de archivos, y su estado actual solo está en la memoria del kernel de Linux y no en el disco. Si TEE quiere intervenir y trabajar con el sistema de archivos en paralelo, será muy difícil. Con la red compartiendo lo mismo.

Además, TEE es un sistema operativo bastante pequeño, y no sería rentable implementar controladores de bajo nivel para trabajar con medios, con un controlador de red y admitir una pila de red o un controlador FS. Además, esto aumenta enormemente la superficie de ataque: habría una posibilidad de romper TEE al deslizar un inodo inusual en ext2 o algo así. No queremos eso.
Por lo tanto, cuando se inicia el sistema operativo, se carga el llamado Suplicante, un programa asistente. Siempre está conectado a TEE, y TEE lo usa para acceder a los recursos del Mundo Normal.

Por lo tanto, si TEE desea descargar la imagen del trustlet del sistema de archivos, llama al Solicitante:

TEE: ¿Qué pasa con un objeto con tal UUID?
Solicitante: (Carga un objeto del sistema de archivos) ¡Lo siento, señor!

Por supuesto, tales llamadas deben verificarse por seguridad. En este caso, verificamos la firma en el trustlet y casi no nos arriesgamos: la firma es correcta y el trustlet funciona o la firma es incorrecta. Es decir, lo arriesgamos: puede que no haya un trustlet, es posible que no se lance Supplicant, pero esta es otra parte del modelo de amenaza.

Biblioteca de espacio de usuario


La interfaz del programa (llamadas a TEEC_OpenSession, etc.) se implementa utilizando una biblioteca que transmite una llamada desde el nivel de aplicación a TEE.

Al implementar TEE en TrustZone, para esto, la biblioteca primero debe transferir la llamada al nivel del kernel del SO, ya que solo el kernel del SO puede llamar a SMC (Secure Monitor Call).
En el paquete Linux + OP-TEE, la biblioteca del espacio de usuario es libteec. Traduce las llamadas de la API del cliente GlobalPlatform TEE al controlador del núcleo a través de operaciones ioctl en el archivo del dispositivo: cuando se inicia el sistema operativo, se carga el módulo del núcleo (controlador), el controlador crea el archivo del dispositivo. Al abrir el archivo del dispositivo con libteec, el programa de usuario puede trabajar con la API del cliente TEE.

Es decir, este diseño funciona:
Aplicación> libteec> archivo de dispositivo> controlador de kernel> SMC> TEE> confianza.

Un ejemplo de un trustlet


Así es como funciona en una aplicación real:
imagen
Aquí, el trustlet se utiliza para firmar documentos electrónicamente. Un programa de Linux llama al trustlet, para lo cual se crea un contexto TEE, una sesión con el trustlet, se transmiten los datos para la firma y se devuelve la firma electrónica.

Conclusión


En este artículo, descubrimos qué son TEE y trustlets. Nos reunimos con la API TEE y aprendimos cómo se llaman los trustlets.

Deliberadamente dejamos de lado muchas cosas, como el uso de la memoria compartida y la escritura de trastlets, porque el artículo no pretende ser una guía exhaustiva.

Si está interesado en el tema de TEE, continúe estudiando por su cuenta: puede comenzar estudiando las especificaciones de GlobalPlatform o explorando OP-TEE. También puede enviarnos un currículum marcado como "TrustZone".

Source: https://habr.com/ru/post/es428311/


All Articles