Como a maioria dos softwares de criptografia de disco e de arquivos, a criptografia de armazenamento do Android tradicionalmente depende da presença das chaves de criptografia brutas na memória do sistema para que a criptografia possa ser realizada. Mesmo quando a criptografia é realizada por hardware dedicado em vez de software, o software geralmente ainda precisa gerenciar as chaves de criptografia brutas.
Tradicionalmente, isso não é visto como um problema porque as chaves não estão presentes durante um ataque off-line, que é o principal tipo de ataque contra o qual a criptografia de armazenamento se destina a proteger. No entanto, há uma vontade de oferecer mais proteção contra outros tipos de ataques, como ataques de inicialização fria e ataques on-line em que um invasor pode vazar a memória do sistema sem comprometer totalmente o dispositivo.
Para resolver esse problema, o Android 11 introduziu suporte para chaves encapsuladas em hardware, quando houver suporte de hardware. Chaves encapsuladas em hardware são chaves de armazenamento conhecidas apenas na forma bruta para hardware dedicado. O software só vê e trabalha com essas chaves na forma encapsulada (criptografada). Esse hardware precisa ser capaz de gerar e importar chaves de armazenamento, agrupar chaves de armazenamento em formas temporárias e de longo prazo, extrair subchaves, programar diretamente uma subchave em um mecanismo de criptografia inline e retornar uma subchave separada para o software.
Observação:um mecanismo de criptografia inline (ou hardware de criptografia inline) se refere a hardware que criptografa/descriptografa dados enquanto está indo para/vindo do dispositivo de armazenamento. Geralmente, é um controlador de host UFS ou eMMC que implementa as extensões de criptografia definidas pela especificação JEDEC correspondente.
Design
Esta seção apresenta o design do recurso de chaves protegidas por hardware, incluindo qual suporte de hardware é necessário. Esta discussão se concentra na criptografia baseada em arquivos (FBE, na sigla em inglês), mas a solução também se aplica à criptografia de metadados.
Uma maneira de evitar a necessidade de chaves de criptografia brutas na memória do sistema é mantê-las somente nos keyslots de um mecanismo criptográfico inline. No entanto, essa abordagem apresenta alguns problemas:
- O número de chaves de criptografia pode exceder o número de slots de chaves.
- Os mecanismos de criptografia inline geralmente perdem o conteúdo dos keyslots se o controlador de armazenamento (geralmente UFS ou eMMC) for redefinido. A redefinição do controlador de armazenamento é um procedimento padrão de recuperação de erros que é executado se determinados tipos de erros de armazenamento ocorrerem, e esses erros podem ocorrer a qualquer momento. Portanto, quando a criptografia inline está sendo usada, o sistema operacional precisa estar sempre pronto para reprogramar os slots de chave sem intervenção do usuário.
- Os mecanismos criptográficos em linha só podem ser usados para criptografar/descriptografar blocos completos de dados em disco. No entanto, no caso da FBE, o software ainda precisa ser capaz de fazer outros trabalhos criptográficos, como criptografia de nomes de arquivos e derivação de identificadores de chaves. O software ainda precisaria de acesso às chaves FBE brutas para fazer esse outro trabalho.
Para evitar esses problemas, as chaves de armazenamento são transformadas em chaves encapsuladas por hardware, que só podem ser descompactadas e usadas por hardware dedicado. Isso permite que um número ilimitado de chaves seja aceito. Além disso, a hierarquia de chaves é modificada e parcialmente movida para esse hardware, o que permite que uma subchave seja retornada ao software para tarefas que não podem usar um mecanismo de criptografia inline.
Hierarquia de chaves
As chaves podem ser derivadas de outras chaves usando uma função de derivação de chave (KDF, na sigla em inglês), como HKDF, o que resulta em uma hierarquia de chaves.
O diagrama a seguir mostra uma hierarquia de chaves típica para FBE quando as chaves envoltas em hardware não são usadas:
A chave de classe FBE é a chave de criptografia bruta que o Android transmite ao kernel do Linux para desbloquear um conjunto específico de diretórios criptografados, como o armazenamento criptografado por credenciais de um usuário específico do Android. No kernel, essa chave é chamada de chave mestra do fscrypt. A partir dessa chave, o kernel deriva as seguintes subchaves:
- O identificador da chave. Ele não é usado para criptografia, mas é um valor usado para identificar a chave com que um arquivo ou diretório específico é protegido.
- A chave de criptografia do conteúdo do arquivo
- A chave de criptografia dos nomes de arquivos
Em contraste, o diagrama a seguir mostra a hierarquia de chaves para FBE quando chaves de hardware são usadas:
Em comparação com o caso anterior, um nível adicional foi adicionado à hierarquia de chaves, e a chave de criptografia do conteúdo do arquivo foi realocada. O nó raiz ainda representa a chave que o Android transmite ao Linux para desbloquear um conjunto de diretórios criptografados. No entanto, agora essa chave está em um formato encapsulado temporário e, para ser usada, ela precisa ser transmitida para hardware dedicado. Esse hardware precisa implementar duas interfaces que usam uma chave encapsulada temporariamente:
- Uma interface para derivar
inline_encryption_key
e programá-lo diretamente em uma chave do mecanismo de criptografia inline. Isso permite que o conteúdo do arquivo seja criptografado/descriptografado sem que o software tenha acesso à chave bruta. Nos kernels comuns do Android, essa interface corresponde à operaçãoblk_crypto_ll_ops::keyslot_program
, que precisa ser implementada pelo driver de armazenamento. - Uma interface para derivar e retornar
sw_secret
("chave secreta do software", também chamada de "secreta bruta" em alguns lugares), que é a chave usada pelo Linux para derivar subchaves para tudo que não seja a criptografia do conteúdo do arquivo. Nos kernels comuns do Android, essa interface corresponde à operaçãoblk_crypto_ll_ops::derive_sw_secret
, que precisa ser implementada pelo driver de armazenamento.
Para derivar inline_encryption_key
e sw_secret
da
chave de armazenamento bruta, o hardware precisa usar uma KDF com criptografia forte. Esse KDF
precisa seguir as práticas recomendadas de criptografia. Ele precisa ter uma força de segurança de pelo
menos 256 bits, o suficiente para qualquer algoritmo usado mais tarde. Ela também precisa usar um rótulo distinto, um contexto e uma string de informações específicas do app ao derivar cada tipo de subchave para garantir que as subchaves resultantes sejam criptograficamente isoladas, ou seja, o conhecimento de uma delas não revela nenhuma outra. O alongamento de chave não é necessário, porque a chave de armazenamento bruta já é uma
chave uniformemente aleatória.
Tecnicamente, qualquer KDF que atenda aos requisitos de segurança pode ser usado.
No entanto, para fins de teste, é necessário implementar novamente o mesmo KDF no
código de teste. Atualmente, um KDF foi analisado e implementado. Ele pode ser encontrado
no código-fonte de vts_kernel_encryption_test
.
Recomendamos que o hardware use essa KDF, que usa o NIST SP 800-108 "KDF em modo de contador" com AES-256-CMAC como PRF. Para ser compatível, todas
as partes do algoritmo precisam ser idênticas, incluindo a escolha de contextos de KDF
e rótulos para cada subchave.
Encapsulamento de chaves
Para atender aos objetivos de segurança das chaves protegidas por hardware, dois tipos de chaves protegidas são definidos:
- Encapsulamento temporário: o hardware criptografa a chave bruta usando uma chave gerada aleatoriamente em cada inicialização e que não é exposta diretamente fora do hardware.
- Envolvimento de longo prazo: o hardware criptografa a chave bruta usando uma chave exclusiva e persistente integrada ao hardware que não é diretamente exposta fora dele.
Todas as chaves transmitidas ao kernel do Linux para desbloquear o armazenamento são encapsuladas de forma temporária. Isso garante que, se um invasor conseguir extrair uma chave em uso da memória do sistema, ela não poderá ser usada fora do dispositivo, mas também no dispositivo após uma reinicialização.
Ao mesmo tempo, o Android ainda precisa armazenar uma versão criptografada das chaves no disco para que elas possam ser desbloqueadas. As chaves brutas funcionam para esse fim. No entanto, é recomendável que as chaves brutas nunca estejam presentes na memória do sistema para que nunca possam ser extraídas para uso fora do dispositivo, mesmo que sejam extraídas durante a inicialização. Por esse motivo, o conceito de agrupamento de longo prazo é definido.
Para oferecer suporte ao gerenciamento de chaves unidas dessas duas maneiras diferentes, o hardware precisa implementar as seguintes interfaces:
- Interfaces para gerar e importar chaves de armazenamento, retornando-as no
formato encapsulado de longo prazo. Essas interfaces são acessadas indiretamente pelo
KeyMint e correspondem à tag
TAG_STORAGE_KEY
do KeyMint. A capacidade "gerar" é usada porvold
para gerar novas chaves de armazenamento para uso pelo Android, enquanto a capacidade "importar" é usada porvts_kernel_encryption_test
para importar chaves de teste. - Uma interface para converter uma chave de armazenamento encapsulada de longo prazo em uma
chave de armazenamento encapsulada de forma temporária. Isso corresponde ao
método KeyMint
convertStorageKeyToEphemeral
. Esse método é usado porvold
evts_kernel_encryption_test
para desbloquear o armazenamento.
O algoritmo de agrupamento de chaves é um detalhe de implementação, mas precisa usar um AEAD forte, como AES-256-GCM com IVs aleatórios.
Mudanças necessárias no software
O AOSP já tem uma estrutura básica para oferecer suporte a chaves encapsuladas em hardware. Isso
inclui o suporte em componentes do espaço do usuário, como vold
, bem como
o suporte do kernel do Linux em blk-crypto, fscrypt e
dm-default-key.
No entanto, são necessárias algumas mudanças específicas da implementação.
Mudanças no KeyMint
A implementação do KeyMint do dispositivo precisa ser modificada para oferecer suporte a
TAG_STORAGE_KEY
e implementar o
método convertStorageKeyToEphemeral
.
No Keymaster, exportKey
foi usado em vez de
convertStorageKeyToEphemeral
.
Mudanças no kernel do Linux
O driver do kernel do Linux para o mecanismo de criptografia inline do dispositivo precisa ser modificado para oferecer suporte a chaves encapsuladas por hardware.
Para kernels android14
e mais recentes,
defina BLK_CRYPTO_KEY_TYPE_HW_WRAPPED
em blk_crypto_profile::key_types_supported
,
faça com que blk_crypto_ll_ops::keyslot_program
e blk_crypto_ll_ops::keyslot_evict
ofereçam suporte à programação/remoção de chaves envoltas em hardware
e implemente blk_crypto_ll_ops::derive_sw_secret
.
Para kernels android12
e android13
,
defina BLK_CRYPTO_FEATURE_WRAPPED_KEYS
em blk_keyslot_manager::features
,
faça com que blk_ksm_ll_ops::keyslot_program
e blk_ksm_ll_ops::keyslot_evict
ofereçam suporte à programação/remoção de chaves agrupadas por hardware
e implemente blk_ksm_ll_ops::derive_raw_secret
.
Para kernels android11
,
defina BLK_CRYPTO_FEATURE_WRAPPED_KEYS
em keyslot_manager::features
,
faça com que keyslot_mgmt_ll_ops::keyslot_program
e keyslot_mgmt_ll_ops::keyslot_evict
ofereçam suporte à programação/remoção de chaves envoltas em hardware
e implemente keyslot_mgmt_ll_ops::derive_raw_secret
.
Teste
Embora a criptografia com chaves encapsuladas em hardware seja mais difícil de testar do que a criptografia
com chaves padrão, ainda é possível fazer isso importando uma chave de teste e
reimplementando a derivação de chaves feita pelo hardware. Isso é implementado
em vts_kernel_encryption_test
. Para executar esse teste,
execute:
atest -v vts_kernel_encryption_test
Leia o registro de teste e verifique se os casos de teste de chave encapsulada por hardware (por exemplo,
FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy
e
DmDefaultKeyTest.TestHwWrappedKey
) não foram ignorados devido à detecção
do suporte a chaves encapsuladas por hardware, já que os resultados do teste ainda são
"aprovados" nesse caso.
Ativar chaves
Quando o suporte à chave de hardware do dispositivo estiver funcionando corretamente, você poderá
fazer as seguintes mudanças no arquivo fstab
do dispositivo para que
o Android o use para criptografia de chaves de hardware e de metadados:
- FBE: adicione a flag
wrappedkey_v0
ao parâmetrofileencryption
. Por exemplo, usefileencryption=::inlinecrypt_optimized+wrappedkey_v0
. Para mais detalhes, consulte a documentação do FBE. - Criptografia de metadados: adicione a flag
wrappedkey_v0
ao parâmetrometadata_encryption
. Por exemplo, usemetadata_encryption=:wrappedkey_v0
. Para mais detalhes, consulte a documentação sobre criptografia de metadados.