Schemat podpisu plików APK w wersji 4

Android 11 obsługuje schemat podpisywania zgodny ze strumieniowaniem, czyli schemat podpisu plików APK w wersji 4. Podpis w wersji 4 jest oparty na drzewie skrótów Merkle obliczonym na podstawie wszystkich bajtów pliku APK. Schemat jest zgodny ze strukturą drzewa skrótów fs-verity (np. dopełnianie solą zerami i dopełnianie ostatniego bloku zerami). Android 11 przechowuje podpis w osobnym pliku <apk name>.apk.idsig. Podpis w wersji 4 wymaga uzupełniającego podpisu w wersji 2 lub 3.

Format pliku

Wszystkie pola liczbowe są zapisywane w formacie little endian. Wszystkie pola zajmują dokładnie tyle bajtów, ile wynosi ich sizeof(), bez dodawania domyślnego dopełnienia ani wyrównania.

Poniższa struktura pomocnicza upraszcza definicje:

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

Zawartość pliku głównego:

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 to parametr używany do generowania drzewa skrótów i skrótu głównego:

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 to struktura:

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 jest pobierany z bloku podpisywania w wersji 3 pliku APK. Jeśli tego bloku nie ma, jest on pobierany z bloku v2 (patrz apk_digest).

Aby utworzyć i zweryfikować kod, musi on signatureserializowaćsignature te dane do postaci binarnego obiektu blob i przekazać je do algorytmu podpisywania i weryfikacji jako podpisane dane:

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 to całe drzewo Merkle pliku APK obliczone zgodnie z opisem w dokumentacji fs-verity.

producenci i konsumenci,

Narzędzie apksignerAndroid SDK generuje plik podpisu w wersji 4 po uruchomieniu go z parametrami domyślnymi. Podpisywanie w wersji 4 możesz wyłączyć w taki sam sposób jak inne schematy podpisywania. Narzędzie może też sprawdzić, czy podpis w wersji 4 jest prawidłowy.

adb oczekuje, że plik .apk.idsig będzie znajdować się obok pliku APK podczas uruchamiania polecenia adb install --incremental. adb domyślnie używa też pliku IDSIG do próby instalacji przyrostowej, a jeśli plik jest nieprawidłowy lub go brakuje, wraca do zwykłej instalacji.

Gdy tworzona jest sesja instalacji, nowe API instalacji strumieniowej w PackageInstaller akceptuje uproszczony podpis w wersji 4 jako osobny argument podczas dodawania pliku do sesji. W tym momencie wartość signing_info jest przekazywana do IncFS jako cały obiekt binarny. IncFS wyodrębnia z blobu hash główny.

Podczas zatwierdzania sesji instalacji usługa PackageManagerService wykonuje wywołanie ioctl, aby pobrać z IncFS signing_info, przeanalizować go i zweryfikować podpis.

Komponent Incremental Data Loader przesyła strumieniowo część drzewa Merkle podpisu za pomocą natywnego interfejsu API narzędzia do wczytywania danych. Polecenie powłoki usługi package install-incremental przyjmuje jako parametr każdego dodanego pliku plik podpisu v4 bez dodatkowych informacji zakodowany w formacie Base64. Odpowiednie drzewo Merkle musi zostać wysłane do stdin polecenia.

apk_digest

apk_digest to pierwszy dostępny w kolejności skrót treści:

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

Zobacz sekwencję sygnatariusza z prefiksem długości w schemacie podpisu plików APK w wersji 3.

Weryfikacja i testowanie

Proces weryfikacji plików APK w wersji 4 przedstawiono na tym rysunku:

Proces weryfikacji plików APK w wersji 4

Rysunek 1. Proces weryfikacji plików APK w wersji 4.

Sprawdź wdrożenie za pomocą testów jednostkowych funkcji i CTS:

  • CtsIncrementalInstallHostTestCases
  • /android/cts/hostsidetests/incrementalinstall

Testowanie formatu podpisu

Aby przetestować format podpisu, skonfiguruj środowisko kompilacji i przeprowadź te testy ręczne:

$ atest PackageManagerShellCommandTest
PackageManagerShellCommandIncrementalTest

Testowanie formatu podpisu za pomocą pakietu Android SDK (ADB i apksigner)

Aby przetestować format podpisu za pomocą pakietu Android SDK:

  1. Skonfiguruj środowisko kompilacji i upewnij się, że masz wdrożoną funkcję IncFS.
  2. Wgraj kompilację na docelowe urządzenie fizyczne lub emulator.
  3. Wygeneruj lub uzyskaj istniejący plik APK, a następnie utwórz klucz podpisywania debugowania.
  4. Podpisz i zainstaluj plik APK w formacie podpisu v4 z folderu build-tools.

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

    Zainstaluj
    $ ./adb install game.apk

    Gdzie można znaleźć te testy
    /android/cts/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java