Chaves encapsuladas em hardware

Como a maioria dos softwares de criptografia de disco e arquivo, a criptografia de armazenamento do Android tradicionalmente depende da presença de chaves de criptografia brutas na memória do sistema para que a criptografia possa ser executada. Mesmo quando a criptografia é realizada por hardware dedicado e não por 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 estarão presentes durante um ataque offline, que é o principal tipo de ataque contra o qual a criptografia de armazenamento se destina a proteger. No entanto, há um desejo de fornecer maior proteção contra outros tipos de ataques, como ataques de inicialização a frio e ataques online em que um invasor pode vazar memória do sistema sem comprometer totalmente o dispositivo.

Para resolver esse problema, o Android 11 introduziu o suporte para chaves encapsuladas em hardware , onde o suporte de hardware está presente. Chaves encapsuladas em hardware são chaves de armazenamento que são conhecidas apenas na forma bruta para hardware dedicado; o software só vê e trabalha com essas chaves em forma encapsulada (criptografada). Esse hardware deve ser capaz de gerar e importar chaves de armazenamento, agrupar chaves de armazenamento em formas efêmeras e de longo prazo, derivar subchaves, programar diretamente uma subchave em um mecanismo de criptografia embutido e retornar uma subchave separada ao software.

Nota : Um mecanismo de criptografia em linha (ou hardware de criptografia em linha ) refere-se ao hardware que criptografa/descriptografa dados enquanto estão a caminho do dispositivo de armazenamento. Normalmente, este é um controlador de host UFS ou eMMC que implementa as extensões de criptografia definidas pela especificação JEDEC correspondente.

Projeto

Esta seção apresenta o design do recurso de chaves encapsuladas em hardware, incluindo qual suporte de hardware é necessário para ele. Esta discussão se concentra na criptografia baseada em arquivo (FBE), 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 seria mantê-las apenas nos slots de chaves de um mecanismo de criptografia em linha. No entanto, esta abordagem esbarra em alguns problemas:

  • O número de chaves de criptografia pode exceder o número de slots de chaves.
  • Os mecanismos de criptografia em linha só podem ser usados ​​para criptografar/descriptografar blocos completos de dados em disco. No entanto, no caso do FBE, o software ainda precisa ser capaz de fazer outro trabalho criptográfico, como criptografia de nomes de arquivos e derivação de identificadores de chave. 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 em hardware , que só podem ser descompactadas e usadas por hardware dedicado. Isso permite que um número ilimitado de chaves seja suportado. Além disso, a hierarquia de chaves é modificada e parcialmente movida para esse hardware, o que permite que uma subchave seja devolvida ao software para tarefas que não podem usar um mecanismo de criptografia embutido.

Hierarquia de chaves

As chaves podem ser derivadas de outras chaves usando um KDF (função de derivação de chave) como HKDF , resultando em uma hierarquia de chaves .

O diagrama a seguir descreve uma hierarquia de chaves típica para FBE quando as chaves encapsuladas em hardware não são usadas:

Hierarquia de chaves FBE (padrão)
Figura 1. Hierarquia de chaves FBE (padrão)

A chave de classe FBE é a chave de criptografia bruta que o Android passa para o kernel do Linux para desbloquear um conjunto específico de diretórios criptografados, como o armazenamento criptografado por credencial para um determinado usuário do Android. (No kernel, essa chave é chamada de chave mestra fscrypt .) Dessa chave, o kernel deriva as seguintes subchaves:

  • O identificador de chave. Isso não é usado para criptografia, mas é um valor usado para identificar a chave com a qual um determinado arquivo ou diretório está protegido.
  • A chave de criptografia do conteúdo do arquivo
  • A chave de criptografia de nomes de arquivos

Em contraste, o diagrama a seguir descreve a hierarquia de chaves para FBE quando chaves encapsuladas em hardware são usadas:

Hierarquia de chaves FBE (com chave encapsulada em hardware)
Figura 2. Hierarquia de chaves FBE (com chave encapsulada em hardware)

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 passa para o Linux para desbloquear um conjunto de diretórios criptografados. No entanto, agora essa chave está em forma efêmera e, para ser usada, deve ser passada para um hardware dedicado. Este hardware deve implementar duas interfaces que recebem uma chave efêmera:

  • Uma interface para derivar inline_encryption_key e programá-la diretamente em um slot de 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ção blk_ksm_ll_ops::keyslot_program , que deve ser implementada pelo driver de armazenamento.
  • Uma interface para derivar e retornar sw_secret ("segredo de software" -- também chamado de "segredo bruto" em alguns lugares), que é a chave que o Linux usa para derivar as subchaves para tudo que não seja a criptografia do conteúdo do arquivo. Nos kernels comuns do Android, essa interface corresponde à operação blk_ksm_ll_ops::derive_raw_secret , que deve ser implementada pelo driver de armazenamento.

Para derivar inline_encryption_key e sw_secret da chave de armazenamento bruta, o hardware deve usar um KDF criptograficamente forte. Este KDF deve seguir as melhores práticas de criptografia; ele deve ter um nível de segurança de pelo menos 256 bits, ou seja, suficiente para qualquer algoritmo usado posteriormente. Ele também deve usar um rótulo distinto, contexto e/ou string de informações específicas do aplicativo ao derivar cada tipo de subchave para garantir que as subchaves resultantes sejam isoladas criptograficamente, ou seja, o conhecimento de uma delas não revela nenhuma outra. O alongamento de chave não é necessário, pois 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 reimplementar o mesmo KDF no código de teste. Atualmente, um KDF foi revisado e implementado; ele pode ser encontrado no código-fonte para vts_kernel_encryption_test . Recomenda-se que o hardware use este KDF, que usa NIST SP 800-108 "KDF in Counter Mode" com AES-256-CMAC como PRF. Observe que, para ser compatível, todas as partes do algoritmo devem ser idênticas, incluindo a escolha de contextos e rótulos KDF para cada subchave.

Embrulho de chave

Para atender aos objetivos de segurança das chaves encapsuladas por hardware, são definidos dois tipos de encapsulamento de chaves:

  • Envelopamento efêmero : o hardware criptografa a chave bruta usando uma chave que é gerada aleatoriamente a cada inicialização e não é exposta diretamente fora do hardware.
  • Agrupamento de longo prazo : o hardware criptografa a chave bruta usando uma chave única e persistente incorporada ao hardware que não é exposta diretamente fora do hardware.

Todas as chaves passadas para o kernel do Linux para desbloquear o armazenamento são encapsuladas de forma efêmera. Isso garante que, se um invasor conseguir extrair uma chave em uso da memória do sistema, essa chave ficará inutilizável não apenas fora do dispositivo, mas também no dispositivo após uma reinicialização.

Ao mesmo tempo, o Android ainda precisa ser capaz de armazenar uma versão criptografada das chaves no disco para que elas possam ser desbloqueadas em primeiro lugar. As chaves brutas funcionariam para essa finalidade. No entanto, é desejável que as chaves brutas nunca estejam presentes na memória do sistema para que nunca possam ser extraídas para serem usadas fora do dispositivo, mesmo se extraídas no momento da inicialização. Por esta razão, o conceito de embalagem a longo prazo é definido.

Para oferecer suporte ao gerenciamento de chaves encapsuladas dessas duas maneiras diferentes, o hardware deve implementar as seguintes interfaces:

  • Interfaces para gerar e importar chaves de armazenamento, retornando-as em forma encapsulada de longo prazo. Essas interfaces são acessadas indiretamente por meio do KeyMint e correspondem à tag TAG_STORAGE_KEY KeyMint. A habilidade "generate" é usada por vold para gerar novas chaves de armazenamento para uso pelo Android, enquanto a habilidade "import" é usada por vts_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 efêmera. Isso corresponde ao método convertStorageKeyToEphemeral KeyMint. Esse método é usado por vold e vts_kernel_encryption_test para desbloquear o armazenamento.

O algoritmo de encapsulamento de chave é um detalhe de implementação, mas deve usar um AEAD forte, como AES-256-GCM com IVs aleatórios.

Alterações de software necessárias

O AOSP já possui uma estrutura básica para suportar chaves encapsuladas em hardware. Isso inclui o suporte em componentes do espaço do usuário, como vold , bem como o suporte do kernel Linux em blk-crypto , fscrypt e dm-default-key .

No entanto, algumas mudanças específicas de implementação são necessárias.

Alterações do KeyMint

A implementação do KeyMint do dispositivo deve ser modificada para suportar 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 em linha do dispositivo deve ser modificado para definir BLK_CRYPTO_FEATURE_WRAPPED_KEYS , fazer com que as keyslot_program() e keyslot_evict() suportem programação/remoção de chaves encapsuladas em hardware e implementem a operação 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 testar importando uma chave de teste e reimplementando a derivação de chave que o hardware faz. Isso é implementado em vts_kernel_encryption_test . Para executar este teste, execute:

atest -v vts_kernel_encryption_test

Leia o log de teste e verifique se os casos de teste de chave encapsulada em hardware (por exemplo, FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy e DmDefaultKeyTest.TestHwWrappedKey ) não foram ignorados devido ao suporte para chaves encapsuladas em hardware não serem detectadas, pois os resultados do teste ainda serão "passados" em Aquele caso.

Possibilitando

Quando o suporte à chave encapsulada por hardware do dispositivo estiver funcionando corretamente, você poderá fazer as seguintes alterações no arquivo fstab do dispositivo para que o Android o use para FBE e criptografia de metadados:

  • FBE: adicione o sinalizador wrappedkey_v0 ao parâmetro fileencryption . Por exemplo, use fileencryption=::inlinecrypt_optimized+wrappedkey_v0 . Para obter mais detalhes, consulte a documentação do FBE .
  • Criptografia de metadados: adicione o sinalizador wrappedkey_v0 ao parâmetro metadata_encryption . Por exemplo, use metadata_encryption=:wrappedkey_v0 . Para obter mais detalhes, consulte a documentação de criptografia de metadados .