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

Na tej stronie opisaliśmy różne metody, które pozwolą Ci sprawdzić, czy pakiet APK zainstalowany na urządzeniu z Androidem odpowiada twierdzeniom zawartym w pliku roszczeń. Polega to na pobraniu z urządzenia odpowiedniego pliku APK, sprawdzeniu integralności jego kodu i wykonaniu dowodu włączenia do dziennika wyodrębnionego artefaktu.

Proces weryfikacji

Dziennik przejrzystości jest realizowany za pomocą drzewa Merkla, które składa się z haszy. Węzeł liściowy zawiera dane, a węzeł nadrzędny zawiera hasz swoich węzłów podrzędnych.

Aby zweryfikować odporność na manipulację dzienników przejrzystości, w drzewie Merkla wykonuje się 2 rodzaje obliczeń: dowód na dołączenie i dowód na spójność. Pierwsza z nich potwierdza, że dziennik zawiera wpis odpowiadający konkretnej wersji pliku APK. Wpis w logu zawiera ciąg znaków identyfikatora SHA-256 tokenu podpisu kodu w formie tokenu internetowego JSON (JWT), który można uzyskać z odpowiednich plików APK. Drugi dowodzi, że gdy do drzewa zostaną dodane nowe wpisy, nowy punkt kontrolny jest (kryptograficznie) zgodny z poprzednią wersją drzewa.

Aby zweryfikować objęty treścią APK, wykonaj test potwierdzenia włączenia na podstawie punktu kontrolnego z możliwością weryfikacji. Planujemy zintegrować ten dziennik z publiczną siecią świadków za pomocą ujednoliconego protokołu świadków. Dzięki temu będziesz mieć kontrolny punkt z potwierdzeniem, który gwarantuje spójność dziennika.

Jeśli chcesz się przekonać, że pakiet APK na Twoim urządzeniu jest zgodny z roszczeniem zgłoszonym w ramach modelu roszczenia, zapoznaj się z informacjami poniżej.

Uwzględnienie

Użytkownik Androida może sprawdzić, czy objęty APK na jego urządzeniu znajduje się w rejestrze. Aby to zrobić, musi najpierw wyodrębnić plik APK i odpowiednie metadane, a potem porównać ponownie obliczony hasz główny z haszem głównym zawartym w opublikowanym punkcie kontrolnym. Jeśli są zgodne, użytkownik Androida może mieć pewność, że korzysta z pewnych zabezpieczeń opisanych w modelu zagrożeń.

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

Jak już wspomnieliśmy, listę plików APK, które są obecnie objęte sprawdzaniem, znajdziesz na stronie Przegląd.

Wymagania wstępne weryfikacji

Zanim przejdziesz do weryfikacji, czy wyodrębniony przez Ciebie plik APK jest zgodny z naszą rezerwacją, musisz zainstalować te narzędzia na komputerze połączonym z siecią.

Android Debug Bridge (ADB)

ADB to narzędzie, które komunikuje się z urządzeniem z Androidem. Jest dostępne na stronie narzędzi platformy Android SDK.

bundletool

bundletool to narzędzie służące do tworzenia pakietów aplikacji na Androida (AAB). Można go też użyć do konwertowania pakietów AAB na pliki APK, które można instalować na urządzeniach. Można go pobrać z GitHub.

Androguard

Androguard to zbiór narzędzi służących do analizowania plików APK. Można go pobrać i zainstalować na stronie Androguard.

Walidator dowodów na dołączenie

To moduł Go opublikowany w repozytorium Git w ramach projektu Android Open Source (AOSP) o nazwie avb. Narzędzie to może wysyłać zapytania do dziennika przejrzystości plików APK usług systemowych Google i wyprowadzać informacje o tym, czy dany pakiet jest uwzględniony w dzienniku. Przykład zastosowania tej funkcji znajdziesz w następnej sekcji.

Aby pobrać to narzędzie, musisz najpierw sklonować repozytorium avb.

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

Kod źródłowy weryfikatora znajduje się w tools/transparency/verify w repozytorium avb.

Tworzenie ładunku do weryfikacji

Aby potwierdzić, że wyodrębniony z urządzenia plik APK jest zgodny z naszą deklaracją, musisz utworzyć ładunek logu na podstawie informacji z pliku APK.

Zanim zaczniesz, sprawdź, czy na Twoim urządzeniu można używać adb. Aby to zrobić, włącz debugowanie ADB na urządzeniu.

Następnie znajdź miejsce na urządzeniu, w którym zainstalowano plik APK. W tym przewodniku użyjemy pliku APK weryfikatora kluczy systemowych Androida (com.google.android.contactkeys) jako przykładu.

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ższa komenda zwróci ścieżkę wskazującą miejsce instalacji pliku 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ć i potwierdzić nazwę pakietu pliku APK, który właśnie pobrałeś, musisz najpierw rozpakować plik APK, ponieważ jest on specjalnym rodzajem pliku ZIP.

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

W tym kroku rozpakowujesz wszystkie pliki, które składają się na plik APK. Nazwa pakietu i wersja znajdują się w pliku manifestu pakietu APK, który zwykle ma nazwę AndroidManifest.xml.

Uzyskany plik manifestu jest jednak w postaci binarnej, która nie jest czytelna dla człowieka. Aby przekonwertować binarne dane XML do postaci zrozumiałej dla człowieka, używamy narzędzia axml z pakietu androguard (które należy zainstalować w sekcji wymagania wstępne).

computer:/tmp/testdir$ androguard axml extracted/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1413" android:versionName="1.48.688082145" android:compileSdkVersion="35" android:compileSdkVersionCodename="VanillaIceCream" android:requiredSplitTypes="" android:splitTypes="" package="com.google.android.contactkeys" platformBuildVersionCode="35" platformBuildVersionName="VanillaIceCream">

Na podstawie powyższych danych możemy stwierdzić, ż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 haszowania mamy teraz wszystkie informacje potrzebne do złożenia ładunku dziennika zgodnie z formatem opisanym w sekcji Treść dziennika. W tym przykładzie odpowiadający ładunek logowania 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ć treści w pliku, np. payload.txt. Przyda się to później, gdy przeprowadzisz test na tematykę.

Weryfikowanie autentyczności podpisu kodu pliku APK

Teraz musimy zweryfikować autentyczność tokena sygnatury kodu wbudowanego w pliku APK. W tym celu używamy bundletool i klucza publicznego pary kluczy, która została użyta do podpisania. 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 instrukcje poniżej, aby przeprowadzić weryfikację podpisu kodu.

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

Teraz możesz użyć polecenia check-transparencybundletool, aby sprawdzić, czy podpis kodu w pliku APK kandydata jest zgodny z tym, który został opublikowany.

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 tego polecenia wskazują, że podpis przejrzystości koduprzejrzystość kodu zostały zweryfikowane. Jeśli nie są zgodne, np. 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 pliku APK może być naruszona i nie należy mu ufać. Pamiętaj, aby sprawdzić, czy weryfikujesz je na podstawie prawidłowego certyfikatu klucza publicznego. Jeśli wszystko inne jest w porządku, oznacza to, że autentyczność podpisu kodu została zweryfikowana w przypadku pliku APK, który sprawdzasz.

Weryfikacja dołączonych produktów (dowód dołączenia)

Korzystając z utworzonego wcześniej ładunku, możesz teraz sprawdzić, czy dany pakiet został uwzględniony w logu przejrzystości.

Narzędzie do weryfikacji włączenia zostało opublikowane w avb repozytorium w ramach Projektu Android Open Source. Aby 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_system_apk

Weryfikator używa odpowiedniego punktu kontrolnego i treści dziennika (znajdują się w katalogu płytek), aby sprawdzić, czy plik APK jest w dzienniku przejrzystości, i czy rzeczywiście został opublikowany przez Google.

Wynik polecenia jest zapisywany na wyjściu standardowym:

  • OK. inclusion check success! jeśli kod pakietu jest zawarty w logu,
  • FAILURE jeśli tak nie jest.