Architektura podpisywania na urządzeniu

Od Androida 12 moduł Android Runtime (ART) jest modułem Mainline. Aktualizacja modułu może wymagać ponownego skompilowania artefaktów kompilacji AOT (z wyprzedzeniem) z bootclasspathjars i serwera systemowego. Ponieważ te artefakty są wrażliwe na zagrożenia, Android 12 korzysta z funkcji podpisywania na urządzeniu, aby zapobiec ich modyfikowaniu. Ta strona zawiera informacje o architekturze podpisywania na urządzeniu i jej interakcji z innymi funkcjami zabezpieczeń Androida.

Projektowanie ogólne

Podpisywanie na urządzeniu składa się z 2 głównych elementów:

  • odrefresh jest częścią głównego modułu ART. Jest odpowiedzialny za generowanie artefaktów w czasie działania. Porównuje istniejące artefakty z zainstalowaną wersją modułu ART, plików JAR bootclasspath i plików JAR serwera systemowego, aby określić, czy są one aktualne, czy trzeba je wygenerować ponownie. Jeśli trzeba je wygenerować ponownie, odrefresh wygeneruje je i zapisze.

  • odsign to plik binarny, który jest częścią platformy Android. Uruchamia się podczas wczesnego rozruchu, tuż po zamontowaniu partycji /data. Jego główną odpowiedzialnością jest wywołanie odrefresh w celu sprawdzenia, czy należy wygenerować lub zaktualizować jakiekolwiek artefakty. W przypadku nowych lub zaktualizowanych artefaktów generowanych przez odrefresh odsign oblicza funkcję haszowania. Wynik takiego obliczenia nazywamy sumarycznym wartością haszową pliku. W przypadku wszystkich istniejących artefaktów odsign sprawdza, czy ich podsumowania są zgodne z podsumowaniami 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, odrefresh i odsign odrzucają wszystkie istniejące elementy w /data i próbują je wygenerować ponownie. Jeśli to się nie uda, system przełączy się na tryb JIT.

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

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

fs-verity to funkcja jądra Linuxa, która weryfikuje dane pliku na podstawie drzewa Merkle. Włączenie fs-verity w pliku powoduje, że system plików tworzy drzewo Merkla na podstawie danych pliku za pomocą funkcji SHA-256, przechowuje je w ukrytej lokalizacji obok pliku i oznacza plik jako tylko do odczytu. fs-verity automatycznie weryfikuje dane pliku na podstawie drzewa Merkla na żądanie, gdy jest on odczytywany. fs-verity udostępnia hasz główny drzewa Merkla jako wartość o nazwie fs-verity file digest (treść pliku w formacie digest) i zapewnia, że wszystkie dane odczytane z pliku są zgodne z tym treścią.

odsign używa fs-verity, aby poprawić wydajność uruchamiania przez optymalizację uwierzytelniania kryptograficznego artefaktów skompilowanych na urządzeniu w momencie uruchamiania. Po wygenerowaniu artefaktu odsign włącza fs-verity. Podczas weryfikacji artefaktu odsign weryfikuje skrót pliku fs-verity zamiast pełnego hasha pliku. Dzięki temu nie trzeba odczytywać i zaszyfrowywać wszystkich danych artefaktu podczas uruchamiania. Zamiast tego dane artefaktu są szyfrowane 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 używa domyślnie obliczonych digestów plików w przestrzeni użytkownika. odsign używa tego samego algorytmu haszowania na podstawie drzewa Merkla co fs-verity, więc w obu przypadkach wyniki są takie same. fs-verity jest wymagany na wszystkich urządzeniach z Androidem 11 lub nowszym.

przechowywanie skrótów plików;

odsign przechowuje podsumowania plików artefaktów w osobnym pliku o nazwie odsign.info. Aby mieć pewność, że odsign.info nie został zmodyfikowany, odsign.info jest podpisany kluczem podpisującym, który ma ważne właściwości zabezpieczeń. W szczególności klucz może być generowany i używany tylko podczas wczesnego uruchamiania, gdy działa tylko zaufany kod. Więcej informacji znajdziesz w artykule Zaufane klucze podpisywania.

Weryfikacja skrótów plików

Podczas każdego uruchamiania, jeśli odrefresh stwierdzi, że istniejące elementy są aktualne, odsign sprawdza, czy pliki nie zostały zmodyfikowane od momentu ich wygenerowania. odsign robi to, weryfikując podsumowania plików. Najpierw weryfikuje podpis odsign.info. Jeśli podpis jest prawidłowy, 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 boot stage keys, 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żywanie go do podpisywania własnej wersji odsign.info?

Klucze etapów rozruchu dzielą cykl rozruchu Androida na poziomy i za pomocą kryptografii powiązane są z tworzeniem i używaniem klucza na określonym poziomie. odsign tworzy klucz podpisywania 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 poziomu init.rc. Na przykład ten kod ustawia poziom rozruchu 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 klucz podpisywania, który tworzy, jest powiązany z tym poziomem. Zanim odsign użyje klucza do podpisania artefaktów, sprawdza, czy klucz jest powiązany z poziomem uruchamiania 30.

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

  • Użytkownicy atakujący nie mogą używać wygenerowanego klucza, ponieważ do czasu, gdy będą mogli uruchomić złośliwy kod, poziom rozruchu wzrośnie powyżej 30, a Keystore odrzuci operacje korzystające z klucza.
  • Atakujący nie mogą utworzyć nowego klucza, ponieważ zanim zdążą uruchomić złośliwy kod, poziom rozruchu wzrośnie powyżej 30, a Keystore odmówi utworzenia nowego klucza na tym poziomie. Jeśli atakujący utworzy nowy klucz, który nie jest powiązany z poziomem rozruchu 30, odsign go odrzuci.

Sklep kluczy zapewnia prawidłowe egzekwowanie poziomu rozruchu. W następnych sekcjach znajdziesz więcej informacji o tym, jak to zrobić w przypadku różnych wersji Keymaster.

Implementacja Keymastera 4.0

Różne wersje Keymastera obsługują implementację kluczy na etapie uruchamiania w inny sposób. Na urządzeniach z Keymaster 4.0 TEE/Strongbox implementacja Keymaster działa 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 klucz można użyć tylko raz podczas uruchamiania.
  2. Jeśli podczas uruchamiania zwiększy się poziom rozruchu, z K0 można wygenerować nowy klucz dla tego poziomu za pomocą funkcji HKDF: Ki+i=HKDF(Ki, "some_fixed_string"). Jeśli na przykład przechodzisz z poziom boot 0 na boot level 10, HKDF jest wywoływany 10 razy, aby wyprowadzić K10 z K0.
  3. Gdy zmieni się poziom rozruchu, 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 też użyć tego klucza później podczas uruchamiania, ponieważ zawsze występuje co najmniej jeden poziom uruchamiania (do ostatniego poziomu).

Gdy klient Keystore, np. odsign, poprosi o utworzenie klucza na poziomie uruchamiania i, jego blob zostanie zaszyfrowany kluczem Ki. Klucz Ki jest niedostępny po poziomie rozruchu i, więc nie można go utworzyć ani odszyfrować na późniejszych etapach rozruchu.

Wdrożenie Keymaster 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 klawiszem MAX_USES_PER_BOOT, ale EARLY_BOOT_ONLY, który został wprowadzony w Keymaster 4.1. Klucz EARLY_BOOT_ONLY może być używany tylko we wczesnych fazach rozruchu, gdy nie jest wykonywany żaden niesprawdzony kod. Zapewnia to dodatkowy poziom ochrony: w ramach implementacji Keymaster 4.0 atakujący, który skompromituje system plików i SELinux, może zmodyfikować bazę danych Keystore, aby utworzyć własny klucz MAX_USES_PER_BOOT=1, za pomocą którego będzie podpisywać artefakty. Taki atak jest niemożliwy w przypadku implementacji Keymaster 4.1 i KeyMint 1.0, ponieważ klucze EARLY_BOOT_ONLY można utworzyć tylko podczas wczesnego uruchamiania.

Publiczny składnik zaufanych kluczy podpisujących

odsign pobiera komponent klucza publicznego klucza podpisywania z magazynu kluczy. Keystore nie pobiera jednak tego klucza publicznego z urządzenia TEE/SE, które przechowuje odpowiadający mu klucz prywatny. Zamiast tego pobiera klucz publiczny ze swojej własnej bazy danych na dysku. Oznacza to, że atakujący, który złamie system plików, może zmodyfikować bazę danych Keystore, aby zawierała klucz publiczny, który jest częścią pary kluczy publiczno-prywatnych znajdujących się pod jego kontrolą.

Aby zapobiec temu atakowi, odsign tworzy dodatkowy klucz HMAC na tym samym poziomie rozruchu co klucz podpisywania. Następnie, podczas tworzenia klucza podpisywania odsignużywa tego klucza HMAC do utworzenia podpisu klucza publicznego i przechowuje go na dysku. Podczas kolejnych rozruchów, pobieranie klucza publicznego klucza podpisującego odbywa się za pomocą klucza HMAC, aby sprawdzić, czy podpis na dysku jest zgodny z podpisem pobranego klucza publicznego. Jeśli są zgodne, klucz publiczny jest wiarygodny, ponieważ klucz HMAC może być używany tylko na wczesnych poziomach uruchamiania, a zatem nie mógł zostać utworzony przez atakującego.