Architektura podpisywania na urządzeniu

Od Androida 12 moduł Android Runtime (ART) jest modułem Mainline. Aktualizacja modułu może wymagać ponownego utworzenia artefaktów kompilacji AOT (ahead-of-time) plików JAR bootclasspath i serwera systemowego. Ponieważ te artefakty są wrażliwe pod względem bezpieczeństwa, Android 12 korzysta z funkcji podpisywania na urządzeniu, aby zapobiec ich modyfikacji. Na tej stronie opisujemy architekturę podpisywania na urządzeniu i jej interakcje z innymi funkcjami zabezpieczeń Androida.

Projekt wysokiego poziomu

Podpisywanie na urządzeniu ma 2 główne komponenty:

  • odrefresh jest częścią modułu ART Mainline. Odpowiada za generowanie artefaktów środowiska wykonawczego. Sprawdza istniejące artefakty z zainstalowaną wersją modułu ART, plikami JAR ścieżki rozruchowej i plikami JAR serwera systemowego, aby określić, czy są aktualne, czy wymagają ponownego wygenerowania. Jeśli trzeba je ponownie wygenerować, odrefresh generuje je i przechowuje.

  • odsign to plik binarny, który jest częścią platformy Android. Jest on uruchamiany podczas wczesnego rozruchu, zaraz po zamontowaniu partycji /data. Jego głównym zadaniem jest wywoływanie usługi odrefresh w celu sprawdzenia, czy należy wygenerować lub zaktualizować jakieś artefakty. W przypadku wszystkich nowych lub zaktualizowanych artefaktów, które odrefreshgeneruje, odsign oblicza funkcję skrótu. Wynik takiego obliczenia haszu jest nazywany skrótem pliku. W przypadku wszystkich artefaktów, które już istnieją, odsign sprawdza, czy skróty istniejących artefaktów są zgodne ze skrótami obliczonymi wcześniej przez odsign. Dzięki temu masz pewność, że artefakty nie zostały zmodyfikowane.

W przypadku błędów, np. gdy skrót pliku nie pasuje, odrefreshodsign odrzucają wszystkie istniejące artefakty w /data i próbują je ponownie wygenerować. Jeśli to się nie uda, system przełączy się na tryb JIT.

odrefreshodsign są chronione przez dm-verity i stanowią część łańcucha weryfikacji podczas uruchamiania Androida.

Obliczanie skrótów plików za pomocą fs-verity

fs-verity to funkcja jądra systemu Linux, która przeprowadza weryfikację danych plików na podstawie drzewa Merkle. Włączenie weryfikacji systemu plików w przypadku pliku powoduje, że system plików tworzy drzewo Merkle na podstawie danych pliku przy użyciu identyfikatorów SHA-256, przechowuje je w ukrytej lokalizacji obok pliku i oznacza plik jako tylko do odczytu. Weryfikacja systemu plików automatycznie weryfikuje dane pliku na podstawie drzewa Merkle na żądanie podczas odczytu. Weryfikacja systemu plików udostępnia główny identyfikator drzewa Merkle jako wartość o nazwie identyfikator pliku weryfikacji systemu plików i zapewnia, że wszystkie dane odczytane z pliku są zgodne z tym identyfikatorem.

odsign wykorzystuje fs-verity do zwiększania wydajności uruchamiania przez optymalizację kryptograficznego uwierzytelniania skompilowanych artefaktów na urządzeniu w momencie uruchamiania. Gdy wygenerowany zostanie artefakt, odsign włącza w nim fs-verity. Gdy odsignweryfikuje artefakt, sprawdza skrót pliku fs-verity zamiast pełnego skrótu pliku. Eliminuje to konieczność odczytywania i haszowania wszystkich danych artefaktu podczas uruchamiania. Dane artefaktu są zamiast tego haszowane na żądanie przez fs-verity w miarę ich używania, blok po bloku.

Na urządzeniach, których jądro nie obsługuje fs-verity, odsign wraca do obliczania skrótów plików w przestrzeni użytkownika. odsign używa tego samego algorytmu haszującego opartego na drzewie Merkle co fs-verity, więc w obu przypadkach skróty są takie same.odsignfs-verity jest wymagany na wszystkich urządzeniach, które zostały wprowadzone na rynek z Androidem 11 lub nowszym.

Przechowywanie skrótów plików

odsign przechowuje skróty plików artefaktów w osobnym pliku o nazwie odsign.info. Aby mieć pewność, że odsign.info nie zostało zmodyfikowane, odsign.info jest podpisane kluczem podpisu, który ma ważne właściwości zabezpieczające. W szczególności klucz może być generowany i używany tylko podczas wczesnego rozruchu, kiedy działa tylko zaufany kod. Więcej informacji znajdziesz w sekcji Zaufane klucze podpisywania.

Weryfikacja skrótów plików

Podczas każdego uruchomienia, jeśli odrefresh stwierdzi, że istniejące artefakty są aktualne, odsign sprawdza, czy pliki nie zostały zmodyfikowane od czasu ich wygenerowania. odsign weryfikuje to, sprawdzając skróty plików. Najpierw weryfikuje podpis odsign.info. Jeśli podpis jest prawidłowy, usługa odsign sprawdza, czy skrót każdego pliku jest zgodny z odpowiednim skrótem w odsign.info.

Zaufane klucze podpisywania

Android 12 wprowadza nową funkcję Keystore o nazwie klucze etapu rozruchu, która rozwiązuje te problemy z bezpieczeństwem:

  • Co uniemożliwia atakującemu użycie naszego klucza podpisywania do podpisania własnej wersji odsign.info?
  • Co uniemożliwia atakującemu wygenerowanie własnego klucza podpisywania i użycie go do podpisania własnej wersji odsign.info?

Klucze etapu rozruchu dzielą cykl rozruchu Androida na poziomy i kryptograficznie wiążą tworzenie i używanie klucza z określonym poziomem. odsign tworzy klucz podpisu na wczesnym etapie, gdy działa tylko zaufany kod, chroniony przez dm-verity.

Poziomy etapu rozruchu są numerowane od 0 do magicznej liczby 1000000000. Podczas procesu uruchamiania Androida możesz zwiększyć poziom uruchamiania, ustawiając właściwość systemu z init.rc. Na przykład ten kod ustawia poziom uruchamiania na 10:

setprop keystore.boot_level 10

Klienci Keystore mogą tworzyć klucze powiązane z określonym poziomem uruchamiania. Jeśli na przykład utworzysz klucz dla poziomu rozruchu 10, będzie on mógł być używany tylko wtedy, gdy urządzenie jest na poziomie rozruchu 10.

odsign używa poziomu rozruchu 30, a utworzony przez niego klucz podpisywania jest powiązany z tym poziomem rozruchu. Przed użyciem klucza do podpisywania artefaktów odsign sprawdza, czy klucz jest powiązany z poziomem rozruchu 30.

Zapobiega to 2 atakom opisanym wcześniej w tej sekcji:

  • Atakujący nie mogą użyć wygenerowanego klucza, ponieważ zanim będą mieli szansę na uruchomienie złośliwego kodu, poziom rozruchu wzrośnie powyżej 30, a Keystore odmówi operacji, które używają tego klucza.
  • Atakujący nie mogą utworzyć nowego klucza, ponieważ zanim będą mieli szansę na uruchomienie złośliwego kodu, poziom rozruchu wzrośnie powyżej 30, a Keystore odmówi utworzenia nowego klucza z tym poziomem rozruchu. Jeśli osoba atakująca utworzy nowy klucz, który nie jest powiązany z poziomem rozruchu 30, urządzenie odsign go odrzuci.

Keystore zapewnia prawidłowe egzekwowanie poziomu rozruchu. W sekcjach poniżej znajdziesz więcej informacji o tym, jak to zrobić w przypadku różnych wersji KeyMint (wcześniej Keymaster).

Implementacja Keymastera 4.0

Różne wersje Keymastera inaczej obsługują implementację kluczy etapu rozruchu. Na urządzeniach z TEE/StrongBox w wersji Keymaster 4.0 Keymaster obsługuje implementację w ten sposób:

  1. Podczas pierwszego uruchomienia Keystore tworzy klucz symetryczny K0 z tagiem MAX_USES_PER_BOOT ustawionym na 1. Oznacza to, że klucza można użyć tylko raz podczas uruchamiania.
  2. Jeśli podczas uruchamiania poziom uruchamiania zostanie zwiększony, nowy klucz dla tego poziomu uruchamiania można wygenerować z K0 za pomocą funkcji HKDF: Ki+i=HKDF(Ki, "some_fixed_string"). Jeśli na przykład przejdziesz z poziomu rozruchu 0 na poziom 10, funkcja HKDF zostanie wywołana 10 razy, aby wyprowadzić klucz K10 z klucza K0.
  3. Gdy poziom rozruchu się zmieni, klucz poprzedniego poziomu rozruchu zostanie usunięty z pamięci, a klucze powiązane z poprzednimi poziomami rozruchu nie będą już dostępne.

    Klucz K0 to klucz MAX_USES_PER_BOOT=1. Oznacza to, że nie można go użyć później podczas uruchamiania, ponieważ zawsze następuje co najmniej jedna zmiana poziomu uruchamiania (do poziomu końcowego).

Gdy klient Keystore, np. odsign, zażąda utworzenia klucza na poziomie rozruchu i, jego obiekt blob zostanie zaszyfrowany kluczem Ki. Ponieważ Ki nie jest dostępny po poziomie rozruchu i, tego klucza nie można utworzyć ani odszyfrować na późniejszych etapach rozruchu.

Implementacja Keymastera 4.1 i KeyMint 1.0

Implementacje Keymaster 4.1 i KeyMint 1.0 są w dużej mierze takie same jak implementacja Keymaster 4.0. Główna różnica polega na tym, że K0 nie jest kluczem MAX_USES_PER_BOOT, ale kluczem EARLY_BOOT_ONLY, który został wprowadzony w Keymasterze 4.1. Klucza EARLY_BOOT_ONLY można używać tylko we wczesnych fazach rozruchu, gdy nie jest uruchomiony żaden niezaufany kod. Zapewnia to dodatkowy poziom ochrony: w implementacji Keymaster 4.0 atakujący, który naruszy system plików i SELinux, może zmodyfikować bazę danych Keystore, aby utworzyć własny klucz MAX_USES_PER_BOOT=1 do podpisywania artefaktów. Taki atak jest niemożliwy w przypadku implementacji Keymaster 4.1 i KeyMint 1.0, ponieważ EARLY_BOOT_ONLYklucze można tworzyć tylko podczas wczesnego rozruchu.

Publiczny komponent zaufanych kluczy podpisywania

odsign pobiera z magazynu kluczy komponent klucza publicznego klucza podpisywania. Jednak Keystore nie pobiera tego klucza publicznego z TEE/SE, w którym znajduje się odpowiedni klucz prywatny. Zamiast tego pobiera klucz publiczny z własnej bazy danych na dysku. Oznacza to, że atakujący, który uzyska dostęp do systemu plików, może zmodyfikować bazę danych magazynu kluczy, aby zawierała klucz publiczny będący częścią pary kluczy publiczny/prywatny, nad którą ma kontrolę.

Aby zapobiec temu atakowi, odsign tworzy dodatkowy klucz HMAC o tym samym poziomie rozruchu co klucz podpisu. Następnie podczas tworzenia klucza podpisu odsignużywa tego klucza HMAC do utworzenia podpisu klucza publicznego i zapisuje go na dysku. Podczas kolejnych uruchomień, gdy pobierany jest klucz publiczny klucza podpisywania, używany jest klucz HMAC do sprawdzenia, czy podpis na dysku pasuje do podpisu pobranego klucza publicznego. Jeśli są zgodne, klucz publiczny jest wiarygodny, ponieważ klucz HMAC może być używany tylko na wczesnych poziomach rozruchu, a więc nie mógł zostać utworzony przez atakującego.