Esta página fornece mais detalhes e diretrizes para ajudar os implementadores da camada de abstração de hardware (HAL) do KeyMint. A documentação principal do HAL é a especificação da interface AIDL.
Uso indevido da API
Os autores de chamadas podem criar chaves KeyMint com autorizações válidas como parâmetros de API, mas que tornam as chaves resultantes não seguras ou inutilizáveis. As implementações do KeyMint não precisam falhar nesses casos ou emitir um diagnóstico. O uso de chaves muito pequenas, a especificação de parâmetros de entrada irrelevantes, a reutilização de IVs ou valores de uso único, a geração de chaves sem propósito (ou seja, inúteis) e assim por diante não podem ser diagnosticados por implementações.
É responsabilidade dos apps, do framework e do Android Keystore garantir que as chamadas para módulos KeyMint sejam sensatas e úteis.
Ponto de entrada addRngEntropy
O ponto de entrada addRngEntropy
adiciona a entropia fornecida pelo autor da chamada ao
pool usado pela implementação do KeyMint para gerar números aleatórios, chaves
e IVs.
As implementações do KeyMint precisam misturar com segurança a entropia fornecida no
pool, que também precisa conter entropia gerada internamente de um gerador de números aleatórios
de hardware. A mistura precisa ser processada para que um invasor que tenha
controle total dos bits fornecidos por addRngEntropy
ou dos
gerados por hardware (mas não ambos) não tenha uma vantagem significativa na
previsão dos bits gerados pelo pool de entropia.
Principais características
Cada um dos mecanismos (generateKey
, importKey
e importWrappedKey
) que criam chaves KeyMint retorna as características da chave
recém-criada, divididas adequadamente nos níveis de segurança
que aplicam cada característica. As características retornadas incluem todos
os parâmetros especificados para a criação de chaves,
exceto Tag::APPLICATION_ID
e Tag::APPLICATION_DATA
.
Se essas tags forem incluídas nos parâmetros de chave, elas serão removidas das
características retornadas para que não seja possível encontrar os valores delas
analisando o keyblob retornado. No entanto, eles são criptograficamente vinculados ao
blob de chave. Se os valores corretos não forem fornecidos quando a chave for
usada, o uso falhará. Da mesma forma,
Tag::ROOT_OF_TRUST
é
vinculado criptograficamente à chave, mas não pode ser especificado durante
a criação ou importação de chaves e nunca é retornado.
Além das tags fornecidas, a implementação do KeyMint também
adiciona Tag::ORIGIN
, indicando a maneira como a chave foi
criada (KeyOrigin::GENERATED
,
KeyOrigin::IMPORTED
ou KeyOrigin::SECURELY_IMPORTED
).
Resistência a reversão
A resistência a reversão é indicada por Tag::ROLLBACK_RESISTANCE
e significa que, depois que uma chave é excluída com deleteKey
ou deleteAllKeys
, o hardware seguro
garante que ela nunca mais será usada.
As implementações do KeyMint retornam o material de chave gerado ou importado para o autor da chamada como um keyblob, um formulário criptografado e autenticado. Quando o Keystore exclui o keyblob, a chave é excluída, mas um invasor que conseguiu recuperar o material principal pode restaurá-lo no dispositivo.
Uma chave é resistente a reversão se o hardware seguro garante que as chaves excluídas não possam ser restauradas mais tarde. Isso geralmente é feito armazenando metadados de chave adicionais em um local confiável que não pode ser manipulado por um invasor. Em dispositivos móveis, o mecanismo usado para isso geralmente é blocos de memória protegidos contra repetição (RPMB, na sigla em inglês). Como o número de chaves que podem ser criadas é essencialmente ilimitado e o armazenamento confiável usado para a resistência à reversão pode ser limitado em tamanho, a implementação pode falhar nas solicitações para criar chaves resistentes à reversão quando o armazenamento estiver cheio.
begin
O ponto de entrada begin()
inicia uma operação criptográfica usando a
chave especificada, para a finalidade especificada, com os parâmetros especificados (conforme
apropriado). Ele retorna um novo objeto Binder IKeyMintOperation
usado para concluir a operação. Além disso, um valor de desafio é
retornado e usado como parte do token de autenticação em operações
autenticadas.
Uma implementação do KeyMint oferece suporte a pelo menos 16 operações
simultâneas. O keystore usa até 15, deixando uma para vold
usar para criptografia
de senha. Quando o Keystore tem 15 operações em andamento (begin()
foi
chamado, mas finish
ou abort
não foram
chamados) e recebe uma solicitação para iniciar a 16ª, ele chama
abort()
na operação menos usada recentemente para reduzir o número de
operações ativas para 14 antes de chamar begin()
para iniciar a
operação recém-solicitada.
Se Tag::APPLICATION_ID
ou Tag::APPLICATION_DATA
foram especificados durante a geração ou importação de chaves, as chamadas para begin()
precisam
incluir essas tags com os valores especificados originalmente no
argumento params
para este método.
Tratamento de erros
Se um método em um IKeyMintOperation
retornar um código de erro diferente
de ErrorCode::OK
, a operação será abortada e o objeto
Binder da operação será invalidado. Qualquer uso futuro do objeto
retorna ErrorCode::INVALID_OPERATION_HANDLE
.
Aplicação de autorização
A aplicação de autorização de chave é realizada principalmente
em begin()
. A única exceção é o caso em que a chave
tem um ou mais valores Tag::USER_SECURE_ID
e
não tem um valor Tag::AUTH_TIMEOUT
.
Nesse caso, a chave exige uma autorização por operação, e os métodos update()
ou finish()
recebem um token de autenticação no argumento
authToken
. Para garantir que o token seja válido, a implementação do KeyMint:
- Verifica a assinatura HMAC no token de autenticação.
- Verifica se o token contém um ID de usuário seguro que corresponde a um associado à chave.
- Verifica se o tipo de autenticação do token corresponde ao
Tag::USER_AUTH_TYPE
da chave. - Verifica se o token contém o valor do desafio para a operação atual no campo de desafio.
Se essas condições não forem atendidas, o KeyMint
vai retornar ErrorCode::KEY_USER_NOT_AUTHENTICATED
.
O autor da chamada fornece o token de autenticação para cada chamada para update()
e
finish()
. A implementação só pode validar o token uma vez.