APK signature scheme v4

Android 11 supports a streaming-compatible signing scheme with the APK signature scheme v4. The v4 signature is based on the Merkle hash tree calculated over all bytes of the APK. The scheme follows the structure of the fs-verity hash tree exactly (for example, zero-padding the salt and zero-padding the last block). Android 11 stores the signature in a separate file, <apk name>.apk.idsig. A v4 signature requires a complementary v2 or v3 signature.

File format

All numeric fields are in little endian. All fields occupy exactly the number of bytes as their sizeof(), with no implicit padding or alignment added.

The following helper struct simplifies the definitions:

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

Main file content:

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 is the parameter used for hash tree generation plus the root hash:

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 is the following 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 is taken from the APK's v3 signing block. If that block isn't present, it's taken from the v2 block (see apk_digest).

To create and verify, a signature code must serialize the following data into binary blob and pass it into the signing and verifying algorithm as the signed data:

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 is the whole Merkle tree of the APK, calculated as described in the fs-verity documentation.

Producers and consumers

The apksigner Android SDK tool generates the v4 signature file if you run it with default parameters. You can disable v4 signing the same way as the other signing schemes. The tool can also verify if the v4 signature is valid.

adb expects the .apk.idsig file to be present next to the APK when running the adb install --incremental command. adb also uses the IDSIG file to try incremental installation by default, and falls back to a regular installation if it's missing or invalid.

When an installation session is created, the new streaming installation API in PackageInstaller accepts the stripped v4 signature as a separate argument when adding a file to the session. At this point, signing_info is passed into IncFS as a whole blob. IncFS extracts root hash from the blob.

When the installation session is being committed, PackageManagerService does an ioctl call to retrieve the signing_info blob from IncFS, parses it, and verifies the signature.

The Incremental Data Loader component streams the Merkle tree part of the signature through the data loader native API. The package service shell command install-incremental accepts the stripped v4 signature file encoded as Base64 as a parameter for each added file. The corresponding Merkle tree has to be sent into the command's stdin.

apk_digest

apk_digest is the first available content digest in order:

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

See length-prefixed sequence of length-prefixed signer in APK signature scheme v3.

Validation and testing

The APK validation process v4 is illustrated in the following figure:

APK validation process v4

Figure 1. APK validation process v4.

Validate the implementation using feature unit tests and CTS:

  • CtsIncrementalInstallHostTestCases
  • /android/cts/hostsidetests/incrementalinstall

Test the signature format

To test the signature format, set up a build environment and run the following manual tests:

$ atest PackageManagerShellCommandTest
PackageManagerShellCommandIncrementalTest

Test the signature format with Android SDK (ADB and apksigner)

Use this process test the signature format with Android SDK:

  1. Set up a build environment and ensure you've completed the implementation of IncFS.
  2. Flash the build on a target physical device or emulator.
  3. Generate or obtain an existing APK and then create a debug signing key.
  4. Sign and install the APK with v4 signature format from the build-tools folder.

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

    Install
    $ ./adb install game.apk

    Where these tests can be found
    /android/cts/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java