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. It 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.idsigA 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(), no implicit padding or alignment added.

Below is a helper struct to simplify 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 parameters used for hash tree generation + 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, or, if not present, from the v2 block (see apk_digest)

To create and verify a signature code has to serialize the following data into binary blob and pass it into the signing / 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;
};
  1. merkle_tree is the whole Merkle tree of the APK, calculated as described in the fs-verity documentation.

Producers and consumers

apksigner Android SDK tool now generates the v4 signature file if you run it with default parameters. v4 signing can be disabled the same way as the other signing schemes. It 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
It will also use the .idsig file to try Incremental installation by default, and will fall back to a regular installation if it's missing or invalid.

When an installation session is created, the new streaming installation API in the 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 to retrieve the signing_info blob from incfs, parses it and verifies the signature.

Incremental Data Loader component is expected to stream the Merkle tree part of the signature through the data loader native API.
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, 1MB block, SHA2-512 (CONTENT_DIGEST_CHUNKED_SHA512),
  2. V3, 4KB block, SHA2-256 (CONTENT_DIGEST_VERITY_CHUNKED_SHA256),
  3. V3, 1MB block, SHA2-256 (CONTENT_DIGEST_CHUNKED_SHA256),
  4. V2, SHA2-512,
  5. V2, SHA2-256.

See length-prefixed sequence of length-prefixed signatures in APK Signature Scheme v3.

apk validation process v4
Figure 1: APK validation process v4

Validation and testing

Validate the implementation using Feature Unit Tests and CTS.

  • CtsIncrementalInstallHostTestCases
    • /android/cts/hostsidetests/incrementalinstall

Testing the signature format

To test the signature format, setup a development environment and run the following manual tests:

$ atest PackageManagerShellCommandTest
PackageManagerShellCommandIncrementalTest

Testing Signature Format with Android SDK (ADB and apksigner)

To test the signature format with Android SDK, setup a development environment and ensure you have completed the implementation of IncFS. Then flash the build on a target physical device or emulator. You need to generate or obtain an existing APK and then create a debug signing key. Finally, 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 can these tests be found?

/android/cts/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java