Chaves encapsuladas em hardware

Como a maioria dos softwares de criptografia de arquivos e discos, a criptografia de armazenamento do Android tradicionalmente depende das chaves de criptografia brutas presentes na memória do sistema para que a criptografia possa ser executada. Mesmo quando a criptografia é executada por hardware dedicado e não por software, o software geralmente 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 o objetivo da criptografia. No entanto, existe o desejo de oferecer maior proteção contra outros tipos de ataques, como inicialização a frio on-line e ataques on-line em que um invasor pode vazar da memória sem comprometer completamente o dispositivo.

Para resolver esse problema, o Android 11 introduziu o suporte para chaves encapsuladas por hardware, quando houver suporte de hardware. As chaves encapsuladas em hardware são chaves de armazenamento conhecidas apenas na forma bruta por hardware dedicado, o software só vê e funciona com essas chaves em (criptografado). Esse hardware precisa ser capaz de gerar e importar de armazenamento, encapsulando as chaves de armazenamento de formas efêmeras e de longo prazo, obtendo subchaves, programando diretamente uma subchave em um mecanismo criptográfico em linha e retornando uma subchave separada para o software.

Observação: um mecanismo criptográfico inline (ou inline) de criptografia) refere-se ao hardware que criptografa/descriptografa dados enquanto está a caminho de/para o dispositivo de armazenamento. Geralmente é um host UFS ou eMMC que implementa as extensões criptográficas definidas pelo especificação JEDEC.

Design

Esta seção apresenta o design do recurso de chaves encapsuladas por hardware, incluindo: qual suporte de hardware é necessário. Essa discussão se concentra na criptografia baseada em arquivos (FBE, na sigla em inglês), mas a se aplica a metadados a criptografia de dados.

Uma maneira de evitar a necessidade de chaves de criptografia brutas na memória do sistema é mantê-los apenas nos keyslots de um mecanismo criptográfico em linha. No entanto, tem alguns problemas:

  • O número de chaves de criptografia pode exceder o número de slots de chaves.
  • Os mecanismos criptográficos em linha só podem ser usados para criptografar/descriptografar blocos completos de os dados no disco. No entanto, no caso da FBE, o software ainda precisa ser capaz de fazer outros trabalhos criptográficos, como criptografar nomes de arquivos e derivar chaves identificadores. O software ainda precisaria de acesso às chaves FBE brutas para para fazer esse outro trabalho.

Para evitar esses problemas, as chaves de armazenamento são transformadas chaves encapsuladas por hardware, que só podem ser desencapsuladas e usadas por hardware dedicado. Isso permite que um número ilimitado de chaves seja aceito. Em Além disso, a hierarquia das chaves é modificada e parcialmente movida para esse hardware, que permite que uma subchave seja retornada ao software para tarefas que não podem usar uma mecanismo de criptografia em linha.

Hierarquia de chaves

As chaves podem ser derivadas de outras chaves usando uma função de derivação de chaves (KDF, na sigla em inglês) como HKDF (links em inglês), resultando em uma hierarquia de chaves.

O diagrama a seguir mostra uma hierarquia de chaves típica para a FBE quando as chaves encapsuladas por 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 transmite ao Linux para desbloquear um determinado conjunto de diretórios criptografados, como armazenamento criptografado por credenciais para um usuário Android específico. (No kernel, esse é chamada de chave mestra fscrypt.) A partir dessa chave, o kernel deriva as seguintes subchaves:

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

Em contraste, o diagrama a seguir mostra a hierarquia de chaves da FBE quando chaves encapsuladas por hardware são usadas:

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

Em comparação com o caso anterior, um nível extra foi adicionado à chave e a chave de criptografia do conteúdo do arquivo foi realocada. A raiz ainda representa a chave que o Android passa ao Linux para desbloquear um conjunto de em diretórios criptografados. No entanto, agora que a chave está em um formato efêmero, e Para ser usado, ele deve ser passado para um hardware dedicado. Esse hardware precisa implementar duas interfaces que usam uma chave encapsulada efêmera:

  • Uma interface para derivar inline_encryption_key e criar programá-lo em um keyslot do mecanismo de criptografia inline. Isso permite que arquivos que o conteúdo seja criptografado/descriptografado sem que o software tenha acesso ao material bruto de dados. Nos kernels comuns do Android, essa interface corresponde ao blk_crypto_ll_ops::keyslot_program, que precisa ser implementado pelo driver de armazenamento.
  • Uma interface para derivar e retornar sw_secret, ou seja, segredo" também chamado de "segredo bruto", em alguns lugares), que é a chave O Linux usa para derivar subchaves para tudo, menos o conteúdo do arquivo criptografia. Nos kernels comuns do Android, essa interface corresponde ao blk_crypto_ll_ops::derive_sw_secret, que precisa ser implementado pelo driver de armazenamento.

Para derivar inline_encryption_key e sw_secret do o hardware precisa usar uma KDF com criptografia forte. Este KDF precisa seguir as práticas recomendadas de criptografia; ele deve ter uma força de segurança de pelo menos mínimo de 256 bits, ou seja, o suficiente para qualquer algoritmo usado posteriormente. Ele também precisa usar identificador distinto, contexto e/ou informações específicas do aplicativo quando derivando cada tipo de subchave para garantir que as subchaves resultantes são criptograficamente isolados, ou seja, o conhecimento de um deles não revela nenhuma entre si. Não é necessário alongar a chave, porque a chave de armazenamento bruta já é uma uma chave aleatória de modo uniforme.

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 em código de teste. Atualmente, uma KDF foi analisada e implementada. ele pode ser encontrado no código-fonte de vts_kernel_encryption_test. Recomendamos que o hardware use essa KDF, que usa NIST SP 800-108 "KDF em modo de contador" com AES-256-CMAC como PRF. Para ser compatível, todos partes do algoritmo precisam ser idênticas, incluindo a escolha dos contextos de KDF e rótulos para cada subchave.

Encapsulamento de chaves

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

  • Encapsulamento efêmero: o hardware criptografa a chave bruta usando uma chave. gerado aleatoriamente a cada inicialização e não é exposto diretamente fora do hardware.
  • Encapsulamento de longo prazo: o hardware criptografa a chave bruta usando uma exclusiva e persistente incorporada ao hardware que não é expostos fora do hardware.

Todas as chaves passadas para o kernel do Linux para desbloquear o armazenamento são de maneira efêmera. Isso garante que, se um invasor for capaz de extrair uma chave em uso da memória do sistema, ela não poderá apenas ser usada fora do dispositivo, mas também no dispositivo após a reinicialização.

Ao mesmo tempo, o Android ainda precisa armazenar uma versão criptografada das chaves no disco para que possam ser desbloqueadas. O material bruto funcionam para essa finalidade. No entanto, é desejável nunca ter o valor bruto estejam presentes na memória do sistema para que nunca possam ser extraídas ser usada fora do dispositivo, mesmo que extraídas no momento da inicialização. Por isso, o conceito de encapsulamento de longo prazo.

Para oferecer suporte ao gerenciamento de chaves unidas dessas duas maneiras diferentes, o hardware precisa implemente as seguintes interfaces:

  • Interfaces para gerar e importar chaves de armazenamento, retornando-as em com wrapper de longo prazo. Essas interfaces são acessadas indiretamente por KeyMint e eles correspondem à tag TAG_STORAGE_KEY do KeyMint. A opção a capacidade é usada por vold para gerar mais armazenamento para uso no Android, enquanto a opção de "importação" habilidade é usada pela vts_kernel_encryption_test para importar chaves de teste.
  • Uma interface para converter uma chave de armazenamento encapsulada de longo prazo em uma de armazenamento temporário. Isso corresponde à Método convertStorageKeyToEphemeral KeyMint. Esse método é usado por vold e vts_kernel_encryption_test para para liberar o armazenamento.

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

Mudanças de software necessárias

O AOSP já tem um framework básico para oferecer suporte a chaves encapsuladas por hardware. Isso inclui o suporte em componentes do espaço do usuário, como vold, bem como como o suporte ao 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 deve ser modificada para oferecer suporte TAG_STORAGE_KEY e implemente convertStorageKeyToEphemeral.

No Keymaster, exportKey era 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 dar suporte a chaves encapsuladas por hardware.

Para kernels android14 e mais recentes, definir BLK_CRYPTO_KEY_TYPE_HW_WRAPPED em blk_crypto_profile::key_types_supported, deixar blk_crypto_ll_ops::keyslot_program e blk_crypto_ll_ops::keyslot_evict oferecer suporte à programação/remoção de chaves encapsuladas por hardware e implementar blk_crypto_ll_ops::derive_sw_secret.

Para os kernels android12 e android13, definir BLK_CRYPTO_FEATURE_WRAPPED_KEYS em blk_keyslot_manager::features, deixar blk_ksm_ll_ops::keyslot_program e blk_ksm_ll_ops::keyslot_evict oferecer suporte à programação/remoção de chaves encapsuladas por hardware e implementar blk_ksm_ll_ops::derive_raw_secret.

Para kernels android11, definir BLK_CRYPTO_FEATURE_WRAPPED_KEYS em keyslot_manager::features, deixar keyslot_mgmt_ll_ops::keyslot_program e keyslot_mgmt_ll_ops::keyslot_evict oferecer suporte à programação/remoção de chaves encapsuladas por hardware e implementar 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 testes importando uma chave de teste e reimplementando a derivação de chave que o hardware faz. Isso foi implementado em vts_kernel_encryption_test. Para executar esse teste, executar:

atest -v vts_kernel_encryption_test

Leia o registro do teste e verifique se os casos de teste de chave encapsulados por hardware (por exemplo, FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy e DmDefaultKeyTest.TestHwWrappedKey) não foram ignoradas devido ao suporte para chaves encapsuladas por hardware não serem detectadas, já que os resultados do teste ainda serão "aprovado" nesse caso.

Ativando

Quando o suporte a chaves encapsuladas por hardware do dispositivo estiver funcionando corretamente, você poderá faça as seguintes mudanças no arquivo fstab do dispositivo para O Android usa para FBE e criptografia de metadados:

  • FBE: adicione a flag wrappedkey_v0 ao parâmetro fileencryption. Por exemplo, use fileencryption=::inlinecrypt_optimized+wrappedkey_v0: Para mais detalhes, consulte a FBE Documentação.
  • Criptografia de metadados: adicione a sinalização wrappedkey_v0 ao parâmetro metadata_encryption. Por exemplo, use metadata_encryption=:wrappedkey_v0: Para mais detalhes, consulte a metadados de criptografia de dados.