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ługiodrefresh
w celu sprawdzenia, czy należy wygenerować lub zaktualizować jakieś artefakty. W przypadku wszystkich nowych lub zaktualizowanych artefaktów, któreodrefresh
generuje,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 przezodsign
. Dzięki temu masz pewność, że artefakty nie zostały zmodyfikowane.
W przypadku błędów, np. gdy skrót pliku nie pasuje, odrefresh
i odsign
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.
odrefresh
i odsign
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 odsign
weryfikuje 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.odsign
fs-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:
- Podczas pierwszego uruchomienia Keystore tworzy klucz symetryczny K0 z tagiem
MAX_USES_PER_BOOT
ustawionym na1
. Oznacza to, że klucza można użyć tylko raz podczas uruchamiania. - 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. 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_ONLY
klucze 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 odsign
uż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.