Esquema de assinatura de APK v4

O Android 11 é compatível com um esquema de assinatura compatível com streaming com o esquema de assinatura de APK v4. A assinatura v4 é baseada na árvore de hash de Merkle calculada em todos os bytes do APK. O esquema segue exatamente a estrutura da árvore de hash fs-verity (por exemplo, preenchimento com zero do sal e do último bloco). O Android 11 armazena a assinatura em um arquivo separado, <apk name>.apk.idsig. Uma assinatura v4 requer uma assinatura complementar v2 ou v3.

Formato do arquivo

Todos os campos numéricos estão em little endian. Todos os campos ocupam exatamente o número de bytes do sizeof(), sem adição de padding ou alinhamento implícito.

A struct auxiliar a seguir simplifica as definições:

template <class SizeT>
struct sized_bytes {
        SizeT size;
        byte bytes[size];
};

Conteúdo do arquivo principal:

struct V4Signature {
        int32 version; // only version 2 is supported as of now
        sized_bytes<int32> hashing_info;
        sized_bytes<int32> signing_info;
        sized_bytes<int32> merkle_tree;  // optional
};

hashing_info é o parâmetro usado para a geração da árvore de hash e o hash raiz:

struct hashing_info.bytes {
    int32 hash_algorithm;    // only 1 == SHA256 supported
    int8 log2_blocksize;     // only 12 (block size 4096) supported now
    sized_bytes<int32> salt; // used exactly as in fs-verity, 32 bytes max
    sized_bytes<int32> raw_root_hash; // salted digest of the first Merkle tree page
};

signing_info é a seguinte struct:

struct signing_info.bytes {
    sized_bytes<int32> apk_digest; // used to match with the corresponding APK
    sized_bytes<int32> x509_certificate; // ASN.1 DER form
    sized_bytes<int32> additional_data; // a free-form binary data blob
    sized_bytes<int32> public_key; // ASN.1 DER, must match the x509_certificate
    int32 signature_algorithm_id; // see the APK v2 doc for the list
    sized_bytes<int32> signature;
};

apk_digest é extraído do bloco de assinatura v3 do APK. Se esse bloco não estiver presente, ele será extraído do bloco v2 (consulte apk_digest).

Para criar e verificar, um código signature precisa serializar os seguintes dados em um blob binário e transmiti-los ao algoritmo de assinatura e verificação como os dados assinados:

struct V4DataForSigning {
        int32 size;
        int64 file_size; // the size of the file that's been hashed.
        hashing_info.hash_algorithm;
        hashing_info.log2_blocksize;
        hashing_info.salt;
        hashing_info.raw_root_hash;
        signing_info.apk_digest;
        signing_info.x509_certificate;
        signing_info.additional_data;
};

merkle_tree é toda a árvore de Merkle do APK, calculada conforme descrito na documentação do fs-verity.

Produtores e consumidores

A ferramenta apksigner do SDK do Android gera o arquivo de assinatura v4 se você executar com parâmetros padrão. É possível desativar a assinatura v4 da mesma forma que os outros esquemas de assinatura. A ferramenta também pode verificar se a assinatura v4 é válida.

O adb espera que o arquivo .apk.idsig esteja presente ao lado do APK ao executar o comando adb install --incremental. O adb também usa o arquivo IDSIG para tentar uma instalação incremental por padrão e volta a uma instalação normal se ele estiver ausente ou inválido.

Quando uma sessão de instalação é criada, a nova API de instalação por streaming em PackageInstaller aceita a assinatura v4 removida como um argumento separado ao adicionar um arquivo à sessão. Nesse ponto, signing_info é transmitido para o IncFS como um blob inteiro. O IncFS extrai o hash da raiz do blob.

Quando a sessão de instalação está sendo confirmada, o PackageManagerService faz uma chamada ioctl para recuperar o blob signing_info do IncFS, analisa e verifica a assinatura.

O componente Incremental Data Loader transmite a parte da árvore de Merkle da assinatura pela API nativa do carregador de dados. O comando de shell do serviço package install-incremental aceita o arquivo de assinatura v4 sem informações codificado como Base64 como um parâmetro para cada arquivo adicionado. A árvore de Merkle correspondente precisa ser enviada para o stdin do comando.

apk_digest

apk_digest é o primeiro resumo de conteúdo disponível na ordem:

  1. V3, bloco de 1 MB, SHA2-512 (CONTENT_DIGEST_CHUNKED_SHA512)
  2. V3, bloco de 4 KB, SHA2-256 (CONTENT_DIGEST_VERITY_CHUNKED_SHA256)
  3. V3, bloco de 1 MB, SHA2-256 (CONTENT_DIGEST_CHUNKED_SHA256)
  4. V2, SHA2-512
  5. V2, SHA2-256

Consulte sequência de assinante com prefixo de comprimento no esquema de assinatura de APK v3.

Validação e teste

O processo de validação de APK v4 está ilustrado na figura a seguir:

Processo de validação de APK v4

Figura 1. Processo de validação de APK v4.

Valide a implementação usando testes de unidade de recursos e o CTS:

  • CtsIncrementalInstallHostTestCases
  • /android/cts/hostsidetests/incrementalinstall

Testar o formato da assinatura

Para testar o formato da assinatura, configure um ambiente de build e execute os seguintes testes manuais:

$ atest PackageManagerShellCommandTest
PackageManagerShellCommandIncrementalTest

Testar o formato de assinatura com o SDK do Android (ADB e apksigner)

Use este processo para testar o formato de assinatura com o SDK do Android:

  1. Configure um ambiente de build e verifique se você concluiu a implementação do IncFS.
  2. Atualize o build em um dispositivo físico ou emulador de destino.
  3. Gere ou obtenha um APK e crie uma chave de assinatura de depuração.
  4. Assine e instale o APK com o formato de assinatura v4 na pasta build-tools.

    Sinal
    $ ./apksigner sign --ks debug.keystore game.apk

    Instalar
    $ ./adb install game.apk

    Onde encontrar esses testes
    /android/cts/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java