TrustZone: SO confiável e seus aplicativos

Nos artigos anteriores, vimos o dispositivo de hardware TrustZone e a operação do mecanismo Secure Monitor. Hoje vamos nos concentrar no SO confiável (TEE) e em seus aplicativos. E se da última vez houve coisas de nível bastante baixo, agora tudo estará em um nível muito alto - no nível do sistema operacional.

O que é TEE?


O que é TEE? Este é o ambiente de execução confiável (Trusted Execution Environment), em primeiro lugar - este é o ambiente de execução dos programas. Nós o descrevemos em termos de função e propriedades, mas não no sentido de programação, mas no sentido filosófico.

Por exemplo, um trem de longa distância, trem e táxi têm uma das funções mais importantes - transportar pessoas. Mas de acordo com suas propriedades, elas diferem, por exemplo: um trem transporta entre cidades, um trem elétrico - fora da cidade e um táxi - principalmente na cidade. Treinar e treinar para bilhetes, táxi - não. E assim por diante

A função TEE é armazenar com segurança alguns dados para nós e iniciar aplicativos para nós. Queremos transmitir comandos TEE: inicie tal e qual aplicação, pegue esses e tais dados e faça isso e aquilo com eles. Ao mesmo tempo, não podemos ver o código do aplicativo, nem os dados. Nós apenas obteremos o resultado. A interação com o TEE é muito semelhante ao RPC.

Essa função é ideal para várias criptografia, por exemplo, para assinatura eletrônica: as chaves são armazenadas no TEE, e solicitamos ao TEE que assine os dados transmitidos com uma chave armazenada no TEE. Obtemos o resultado, mas não temos acesso à chave.

O TEE possui várias propriedades, mas as principais são: a) confiamos em sua implementação eb) é separado de forma confiável do sistema operacional principal do dispositivo, protegido, é difícil de quebrar ou quebrar. Existem outras propriedades, mas chamamos isso de sistema operacional confiável para isso. Propriedade b) o mais importante é que o ETE é separado e difícil de quebrar, ou seja, é protegido.

Se você observar o TEE através do prisma de funções e propriedades, fica claro que o TEE não é nem um pouco sobre TrustZone. O TrustZone é uma das maneiras de separar o TEE do sistema operacional principal (convidado).

Opções de implementação de TEE


Se as principais propriedades do TEE são separadas e difíceis de serem quebradas, podemos criar opções diferentes para implementar o TEE:

  • Use o TrustZone - obtemos a separação do TEE e do SO principal no mesmo núcleo do processador.
  • Execute o TEE em um núcleo separado dentro do sistema em um chip e se comunique com ele por meio de uma interface de hardware. Alguns processadores especializados têm núcleos confiáveis ​​separados para executar o TEE, mas você também não pode comprá-los na loja. Mas você pode pegar um cristal de núcleo duplo, por exemplo, Cortex-A + Cortex-M0 / M4 e executá-lo no Cortex-M TEE.
  • Execute o TEE em um chip separado e estabeleça uma conexão segura com ele por meio de uma interface externa, por exemplo, SPI ou SMbus. Para proteger a comunicação, use métodos criptográficos.
    Esse método é usado quando você estabelece uma conexão com um cartão inteligente, como um cartão de pagamento plástico com chip. Em certo sentido, o TEE é executado no chip, porque, a nosso pedido, realiza transações financeiras com muita confiança, armazena dados etc.
    O mesmo método é usado no TPM (Trusted Platform Module) da arquitetura moderna de PC.

Falaremos apenas sobre a implementação do TEE no TrustZone, porque esta é uma versão muito comum da implementação do TEE. Mas grande parte dos itens acima se aplica ao ETE em geral.

TEE como SO


Em artigos anteriores, sempre chamamos o TEE de um sistema operacional confiável e dizemos que ele é muito parecido com sistemas operacionais reais.

Sem pretender ser geral, dizemos que a maior parte dos ETE tem:
  • aplicativos e processos: o TEE pode baixar aplicativos e executá-los;
  • separação de processo e memória do kernel: usada pela MMU para proteger o espaço da memória do processo e para proteger a memória principal do TEE;
  • threads, interações de processos;
  • armazenamento de dados.

Você pode criar versões mais truncadas do TEE, por exemplo, sem carregamento dinâmico de aplicativo, sem interação do processo, sem encadeamentos, mas os aplicativos em si, armazenamento de dados e separação da memória do processo e do espaço do kernel permanecerão.
Texto oculto
Um exemplo de TEE truncado pode ser visto agora no projeto ARM Trusted Firmware-M para a nova geração de microcontroladores Cortex-M na plataforma ARMv8-M. Este é um TEE simplificado, agora há suporte para microcontroladores nos núcleos Cortex-M23 e Cortex-M33. Estes são microcontroladores baseados em flash, aproximadamente equivalentes ao Cortex-M0 e Cortex-M3, mas com suporte da TrustZone. Eles têm pouca RAM, o programa é executado principalmente a partir do Flash e, portanto, no TEE não há carregamento dinâmico de programas. No momento, o TF-M também é de rosca única.

Interface de software TEE


Para interagir com outros componentes de software, o TEE possui uma API:

  • O TEE fornece uma API para programas por meio de chamadas do sistema (Supervisor Call, comando SVC);
  • O TEE fornece a API para o mundo normal por meio de chamadas para o Secure Monitor (comando SMC).

Através de chamadas do sistema, os programas salvam dados e chamam as funções do SO. Como qualquer sistema operacional decente, o TEE tenta abstrair programas de hardware em um grau ou outro.
Por exemplo, os resumos do Linux funcionam com arquivos por meio de chamadas de abertura, leitura, gravação e fechamento - todas as funções do stdio recaem basicamente nas chamadas do sistema OS. E o TEE também permite que seus aplicativos trabalhem com dados armazenados por meio de chamadas que abstraem armazenam e carregam objetos (blocos de dados) no armazenamento. O TEE também pode fornecer algumas funções criptográficas no nível do sistema, etc.

Há um conjunto de especificações GlobalPlatform para TEE, que descrevem APIs, requisitos, cenários de uso etc.
As principais APIs do TEE para seus programas são descritas na Especificação interna da API do TEE. Ele descreve funções de armazenamento de dados, funções criptográficas, etc. E a "API do cliente TEE" descreve como chamar aplicativos do Mundo Normal.

Se o seu TEE implementar essas APIs, escrever um aplicativo para ele será bastante fácil. Graças a uma API, a portabilidade dos programas também é implementada.

Diferenças entre o TEE e o SO normal


As duas principais diferenças entre o TEE e o Linux e outros sistemas operacionais comuns que nos são familiares são:

  1. O TEE executa ações não no comando do usuário, mas no comando do Mundo Normal;
  2. O TEE no TrustZone não possui seu próprio agendador.

Em um sistema operacional normal, o usuário gera alguns comandos de entrada de entrada, clica nos ícones e o sistema operacional processa essa entrada, a transfere para programas e os programas são processados. Na versão do servidor, a entrada não vem do usuário, mas de certos clientes, provavelmente pela rede. Mas o sistema operacional, no entanto, atua com base em informações externas.

O TEE não processa dados externos nem os transfere para aplicativos. Em vez disso, processa os comandos e dados transmitidos do Normal World por meio da API do cliente TEE, e isso é quase tudo. Acontece que o TEE atua no sistema operacional como uma biblioteca com uma interface RPC, cujas funções são chamadas. Após o processamento das funções, o TEE pode não fazer nada.

A segunda diferença segue da primeira. O TEE do administrador compartilha o tempo da CPU com o Normal World e é chamado como uma biblioteca. O TEE não aloca constantemente o tempo do processador, gasta o tempo necessário para concluir a solicitação e depois transfere o controle para o Mundo Normal. E, nesse caso, ela não deve ter seu próprio agendador - ela precisa de um agendador de sistema operacional convidado.

O agendador principal do SO transfere o controle para o TEE indiretamente:

  • o planejador define a tarefa a ser concluída;
  • a tarefa chama a chamada de sistema do kernel;
  • uma chamada do sistema chama TEE, se necessário;
  • O TEE trabalha o tempo necessário para concluir a solicitação e retorna o controle ao Mundo Normal.

Aplicações TEE


Os aplicativos em execução no TEE são chamados de trustlets - semelhantes aos applets executados em cartões inteligentes.
Citação da Wikipedia:
Applet (Eng. Applet do aplicativo - application e -let - diminuto sufixo) é um componente de software independente que trabalha no contexto de outro aplicativo com peso total, projetado para uma tarefa restrita e sem valor isolado do aplicativo base.

Trustlet é um Applet Confiável. Este é um programa para o ETE, como já descobrimos, ele se comunica com o ETE através de chamadas do sistema, possui um ciclo de vida, etc.

Mas, ainda assim, o nome indica que é um componente não independente. Aqui, a independência é expressa no fato de que o trustlet fará chamadas do Mundo Normal e depois será desconectado junto com o TEE. Se girar em um loop infinito, o núcleo do processador deixará de funcionar como um sistema operacional e tudo acabará travando. Mas o programa para um sistema operacional normal pode girar em um loop sem fim e o meu para contar algumas tarefas, isso é completamente normal para o programa. A este respeito, é independente do trustlet.

O trustlet deve ter algum tipo de identificador para que o Mundo Normal possa chamá-lo. É habitual fornecer trustlets como UUIDs - identificadores exclusivos.

Ciclo de vida do Trustlet


Considere como o trastlet é iniciado e os comandos são executados.

Seria lógico carregar o trustlet na memória e começar a trabalhar, mas na API do cliente GlobalPlatform TEE, para iniciar o trustlet, é necessário criar um contexto e estabelecer uma sessão com o trustlet.

Criar um contexto é o estabelecimento de uma conexão entre o Mundo Normal e o TEE. Nesse caso, a especificação GlobalPlatform supõe que o dispositivo possa ter várias TEEs e, no momento da criação do contexto, você pode escolher qual TEE entrar em contato.

Na API do cliente GlobalPlatform TEE, uma função é fornecida para isso:

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

Essa função é chamada do aplicativo Mundo Normal. Aqui o nome indica o TEE selecionável. Se queremos o TEE por padrão ou temos certeza de que temos apenas um TEE, substituímos NULL. No contexto, o contexto criado é salvo.

Depois de criar o contexto, você precisa estabelecer uma sessão com a confiança. Aqui, o UUID do trustlet é útil para nós. Para fazer isso, a função é chamada:

 TEEC_Result TEEC_OpenSession (
	 Contexto TEEC_Context *, sessão TEEC_Session *,
	 const TEEC_UUID * destino, uint32_t connectionMethod,
	 const void * connectionData, operação TEEC_Operation *,
	 uint32_t * returnOrigin)

Uma sessão é equivalente a trabalhar com uma instância de programa em um sistema operacional regular: pode haver muitas instâncias do mesmo programa no sistema operacional e elas funcionarão independentemente. Mas há muitas sessões no TEE e, em essência, essas são conexões com instâncias exclusivas do trustlet na memória. Nesse caso, a área de código provavelmente será a mesma, mapeada via MMU para a memória de diferentes processos. Mas cada processo terá sua própria área de dados, permitindo que as instâncias funcionem independentemente. Assim como no Linux.

Quando TEEC_OpenSession é chamado, o contexto e o UUID da confiança de destino são transmitidos como entrada. A sessão estabelecida será salva em "sessão". Alguns parâmetros a seguir não serão considerados, eles não são tão importantes para a compreensão.

No momento em que a sessão é criada, o trustlet pode ser carregado na memória. É o que acontece com os aplicativos no sistema operacional. No TEE grande, o vinculador é responsável por isso, faz o download da imagem binária do trustlet, esse é um arquivo ELF assinado. Se for um TEE pequeno, o trustlet já deverá estar carregado na memória - ele pode ser vinculado estaticamente ou, para microcontroladores flash, gravado na memória flash no endereço especificado.

Vamos supor que temos um TEE grande e precisamos carregar o trustlet na memória. De onde ele veio? Em princípio, o TEE no momento do carregamento precisa de um objeto com um determinado UUID, e o mecanismo para obter esse objeto pode ser qualquer:

  • o objeto já pode estar na memória;
  • o objeto pode ser colocado estaticamente na memória flash (para microcontroladores flash);
  • o objeto pode ser vinculado estaticamente ao TEE - para trustlets do sistema;
  • finalmente, você pode fazer o download do arquivo para a RAM do sistema de arquivos ou mesmo pela rede.

Pergunte a si mesmo mais tarde, como esse TEE baixa dados de um sistema de arquivos ou de uma rede? !!!

Depois de baixar a imagem do trustlet, sua assinatura digital é verificada. Um sistema de certificação é usado e o TEE verificará se a confiança é assinada por uma parte em que o TEE confia. Isso é muito importante porque elimina a possibilidade de baixar um trustlet falsificado com algum malware.

Quando a imagem do trustlet é recebida e a assinatura é verificada, o TEE cria o espaço de endereço para a instância do trustlet na MMU e o vinculador carrega a área de código na memória, mapeia-a para o espaço de endereço do trustlet e inicializa a área de dados. O resultado é uma instância totalmente inicializada do trustlet para trabalhar com o aplicativo de chamada específico - essa é a criação da sessão.

Depois que a sessão é criada, o trustlet fica pronto e pode executar solicitações do aplicativo de chamada. Para chamar as funções de confiança do sistema operacional, a função é usada:

 TEEC_Result TEEC_InvokeCommand (
	 Sessão TEEC_Session *,
	 uint32_t commandID,
	 Operação TEEC_Operation *,
	 uint32_t * returnOrigin) 

Aqui, "session" indica nossa sessão, ou seja, a instância TEE e a instância de trustlet com a qual estamos trabalhando.

"CommandID" indica a função chamada do trustlet. Esta é a função trustlet, não a função TEE. Tudo que o TEE se importa é iniciar o trustlet e enviar comandos, e quais os números de ID do comando a serem atribuídos para se comunicar com o trustlet são com você, não há regra ou lista global de funções.

Se você precisar passar parâmetros para a função chamada, eles serão passados ​​pela operação - este é um ponteiro para a estrutura TEEC_Operation. Agora não entraremos em detalhes demais, apenas observe que essa estrutura contém até 4 parâmetros de função (tipo TEEC_Parameter). Os parâmetros podem ser um simples TEEC_Value ou um ponteiro para a memória. Os parâmetros também têm tipificação na direção: TEEC_VALUE_INPUT (entrada), TEEC_VALUE_OUTPUT (saída) ou TEEC_VALUE_INOUT (bidirecional).

Se passarmos um ponteiro para a estrutura TEEC_Operation, devemos primeiro inicializá-lo: defina todos os valores e direções. Após a conclusão da chamada, podemos verificar os valores retornados nesta estrutura (para TEEC_VALUE_OUTPUT e TEEC_VALUE_INOUT).

Durante a sessão, podemos chamar as funções do trustlet quantas vezes for necessário. No final do trabalho, você precisará finalizar a sessão e liberar o contexto chamando TEEC_CloseSession e TEEC_FinalizeContext.

Tudo isso lembra muito o RPC, certo? Em princípio, todas as operações com o TEE são projetadas como RPC e, graças a isso, você pode trabalhar com uma variedade de implementações do TEE: no TrustZone, em um núcleo separado, em um chip separado.

Suplicante


Acima, nos perguntamos: como o TEE baixa dados de um sistema de arquivos ou de uma rede?
Se você pensar bem, o próprio TEE não terá acesso ao sistema de arquivos do SO. Ou seja, o TEE implementado no TrustZone poderia ter esse acesso, mas seria necessário compartilhá-lo com o Normal World, e isso não é tão simples. Por exemplo, o Linux trabalha constantemente com o sistema de arquivos, e seu estado atual está apenas na memória do kernel do Linux, e não no disco. Se o TEE quiser intervir e trabalhar com o sistema de arquivos em paralelo, será muito difícil. Com a rede compartilhando o mesmo.

Além disso, o TEE é um sistema operacional bastante pequeno e não seria lucrativo implementar drivers de baixo nível para trabalhar com mídia, com um controlador de rede e dar suporte a uma pilha de rede ou driver FS. Além disso, isso aumenta muito a superfície de ataque - haveria uma chance de quebrar o TEE ao deslizar um inode incomum no ext2 ou algo parecido. Nós não queremos isso.
Portanto, quando o sistema operacional é iniciado, o suplicante é carregado - um programa assistente. Ele está sempre conectado ao TEE, e o TEE o utiliza para acessar os recursos do Mundo Normal.

Portanto, se o TEE desejar fazer o download da imagem do trustlet do sistema de arquivos, ele chamará Suplicante:

TEE: E quanto a um objeto com esse UUID?
Requerente: (Carrega um objeto do sistema de arquivos) Desculpe, senhor!

Obviamente, essas chamadas devem ser verificadas quanto à segurança. Nesse caso, verificamos a assinatura no trustlet e não assumimos quase nenhum risco - a assinatura está correta e o trustlet vai funcionar ou a assinatura está incorreta. Ou seja, corremos o risco - pode não haver um trustlet, o suplicante pode não ser iniciado, mas essa é outra parte do modelo de ameaça.

Biblioteca do espaço do usuário


A interface do programa (chamadas para TEEC_OpenSession, etc.) é implementada usando uma biblioteca que transmite uma chamada do nível do aplicativo para o TEE.

Ao implementar o TEE no TrustZone, para isso, a biblioteca deve primeiro transferir a chamada para o nível do kernel do SO, pois somente o kernel do SO pode chamar SMC (Secure Monitor Call).
No pacote configurável Linux + OP-TEE, a biblioteca userspace é libteec. Ele converte as chamadas da API do cliente GlobalPlatform TEE para o driver do kernel através de operações ioctl no arquivo do dispositivo: quando o sistema operacional é iniciado, o módulo do kernel (driver) é carregado, o driver cria o arquivo do dispositivo. Ao abrir o arquivo do dispositivo com libteec, o programa do usuário pode trabalhar com a API do cliente TEE.

Ou seja, esse design funciona:
Aplicativo> libteec> arquivo de dispositivo> driver do kernel> SMC> TEE> trust.

Um exemplo de um trustlet


Veja como funciona em um aplicativo real:
imagem
Aqui, o trustlet é usado para assinar documentos eletronicamente. Um programa do Linux chama o trustlet, para o qual é criado um contexto TEE, uma sessão com o trustlet, os dados para assinatura são transmitidos e a assinatura eletrônica é retornada.

Conclusão


Neste artigo, descobrimos o que são TEE e trustlets. Nós nos encontramos com a API do TEE e aprendemos como os trustlets são chamados.

Deixamos de lado muitas coisas deliberadamente, como o uso de Memória compartilhada e a escrita de trastlets, porque o artigo não pretende ser um guia completo.

Se você está interessado no tópico do TEE, continue estudando por conta própria: pode começar estudando as especificações do GlobalPlatform ou explorando o OP-TEE. Você também pode nos enviar um currículo marcado como "TrustZone".

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


All Articles