Pełna weryfikacja przejrzystości plików APK w usługach systemowych Google

Na tej stronie opisujemy różne metody zapewnienia, że plik APK zainstalowany na urządzeniu z Androidem odpowiada oświadczeniu złożonemu w modelu zgłaszającego. Obejmuje to pobranie danego pliku APK z urządzenia, sprawdzenie integralności jego kodu i przeprowadzenie weryfikacji włączenia do dziennika wyodrębnionego artefaktu.

Proces weryfikacji

Dziennik przejrzystości jest implementowany za pomocą drzewa Merkle składającego się z skrótów. Węzeł liścia zawiera dane, a węzeł nadrzędny zawiera skrót swoich węzłów podrzędnych.

Zasadniczo w drzewie Merkle wykonuje się 2 obliczenia, aby zweryfikować właściwość dzienników przejrzystości, która polega na tym, że nie można ich modyfikować: weryfikacja włączenia i weryfikacja spójności. Pierwsza z nich potwierdza, że dziennik zawiera wpis odpowiadający konkretnej wersji pliku APK. Wpis w dzienniku zawiera skrót, który jest skrótem SHA256 tokena podpisu kodu w postaci tokena internetowego JSON (JWT). Można go uzyskać z odpowiednich plików APK. Druga z nich potwierdza, że gdy do drzewa dodawane są nowe wpisy, nowy punkt kontrolny jest (kryptograficznie) zgodny z poprzednią wersją drzewa.

Aby zweryfikować objęty plik APK, przeprowadź test weryfikacji włączenia na podstawie zaobserwowanego punktu kontrolnego. Planujemy zintegrować ten dziennik z publiczną siecią obserwatorów za pomocą standardowego protokołu obserwatorów. Zapewni to zaobserwowany punkt kontrolny, który gwarantuje spójność dziennika.

Jeśli chcesz się przekonać, że plik APK na Twoim urządzeniu jest zgodny z oświadczeniem złożonym w modelu zgłaszającego, przeczytaj tekst poniżej.

Weryfikacja włączenia

Użytkownik Androida może sprawdzić, czy objęty plik APK na jego urządzeniu znajduje się w dzienniku. W tym celu musi najpierw wyodrębnić plik APK i jego odpowiednie metadane, a następnie porównać ponownie obliczony skrót główny ze skrótem głównym zawartym w opublikowanym punkcie kontrolnym. Jeśli są one zgodne, użytkownik Androida może mieć pewność, że korzysta z niektórych zabezpieczeń opisanych w modelu zagrożeń.

Jak sprawdzić, czy plik APK jest uwzględniony w dzienniku

Jak wspomnieliśmy wcześniej, listę obecnie objętych plików APK znajdziesz na stronie Przegląd.

Weryfikacja – wymagania wstępne

Zanim przejdziesz do weryfikacji, czy artefakt (oprogramowanie układowe lub plik APK) jest zgodny z oświadczeniem, upewnij się, że na komputerze połączonym z siecią są dostępne te narzędzia. W zależności od celu weryfikacji nie wszystkie z nich mogą być potrzebne.

Golang

Narzędzie do weryfikacji jest napisane w języku Go. Aby je skompilować, zainstaluj język Go w wersji 1.24 lub nowszej ze strony Go.

Android Debug Bridge (ADB)

ADB to narzędzie, które komunikuje się z urządzeniem z Androidem, aby sprawdzić obraz lub wyodrębnić pliki APK. Jest dostępne na stronie Android SDK Platform Tools website.

Weryfikator włączenia

Weryfikator włączenia to moduł Go, który wysyła zapytania do dziennika przejrzystości i informuje, czy artefakt jest uwzględniony w dzienniku. Jest on opublikowany w repozytorium GitHub android-binary-transparency pod adresem https://github.com/android/android-binary-transparency/tree/main.

Aby sklonować repozytorium AOSP avb:

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

Aby sklonować repozytorium GitHub android-binary-transparency:

computer:~$ git clone https://github.com/android/android-binary-transparency

AAPT2

AAPT2 (Android Asset Packaging Tool) to narzędzie do kompilacji używane do kompilowania i pakowania zasobów aplikacji na Androida. Można je znaleźć jako samodzielne narzędzie w Android SDK Build Tools w wersji 26.0.2 lub nowszej.

bundletool

bundletool to narzędzie używane do tworzenia pakietu Android App Bundle (AAB). Może też służyć do weryfikowania podpisów kodu APK. Można je pobrać z GitHub.

wget lub curl

wget i curl to narzędzia, których można używać do pobierania plików z internetu. Upewnij się, że masz zainstalowane jedno z tych narzędzi, ponieważ skrypty weryfikacyjne mogą z nich korzystać.

Avbtool

avbtool służy do obliczania skrótu VBMeta obrazów fabrycznych i weryfikacji plików binarnych. Po sklonowaniu repozytorium avb (jak pokazano w sekcji Weryfikator włączenia ) plik avbtool.py można znaleźć w sklonowanym katalogu avb. Dodaj ten katalog do zmiennej środowiskowej $PATH.

computer:~$ PATH=$PATH:DIRECTORY_CONTAINING_AVBTOOL

Fbpacktool

fbpacktool to skrypt w Pythonie używany do rozpakowywania poszczególnych komponentów w Pixel's bootloader.img. Nie jest on potrzebny, jeśli weryfikujesz pliki APK. To narzędzie jest ważne, ponieważ umożliwia avbtool.py wykrywanie obrazów wymaganych do weryfikacji.

Aby korzystać z tego narzędzia, pobierz te 3 skrypty w Pythonie do katalogu, w którym znajduje się avbtool.py, ustaw uprawnienia wykonywania dla fbpacktool.py, i utwórz wygodny link symboliczny o nazwie fbpacktool do fbpacktool.py.

Jeśli masz wget:

computer:dir_to_avbtool$ wget https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/fbpacktool.py \
https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/fbpack.py \
https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/packedstruct.py
computer:dir_to_avbtool$ chmod +x fbpacktool.py
computer:dir_to_avbtool$ ln -s fbpacktool.py fbpacktool

Jeśli masz curl:

computer:dir_to_avbtool$ curl -O https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/fbpacktool.py
computer:dir_to_avbtool$ curl -O https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/fbpack.py
computer:dir_to_avbtool$ curl -O https://source.android.com/devices/bootloader/tools/pixel/fw_unpack/packedstruct.py
computer:dir_to_avbtool$ chmod +x fbpacktool.py
computer:dir_to_avbtool$ ln -s fbpacktool.py fbpacktool

OpenSSL

openssl pomaga wykonywać różne operacje kryptograficzne za pomocą terminala w procesie weryfikacji. Upewnij się, że jest zainstalowany w systemie. Możesz to sprawdzić w terminalu, uruchamiając to polecenie i upewniając się, że zwrócony wynik nie jest pusty:

computer:~$ which openssl
/usr/bin/openssl

Tworzenie ładunku do weryfikacji

Aby sprawdzić, czy plik APK wyodrębniony z urządzenia jest zgodny z naszymi oświadczeniami, musisz utworzyć ładunek dziennika na podstawie informacji pochodzących z pliku APK.

Zanim zaczniesz, upewnij się, że adb może być używane na Twoim urządzeniu, włączając debugowanie adb na swoim urządzeniu.

Następnie znajdź miejsce, w którym plik APK jest zainstalowany na urządzeniu. Na potrzeby tego przewodnika jako przykładu użyjemy pliku APK weryfikatora kluczy systemowych Androida (com.google.android.contactkeys).

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

Jeśli na urządzeniu jest zainstalowany plik APK weryfikatora kluczy systemowych Androida, powyższe polecenie zwróci ścieżkę wskazującą miejsce jego instalacji na urządzeniu. W przeciwnym razie nie zobaczysz żadnych danych wyjściowych.

Następnie pobierz plik APK z urządzenia z Androidem na komputer, na którym pracujesz, za pomocą tego polecenia (pamiętaj, że rzeczywista lokalizacja i nazwa pliku APK na urządzeniu mogą się różnić):

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

Aby uzyskać, a tym samym zweryfikować nazwę pakietu właśnie pobranego pliku APK, musisz najpierw rozpakować plik APK, ponieważ jest to specjalny rodzaj pliku ZIP.

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

Ten krok rozpakowuje wszystkie pliki, z których składa się plik APK. Nazwę pakietu i wersję można znaleźć w manifeście pliku APK, który zwykle znajduje się w pliku o nazwie AndroidManifest.xml.

Uzyskany plik manifestu jest jednak w postaci binarnej, która nie jest czytelna dla człowieka. Aby przekonwertować binarny plik XML na postać czytelną dla człowieka, używamy narzędzia aapt2 zainstalowanego wcześniej (zgodnie z wymaganiami wstępnymi wstępnymi).

computer:/tmp/testdir$ aapt2 dump badging ./contactkeys_candidate.apk
package: name='com.google.android.contactkeys' versionCode='7805' versionName='1.219.791156583' platformBuildVersionName='Baklava' platformBuildVersionCode='36' compileSdkVersion='36' compileSdkVersionCodename='Baklava'

Z powyższych danych wyjściowych możemy teraz mieć pewność, że nazwa pakietu tego pliku APK to com.google.android.contactkeys, a numer wersji (versionCode) to 1413.

Teraz wyszukamy w pliku APK podpis przejrzystości kodu. Powinien to być plik o nazwie code_transparency_signed.jwt znajdujący się w folderze META-INF wśród innych wyodrębnionych plików z pliku APK.

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

Dzięki temu ciągowi skrótu mamy teraz wszystkie informacje potrzebne do utworzenia ładunku dziennika zgodnie z formatem opisanym w sekcji Zawartość dziennika. W tym przykładzie odpowiedni ładunek dziennika powinien wyglądać tak:

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

Zwróć też uwagę na znak nowego wiersza po wersji pakietu.

Możesz zapisać zawartość w pliku, np. payload.txt. Przyda się to później podczas testu weryfikacji włączenia.

Weryfikowanie autentyczności podpisu kodu APK

Teraz powinniśmy zweryfikować autentyczność tokena podpisu kodu osadzonego w pliku APK. Aby to zrobić, używamy bundletool i klucza publicznego pary kluczy, która została użyta do podpisania pliku. Są one publikowane w każdej sekcji odpowiednich plików APK. Zakładając, że certyfikat klucza publicznego (np. weryfikatora kluczy systemowych Androida) został zapisany w pliku o nazwie signing_cert_pubkey.pem, wykonaj weryfikację podpisu kodu zgodnie z instrukcjami poniżej.

Najpierw utwórz archiwum ZIP i dodaj do niego plik APK.

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

Możemy teraz użyć polecenia check-transparency narzędzia bundletool, aby sprawdzić, czy podpis kodu osadzony w pliku APK jest zgodny z opublikowanym.

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.

Upewnij się, że dane wyjściowe powyższego polecenia wskazują, że zweryfikowano zarówno podpis przejrzystości kodu, jak i przejrzystość kodu. Jeśli tak nie jest, np. jeśli widzisz dane wyjściowe takie jak Code transparency verification failed because the provided public key certificate does not match the transparency file, oznacza to, że integralność kodu danego pliku APK może być naruszona i nie należy mu ufać. Pamiętaj, aby sprawdzić, czy weryfikujesz plik za pomocą prawidłowego certyfikatu klucza publicznego. W przeciwnym razie, jeśli wszystko inne się zgadza, oznacza to, że autentyczność podpisu kodu została zweryfikowana w przypadku pliku APK, który sprawdzasz.

Weryfikowanie włączenia pakietu (weryfikacja włączenia)

Za pomocą utworzonego wcześniej ładunku możesz teraz sprawdzić, czy dany pakiet został uwzględniony w dzienniku przejrzystości.

Narzędzie do weryfikacji włączenia zostało opublikowane w avb repozytorium w ramach projektu Android Open Source Project. Aby je uruchomić:

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_1p_code

Weryfikator używa odpowiedniego punktu kontrolnego i zawartości dziennika (znajdującej się w katalogu tile), aby sprawdzić, czy ładunek pliku APK znajduje się w dzienniku przejrzystości , i potwierdzić, że został opublikowany przez Google.

Dane wyjściowe polecenia są zapisywane w stdout:

  • OK. inclusion check success! (OK. Weryfikacja włączenia zakończyła się pomyślnie), jeśli kod pakietu jest uwzględniony w dzienniku,
  • FAILURE (NIEPOWODZENIE), jeśli nie jest.