A disponibilidade de um ambiente de execução confiável em um sistema em um chip (SoC) oferece uma oportunidade para que os dispositivos Android forneçam serviços de segurança robustos com suporte de hardware para o SO Android, para serviços de plataforma e até mesmo para apps de terceiros. Os desenvolvedores que procuram as extensões específicas do Android precisam acessar android.security.keystore.
Antes do Android 6.0, o Android já tinha uma API de serviços criptográficos simples e com suporte a hardware, fornecida pelas versões 0.2 e 0.3 da camada de abstração de hardware (HAL) do Keymaster. O keystore fornece operações de assinatura e verificação digitais, 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 alcançados facilmente com apenas uma API de assinatura. O keystore no Android 6.0 expandiu a API Keystore para oferecer uma gama mais ampla de recursos.
No Android 6.0, o Keystore adicionou primitivas criptográficas simétricas, AES e HMAC, além de um sistema de controle de acesso para chaves protegidas por hardware. Os controles de acesso são especificados durante a geração de chaves e aplicados durante o ciclo de vida da chave. As chaves podem ser restringidas para uso somente após a autenticação do usuário e apenas para fins específicos ou com parâmetros criptográficos específicos. Para mais informações, consulte a página Tags de autorização.
Além de expandir o intervalo de 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, reduzindo o risco de comprometimento da segurança devido ao uso indevido de chaves
- Um esquema de controle de acesso para permitir a restrição de chaves a usuários e clientes especificados e um período definido
No Android 7.0, o Keymaster 2 adicionou suporte para atestado de chaves 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 dos controles de acesso para verificar remotamente a existência da chave em hardware seguro e a configuração.
A vinculação de versão vincula as chaves ao sistema operacional e à versão do nível do patch. Isso garante que um invasor que descubra uma vulnerabilidade em uma versão antiga do sistema ou do software TEE não possa retornar um dispositivo à versão vulnerável e usar 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 ser usada, e a versão anterior da chave é invalidada. À medida que o dispositivo é atualizado, as chaves "engrengam" com o dispositivo, mas qualquer reversão do dispositivo para uma versão anterior faz com que as chaves fiquem 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 HAL C++ gerada a partir de uma definição na nova linguagem de definição de interface de hardware (HIDL, na sigla em inglês). Como parte da mudança, muitos dos tipos de argumentos foram alterados, embora os tipos e métodos tenham uma correspondência um a um com os tipos antigos e os métodos de estrutura HAL.
Além dessa revisão da interface, o Android 8.0 estendeu o recurso de atestado do Keymaster 2 para oferecer suporte ao atestado de ID. O atestado de identidade oferece um mecanismo limitado e opcional para atestados fortes de identificadores de hardware, como número de série do dispositivo, nome do produto e ID do smartphone (IMEI / MEID). Para implementar essa adição, o Android 8.0 mudou o esquema de atestado ASN.1 para adicionar atestado de identidade. As implementações do Keymaster precisam encontrar uma maneira segura de extrair os itens de dados relevantes e definir um mecanismo para desativar o recurso de forma segura e permanente.
No Android 9, as atualizações incluíam:
- Atualize para Keymaster 4.
- Suporte para elementos Secure incorporados
- Suporte para importação de chave segura
- Suporte para criptografia 3DES
- Mudanças na vinculação de versões para que boot.img e system.img tenham versões definidas separadamente para permitir atualizações independentes
Glossário
Confira uma visão geral rápida dos componentes do Keystore e das relações deles.
O AndroidKeystore é a API e o componente do Android Framework usados
por apps para acessar a funcionalidade do Keystore. Ele é implementado como uma extensão das
APIs padrão da Java Cryptography Architecture e consiste em código Java que
é executado no espaço de processo do próprio app. O AndroidKeystore
atende às solicitações
do app para o comportamento do keystore encaminhando-as para o daemon do keystore.
O daemon do keystore é um daemon do sistema Android que fornece acesso a todas as funcionalidades do keystore com uma API Binder. Ele é responsável por armazenar "blobs de chave", que contêm o material da chave secreta real, criptografado para que o Keystore possa armazená-los, mas não usá-los nem revelá-los.
O keymasterd é um servidor HIDL que fornece acesso ao TA do Keymaster. Esse nome não é padronizado e é usado para fins conceituais.
O Keymaster TA (app confiável) é o software executado em um contexto seguro, geralmente no TrustZone em um SoC ARM, que fornece todas as operações seguras do keystore, tem acesso ao material de chave bruto, valida todas as condições de controle de acesso em chaves etc.
O LockSettingsService é o componente do sistema Android responsável
pela autenticação do usuário, tanto por senha quanto por impressão digital. Ele não faz parte do
Keystore, mas é relevante porque muitas operações de chave do Keystore exigem a autenticação
do usuário. O LockSettingsService
interage com o TA do Gatekeeper
e o TA da impressão digital para receber tokens de autenticação, que são fornecidos ao
daemon do keystore e, por fim, consumidos pelo app TA
do Keymaster.
O TA do gatekeeper (app confiável) é outro componente executado no contexto seguro, responsável por autenticar as senhas do usuário e gerar tokens de autenticação usados para provar ao TA do Keymaster que uma autenticação foi feita para um usuário específico em um determinado momento.
O TA de impressão digital (app confiável) é outro componente executado no contexto seguro, responsável por autenticar as impressões digitais do usuário e gerar tokens de autenticação usados para provar ao TA do Keymaster que uma autenticação foi feita para um usuário específico em um determinado momento.
Arquitetura
A API Android Keystore e a HAL do Keymaster fornecem um conjunto básico, mas adequado, de primitivas criptográficas para permitir a implementação de protocolos usando chaves controladas por hardware.
O HAL do Keymaster é uma biblioteca de carregamento dinâmico fornecida pelo OEM e usada pelo serviço de keystore para fornecer serviços criptográficos com suporte de hardware. Para manter as coisas seguras, as implementações do HAL não executam operações sensíveis no espaço do usuário ou mesmo no espaço do kernel. As operações sensíveis são delegadas a um processador seguro acessado por alguma interface do kernel. A arquitetura resultante é assim:
![Acesso ao Keymaster](https://source.android.google.cn/static/docs/security/images/access-to-keymaster.png?authuser=002&hl=pt-br)
Figura 1. Acesso ao Keymaster
Em um dispositivo Android, o "cliente" do HAL do Keymaster consiste em várias camadas (por exemplo, app, framework, daemon do Keystore), mas isso pode ser ignorado para os fins 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 apps. A API de nível superior é descrita no site para desenvolvedores Android.
O objetivo do HAL do Keymaster não é implementar os algoritmos sensíveis à segurança, mas apenas encaminhar e decodificar solicitações para o mundo seguro. O formato de fiação é definido pela implementação.
Compatibilidade com versões anteriores
A HAL Keymaster 1 é totalmente incompatível com as HALs lançadas anteriormente, por exemplo, Keymaster 0.2 e 0.3. Para facilitar a interoperabilidade em dispositivos com o Android 5.0 e versões anteriores lançados com as HALs Keymaster mais antigas, o Keystore oferece um adaptador que implementa a HAL Keymaster 1 com chamadas para a biblioteca de hardware existente. O resultado não pode fornecer o conjunto completo de funcionalidades no HAL do Keymaster 1. Ele só oferece suporte a algoritmos RSA e ECDSA, e toda a aplicação de autorização de chaves é realizada 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 entradas. Isso reduz o número de viagens de ida e volta para o TEE em
casos em que a entrada está disponível de uma só vez e simplifica a implementação da
descriptografia AEAD.
No Android 8.0, o Keymaster 3 fez a transição da HAL de estrutura C
antigo para a interface HAL C++, gerada a partir de uma definição na nova
Linguagem de definição de interface de hardware (HIDL, na sigla em inglês). Uma implementação HAL
do novo estilo é criada subclassificando a classe
IKeymasterDevice
gerada e implementando os métodos
virtuais puros. Como parte da mudança, muitos dos tipos de argumentos foram alterados,
embora os tipos e métodos tenham uma correspondência um a um com os tipos
antigos e os métodos de estrutura HAL.
Visão geral da HIDL
A linguagem de definição de interface de hardware (HIDL) fornece um mecanismo de implementação independente de linguagem para especificar interfaces de hardware. No momento, a ferramenta HIDL oferece suporte à geração de interfaces C++ e Java. Esperamos que a maioria dos implementadores do ambiente de execução confiável (TEE) ache a ferramenta C++ mais conveniente. Portanto, esta página discute apenas a representação em C++.
As interfaces HIDL consistem em um conjunto de métodos, expressos como:
methodName(INPUT ARGUMENTS) generates (RESULT ARGUMENTS);
Há vários tipos predefinidos, e as HALs podem definir novos tipos enumerados e de estrutura. Para mais detalhes sobre o HIDL, consulte a seção de 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);
Esse é o equivalente do seguinte da HAL keymaster2:
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
é removido porque é
implícito. O argumento params
não é mais um struct que contém um
ponteiro que faz referência a uma matriz de objetos key_parameter_t
, mas um
vec
(vetor) que contém 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;
Em que 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 do HAL substitui esse
método generateKey
e chama o ponteiro de função
generateKey_cb
para retornar o resultado da operação ao autor da chamada. A chamada de ponteiro de função é síncrona. O autor da chamada 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 retorna ao autor da chamada.
Para conferir um exemplo detalhado, consulte a implementação padrão em
hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp
.
A implementação padrão oferece 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 app tem o próprio namespace. Mas para cada regra, há uma exceção. O keystore tem alguns mapas codificados que permitem que determinados componentes do sistema acessem outros namespace. Esse é um instrumento muito simples, porque dá a um componente o controle total sobre outro namespace. Além disso, há a questão dos componentes do fornecedor como clientes do Keystore. No momento, não temos como estabelecer um espaço para componentes do fornecedor, por exemplo, o supplicant do WPA.
Para acomodar os componentes do fornecedor e generalizar o controle de acesso sem exceções codificadas, o Keystore 2.0 apresenta domínios e namespaces SELinux.
Domínios do keystore
Com os domínios do keystore, podemos separar os namespaces dos UIDs. Os clientes que acessam uma chave no Keystore precisam especificar o domínio, o namespace e o alias que querem acessar. Com base nessa tupla e na identidade do autor da chamada, podemos determinar qual chave o autor da chamada quer acessar e se ela tem as permissões adequadas.
Apresentamos cinco parâmetros de domínio que governam como as chaves podem ser acessadas. Elas controlam a semântica do parâmetro de namespace do descritor de chave e como o controle de acesso é realizado.
DOMAIN_APP
: o domínio do app abrange o comportamento legado. O SPI do Java Keystore usa esse domínio por padrão. Quando esse domínio é usado, o argumento de namespace é ignorado e o UID do autor da chamada é usado. O acesso a esse domínio é controlado pelo rótulo do keystore para a classekeystore_key
na política do SELinux.DOMAIN_SELINUX
: esse domínio indica que o namespace tem um rótulo na política do SELinux. O parâmetro de namespace é procurado e convertido em um contexto de destino, e uma verificação de permissão é realizada para o contexto de chamada do SELinux para a classekeystore_key
. Quando a permissão é estabelecida para a operação, a tupla completa é usada para a pesquisa de chave.DOMAIN_GRANT
: o domínio de concessão indica que o parâmetro de namespace é um identificador de concessão. O parâmetro de alias é ignorado. As verificações do SELinux são realizadas quando a concessão é criada. O controle de acesso adicional verifica apenas se o UID do autor da chamada corresponde ao UID dos beneficiários do acesso solicitado.DOMAIN_KEY_ID
: este domínio indica que o parâmetro de namespace é um ID de chave exclusivo. A chave pode ter sido criada comDOMAIN_APP
ouDOMAIN_SELINUX
. A verificação de permissão é realizada depois que odomain
e onamespace
foram carregados do banco de dados de chaves da mesma forma que se o blob fosse carregado pelo tupla de domínio, namespace e alias. O motivo do domínio do ID da chave é a continuidade. Ao acessar uma chave por alias, as chamadas subsequentes podem operar em chaves diferentes, porque uma nova chave pode ter sido gerada ou importada e vinculada a essa alias. No entanto, o ID da chave nunca muda. Portanto, ao usar uma chave por ID de chave depois de ser carregada do banco de dados do keystore usando o alias uma vez, é possível ter certeza de que é a mesma chave, desde que o ID de chave ainda exista. Essa funcionalidade não é exposta aos desenvolvedores de apps. Em vez disso, ele é usado no SPI do Keystore do Android para oferecer uma experiência mais consistente, mesmo quando usado simultaneamente de forma não segura.DOMAIN_BLOB
: o domínio do blob indica que o autor da chamada gerencia o blob por conta própria. Isso é usado para clientes que precisam acessar o keystore antes que a partição de dados seja montada. O blob de chave é incluído no campoblob
do descritor de chave.
Usando o domínio SELinux, podemos conceder aos componentes do fornecedor acesso a namespaces de Keystore muito específicos, que podem ser compartilhados por 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 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 conceder acesso a ele
adicionando uma política adequada. Por exemplo, para permitir que
wpa_supplicant
receba e use chaves no novo namespace, adicione
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 precisa ser especificado. Para
carregar e importar chaves do e para o Keystore, o ID do namespace é especificado
usando 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 precisa 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 do Keystore 2.0 SELinux. Cada partição tem um intervalo reservado diferente de 10.000 IDs de namespace para evitar colisões.
Partição | Intervalo | Arquivos de configuração |
---|---|---|
Sistema | 0 a 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 |
Produto | 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"
, pelo ID numérico.
Além disso, os seguintes namespaces foram definidos. Se elas substituem regras especiais, a tabela a seguir indica o UID a que elas correspondem.
ID do namespace | Rótulo SEPolicy | UID | Descrição |
---|---|---|---|
0 | su_key | N/A | Chave de superusuário. Usado apenas para testes em builds userdebug e eng. Não é relevante para builds de usuários. |
1 | shell_key | N/A | Namespace disponível para o shell. Usado principalmente para testes, mas também pode ser usado em builds do usuário na linha de comando. |
100 | vold_key | N/A | Destinado ao uso pelo vold. |
101 | odsing_key | N/A | Usado pelo daemon de assinatura no dispositivo. |
102 | wifi_key | AID_WIFI(1010) | Usado pelo subsistema de Wi-Fi do Android, incluindo wpa_supplicant. |
120 | resume_on_reboot_key | AID_SYSTEM(1000) | Usado pelo servidor do sistema do Android para oferecer suporte à retomada na reinicialização. |
Vetores de acesso
A classe keystore_key
do SELinux envelheceu um pouco, e algumas
das permissões, como verify
ou sign
, perderam
o significado. Confira o novo conjunto de permissões, keystore2_key
,
que o Keystore 2.0 exige.
Permissão | Significado |
---|---|
delete
|
Verificado ao remover chaves do keystore. |
get_info
|
É marcada quando os metadados de uma chave são solicitados. |
grant
|
O autor da chamada precisa dessa permissão para criar uma concessão à chave no contexto alvo. |
manage_blob
|
O autor da chamada pode usar DOMAIN_BLOB no namespace SELinux fornecido,
gerenciando blobs por conta própria. Isso é útil especificamente para
vold. |
rebind
|
Essa permissão controla se um alias pode ser vinculado a uma nova chave. Isso é necessário para a inserção e implica que a chave vinculada anteriormente é excluída. É 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 removíveis, e a criação de operações nunca falha, a menos que todos os slots de operação sejam ocupados por operações não removíveis. |
update
|
Obrigatório para atualizar o subcomponente de uma chave. |
use
|
É marcada quando uma operação Keymint que usa o material da chave é criada, por exemplo, para assinatura, criptografia ou 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 na
classe de segurança keystore2
do SELinux:
Permissão | Significado |
---|---|
add_auth
|
Obrigatório pelo provedor de autenticação, como Gatekeeper ou BiometricsManager, para adicionar tokens de autenticação. |
clear_ns
|
Anteriormente chamada de clear_uid, essa permissão permite que um usuário que não é proprietário de um namespace exclua todas as chaves desse namespace. |
list
|
É necessário para que o sistema enumere chaves por várias propriedades, como
propriedade ou limite de autenticação. Essa permissão não é necessária para autores da chamada
que enumeram os 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 não possam ser criadas. |
reset
|
Essa permissão permite redefinir o Keystore para a configuração original, excluindo todas as chaves que não são vitais para o funcionamento do SO Android. |
unlock
|
Essa permissão é necessária para tentar desbloquear a chave mestra para chaves vinculadas à autenticação. |