No Keymaster 1, todas as chaves do keymaster eram vinculadas criptograficamente à raiz de confiança do dispositivo ou à chave de inicialização verificada. No Keymaster 2 e 3, todas as chaves também são vinculadas ao sistema operacional e ao nível de patch da imagem do sistema. Isso garante que um invasor que descubra uma vulnerabilidade em uma versão antiga do sistema ou do software TEE não possa retornar um dispositivo à versão vulnerável e usar 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 ser usada e a versão anterior da chave é invalidada. Dessa forma, à medida que o dispositivo é atualizado, as chaves *aumentam* junto com o dispositivo, mas qualquer reversão do dispositivo para uma versão anterior faz com que as chaves fiquem inutilizáveis.
Para oferecer suporte à estrutura modular do Treble e romper a vinculação de system.img a boot.img, o Keymaster 4 mudou o modelo de vinculaçã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 de maneira independente, fornecendo ainda proteção contra reversão.
No Android 9, as partições boot
, system
e vendor
têm o próprio nível de patch.
- Os dispositivos com inicialização verificada do Android (AVB) podem colocar todos os níveis de patch
e a versão do sistema no vbmeta, para que o carregador de inicialização possa fornecê-los ao
Keymaster. Para partições encadeadas, as informações de versão da partição estão
no vbmeta encadeado. Em geral, as informações de versão precisam estar no
vbmeta struct
que contém os dados de verificação (hash ou hashtree) de uma determinada partição. - Em dispositivos sem AVB:
- As implementações da Inicialização verificada precisam fornecer um hash dos metadados da versão para o carregador de inicialização, para que ele possa fornecer o hash ao Keymaster.
boot.img
pode continuar armazenando o nível do patch no cabeçalhosystem.img
pode continuar armazenando o nível do patch e a versão do SO em propriedades somente leituravendor.img
armazena o nível do patch na propriedade somente leituraro.vendor.build.version.security_patch
.- O gerenciador de inicialização pode fornecer um hash de todos os dados validados pela inicialização verificada para o keymaster.
- No Android 9, use as tags a seguir para fornecer informações de versão para
as seguintes partições:
VENDOR_PATCH_LEVEL
: partiçãovendor
BOOT_PATCH_LEVEL
: partiçãoboot
OS_PATCH_LEVEL
eOS_VERSION
: partiçãosystem
. OOS_VERSION
foi removido do cabeçalhoboot.img
.
-
As implementações do Keymaster precisam 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()
for rolado para um nível de patch mais alto, se necessário.
Mudanças na HAL
Para oferecer suporte à vinculaçã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 SO ou um nível de patch correspondente à versão do SO do sistema atual,
respectivamente, é rejeitada com ErrorCode::KEY_REQUIRES_UPGRADE
.
Tag::OS_VERSION
é um valor UINT
que representa as
porções principais, secundárias e subsecundárias de uma versão do sistema Android como MMmmss,
em que MM é a versão principal, mm é a versão secundária e ss é a versão subsecundá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 AAAAMM, em que AAAA é 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.
UpgradeKey
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
à 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 dispositivokeyBlobToUpgrade
é a chave que precisa ser atualizadaupgradeParams
são os parâmetros necessários para atualizar a chave. Eles incluemTag::APPLICATION_ID
eTag::APPLICATION_DATA
, que são necessários para descriptografar o blob de chave, se eles tiverem sido fornecidos durante a geração.upgradedKeyBlob
é o parâmetro de saída, usado para retornar o novo blob de chave.
Se upgradeKey
for chamado com um blob de chave que não pode ser analisado ou
for inválido, ele retornará ErrorCode::INVALID_KEY_BLOB
. Se ele
for chamado com uma chave cujo nível de patch seja maior que o valor atual do sistema,
ele retornará ErrorCode::INVALID_ARGUMENT
. Se ele for chamado com uma chave
cuja versão do SO é maior que o valor atual do sistema e o valor do sistema
for diferente de zero, ele retornará ErrorCode::INVALID_ARGUMENT
. Os upgrades de versão do SO
de valores diferentes de zero para zero são permitidos. Em caso de erros
na 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 retorna
ErrorCode::OK
e retorna um novo blob de chave em
upgradedKeyBlob
.
O keyBlobToUpgrade
permanece válido após a chamada upgradeKey
e pode, teoricamente, ser usado novamente se o dispositivo for 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
, upgradedKeyBlob
também
precisa ter (e precisa ser resistente a reversão).
Configuração segura
Para implementar a vinculação de versão, o TA do keymaster precisa de uma maneira de receber com segurança a versão atual do SO e o nível do patch (informações da versão) e garantir que as informações recebidas correspondam às informações sobre o sistema em execução.
Para oferecer suporte ao envio seguro de informações de versão para o TA, um campo OS_VERSION
foi adicionado ao cabeçalho da imagem de inicialização. O script de build da imagem de inicialização
preenche esse campo automaticamente. OEMs e implementadores de TA do keymaster
precisam trabalhar juntos para modificar os carregados de inicialização do dispositivo e extrair as informações
de versão da imagem de inicialização e transmiti-las ao TA antes que o sistema
não seguro seja inicializado. Isso garante que os invasores não possam interferir no provisionamento
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 de configuração foi adicionado à 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
. Esse método é chamado por clientes keymaster2
após a abertura do HAL, mas antes de chamar outros métodos. Se qualquer outro método
for chamado antes da configuração, o TA vai retornar
ErrorCode::KEYMASTER_NOT_CONFIGURED
.
Na primeira vez que configure
for chamado após a inicialização do dispositivo, ele
vai verificar se as informações de versão fornecidas correspondem às fornecidas pelo
carregador de inicialização. Se as informações de versão não corresponderem,
configure
vai retornar ErrorCode::INVALID_ARGUMENT
, e todos
os outros métodos do keymaster vão continuar retornando
ErrorCode::KEYMASTER_NOT_CONFIGURED
. Se as informações corresponderem,
configure
vai retornar ErrorCode::OK
, e outros métodos
do keymaster vão começar a funcionar normalmente.
As chamadas subsequentes para configure
retornam o mesmo valor retornado pela
primeira chamada e não mudam o estado do keymaster.
Como configure
é chamado pelo sistema cujo conteúdo ele
pretende validar, há uma janela estreita de oportunidade para que um invasor
comprometa a imagem do sistema e a force a fornecer informações de versão que
correspondem à imagem de inicialização, mas que não é a versão real 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 configure
ser chamado muito cedo na
inicialização do sistema devem dificultar a exploração dessa janela de oportunidade.