Verificação completa de transparência do APK dos serviços do sistema do Google

Esta página descreve vários métodos para garantir que o APK instalado no dispositivo Android corresponda à reivindicação feita no modelo de autor da reivindicação. Isso envolve extrair o APK em questão do seu dispositivo, verificar a integridade do código e realizar uma prova de inclusão de registro no artefato extraído.

Processo de verificação

Um registro de transparência é implementado com uma árvore Merkle que consiste em hashes. Um nó folha contém dados, e um nó pai contém o hash dos filhos.

Basicamente, duas computações são realizadas na árvore Merkle para verificar a propriedade de violação evidente dos registros de transparência: a prova de inclusão e a prova de consistência. O primeiro prova que o registro inclui uma entrada correspondente a uma versão específica do APK. A entrada de registro inclui um hash, que é o resumo SHA256 do token de assinatura do código na forma de um JSON Web Token (JWT), que pode ser extraído dos APKs correspondentes. O último prova que, quando novas entradas são adicionadas à árvore, o novo ponto de verificação é (criptograficamente) consistente com a versão anterior da árvore.

Para verificar um APK coberto, execute um teste de prova de inclusão com base em um ponto de verificação com testemunha. Estamos planejando integrar esse registro a uma rede de testemunhas pública usando um protocolo de testemunha padronizado. Isso vai fornecer um ponto de verificação com testemunhas, que garante a consistência do registro.

Se você quiser se convencer de que o APK que está no seu dispositivo está em conformidade com a reivindicação feita no modelo do reclamante, consulte o texto abaixo.

Comprovante de inclusão

Um usuário do Android pode verificar se um APK coberto no dispositivo está no registro extraindo primeiro o APK e os metadados relevantes dele e, em seguida, comparando o hash raiz recalculado com o hash raiz contido no ponto de verificação publicado. Se eles corresponderem, o usuário do Android poderá ter certeza de algumas proteções descritas no modelo de ameaças.

Como verificar a inclusão de um APK no registro

Conforme descrito anteriormente, a lista de APKs que estão atualmente cobertos pode ser encontrada na página de visão geral.

Pré-requisitos para verificação

Antes de verificar se o APK que você acabou de extrair do dispositivo está de acordo com nossa reivindicação, você vai precisar instalar as ferramentas a seguir em um computador conectado à rede.

Android Debug Bridge

ADB é uma ferramenta que se comunica com um dispositivo Android, disponível no site do Android SDK Platform Tools.

bundletool

O bundletool é uma ferramenta usada para criar um Android App Bundle (AAB). Ele também pode ser usado para converter um AAB em APKs que podem ser instalados em dispositivos. O download pode ser feito no GitHub.

Androguard

O Androguard é um conjunto de ferramentas usado para analisar APKs. Ele pode ser feito no site do Androguard.

Verificador de prova de inclusão

Este é um módulo Go que publicamos em um repositório Git no Android Open Source Project (AOSP) chamado avb. Ele pode consultar o registro de transparência do APK dos serviços do sistema do Google e mostrar se um pacote está incluído no registro. Um exemplo de como isso é usado pode ser encontrado em uma seção posterior.

Para fazer o download dessa ferramenta, primeiro clone o repositório avb.

computer:~$ git clone https://android.googlesource.com/platform/external/avb

O código-fonte do verificador pode ser encontrado em tools/transparency/verify no repositório avb.

Criar um payload para verificação

Para verificar se o APK extraído do seu dispositivo está de acordo com nossas reivindicações, crie um payload de registro com base nas informações derivadas do APK.

Antes de começar, verifique se o adb pode ser usado no seu dispositivo ativando a depuração do adb no dispositivo.

Em seguida, localize onde o APK está instalado no seu dispositivo. Para fins deste guia, vamos usar o APK do Verificador de chaves do sistema Android (com.google.android.contactkeys) como exemplo.

computer:~$ adb shell pm list packages -f | grep contactkeys
package:/data/app/~~i5WYSO4PuAAv798-eHdM7A==/com.google.android.contactkeys-PQCKjnn7xDqjeVhcUDibBA==/base.apk=com.google.android.contactkeys

Se o APK do Verificador de chaves do sistema Android estiver instalado no seu dispositivo, o comando acima vai retornar um caminho indicando onde ele está instalado no seu dispositivo. Caso contrário, nenhuma saída será mostrada.

Em seguida, faça o download do APK do dispositivo Android para o computador em que você está trabalhando usando este comando. O local e o nome do arquivo APK no dispositivo podem variar:

computer:~$ mkdir -p /tmp/testdir && cd /tmp/testdir
computer:/tmp/testdir$ adb pull /data/app/~~i5WYSO4PuAAv798-eHdM7A==/com.google.android.contactkeys-PQCKjnn7xDqjeVhcUDibBA==/base.apk ./contactkeys_candidate.apk

Para conferir o nome do pacote do APK que você acabou de fazer o download, primeiro descompacte o arquivo, já que ele é um tipo especial de arquivo ZIP.

computer:/tmp/testdir$ mkdir extracted && unzip contactkeys_candidate.apk -d extracted/

Essa etapa descompacta todos os arquivos que compõem o APK. O nome e a versão do pacote podem ser encontrados no manifesto do APK, que geralmente está em um arquivo chamado AndroidManifest.xml.

No entanto, o arquivo de manifesto recebido está em um formato binário, que não é legível por humanos. Para converter o XML binário em um formato legível por humanos, usamos a ferramenta axml do pacote androguard (conforme necessário para ser instalado na seção pré-requisito).

computer:/tmp/testdir$ androguard axml extracted/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1413" android:versionName="1.48.688082145" android:compileSdkVersion="35" android:compileSdkVersionCodename="VanillaIceCream" android:requiredSplitTypes="" android:splitTypes="" package="com.google.android.contactkeys" platformBuildVersionCode="35" platformBuildVersionName="VanillaIceCream">

Com base na saída acima, agora podemos ter certeza de que o nome do pacote desse APK é com.google.android.contactkeys e o número da versão (versionCode) é 1413.

Agora vamos procurar a assinatura de transparência do código no APK. Ele deve ser um arquivo chamado code_transparency_signed.jwt contido na pasta META-INF entre os outros arquivos extraídos do APK.

computer:/tmp/testdir$ sha256sum extracted/META-INF/code_transparency_signed.jwt
1779a2aee029112c2c9bfc9390b9678f3e5f4595b39705e8528dd522e8042f11  code_transparency_signed.jwt

Com essa string de hash, agora temos todas as informações necessárias para montar um payload de registro de acordo com o formato descrito na seção "Conteúdo do registro". Neste exemplo, um payload de registro correspondente tem esta aparência:

1779a2aee029112c2c9bfc9390b9678f3e5f4595b39705e8528dd522e8042f11
SHA256(Signed Code Transparency JWT)
com.google.android.contactkeys
1143

Observe também o caractere de nova linha após a versão do pacote.

Você pode salvar o conteúdo em um arquivo, como payload.txt. Isso será útil quando você fizer o teste de prova de inclusão mais tarde.

Verificar a autenticidade da assinatura do código do APK

Agora, precisamos verificar a autenticidade do token de assinatura de código incorporado ao APK. Para isso, usamos bundletool e a chave pública do par de chaves que foi usada para assinar. Eles são publicados em cada seção dos respectivos APKs. Supondo que você tenha salvo o certificado de chave pública (por exemplo, para o Android System Key Verifier) em um arquivo chamado signing_cert_pubkey.pem, siga o guia abaixo para realizar a verificação da assinatura do código.

Primeiro, crie um arquivo ZIP e adicione o APK candidato a ele.

computer:/tmp/testdir$ zip -u test.zip contactkeys_candidate.apk
        zip warning: test.zip not found or empty
  adding: contactkeys_candidate.apk (deflated 58%)

computer:/tmp/testdir$ file test.zip
test.zip: Zip archive data, at least v2.0 to extract, compression method=deflate

Agora estamos prontos para usar o comando check-transparency do bundletool para verificar se a assinatura de código incorporada ao APK candidato corresponde à que é publicada.

computer:/tmp/testdir$ java -jar BUNDLETOOL_INSTALL_PATH/bundletool-all-version.jar check-transparency \
  --mode=apk \
  --apk-zip=test.zip \
  --transparency-key-certificate=signing_cert_pubkey.pem

APK signature is valid. SHA-256 fingerprint of the apk signing key certificate (must be compared with the developer's public key manually): D9 E1 73 5B 2A 39 51 27 3A 87 35 B7 66 9E F1 9E F5 3A F1 C1 27 5C BA 31 39 3C 18 40 8B 03 79 D0
Code transparency signature verified for the provided code transparency key certificate.
Code transparency verified: code related file contents match the code transparency file.

Verifique se a saída do comando acima indica que a assinatura de transparência de código e a transparência de código estão verificadas. Se não forem, por exemplo, se você receber uma saída como Code transparency verification failed because the provided public key certificate does not match the transparency file, isso significa que a integridade do código do APK em questão pode estar comprometida e você não deve confiar no APK. Verifique se você está comparando os dados com o certificado de chave pública correto. Caso contrário, se tudo estiver correto, isso significa que a autenticidade da assinatura do código foi verificada para o APK que você está validando.

Como verificar a inclusão do pacote (prova de inclusão)

Usando o payload que você criou antes, agora você pode testar se o pacote em questão foi incluído no registro de transparência.

Uma ferramenta de prova de inclusão foi publicada no repositório avb do Android Open Source Project. Para executar:

computer:external/avb/tools/transparency/verify$ PAYLOAD_PATH=PATH_TO_PAYLOAD_DIR/payload.txt
computer:external/avb/tools/transparency/verify$ go build cmd/verifier/verifier.go
computer:external/avb/tools/transparency/verify$ ./verifier --payload_path=${PAYLOAD_PATH} --log_type=google_system_apk

O verificador usa o ponto de verificação correspondente e o conteúdo do registro (encontrado no diretório de blocos) para verificar se o payload do APK está no registro de transparência, confirmando que ele foi publicado pelo Google.

A saída do comando é gravada em stdout:

  • OK. inclusion check success! se o código do pacote estiver incluído no registro,
  • FAILURE.