Funções do KeyMint

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 da HAL é a especificação da interface AIDL.

Uso indevido da API

Os chamadores podem criar chaves KeyMint com autorizações válidas como parâmetros de API, mas que tornam as chaves resultantes inseguras ou inutilizáveis. As implementações do KeyMint não precisam falhar nesses casos nem 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 nonces, a geração de chaves sem finalidades (portanto, inúteis) e semelhantes não devem 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 entropia fornecida pelo chamador ao pool usado pela implementação do KeyMint para gerar números aleatórios, para 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 por um gerador de números aleatórios de hardware. A combinação precisa ser processada para que um invasor com controle total dos bits fornecidos por addRngEntropy ou dos bits 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 do 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 principais, elas serão removidas das características retornadas para que não seja possível encontrar os valores delas examinando o keyblob retornado. No entanto, eles são vinculados criptograficamente ao keyblob. Assim, se os valores corretos não forem fornecidos quando a chave for usada, o uso vai falhar. Da mesma forma, Tag::ROOT_OF_TRUST é vinculado criptograficamente à chave, mas não pode ser especificado durante a criação ou importação da chave 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 poderá ser usada.

As implementações do KeyMint retornam o material da chave gerada ou importada para o chamador como um keyblob, um formato criptografado e autenticado. Quando o Keystore exclui o keyblob, a chave desaparece, mas um invasor que conseguiu recuperar o material da chave pode restaurá-lo no dispositivo.

Uma chave é resistente a rollback se o hardware seguro garantir que as chaves excluídas não possam ser restauradas posteriormente. 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 é o RPMB (replay protected memory blocks). Como o número de chaves que podem ser criadas é essencialmente ilimitado e o armazenamento confiável usado para resistência a rollback pode ter tamanho limitado, a implementação pode falhar em solicitações para criar chaves resistentes a rollback 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 um para o vold usar na criptografia de senhas. 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 uma 16ª, ele chama abort() na operação usada há menos tempo 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 esse 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á cancelada e o objeto Binder da operação será invalidado. Qualquer uso futuro do objeto retorna ErrorCode::INVALID_OPERATION_HANDLE.

Aplicação da autorização

A aplicação da autorização de chave é feita principalmente em begin(). A única exceção é quando a chave tem um ou mais valores de Tag::USER_SECURE_ID, mas não tem um valor de 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 "challenge".

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 de update() e finish(). A implementação só pode validar o token uma vez.