No Keymaster 1, todas as chaves keymaster eram criptograficamente vinculadas ao dispositivo Root of Trust ou à chave de inicialização verificada. No Keymaster 2 e 3, todas as chaves também estão vinculadas ao sistema operacional e ao nível de patch da imagem do sistema. Isso garante que um invasor que descubra uma fraqueza em uma versão antiga do sistema ou do software TEE não possa reverter um dispositivo para a versão vulnerável e usar as chaves criadas com a versão mais recente. Além disso, quando uma chave com uma determinada versão e nível de patch é usada em um dispositivo que foi atualizado para uma versão ou nível de patch mais recente, a chave é atualizada antes de poder ser usada e a versão anterior da chave é invalidada. Dessa forma, à medida que o dispositivo for atualizado, as chaves serão "atacadas" junto com o dispositivo, mas qualquer reversão do dispositivo para uma versão anterior fará com que as chaves fiquem inutilizáveis.
Para suportar a estrutura modular do Treble e quebrar a ligação de system.img para boot.img, o Keymaster 4 mudou o modelo de ligação de versão de chave para ter níveis de patch separados para cada partição. Isso permite que cada partição seja atualizada independentemente, enquanto ainda fornece proteção contra reversão.
No Android 9, as partições boot
, system
e vendor
têm seu próprio nível de patch.
- Dispositivos com Android Verified Boot (AVB) podem colocar todos os níveis de patch e a versão do sistema em vbmeta, para que o bootloader possa fornecê-los ao Keymaster. Para partições encadeadas, as informações de versão da partição estarão no vbmeta encadeado. Em geral, as informações de versão devem estar na
vbmeta struct
que contém os dados de verificação (hash ou hashtree) para uma determinada partição. - Em dispositivos sem AVB:
- As implementações de inicialização verificada precisam fornecer um hash dos metadados da versão para o carregador de inicialização, para que o carregador de inicialização possa fornecer o hash para o Keymaster.
-
boot.img
pode continuar armazenando o nível de patch no cabeçalho -
system.img
pode continuar armazenando o nível de patch e a versão do SO em propriedades somente leitura -
vendor.img
armazena o nível de patch na propriedade somente leituraro.vendor.build.version.security_patch
. - O bootloader pode fornecer um hash de todos os dados validados pela inicialização verificada para o keymaster.
- No Android 9, use as seguintes tags para fornecer informações de versão para as seguintes partições:
-
VENDOR_PATCH_LEVEL
: partição dovendor
-
BOOT_PATCH_LEVEL
: partição deboot
-
OS_PATCH_LEVEL
eOS_VERSION
: partição dosystem
. (OS_VERSION
é removido do cabeçalhoboot.img
.
-
- As implementações do Keymaster devem tratar todos os níveis de patch de forma independente. As chaves são utilizáveis se todas as informações de versão corresponderem aos valores associados a uma chave e
IKeymaster::upgradeDevice()
rolar para um nível de patch mais alto, se necessário.
Alterações HAL
Para dar suporte à vinculação de versão e ao atestado de versão, o Android 7.1 adicionou as tags Tag::OS_VERSION
e Tag::OS_PATCHLEVEL
e os métodos configure
e upgradeKey
. As tags de versão são adicionadas automaticamente pelas implementações do Keymaster 2+ a todas as chaves recém-geradas (ou atualizadas). Além disso, qualquer tentativa de usar uma chave que não tenha uma versão do sistema operacional ou nível de patch correspondente à versão atual do sistema operacional ou nível de patch, respectivamente, é rejeitada com ErrorCode::KEY_REQUIRES_UPGRADE
.
Tag::OS_VERSION
é um valor UINT
que representa as partes principais, secundárias e secundárias de uma versão do sistema Android como MMmmss, onde MM é a versão principal, mm é a versão secundária e ss é a versão secundária. Por exemplo, 6.1.2 seria representado como 060102.
Tag::OS_PATCHLEVEL
é um valor UINT
que representa o ano e o mês da última atualização do sistema como YYYYMM, onde YYYY é o ano de quatro dígitos e MM é o mês de dois dígitos. Por exemplo, março de 2016 seria representado como 201603.
Chave de atualização
Para permitir que as chaves sejam atualizadas para a nova versão do SO e o nível de patch da imagem do sistema, o Android 7.1 adicionou o método upgradeKey
ao HAL:
Keymaster 3
upgradeKey(vec keyBlobToUpgrade, vec upgradeParams) generates(ErrorCode error, vec upgradedKeyBlob);
Keymaster 2
keymaster_error_t (*upgrade_key)(const struct keymaster2_device* dev, const keymaster_key_blob_t* key_to_upgrade, const keymaster_key_param_set_t* upgrade_params, keymaster_key_blob_t* upgraded_key);
-
dev
é a estrutura do dispositivo -
keyBlobToUpgrade
é a chave que precisa ser atualizada -
upgradeParams
são parâmetros necessários para atualizar a chave. Eles incluirãoTag::APPLICATION_ID
eTag::APPLICATION_DATA
, que são necessários para descriptografar o blob de chaves, caso tenham sido fornecidos durante a geração. -
upgradedKeyBlob
é o parâmetro de saída, usado para retornar o novo blob de chaves.
Se upgradeKey
for chamado com um blob de chaves que não pode ser analisado ou é inválido, ele retornará ErrorCode::INVALID_KEY_BLOB
. Se for chamado com uma chave cujo nível de patch é maior que o valor atual do sistema, ele retornará ErrorCode::INVALID_ARGUMENT
. Se for chamado com uma chave cuja versão do SO é maior que o valor do sistema atual e o valor do sistema for diferente de zero, ele retornará ErrorCode::INVALID_ARGUMENT
. As atualizações da versão do SO de diferente de zero a zero são permitidas. No caso de erros de comunicação com o mundo seguro, ele retorna um valor de erro apropriado (por exemplo, ErrorCode::SECURE_HW_ACCESS_DENIED
, ErrorCode::SECURE_HW_BUSY
). Caso contrário, ele retornará ErrorCode::OK
e retornará um novo blob de chaves em upgradedKeyBlob
.
keyBlobToUpgrade
permanece válido após a chamada upgradeKey
e teoricamente poderia ser usado novamente se o dispositivo fosse rebaixado. Na prática, o keystore geralmente chama deleteKey
no blob keyBlobToUpgrade
logo após a chamada para upgradeKey
. Se keyBlobToUpgrade
tiver a tag Tag::ROLLBACK_RESISTANT
, o upgradedKeyBlob
também deverá tê-la (e deve ser resistente à reversão).
Configuração segura
Para implementar a vinculação de versão, o TA keymaster precisa de uma maneira de receber com segurança a versão atual do sistema operacional e o nível de patch (informações de versão) e garantir que as informações recebidas correspondam fortemente às informações sobre o sistema em execução.
Para oferecer suporte à entrega segura de informações de versão ao TA, um campo OS_VERSION
foi adicionado ao cabeçalho da imagem de inicialização. O script de compilação da imagem de inicialização preenche automaticamente esse campo. OEMs e implementadores de TA keymaster precisam trabalhar juntos para modificar os carregadores de inicialização do dispositivo para extrair as informações de versão da imagem de inicialização e passá-las para o TA antes que o sistema não seguro seja inicializado. Isso garante que os invasores não possam interferir no fornecimento de informações de versão para o TA.
Também é necessário garantir que a imagem do sistema tenha as mesmas informações de versão que a imagem de inicialização. Para isso, o método configure foi adicionado ao HAL do keymaster:
keymaster_error_t (*configure)(const struct keymaster2_device* dev, const keymaster_key_param_set_t* params);
O argumento params
contém Tag::OS_VERSION
e Tag::OS_PATCHLEVEL
. Este método é chamado pelos clientes keymaster2 após abrir o HAL, mas antes de chamar qualquer outro método. Se qualquer outro método for chamado antes de configurar, o TA retornará ErrorCode::KEYMASTER_NOT_CONFIGURED
.
A primeira vez que o configure
é chamado após a inicialização do dispositivo, ele deve verificar se as informações de versão fornecidas correspondem ao que foi fornecido pelo carregador de inicialização. Se as informações de versão não corresponderem, configure
retornará ErrorCode::INVALID_ARGUMENT
e todos os outros métodos keymaster continuarão retornando ErrorCode::KEYMASTER_NOT_CONFIGURED
. Se as informações corresponderem, configure
retornará ErrorCode::OK
e outros métodos keymaster começarão a funcionar normalmente.
As chamadas subsequentes para configure
retornam o mesmo valor retornado pela primeira chamada e não alteram o estado do keymaster. Observe que esse processo exigirá que todas as OTAs atualizem as imagens de sistema e de inicialização; eles não podem ser atualizados separadamente para manter as informações de versão sincronizadas.
Como o configure
será chamado pelo sistema cujo conteúdo ele pretende validar, há uma estreita janela de oportunidade para um invasor comprometer a imagem do sistema e forçá-lo a fornecer informações de versão que correspondam à imagem de inicialização, mas que não sejam a real versão do sistema. A combinação de verificação de imagem de inicialização, validação dm-verity do conteúdo da imagem do sistema e o fato de que configure
é chamado muito cedo na inicialização do sistema deve tornar essa janela de oportunidade difícil de explorar.