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:
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 classekeystore_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 classekeystore_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 comDOMAIN_APP
ouDOMAIN_SELINUX
. A verificação de permissão é executada após odomain
e onamespace
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 campoblob
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:
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 classekeystore_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 classekeystore_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 withDOMAIN_APP
orDOMAIN_SELINUX
. The permission check is performed after thedomain
and thenamespace
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 theblob
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. |