Armazenamento de chaves com suporte de hardware

A disponibilidade de um ambiente de execução confiável em um sistema em um chip (SoC) oferece uma oportunidade para os dispositivos Android fornecerem serviços de segurança fortes com suporte de hardware para o sistema operacional Android, para serviços de plataforma e até mesmo para aplicativos de terceiros. Os desenvolvedores que procuram as extensões específicas do Android devem acessar android.security.keystore .

Antes do Android 6.0, o Android já tinha uma API simples de serviços de criptografia com suporte de hardware, fornecida pelas versões 0.2 e 0.3 do Keymaster Hardware Abstraction Layer (HAL). Keystore forneceu assinatura digital e operações de verificação, além de geração e importação de pares de chaves de assinatura assimétrica. Isso já está implementado em muitos dispositivos, mas há muitos objetivos de segurança que não podem ser facilmente alcançados apenas com uma API de assinatura. O Keystore no Android 6.0 estendeu a API Keystore para fornecer uma gama mais ampla de recursos.

No Android 6.0, o Keystore adicionou primitivas criptográficas simétricas , AES e HMAC e um sistema de controle de acesso para chaves com suporte de hardware. Os controles de acesso são especificados durante a geração da chave e aplicados durante o tempo de vida da chave. As chaves podem ser restritas para serem usadas somente após o usuário ter sido autenticado e apenas para fins específicos ou com parâmetros criptográficos especificados. Para obter mais informações, consulte as páginas Tags e funções de autorização.

Além de expandir o alcance das primitivas criptográficas, o Keystore no Android 6.0 adicionou o seguinte:

  • Um esquema de controle de uso para permitir que o uso de chaves seja limitado, para mitigar o risco de comprometimento da segurança devido ao uso indevido de chaves
  • Um esquema de controle de acesso para habilitar a restrição de chaves para usuários, clientes e um intervalo de tempo definido

No Android 7.0, o Keymaster 2 adicionou suporte para atestado de chave e vinculação de versão. O atestado de chave fornece certificados de chave pública que contêm uma descrição detalhada da chave e seus controles de acesso, para tornar a existência da chave em hardware seguro e sua configuração verificável remotamente.

A ligação de versão liga as chaves ao sistema operacional e à versão de nível de patch. Isso garante que um invasor que descubra um ponto fraco em uma versão antiga do sistema ou no software TEE não possa reverter um dispositivo para a versão vulnerável e usar as chaves criadas com a versão mais recente. Além disso, quando uma chave com uma determinada versão e nível de patch é usada em um dispositivo que foi atualizado para uma versão ou nível de patch mais recente, a chave é atualizada antes de poder ser usada e a versão anterior da chave é invalidada. À medida que o dispositivo é atualizado, as chaves "catraca" avançam junto com o dispositivo, mas qualquer reversão do dispositivo para uma versão anterior torna as chaves inutilizáveis.

No Android 8.0, o Keymaster 3 fez a transição da camada de abstração de hardware (HAL) de estrutura C antiga para a interface C++ HAL gerada a partir de uma definição na nova linguagem de definição de interface de hardware (HIDL). Como parte da mudança, muitos dos tipos de argumento foram alterados, embora os tipos e métodos tenham uma correspondência um-para-um com os tipos antigos e os métodos struct HAL. Consulte a página Funções para obter mais detalhes.

Além dessa revisão de interface, o Android 8.0 estendeu o recurso de atestado do Keymaster 2 para dar suporte ao atestado de ID . O atestado de ID fornece um mecanismo limitado e opcional para atestar fortemente identificadores de hardware, como número de série do dispositivo, nome do produto e ID do telefone (IMEI/MEID). Para implementar essa adição, o Android 8.0 alterou o esquema de atestado ASN.1 para adicionar atestado de ID. As implementações de Keymaster precisam encontrar uma maneira segura de recuperar os itens de dados relevantes, bem como definir um mecanismo para desabilitar o recurso de forma segura e permanente.

No Android 9, as atualizações incluíram:

  • Atualização para Keymaster 4
  • Suporte para elementos seguros incorporados
  • Suporte para importação de chave segura
  • Suporte para criptografia 3DES
  • Alterações na ligação de versão para que boot.img e system.img tenham versões definidas separadamente para permitir atualizações independentes

Glossário

Aqui está uma rápida visão geral dos componentes Keystore e seus relacionamentos.

AndroidKeystore é a API do Android Framework e o componente usado por aplicativos para acessar a funcionalidade Keystore. Ele é implementado como uma extensão das APIs Java Cryptography Architecture padrão e consiste em código Java executado no próprio espaço de processo do aplicativo. AndroidKeystore atende às solicitações do aplicativo para o comportamento do Keystore, encaminhando-as para o daemon do keystore.

O daemon keystore é um daemon do sistema Android que fornece acesso a todas as funcionalidades do Keystore por meio de uma API Binder . É responsável por armazenar "blobs de chaves", que contêm o material real da chave secreta, criptografado para que o Keystore possa armazená-los, mas não usá-los ou revelá-los.

keymasterd é um servidor HIDL que fornece acesso ao Keymaster TA. (Este nome não é padronizado e é para fins conceituais.)

Keymaster TA (aplicativo confiável) é o software executado em um contexto seguro, na maioria das vezes em TrustZone em um ARM SoC, que fornece todas as operações seguras do Keystore, tem acesso ao material bruto da chave, valida todas as condições de controle de acesso nas chaves , etc

LockSettingsService é o componente do sistema Android responsável pela autenticação do usuário, tanto por senha quanto por impressão digital. Não faz parte do Keystore, mas é relevante porque muitas operações de chave do Keystore requerem autenticação do usuário. LockSettingsService interage com o Gatekeeper TA e o Fingerprint TA para obter tokens de autenticação, que ele fornece ao daemon de armazenamento de chaves e que, por fim, são consumidos pelo aplicativo Keymaster TA.

Gatekeeper TA (aplicativo confiável) é outro componente executado no contexto seguro, responsável por autenticar senhas de usuários e gerar tokens de autenticação usados ​​para provar ao Keymaster TA que uma autenticação foi feita para um determinado usuário em um determinado momento.

Fingerprint TA (aplicativo confiável) é outro componente executado no contexto seguro que é responsável por autenticar as impressões digitais do usuário e gerar tokens de autenticação usados ​​para provar ao Keymaster TA que uma autenticação foi feita para um determinado usuário em um determinado momento.

Arquitetura

A Android Keystore API e o Keymaster HAL subjacente fornecem um conjunto básico, mas adequado, de primitivas criptográficas para permitir a implementação de protocolos usando chaves com suporte de hardware e controle de acesso.

O Keymaster HAL é uma biblioteca dinamicamente carregável fornecida pelo OEM usada pelo serviço Keystore para fornecer serviços criptográficos com suporte de hardware. Para manter as coisas seguras, as implementações HAL não executam nenhuma operação sensível no espaço do usuário ou mesmo no espaço do kernel. As operações confidenciais são delegadas a um processador seguro alcançado por meio de alguma interface do kernel. A arquitetura resultante se parece com isso:

Acesso ao Keymaster

Figura 1. Acesso ao Keymaster

Dentro de um dispositivo Android, o "cliente" do Keymaster HAL consiste em várias camadas (por exemplo, aplicativo, estrutura, daemon Keystore), mas isso pode ser ignorado para os propósitos deste documento. Isso significa que a API Keymaster HAL descrita é de baixo nível, usada por componentes internos da plataforma e não exposta a desenvolvedores de aplicativos. A API de nível superior é descrita no site do desenvolvedor Android .

O objetivo do Keymaster HAL não é implementar os algoritmos sensíveis à segurança, mas apenas ordenar e desempacotar solicitações para o mundo seguro. O formato de ligação é definido pela implementação.

Compatibilidade com versões anteriores

O Keymaster 1 HAL é completamente incompatível com os HALs lançados anteriormente, por exemplo, Keymaster 0.2 e 0.3. Para facilitar a interoperabilidade em dispositivos que executam o Android 5.0 e anteriores lançados com os Keymaster HALs mais antigos, o Keystore fornece um adaptador que implementa o Keymaster 1 HAL com chamadas para a biblioteca de hardware existente. O resultado não pode fornecer toda a gama de funcionalidades no Keymaster 1 HAL. Em particular, ele oferece suporte apenas aos algoritmos RSA e ECDSA, e toda a imposição de autorização de chave é executada pelo adaptador, no mundo não seguro.

O Keymaster 2 simplificou ainda mais a interface HAL removendo os métodos get_supported_* e permitindo que o método finish() aceite entrada. Isso reduz o número de viagens de ida e volta ao TEE nos casos em que a entrada está disponível de uma só vez e simplifica a implementação da descriptografia do AEAD.

No Android 8.0, o Keymaster 3 fez a transição da estrutura C HAL de estilo antigo para a interface C++ HAL gerada a partir de uma definição na nova linguagem de definição de interface de hardware (HIDL). Uma implementação HAL de novo estilo é criada criando uma subclasse da classe IKeymasterDevice gerada e implementando os métodos virtuais puros. Como parte da mudança, muitos dos tipos de argumento foram alterados, embora os tipos e métodos tenham uma correspondência um-para-um com os tipos antigos e os métodos struct HAL.

Visão geral do HIDL

A linguagem de definição de interface de hardware (HIDL) fornece um mecanismo independente de linguagem de implementação para especificar interfaces de hardware. As ferramentas HIDL atualmente suportam a geração de interfaces C++ e Java. Espera-se que a maioria dos implementadores do Trusted Execution Environment (TEE) achem as ferramentas C++ mais convenientes, portanto, este documento discute apenas a representação C++.

As interfaces HIDL consistem em um conjunto de métodos, expressos como:

  methodName(INPUT ARGUMENTS) generates (RESULT ARGUMENTS);

Existem vários tipos predefinidos e os HALs podem definir novos tipos enumerados e de estrutura. Para obter mais detalhes sobre HIDL, consulte a seção Referência .

Um exemplo de método do Keymaster 3 IKeymasterDevice.hal é:

generateKey(vec<KeyParameter> keyParams)
        generates(ErrorCode error, vec<uint8_t> keyBlob,
                  KeyCharacteristics keyCharacteristics);

Isso é o equivalente ao seguinte do keymaster2 HAL:

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);

Na versão HIDL, o argumento dev foi removido porque está implícito. O argumento params não é mais uma estrutura contendo um ponteiro referenciando uma matriz de objetos key_parameter_t , mas um vec (vetor) contendo objetos KeyParameter . Os valores de retorno são listados na cláusula " generates ", incluindo um vetor de valores uint8_t para o blob de chave.

O método virtual C++ gerado pelo compilador HIDL é:

Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
                         generateKey_cb _hidl_cb) override;

Onde generateKey_cb é um ponteiro de função definido como:

std::function<void(ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
                   const KeyCharacteristics& keyCharacteristics)>

Ou seja, generateKey_cb é uma função que recebe os valores de retorno listados na cláusula generate. A classe de implementação HAL substitui esse método generateKey e chama o ponteiro de função generateKey_cb para retornar o resultado da operação ao responsável pela chamada. Observe que a chamada do ponteiro de função é síncrona . O chamador chama generateKey e generateKey chama o ponteiro de função fornecido, que é executado até a conclusão, retornando o controle para a implementação generateKey , que então retorna ao chamador.

Para obter um exemplo detalhado, consulte a implementação padrão em hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp . A implementação padrão fornece compatibilidade com versões anteriores para dispositivos com HALS keymaster0, keymaster1 ou keymaster2 de estilo antigo.

Controle de acesso

A regra mais básica do controle de acesso ao Keystore é que cada aplicativo tem seu próprio namespace. Mas para toda regra há uma exceção. Keystore tem alguns mapas embutidos em código que permitem que certos componentes do sistema acessem outros namespaces. Este é um instrumento muito contundente, pois dá a um componente controle total sobre outro namespace. E há a questão dos componentes do fornecedor como clientes do Keystore. No momento, não temos como estabelecer um namespace para componentes do fornecedor, por exemplo, suplicante WPA.

Para acomodar os componentes do fornecedor e generalizar o controle de acesso sem exceções codificadas, o Keystore 2.0 introduz domínios e namespaces SELinux.

Domínios de armazenamento de chaves

Com domínios Keystore, podemos desacoplar namespaces de UIDs. Os clientes que acessam uma chave no Keystore precisam especificar o domínio, o namespace e o alias que desejam acessar. Com base nessa tupla e na identidade do chamador, podemos determinar qual chave o chamador deseja acessar e se ela possui as permissões apropriadas.

Apresentamos cinco parâmetros de domínio que controlam como as chaves podem ser acessadas. Eles controlam a semântica do parâmetro namespace do descritor de chave e como o controle de acesso é executado.

  • DOMAIN_APP : o domínio do aplicativo abrange o comportamento herdado. O Java Keystore SPI usa esse domínio por padrão. Quando esse domínio é usado, o argumento namespace é ignorado e o UID do chamador é usado em seu lugar. O acesso a este domínio é controlado pelo rótulo Keystore para a classe keystore_key na política SELinux.
  • DOMAIN_SELINUX : Este domínio indica que o namespace tem um rótulo na política do SELinux. O parâmetro namespace é pesquisado e traduzido em um contexto de destino, e uma verificação de permissão é executada para o contexto SELinux de chamada para a classe keystore_key . Quando a permissão for estabelecida para a operação especificada, a tupla completa será usada para a pesquisa de chave.
  • DOMAIN_GRANT : o domínio de concessão indica que o parâmetro namespace é um identificador de concessão. O parâmetro alias é ignorado. As verificações do SELinux são executadas quando a concessão é criada. O controle de acesso adicional verifica apenas se o UID do chamador corresponde ao UID do beneficiário da concessão solicitada.
  • DOMAIN_KEY_ID : este domínio indica que o parâmetro namespace é um ID de chave exclusivo. A própria chave pode ter sido criada com DOMAIN_APP ou DOMAIN_SELINUX . A verificação de permissão é executada após o domain e o namespace terem sido carregados do banco de dados de chaves da mesma forma como se o blob fosse carregado pelo domínio, namespace e tupla de alias. A justificativa para o domínio de id de chave é a continuidade. Ao acessar uma chave por alias, as chamadas subsequentes podem operar em chaves diferentes, pois uma nova chave pode ter sido gerada ou importada e vinculada a esse alias. O id da chave, no entanto, nunca muda. Portanto, ao usar uma chave por ID de chave depois de ter sido carregada do banco de dados do Keystore usando o alias uma vez, pode-se ter certeza de que é a mesma chave, desde que a id de chave ainda exista. Essa funcionalidade não é exposta a desenvolvedores de aplicativos. Em vez disso, ele é usado no Android Keystore SPI para fornecer uma experiência mais consistente, mesmo quando usado simultaneamente de maneira insegura.
  • DOMAIN_BLOB : O domínio blob indica que o chamador gerencia o blob sozinho. Isso é usado para clientes que precisam acessar o Keystore antes que a partição de dados seja montada. O blob de chave está incluído no campo blob do descritor de chave.

Usando o domínio SELinux, podemos fornecer aos componentes do fornecedor acesso a namespaces muito específicos do Keystore, que podem ser compartilhados pelos componentes do sistema, como a caixa de diálogo de configurações.

Política do SELinux para keystore_key

Os rótulos de namespace são configurados usando o arquivo keystore2_key_context .
Cada linha nesses arquivos mapeia um ID de namespace numérico para um rótulo do SELinux. Por exemplo,

# 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

Depois de configurar um novo namespace de chave dessa maneira, podemos fornecer acesso a ele adicionando uma política apropriada. Por exemplo, para permitir que wpa_supplicant obtenha e use chaves no novo namespace, adicionaríamos a seguinte linha a hal_wifi_supplicant.te :

allow hal_wifi_supplicant wifi_key:keystore2_key { get, use };

Depois de configurar o novo namespace, o AndroidKeyStore pode ser usado quase como de costume. A única diferença é que o ID do namespace deve ser especificado. Para carregar e importar chaves de e para o Keystore, o ID do namespace é especificado usando o AndroidKeyStoreLoadStoreParameter . Por exemplo,

import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
import java.security.KeyStore;

KeyStore keystore = KeyStore.getInstance("AndroidKeyStore");
keystore.load(new AndroidKeyStoreLoadStoreParameter(102));

Para gerar uma chave em um determinado namespace, o id do namespace deve ser fornecido usando KeyGenParameterSpec.Builder#setNamespace():

import android.security.keystore.KeyGenParameterSpec;
KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder();
specBuilder.setNamespace(102);

Os arquivos de contexto a seguir podem ser usados ​​para configurar namespaces Keystore 2.0 SELinux. Cada partição tem um intervalo reservado diferente de 10.000 ids de namespace para evitar colisões.

partição Faixa Arquivos de configuração
Sistema 0 ... 9.999
/system/etc/selinux/keystore2_key_contexts, /plat_keystore2_key_contexts
Sistema Estendido 10.000 ... 19.999
/system_ext/etc/selinux/system_ext_keystore2_key_contexts, /system_ext_keystore2_key_contexts
produtos 20.000 ... 29.999
/product/etc/selinux/product_keystore2_key_contexts, /product_keystore2_key_contexts
Fornecedor 30.000 ... 39.999
/vendor/etc/selinux/vendor_keystore2_key_contexts, /vendor_keystore2_key_contexts

O cliente solicita a chave solicitando o domínio SELinux e o namespace virtual desejado, neste caso "wifi_key" , por seu id numérico.

Acima disso, os seguintes namespaces foram definidos. Se eles substituem as regras especiais, a tabela a seguir indica o UID ao qual eles costumavam corresponder.

ID do espaço de nomes Rótulo SEPolicy UID Descrição
0 su_key N / D Chave de superusuário. Usado apenas para testes em builds userdebug e eng. Não é relevante nas compilações do usuário.
1 shell_key N / D Namespace disponível para shell. Usado principalmente para testes, mas também pode ser usado em compilações de usuários a partir da linha de comando.
100 vold_key N / D Destinado ao uso por vold.
101 odsing_key N / D Usado pelo daemon de assinatura no dispositivo.
102 wifi_key AID_WIFI(1010) Usado pelo sistema Wifi do Android, incluindo wpa_supplicant.
120 resume_on_reboot_key AID_SYSTEM(1000) Usado pelo servidor do sistema Android para dar suporte à retomada na reinicialização.

Acessar Vetores

A classe keystore_key do SELinux envelheceu um pouco e algumas das permissões, como verify ou sign , perderam o significado. Aqui está o novo conjunto de permissões, keystore2_key , que o Keystore 2.0 aplicará.

Permissão Significado
delete Verificado ao remover chaves do Keystore.
get_info Verificado quando os metadados de uma chave são solicitados.
grant O chamador precisa dessa permissão para criar uma concessão para a chave no contexto de destino.
manage_blob O chamador pode usar DOMAIN_BLOB no namespace SELinux fornecido, gerenciando blobs por conta própria. Isso é especificamente útil para vold.
rebind Essa permissão controla se um alias pode ser vinculado a uma nova chave. Isso é necessário para inserção e implica que a chave vinculada anteriormente será excluída. É basicamente uma permissão de inserção, mas captura melhor a semântica do keystore.
req_forced_op Os clientes com essa permissão podem criar operações não passíveis de remoção, e a criação da operação nunca falha, a menos que todos os slots de operação sejam ocupados por operações não passíveis de remoção.
update Necessário para atualizar o subcomponente de uma chave.
use Verificado ao criar uma operação Keymint que usa o material da chave, por exemplo, para assinatura, en/descriptografia.
use_dev_id Obrigatório ao gerar informações de identificação do dispositivo, como atestado de ID do dispositivo.

Além disso, dividimos um conjunto de permissões de keystore não específicas de chave na classe de segurança SELinux keystore2 :

Permissão Significado
add_auth Exigido pelo provedor de autenticação, como Gatekeeper ou BiometricsManager, para adicionar tokens de autenticação.
clear_ns Anteriormente clear_uid, essa permissão permite que um não proprietário de um namespace exclua todas as chaves desse namespace.
list Exigido pelo sistema para enumerar chaves por várias propriedades, como propriedade ou limite de autenticação. Essa permissão não é exigida por chamadores que enumeram seus próprios namespaces. Isso é coberto pela permissão get_info .
lock Essa permissão permite bloquear o Keystore, ou seja, remover a chave mestra, de modo que as chaves vinculadas à autenticação se tornem inutilizáveis ​​e incriáveis.
reset Essa permissão permite redefinir o Keystore para o padrão de fábrica, excluindo todas as chaves que não são vitais para o funcionamento do sistema operacional Android.
unlock Essa permissão é necessária para tentar desbloquear a chave mestra para chaves com limite de autenticação.
,

A disponibilidade de um ambiente de execução confiável em um sistema em um chip (SoC) oferece uma oportunidade para os dispositivos Android fornecerem serviços de segurança fortes com suporte de hardware para o sistema operacional Android, para serviços de plataforma e até mesmo para aplicativos de terceiros. Os desenvolvedores que procuram as extensões específicas do Android devem acessar android.security.keystore .

Antes do Android 6.0, o Android já tinha uma API simples de serviços de criptografia com suporte de hardware, fornecida pelas versões 0.2 e 0.3 do Keymaster Hardware Abstraction Layer (HAL). Keystore forneceu assinatura digital e operações de verificação, além de geração e importação de pares de chaves de assinatura assimétrica. Isso já está implementado em muitos dispositivos, mas há muitos objetivos de segurança que não podem ser facilmente alcançados apenas com uma API de assinatura. O Keystore no Android 6.0 estendeu a API Keystore para fornecer uma gama mais ampla de recursos.

No Android 6.0, o Keystore adicionou primitivas criptográficas simétricas , AES e HMAC e um sistema de controle de acesso para chaves com suporte de hardware. Os controles de acesso são especificados durante a geração da chave e aplicados durante o tempo de vida da chave. As chaves podem ser restritas para serem usadas somente após o usuário ter sido autenticado e apenas para fins específicos ou com parâmetros criptográficos especificados. Para obter mais informações, consulte as páginas Tags e funções de autorização.

Além de expandir o alcance das primitivas criptográficas, o Keystore no Android 6.0 adicionou o seguinte:

  • Um esquema de controle de uso para permitir que o uso de chaves seja limitado, para mitigar o risco de comprometimento da segurança devido ao uso indevido de chaves
  • Um esquema de controle de acesso para habilitar a restrição de chaves para usuários, clientes e um intervalo de tempo definido

No Android 7.0, o Keymaster 2 adicionou suporte para atestado de chave e vinculação de versão. O atestado de chave fornece certificados de chave pública que contêm uma descrição detalhada da chave e seus controles de acesso, para tornar a existência da chave em hardware seguro e sua configuração verificável remotamente.

A ligação de versão liga as chaves ao sistema operacional e à versão de nível de patch. Isso garante que um invasor que descubra um ponto fraco em uma versão antiga do sistema ou no software TEE não possa reverter um dispositivo para a versão vulnerável e usar as chaves criadas com a versão mais recente. Além disso, quando uma chave com uma determinada versão e nível de patch é usada em um dispositivo que foi atualizado para uma versão ou nível de patch mais recente, a chave é atualizada antes de poder ser usada e a versão anterior da chave é invalidada. À medida que o dispositivo é atualizado, as chaves "catraca" avançam junto com o dispositivo, mas qualquer reversão do dispositivo para uma versão anterior torna as chaves inutilizáveis.

No Android 8.0, o Keymaster 3 fez a transição da camada de abstração de hardware (HAL) de estrutura C antiga para a interface C++ HAL gerada a partir de uma definição na nova linguagem de definição de interface de hardware (HIDL). Como parte da mudança, muitos dos tipos de argumento foram alterados, embora os tipos e métodos tenham uma correspondência um-para-um com os tipos antigos e os métodos struct HAL. Consulte a página Funções para obter mais detalhes.

Além dessa revisão de interface, o Android 8.0 estendeu o recurso de atestado do Keymaster 2 para dar suporte ao atestado de ID . O atestado de ID fornece um mecanismo limitado e opcional para atestar fortemente identificadores de hardware, como número de série do dispositivo, nome do produto e ID do telefone (IMEI/MEID). Para implementar essa adição, o Android 8.0 alterou o esquema de atestado ASN.1 para adicionar atestado de ID. As implementações de Keymaster precisam encontrar uma maneira segura de recuperar os itens de dados relevantes, bem como definir um mecanismo para desabilitar o recurso de forma segura e permanente.

No Android 9, as atualizações incluíram:

  • Atualização para Keymaster 4
  • Suporte para elementos seguros incorporados
  • Suporte para importação de chave segura
  • Suporte para criptografia 3DES
  • Alterações na ligação de versão para que boot.img e system.img tenham versões definidas separadamente para permitir atualizações independentes

Glossário

Aqui está uma rápida visão geral dos componentes Keystore e seus relacionamentos.

AndroidKeystore é a API do Android Framework e o componente usado por aplicativos para acessar a funcionalidade Keystore. Ele é implementado como uma extensão das APIs Java Cryptography Architecture padrão e consiste em código Java executado no próprio espaço de processo do aplicativo. AndroidKeystore atende às solicitações do aplicativo para o comportamento do Keystore, encaminhando-as para o daemon do keystore.

O daemon keystore é um daemon do sistema Android que fornece acesso a todas as funcionalidades do Keystore por meio de uma API Binder . É responsável por armazenar "blobs de chaves", que contêm o material real da chave secreta, criptografado para que o Keystore possa armazená-los, mas não usá-los ou revelá-los.

keymasterd é um servidor HIDL que fornece acesso ao Keymaster TA. (Este nome não é padronizado e é para fins conceituais.)

Keymaster TA (aplicativo confiável) é o software executado em um contexto seguro, na maioria das vezes em TrustZone em um ARM SoC, que fornece todas as operações seguras do Keystore, tem acesso ao material bruto da chave, valida todas as condições de controle de acesso nas chaves , etc

LockSettingsService é o componente do sistema Android responsável pela autenticação do usuário, tanto por senha quanto por impressão digital. Não faz parte do Keystore, mas é relevante porque muitas operações de chave do Keystore requerem autenticação do usuário. LockSettingsService interage com o Gatekeeper TA e o Fingerprint TA para obter tokens de autenticação, que ele fornece ao daemon de armazenamento de chaves e que, por fim, são consumidos pelo aplicativo Keymaster TA.

Gatekeeper TA (aplicativo confiável) é outro componente executado no contexto seguro, responsável por autenticar senhas de usuários e gerar tokens de autenticação usados ​​para provar ao Keymaster TA que uma autenticação foi feita para um determinado usuário em um determinado momento.

Fingerprint TA (aplicativo confiável) é outro componente executado no contexto seguro que é responsável por autenticar as impressões digitais do usuário e gerar tokens de autenticação usados ​​para provar ao Keymaster TA que uma autenticação foi feita para um determinado usuário em um determinado momento.

Arquitetura

A Android Keystore API e o Keymaster HAL subjacente fornecem um conjunto básico, mas adequado, de primitivas criptográficas para permitir a implementação de protocolos usando chaves com suporte de hardware e controle de acesso.

O Keymaster HAL é uma biblioteca dinamicamente carregável fornecida pelo OEM usada pelo serviço Keystore para fornecer serviços criptográficos com suporte de hardware. Para manter as coisas seguras, as implementações HAL não executam nenhuma operação sensível no espaço do usuário ou mesmo no espaço do kernel. As operações confidenciais são delegadas a um processador seguro alcançado por meio de alguma interface do kernel. A arquitetura resultante se parece com isso:

Acesso ao Keymaster

Figura 1. Acesso ao Keymaster

Dentro de um dispositivo Android, o "cliente" do Keymaster HAL consiste em várias camadas (por exemplo, aplicativo, estrutura, daemon Keystore), mas isso pode ser ignorado para os propósitos deste documento. Isso significa que a API Keymaster HAL descrita é de baixo nível, usada por componentes internos da plataforma e não exposta a desenvolvedores de aplicativos. A API de nível superior é descrita no site do desenvolvedor Android .

O objetivo do Keymaster HAL não é implementar os algoritmos sensíveis à segurança, mas apenas ordenar e desempacotar solicitações para o mundo seguro. O formato de ligação é definido pela implementação.

Compatibilidade com versões anteriores

O Keymaster 1 HAL é completamente incompatível com os HALs lançados anteriormente, por exemplo, Keymaster 0.2 e 0.3. Para facilitar a interoperabilidade em dispositivos que executam o Android 5.0 e anteriores lançados com os Keymaster HALs mais antigos, o Keystore fornece um adaptador que implementa o Keymaster 1 HAL com chamadas para a biblioteca de hardware existente. O resultado não pode fornecer toda a gama de funcionalidades no Keymaster 1 HAL. Em particular, ele oferece suporte apenas aos algoritmos RSA e ECDSA, e toda a imposição de autorização de chave é executada pelo adaptador, no mundo não seguro.

O Keymaster 2 simplificou ainda mais a interface HAL removendo os métodos get_supported_* e permitindo que o método finish() aceite entrada. Isso reduz o número de viagens de ida e volta ao TEE nos casos em que a entrada está disponível de uma só vez e simplifica a implementação da descriptografia do AEAD.

No Android 8.0, o Keymaster 3 fez a transição da estrutura C HAL de estilo antigo para a interface C++ HAL gerada a partir de uma definição na nova linguagem de definição de interface de hardware (HIDL). Uma implementação HAL de novo estilo é criada criando uma subclasse da classe IKeymasterDevice gerada e implementando os métodos virtuais puros. Como parte da mudança, muitos dos tipos de argumento foram alterados, embora os tipos e métodos tenham uma correspondência um-para-um com os tipos antigos e os métodos struct HAL.

Visão geral do HIDL

A linguagem de definição de interface de hardware (HIDL) fornece um mecanismo independente de linguagem de implementação para especificar interfaces de hardware. As ferramentas HIDL atualmente suportam a geração de interfaces C++ e Java. Espera-se que a maioria dos implementadores do Trusted Execution Environment (TEE) achem as ferramentas C++ mais convenientes, portanto, este documento discute apenas a representação C++.

As interfaces HIDL consistem em um conjunto de métodos, expressos como:

  methodName(INPUT ARGUMENTS) generates (RESULT ARGUMENTS);

Existem vários tipos predefinidos e os HALs podem definir novos tipos enumerados e de estrutura. Para obter mais detalhes sobre HIDL, consulte a seção Referência .

Um exemplo de método do Keymaster 3 IKeymasterDevice.hal é:

generateKey(vec<KeyParameter> keyParams)
        generates(ErrorCode error, vec<uint8_t> keyBlob,
                  KeyCharacteristics keyCharacteristics);

Isso é o equivalente ao seguinte do keymaster2 HAL:

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);

Na versão HIDL, o argumento dev foi removido porque está implícito. O argumento params não é mais uma estrutura contendo um ponteiro referenciando uma matriz de objetos key_parameter_t , mas um vec (vetor) contendo objetos KeyParameter . Os valores de retorno são listados na cláusula " generates ", incluindo um vetor de valores uint8_t para o blob de chaves.

O método virtual C++ gerado pelo compilador HIDL é:

Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
                         generateKey_cb _hidl_cb) override;

Onde generateKey_cb é um ponteiro de função definido como:

std::function<void(ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
                   const KeyCharacteristics& keyCharacteristics)>

Ou seja, generateKey_cb é uma função que recebe os valores de retorno listados na cláusula generate. A classe de implementação HAL substitui esse método generateKey e chama o ponteiro de função generateKey_cb para retornar o resultado da operação ao responsável pela chamada. Observe que a chamada do ponteiro de função é síncrona . O chamador chama generateKey e generateKey chama o ponteiro de função fornecido, que é executado até a conclusão, retornando o controle para a implementação generateKey , que então retorna ao chamador.

Para obter um exemplo detalhado, consulte a implementação padrão em hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp . A implementação padrão fornece compatibilidade com versões anteriores para dispositivos com HALS keymaster0, keymaster1 ou keymaster2 de estilo antigo.

Controle de acesso

A regra mais básica do controle de acesso ao Keystore é que cada aplicativo tem seu próprio namespace. Mas para toda regra há uma exceção. Keystore tem alguns mapas embutidos em código que permitem que certos componentes do sistema acessem outros namespaces. Este é um instrumento muito contundente, pois dá a um componente controle total sobre outro namespace. E há a questão dos componentes do fornecedor como clientes do Keystore. No momento, não temos como estabelecer um namespace para componentes do fornecedor, por exemplo, suplicante WPA.

Para acomodar os componentes do fornecedor e generalizar o controle de acesso sem exceções codificadas, o Keystore 2.0 introduz domínios e namespaces SELinux.

Domínios de armazenamento de chaves

Com domínios Keystore, podemos desacoplar namespaces de UIDs. Os clientes que acessam uma chave no Keystore precisam especificar o domínio, o namespace e o alias que desejam acessar. Com base nessa tupla e na identidade do chamador, podemos determinar qual chave o chamador deseja acessar e se ela possui as permissões apropriadas.

Apresentamos cinco parâmetros de domínio que controlam como as chaves podem ser acessadas. Eles controlam a semântica do parâmetro namespace do descritor de chave e como o controle de acesso é executado.

  • DOMAIN_APP : o domínio do aplicativo abrange o comportamento herdado. O Java Keystore SPI usa esse domínio por padrão. Quando esse domínio é usado, o argumento namespace é ignorado e o UID do chamador é usado em seu lugar. O acesso a este domínio é controlado pelo rótulo Keystore para a classe keystore_key na política SELinux.
  • DOMAIN_SELINUX : Este domínio indica que o namespace tem um rótulo na política do SELinux. O parâmetro namespace é pesquisado e traduzido em um contexto de destino, e uma verificação de permissão é executada para o contexto SELinux de chamada para a classe keystore_key . Quando a permissão for estabelecida para a operação especificada, a tupla completa será usada para a pesquisa de chave.
  • DOMAIN_GRANT : The grant domain indicates that the namespace parameter is a grant identifier. The alias parameter is ignored. SELinux checks are performed when the grant is created. Further access control only checks if the caller UID matches the grantees UID of the requested grant.
  • DOMAIN_KEY_ID : This domain indicates that the namespace parameter is a unique key id. The key itself may have been created with DOMAIN_APP or DOMAIN_SELINUX . The permission check is performed after the domain and the namespace have been loaded from the key database in the same way as if the blob was loaded by the domain, namespace, and alias tuple. The rationale for the key id domain is continuity. When accessing a key by alias, subsequent calls may operate on different keys, because a new key may have been generated or imported and bound to this alias. The key id, however, never changes. So when using a key by key id after it has been loaded from the Keystore database using the alias once, one can be certain that it is the same key as long as the key id still exists. This functionality is not exposed to app developers. Instead, it is used within the Android Keystore SPI to provide a more consistent experience even when used concurrently in an unsafe way.
  • DOMAIN_BLOB : The blob domain indicates that the caller manages the blob by itself. This is used for clients that need to access the Keystore before the data partition is mounted. The key blob is included in the blob field of the key descriptor.

Using the SELinux domain, we can give vendor components access to very specific Keystore namespaces which can be shared by system components such as the settings dialog.

SELinux policy for keystore_key

Namespace labels are configured using the keystore2_key_context file.
Each line in these files maps a numeric namespace id to an SELinux label. For example,

# 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

After having set up a new key namespace in this way, we can give access to it by adding an appropriate policy. For example, to allow wpa_supplicant to get and use keys in the new namespace we would add the following line to hal_wifi_supplicant.te :

allow hal_wifi_supplicant wifi_key:keystore2_key { get, use };

After setting up the new namespace, AndroidKeyStore can be used almost as usual. The only difference is that the namespace ID must be specified. For loading and importing keys from and into Keystore, the namespace id is specified using the AndroidKeyStoreLoadStoreParameter . For example,

import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
import java.security.KeyStore;

KeyStore keystore = KeyStore.getInstance("AndroidKeyStore");
keystore.load(new AndroidKeyStoreLoadStoreParameter(102));

To generate a key in a given namespace, the namespace id must be given using KeyGenParameterSpec.Builder#setNamespace():

import android.security.keystore.KeyGenParameterSpec;
KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder();
specBuilder.setNamespace(102);

The following context files may be used to configure Keystore 2.0 SELinux namespaces. Each partition has a different reserved range of 10,000 namespace ids to avoid collisions.

Partition Range Config files
System 0 ... 9,999
/system/etc/selinux/keystore2_key_contexts, /plat_keystore2_key_contexts
Extended System 10,000 ... 19,999
/system_ext/etc/selinux/system_ext_keystore2_key_contexts, /system_ext_keystore2_key_contexts
Product 20,000 ... 29,999
/product/etc/selinux/product_keystore2_key_contexts, /product_keystore2_key_contexts
Vendor 30,000 ... 39,999
/vendor/etc/selinux/vendor_keystore2_key_contexts, /vendor_keystore2_key_contexts

The client requests the key by requesting the SELinux domain and the desired virtual namespace, in this case "wifi_key" , by its numeric id.

Above that, the following namespaces have been defined. If they replace special rules, the following table indicates the UID they used to correspond to.

Namespace ID SEPolicy Label UID Description
0 su_key N/A Super user key. Only used for testing on userdebug and eng builds. Not relevant on user builds.
1 shell_key N/A Namespace available to shell. Mostly used for testing, but can be used on user builds as well from the command line.
100 vold_key N/A Intended for use by vold.
101 odsing_key N/A Used by the on-device signing daemon.
102 wifi_key AID_WIFI(1010) Used by Android's Wifi sybsystem including wpa_supplicant.
120 resume_on_reboot_key AID_SYSTEM(1000) Used by Android's system server to support resume on reboot.

Access Vectors

The SELinux class keystore_key has aged quite a bit and some of the permissions, such as verify or sign have lost their meaning. Here is the new set of permissions, keystore2_key , that Keystore 2.0 will enforce.

Permission Meaning
delete Checked when removing keys from Keystore.
get_info Checked when a key's metadata is requested.
grant The caller needs this permission to create a grant to the key in the target context.
manage_blob The caller may use DOMAIN_BLOB on the given SELinux namespace, thereby managing blobs by itself. This is specifically useful for vold.
rebind This permission controls if an alias may be rebound to a new key. This is required for insertion and implies that the previously bound key will be deleted. It is basically an insert permission, but it captures the semantic of keystore better.
req_forced_op Clients with this permission may create unpruneable operations, and operation creation never fails unless all operation slots are taken by unpruneable operations.
update Required to update the subcomponent of a key.
use Checked when creating a Keymint operation that uses the key material, eg, for signing, en/decryption.
use_dev_id Required when generating device identifying information, such as device id attestation.

Additionally, we split out a set of non key specific keystore permissions in the SELinux security class keystore2 :

Permission Meaning
add_auth Required by authentication provider such as Gatekeeper or BiometricsManager for adding auth tokens.
clear_ns Formerly clear_uid, this permission allows a non owner of a namespace to delete all keys in that namespace.
list Required by the system for enumerating keys by various properties, such as ownership or auth boundedness. This permission is not required by callers enumerating their own namespaces. This is covered by the get_info permission.
lock This permission allows to lock Keystore, that is, evict the master key, such that auth bound keys become unusable and uncreatable.
reset This permission allows to reset Keystore to factory default, deleting all keys that are not vital to the functioning of the Android OS.
unlock This permission is required to attempt to unlock the master key for auth bound keys.