Magazyn kluczy wspierany sprzętowo

Dostępność zaufanego środowiska wykonawczego w systemie na chipie (SoC) daje urządzeniom z Androidem możliwość świadczenia wspieranych sprzętowo, silnych usług bezpieczeństwa dla systemu operacyjnego Android, usług platformy, a nawet aplikacji innych firm. Deweloperzy poszukujący rozszerzeń specyficznych dla systemu Android powinni przejść do android.security.keystore .

Przed Androidem 6.0 Android miał już prosty sprzętowy interfejs API usług kryptograficznych, dostarczany przez wersje 0.2 i 0.3 warstwy abstrakcji sprzętowej Keymaster (HAL). Keystore zapewnia cyfrowe podpisywanie i operacje weryfikacji, a także generowanie i import asymetrycznych par kluczy do podpisywania. Jest to już zaimplementowane na wielu urządzeniach, ale istnieje wiele celów związanych z bezpieczeństwem, których nie można łatwo osiągnąć za pomocą samego API podpisu. Magazyn kluczy w systemie Android 6.0 rozszerzył interfejs API magazynu kluczy, aby zapewnić szerszy zakres możliwości.

W Androidzie 6.0 Keystore dodał symetryczne prymitywy kryptograficzne , AES i HMAC oraz system kontroli dostępu do kluczy wspieranych sprzętowo. Kontrola dostępu jest określana podczas generowania klucza i egzekwowana przez cały okres ważności klucza. Klucze można ograniczyć do użytku dopiero po uwierzytelnieniu użytkownika i tylko do określonych celów lub z określonymi parametrami kryptograficznymi. Aby uzyskać więcej informacji, zobacz strony Znaczniki autoryzacji i Funkcje .

Oprócz rozszerzenia zakresu prymitywów kryptograficznych, Keystore w systemie Android 6.0 dodał:

  • Schemat kontroli użycia umożliwiający ograniczenie użycia klucza w celu ograniczenia ryzyka naruszenia bezpieczeństwa w wyniku niewłaściwego użycia kluczy
  • Schemat kontroli dostępu umożliwiający ograniczenie kluczy do określonych użytkowników, klientów i określonego przedziału czasowego

W systemie Android 7.0 Keymaster 2 dodał obsługę poświadczania klucza i wiązania wersji. Zaświadczanie klucza zapewnia certyfikaty klucza publicznego, które zawierają szczegółowy opis klucza i jego kontroli dostępu, aby umożliwić zdalną weryfikację istnienia klucza w bezpiecznym sprzęcie i jego konfiguracji.

Wiązanie wersji wiąże klucze z wersją na poziomie systemu operacyjnego i poprawki. Dzięki temu osoba atakująca, która wykryje słabość w starej wersji systemu lub oprogramowania TEE, nie będzie mogła przywrócić urządzenia do wersji podatnej na ataki i użyć kluczy utworzonych w nowszej wersji. Ponadto, gdy klucz z daną wersją i poziomem poprawki jest używany na urządzeniu, które zostało zaktualizowane do nowszej wersji lub poziomu poprawki, klucz jest aktualizowany przed użyciem, a poprzednia wersja klucza traci ważność. Gdy urządzenie jest aktualizowane, klawisze „zaskakują” do przodu wraz z urządzeniem, ale każde przywrócenie urządzenia do poprzedniej wersji powoduje, że klawisze stają się bezużyteczne.

W systemie Android 8.0 Keymaster 3 przeszedł ze starej warstwy HAL (Hardware Abstraction Layer) o strukturze C do interfejsu C++ HAL wygenerowanego na podstawie definicji w nowym języku HIDL (Hardware Interface Definition Language). W ramach tej zmiany zmieniło się wiele typów argumentów, chociaż typy i metody odpowiadają starym typom i metodom struktury HAL. Zobacz stronę Funkcje , aby uzyskać więcej informacji.

Oprócz tej wersji interfejsu Android 8.0 rozszerzył funkcję atestacji Keymaster 2 o obsługę atestacji tożsamości . Zaświadczanie tożsamości zapewnia ograniczony i opcjonalny mechanizm silnego potwierdzania identyfikatorów sprzętu, takich jak numer seryjny urządzenia, nazwa produktu i identyfikator telefonu (IMEI / MEID). Aby zaimplementować ten dodatek, system Android 8.0 zmienił schemat poświadczania ASN.1, dodając poświadczenie tożsamości. Implementacje Keymaster muszą znaleźć jakiś bezpieczny sposób pobierania odpowiednich elementów danych, a także zdefiniować mechanizm bezpiecznego i trwałego wyłączania tej funkcji.

W systemie Android 9 aktualizacje obejmowały:

  • Zaktualizuj do Keymastera 4
  • Obsługa wbudowanych bezpiecznych elementów
  • Obsługa bezpiecznego importu kluczy
  • Wsparcie dla szyfrowania 3DES
  • Zmiany w powiązaniu wersji, tak aby boot.img i system.img miały oddzielnie ustawione wersje, aby umożliwić niezależne aktualizacje

Słowniczek

Oto krótkie omówienie składników magazynu kluczy i ich relacji.

AndroidKeystore to interfejs API Android Framework i składnik używany przez aplikacje do uzyskiwania dostępu do funkcji magazynu kluczy. Jest zaimplementowany jako rozszerzenie standardowych interfejsów API Java Cryptography Architecture i składa się z kodu Java, który działa we własnej przestrzeni procesowej aplikacji. AndroidKeystore spełnia żądania aplikacji dotyczące zachowania magazynu kluczy, przekazując je do demona magazynu kluczy.

Demon magazynu kluczy to demon systemu Android, który zapewnia dostęp do wszystkich funkcji magazynu kluczy za pośrednictwem interfejsu API programu Binder . Jest odpowiedzialny za przechowywanie „kluczy blob”, które zawierają rzeczywisty materiał tajnego klucza, zaszyfrowany, aby Keystore mógł je przechowywać, ale nie używać ani nie ujawniać.

keymasterd to serwer HIDL, który zapewnia dostęp do Keymaster TA. (Ta nazwa nie jest znormalizowana i służy celom koncepcyjnym).

Keymaster TA (zaufana aplikacja) to oprogramowanie działające w bezpiecznym kontekście, najczęściej w TrustZone na ARM SoC, które zapewnia wszystkie bezpieczne operacje magazynu kluczy, ma dostęp do surowego materiału klucza, weryfikuje wszystkie warunki kontroli dostępu do kluczy itp.

LockSettingsService to komponent systemu Android odpowiedzialny za uwierzytelnianie użytkownika, zarówno hasła, jak i odcisku palca. Nie jest częścią magazynu kluczy, ale ma znaczenie, ponieważ wiele kluczowych operacji magazynu kluczy wymaga uwierzytelnienia użytkownika. LockSettingsService współpracuje z Gatekeeper TA i Fingerprint TA w celu uzyskania tokenów uwierzytelniających, które dostarcza do demona magazynu kluczy i które są ostatecznie zużywane przez aplikację Keymaster TA.

Gatekeeper TA (zaufana aplikacja) to kolejny komponent działający w bezpiecznym kontekście, odpowiedzialny za uwierzytelnianie haseł użytkowników i generowanie tokenów uwierzytelniających, które służą do udowodnienia Keymasterowi TA, że uwierzytelnienie zostało wykonane dla konkretnego użytkownika w określonym momencie.

Fingerprint TA (zaufana aplikacja) to kolejny komponent działający w bezpiecznym kontekście, odpowiedzialny za uwierzytelnianie odcisków palców użytkowników i generowanie tokenów uwierzytelniających, które służą do udowodnienia Keymaster TA, że uwierzytelnienie zostało wykonane dla konkretnego użytkownika w określonym momencie.

Architektura

Interfejs API Android Keystore i leżąca u jego podstaw warstwa Keymaster HAL zapewniają podstawowy, ale odpowiedni zestaw prymitywów kryptograficznych umożliwiających implementację protokołów przy użyciu kluczy sprzętowych z kontrolowanym dostępem.

Keymaster HAL to dynamicznie ładowana biblioteka dostarczana przez producenta OEM, używana przez usługę Keystore do świadczenia usług kryptograficznych wspieranych sprzętowo. Aby zapewnić bezpieczeństwo, implementacje HAL nie wykonują żadnych wrażliwych operacji w przestrzeni użytkownika ani nawet w przestrzeni jądra. Wrażliwe operacje są delegowane do bezpiecznego procesora osiąganego przez jakiś interfejs jądra. Wynikowa architektura wygląda następująco:

Dostęp do Keymastera

Rysunek 1. Dostęp do Keymastera

W urządzeniu z Androidem „klient” Keymaster HAL składa się z wielu warstw (np. aplikacji, struktury, demona magazynu kluczy), ale można to zignorować na potrzeby tego dokumentu. Oznacza to, że opisany interfejs Keymaster HAL API jest niskopoziomowy, używany przez wewnętrzne komponenty platformy i nie jest udostępniany twórcom aplikacji. Interfejs API wyższego poziomu jest opisany w witrynie Android Developer .

Celem warstwy Keymaster HAL nie jest implementacja algorytmów wrażliwych na bezpieczeństwo, a jedynie kierowanie i unmarshal żądań do bezpiecznego świata. Format drutu jest zdefiniowany przez implementację.

Kompatybilność z poprzednimi wersjami

Keymaster 1 HAL jest całkowicie niekompatybilny z poprzednio wydanymi HAL, np. Keymaster 0.2 i 0.3. Aby ułatwić interoperacyjność na urządzeniach z systemem Android 5.0 i wcześniejszymi, które zostały uruchomione ze starszymi warstwami Keymaster HAL, Keystore udostępnia adapter, który implementuje warstwę Keymaster 1 HAL z wywołaniami istniejącej biblioteki sprzętowej. Wynik nie może zapewnić pełnego zakresu funkcjonalności Keymaster 1 HAL. W szczególności obsługuje tylko algorytmy RSA i ECDSA, a całe wymuszanie autoryzacji klucza jest wykonywane przez adapter w niezabezpieczonym świecie.

Keymaster 2 jeszcze bardziej uprościł interfejs HAL, usuwając metody get_supported_* i zezwalając metodzie finish() na przyjmowanie danych wejściowych. Zmniejsza to liczbę podróży w obie strony do TEE w przypadkach, gdy dane wejściowe są dostępne od razu, i upraszcza implementację deszyfrowania AEAD.

W systemie Android 8.0 Keymaster 3 przeszedł ze starej struktury HAL w języku C na interfejs C++ HAL wygenerowany na podstawie definicji w nowym języku HIDL (Hardware Interface Definition Language). Implementacja HAL w nowym stylu jest tworzona przez podklasowanie wygenerowanej klasy IKeymasterDevice i implementację czystych metod wirtualnych. W ramach tej zmiany zmieniło się wiele typów argumentów, chociaż typy i metody odpowiadają starym typom i metodom struktury HAL.

Przegląd HIDL-a

Język definicji interfejsu sprzętowego (HIDL) zapewnia niezależny od języka implementacji mechanizm określania interfejsów sprzętowych. Narzędzia HIDL obsługują obecnie generowanie interfejsów C++ i Java. Oczekuje się, że większość implementatorów Trusted Execution Environment (TEE) uzna narzędzia C++ za wygodniejsze, dlatego w tym dokumencie omówiono tylko reprezentację języka C++.

Interfejsy HIDL składają się z zestawu metod wyrażonych jako:

  methodName(INPUT ARGUMENTS) generates (RESULT ARGUMENTS);

Istnieją różne predefiniowane typy, a warstwy HAL mogą definiować nowe typy wyliczeniowe i strukturalne. Aby uzyskać więcej informacji na temat języka HIDL, zobacz sekcję Informacje .

Przykładowa metoda z Keymaster 3 IKeymasterDevice.hal to:

generateKey(vec<KeyParameter> keyParams)
        generates(ErrorCode error, vec<uint8_t> keyBlob,
                  KeyCharacteristics keyCharacteristics);

Jest to odpowiednik następującego z keymaster2 HAL:

keymaster_error_t (*generate_key)(
        const struct keymaster2_device* dev,
        const keymaster_key_param_set_t* params,
        keymaster_key_blob_t* key_blob,
        keymaster_key_characteristics_t* characteristics);

W wersji HIDL argument dev jest usuwany, ponieważ jest niejawny. Argument params nie jest już strukturą zawierającą wskaźnik odwołujący się do tablicy obiektów key_parameter_t , ale vec (wektor) zawierający obiekty KeyParameter . Zwracane wartości są wymienione w klauzuli " generates ", w tym wektor wartości uint8_t dla kluczowego obiektu blob.

Wirtualna metoda C++ generowana przez kompilator HIDL to:

Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
                         generateKey_cb _hidl_cb) override;

Gdzie generateKey_cb jest wskaźnikiem funkcji zdefiniowanym jako:

std::function<void(ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
                   const KeyCharacteristics& keyCharacteristics)>

Oznacza to, że generateKey_cb jest funkcją, która przyjmuje wartości zwracane w klauzuli generowania. Klasa implementacji HAL zastępuje tę metodę generateKey i wywołuje wskaźnik funkcji generateKey_cb w celu zwrócenia wyniku operacji wywołującemu. Zauważ, że wywołanie wskaźnika funkcji jest synchroniczne . Osoba wywołująca wywołuje generateKey , a funkcja generateKey wywołuje dostarczony wskaźnik funkcji, który wykonuje się do końca, zwracając kontrolę do implementacji generateKey , która następnie wraca do osoby wywołującej.

Aby zapoznać się ze szczegółowym przykładem, zobacz domyślną implementację w hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp . Domyślna implementacja zapewnia kompatybilność wsteczną dla urządzeń ze starym stylem keymaster0, keymaster1 lub keymaster2 HALS.

Kontrola dostępu

Najbardziej podstawową zasadą kontroli dostępu do magazynu kluczy jest to, że każda aplikacja ma własną przestrzeń nazw. Ale od każdej reguły jest wyjątek. Magazyn kluczy ma zakodowane na stałe mapy, które umożliwiają niektórym komponentom systemu dostęp do niektórych innych przestrzeni nazw. Jest to bardzo tępy instrument, ponieważ daje jednemu komponentowi pełną kontrolę nad inną przestrzenią nazw. A potem jest kwestia komponentów dostawcy jako klientów Keystore. Obecnie nie mamy możliwości ustanowienia przestrzeni nazw dla komponentów dostawcy, na przykład WPA suplicant.

Aby uwzględnić komponenty dostawców i uogólnić kontrolę dostępu bez zakodowanych na stałe wyjątków, Keystore 2.0 wprowadza domeny i przestrzenie nazw SELinux.

Domeny magazynu kluczy

Dzięki domenom magazynu kluczy możemy oddzielić przestrzenie nazw od identyfikatorów UID. Klienci uzyskujący dostęp do klucza w Magazynie kluczy muszą określić domenę, przestrzeń nazw i alias, do których chcą uzyskać dostęp. Na podstawie tej krotki i tożsamości dzwoniącego możemy określić, do jakiego klucza dzwoniący chce uzyskać dostęp i czy ma odpowiednie uprawnienia.

Wprowadzamy pięć parametrów domeny, które określają, w jaki sposób można uzyskać dostęp do kluczy. Kontrolują semantykę parametru przestrzeni nazw deskryptora klucza i sposób przeprowadzania kontroli dostępu.

  • DOMAIN_APP : Domena aplikacji obejmuje starsze zachowanie. Interfejs SPI Java Keystore domyślnie korzysta z tej domeny. Gdy ta domena jest używana, argument przestrzeni nazw jest ignorowany, a zamiast tego używany jest identyfikator UID wywołującego. Dostęp do tej domeny jest kontrolowany przez etykietę Keystore do klasy keystore_key w polityce SELinux.
  • DOMAIN_SELINUX : Ta domena wskazuje, że przestrzeń nazw ma etykietę w polityce SELinux. Parametr przestrzeni nazw jest wyszukiwany i tłumaczony na kontekst docelowy oraz przeprowadzane jest sprawdzanie uprawnień dla wywołującego kontekstu SELinux dla klasy keystore_key . Gdy uprawnienie zostało ustanowione dla danej operacji, do wyszukiwania klucza używana jest pełna krotka.
  • DOMAIN_GRANT : domena przyznania wskazuje, że parametr przestrzeni nazw jest identyfikatorem przyznania. Parametr alias jest ignorowany. Kontrole SELinux są przeprowadzane podczas tworzenia grantu. Dalsza kontrola dostępu sprawdza tylko, czy identyfikator UID dzwoniącego jest zgodny z identyfikatorem UID odbiorcy żądanego przydziału.
  • DOMAIN_KEY_ID : ta domena wskazuje, że parametr przestrzeni nazw jest unikalnym identyfikatorem klucza. Sam klucz mógł zostać utworzony za pomocą DOMAIN_APP lub DOMAIN_SELINUX . Sprawdzanie uprawnień jest wykonywane po załadowaniu domain i namespace z bazy danych kluczy w taki sam sposób, jak gdyby obiekt blob został załadowany przez domenę, przestrzeń nazw i krotkę aliasu. Uzasadnieniem dla domeny identyfikatora klucza jest ciągłość. Podczas uzyskiwania dostępu do klucza przez alias, kolejne wywołania mogą działać na różnych kluczach, ponieważ nowy klucz mógł zostać wygenerowany lub zaimportowany i powiązany z tym aliasem. Jednak identyfikator klucza nigdy się nie zmienia. Tak więc, używając klucza według identyfikatora klucza po jednokrotnym załadowaniu go z bazy danych magazynu kluczy przy użyciu aliasu, można mieć pewność, że jest to ten sam klucz, o ile identyfikator klucza nadal istnieje. Ta funkcja nie jest dostępna dla twórców aplikacji. Zamiast tego jest używany w interfejsie SPI Android Keystore, aby zapewnić bardziej spójne działanie, nawet jeśli jest używany jednocześnie w niebezpieczny sposób.
  • DOMAIN_BLOB : Domena obiektu blob wskazuje, że obiekt wywołujący samodzielnie zarządza obiektem blob. Jest to używane w przypadku klientów, którzy muszą uzyskać dostęp do magazynu kluczy przed zamontowaniem partycji danych. Kluczowy obiekt blob jest zawarty w polu obiektu blob deskryptora klucza.

Korzystając z domeny SELinux, możemy zapewnić komponentom dostawcy dostęp do bardzo specyficznych przestrzeni nazw magazynu kluczy, które mogą być współdzielone przez komponenty systemu, takie jak okno dialogowe ustawień.

Polityka SELinux dla keystore_key

Etykiety przestrzeni nazw są konfigurowane przy użyciu pliku keystore2_key_context .
Każda linia w tych plikach odwzorowuje numeryczny identyfikator przestrzeni nazw na etykietę SELinux. Na przykład,

# wifi_key is a keystore2_key namespace intended to be used by wpa supplicant and
# Settings to share keystore keys.
102            u:object_r:wifi_key:s0

Po skonfigurowaniu w ten sposób nowej przestrzeni nazw kluczy, możemy dać do niej dostęp, dodając odpowiednią politykę. Na przykład, aby umożliwić wpa_supplicant pobieranie i używanie kluczy w nowej przestrzeni nazw, dodamy następującą linię do hal_wifi_supplicant.te :

allow hal_wifi_supplicant wifi_key:keystore2_key { get, use };

Po skonfigurowaniu nowej przestrzeni nazw AndroidKeyStore może być używany prawie jak zwykle. Jedyna różnica polega na tym, że należy określić identyfikator przestrzeni nazw. W przypadku ładowania i importowania kluczy z i do magazynu kluczy identyfikator przestrzeni nazw jest określany przy użyciu parametru AndroidKeyStoreLoadStoreParameter . Na przykład,

import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
import java.security.KeyStore;

KeyStore keystore = KeyStore.getInstance("AndroidKeyStore");
keystore.load(new AndroidKeyStoreLoadStoreParameter(102));

Aby wygenerować klucz w danej przestrzeni nazw, należy podać identyfikator przestrzeni nazw za pomocą KeyGenParameterSpec.Builder#setNamespace():

import android.security.keystore.KeyGenParameterSpec;
KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder();
specBuilder.setNamespace(102);

Poniższych plików kontekstowych można użyć do skonfigurowania przestrzeni nazw Keystore 2.0 SELinux. Każda partycja ma inny zarezerwowany zakres 10 000 identyfikatorów przestrzeni nazw, aby uniknąć kolizji.

Przegroda Zakres Pliki konfiguracyjne
System 0 ... 9999
/system/etc/selinux/keystore2_key_contexts, /plat_keystore2_key_contexts
System rozszerzony 10 000 ... 19 999
/system_ext/etc/selinux/system_ext_keystore2_key_contexts, /system_ext_keystore2_key_contexts
Produkt 20 000 ... 29 999
/product/etc/selinux/product_keystore2_key_contexts, /product_keystore2_key_contexts
Sprzedawca 30 000 ... 39 999
/vendor/etc/selinux/vendor_keystore2_key_contexts, /vendor_keystore2_key_contexts

Klient żąda klucza, żądając domeny SELinux i żądanej wirtualnej przestrzeni nazw, w tym przypadku "wifi_key" , za pomocą jej numerycznego identyfikatora.

Powyżej zostały zdefiniowane następujące przestrzenie nazw. Jeśli zastępują zasady specjalne, poniższa tabela wskazuje identyfikator UID, któremu odpowiadały.

Identyfikator przestrzeni nazw Etykieta zasad SE UID Opis
0 su_klucz Nie dotyczy Klucz superużytkownika. Używany tylko do testowania kompilacji userdebug i eng. Nie dotyczy kompilacji użytkowników.
1 klucz_powłoki Nie dotyczy Przestrzeń nazw dostępna dla powłoki. Używany głównie do testowania, ale może być również używany w kompilacjach użytkowników z wiersza poleceń.
100 klucz_wolny Nie dotyczy Przeznaczony do użytku przez wol.
101 odsing_key Nie dotyczy Używany przez demona podpisywania na urządzeniu.
102 klucz_wi-fi AID_WIFI(1010) Używany przez sybsystem Wi-Fi Androida, w tym wpa_supplicant.
120 wznowienie_on_reboot_key SYSTEM_POMOCY(1000) Używany przez serwer systemu Android do obsługi wznawiania po ponownym uruchomieniu.

Dostęp do wektorów

Klasa SELinux keystore_key nieco się zestarzała i niektóre uprawnienia, takie jak verify lub sign , straciły swoje znaczenie. Oto nowy zestaw uprawnień, keystore2_key , który wymusi Keystore 2.0.

Pozwolenie Oznaczający
delete Zaznaczone podczas usuwania kluczy z magazynu kluczy.
get_info Zaznaczane, gdy wymagane są metadane klucza.
grant Obiekt wywołujący potrzebuje tego uprawnienia, aby utworzyć przyznanie klucza w kontekście docelowym.
manage_blob Wywołujący może używać DOMAIN_BLOB w danej przestrzeni nazw SELinux, zarządzając w ten sposób samodzielnie blobami. Jest to szczególnie przydatne dla vold.
rebind To uprawnienie kontroluje, czy alias może zostać ponownie powiązany z nowym kluczem. Jest to wymagane do wstawienia i oznacza, że ​​poprzednio powiązany klucz zostanie usunięty. Zasadniczo jest to uprawnienie do wstawiania, ale lepiej oddaje semantykę magazynu kluczy.
req_forced_op Klienci z tym uprawnieniem mogą tworzyć operacje, których nie można czyścić, a tworzenie operacji nigdy nie kończy się niepowodzeniem, chyba że wszystkie miejsca operacji są zajęte przez operacje, których nie można czyścić.
update Wymagane do aktualizacji składnika podrzędnego klucza.
use Zaznaczone podczas tworzenia operacji Keymint, która wykorzystuje materiał klucza, np. do podpisywania, szyfrowania/odszyfrowywania.
use_dev_id Wymagane podczas generowania informacji identyfikujących urządzenie, takich jak zaświadczenie o identyfikatorze urządzenia.

Dodatkowo podzieliliśmy zestaw uprawnień do magazynu kluczy, które nie są specyficzne dla klucza, w keystore2 klasy bezpieczeństwa SELinux2:

Pozwolenie Oznaczający
add_auth Wymagane przez dostawcę uwierzytelniania, takiego jak Gatekeeper lub BiometricsManager, do dodawania tokenów uwierzytelniania.
clear_ns Dawniej clear_uid, to uprawnienie umożliwia osobie, która nie jest właścicielem przestrzeni nazw, usunięcie wszystkich kluczy w tej przestrzeni nazw.
list Wymagane przez system do wyliczania kluczy według różnych właściwości, takich jak własność lub ograniczenia uwierzytelniania. To uprawnienie nie jest wymagane przez wywołujących wyliczających własne przestrzenie nazw. Jest to objęte uprawnieniem get_info .
lock To uprawnienie umożliwia zablokowanie magazynu kluczy, czyli eksmisję klucza głównego, tak że klucze związane z autoryzacją stają się bezużyteczne i niemożliwe do utworzenia.
reset To uprawnienie pozwala zresetować Keystore do ustawień fabrycznych, usuwając wszystkie klucze, które nie są niezbędne do funkcjonowania systemu operacyjnego Android.
unlock To uprawnienie jest wymagane do podjęcia próby odblokowania klucza głównego dla kluczy związanych z uwierzytelnianiem.
,

Dostępność zaufanego środowiska wykonawczego w systemie na chipie (SoC) daje urządzeniom z Androidem możliwość świadczenia wspieranych sprzętowo, silnych usług bezpieczeństwa dla systemu operacyjnego Android, usług platformy, a nawet aplikacji innych firm. Deweloperzy poszukujący rozszerzeń specyficznych dla systemu Android powinni przejść do android.security.keystore .

Przed Androidem 6.0 Android miał już prosty sprzętowy interfejs API usług kryptograficznych, dostarczany przez wersje 0.2 i 0.3 warstwy abstrakcji sprzętowej Keymaster (HAL). Keystore zapewnia cyfrowe podpisywanie i operacje weryfikacji, a także generowanie i import asymetrycznych par kluczy do podpisywania. Jest to już zaimplementowane na wielu urządzeniach, ale istnieje wiele celów związanych z bezpieczeństwem, których nie można łatwo osiągnąć za pomocą samego API podpisu. Magazyn kluczy w systemie Android 6.0 rozszerzył interfejs API magazynu kluczy, aby zapewnić szerszy zakres możliwości.

W Androidzie 6.0 Keystore dodał symetryczne prymitywy kryptograficzne , AES i HMAC oraz system kontroli dostępu do kluczy wspieranych sprzętowo. Kontrola dostępu jest określana podczas generowania klucza i egzekwowana przez cały okres ważności klucza. Klucze można ograniczyć do użytku dopiero po uwierzytelnieniu użytkownika i tylko do określonych celów lub z określonymi parametrami kryptograficznymi. Aby uzyskać więcej informacji, zobacz strony Znaczniki autoryzacji i Funkcje .

Oprócz rozszerzenia zakresu prymitywów kryptograficznych, Keystore w systemie Android 6.0 dodał:

  • Schemat kontroli użycia umożliwiający ograniczenie użycia klucza w celu ograniczenia ryzyka naruszenia bezpieczeństwa w wyniku niewłaściwego użycia kluczy
  • Schemat kontroli dostępu umożliwiający ograniczenie kluczy do określonych użytkowników, klientów i określonego przedziału czasowego

W systemie Android 7.0 Keymaster 2 dodał obsługę poświadczania klucza i wiązania wersji. Zaświadczanie klucza zapewnia certyfikaty klucza publicznego, które zawierają szczegółowy opis klucza i jego kontroli dostępu, aby umożliwić zdalną weryfikację istnienia klucza w bezpiecznym sprzęcie i jego konfiguracji.

Wiązanie wersji wiąże klucze z wersją na poziomie systemu operacyjnego i poprawki. Dzięki temu osoba atakująca, która wykryje słabość w starej wersji systemu lub oprogramowania TEE, nie będzie mogła przywrócić urządzenia do wersji podatnej na ataki i użyć kluczy utworzonych w nowszej wersji. Ponadto, gdy klucz z daną wersją i poziomem poprawki jest używany na urządzeniu, które zostało zaktualizowane do nowszej wersji lub poziomu poprawki, klucz jest aktualizowany przed użyciem, a poprzednia wersja klucza traci ważność. Gdy urządzenie jest aktualizowane, klawisze „zaskakują” do przodu wraz z urządzeniem, ale każde przywrócenie urządzenia do poprzedniej wersji powoduje, że klawisze stają się bezużyteczne.

W systemie Android 8.0 Keymaster 3 przeszedł ze starej warstwy HAL (Hardware Abstraction Layer) o strukturze C do interfejsu C++ HAL wygenerowanego na podstawie definicji w nowym języku HIDL (Hardware Interface Definition Language). W ramach tej zmiany zmieniło się wiele typów argumentów, chociaż typy i metody odpowiadają starym typom i metodom struktury HAL. Zobacz stronę Funkcje , aby uzyskać więcej informacji.

Oprócz tej wersji interfejsu Android 8.0 rozszerzył funkcję atestacji Keymaster 2 o obsługę atestacji tożsamości . Zaświadczanie tożsamości zapewnia ograniczony i opcjonalny mechanizm silnego potwierdzania identyfikatorów sprzętu, takich jak numer seryjny urządzenia, nazwa produktu i identyfikator telefonu (IMEI / MEID). Aby zaimplementować ten dodatek, system Android 8.0 zmienił schemat poświadczania ASN.1, dodając poświadczenie tożsamości. Implementacje Keymaster muszą znaleźć jakiś bezpieczny sposób pobierania odpowiednich elementów danych, a także zdefiniować mechanizm bezpiecznego i trwałego wyłączania tej funkcji.

W systemie Android 9 aktualizacje obejmowały:

  • Zaktualizuj do Keymastera 4
  • Obsługa wbudowanych bezpiecznych elementów
  • Obsługa bezpiecznego importu kluczy
  • Wsparcie dla szyfrowania 3DES
  • Zmiany w powiązaniu wersji, tak aby boot.img i system.img miały oddzielnie ustawione wersje, aby umożliwić niezależne aktualizacje

Słowniczek

Oto krótkie omówienie składników magazynu kluczy i ich relacji.

AndroidKeystore to interfejs API Android Framework i składnik używany przez aplikacje do uzyskiwania dostępu do funkcji magazynu kluczy. Jest zaimplementowany jako rozszerzenie standardowych interfejsów API Java Cryptography Architecture i składa się z kodu Java, który działa we własnej przestrzeni procesowej aplikacji. AndroidKeystore spełnia żądania aplikacji dotyczące zachowania magazynu kluczy, przekazując je do demona magazynu kluczy.

Demon magazynu kluczy to demon systemu Android, który zapewnia dostęp do wszystkich funkcji magazynu kluczy za pośrednictwem interfejsu API programu Binder . Jest odpowiedzialny za przechowywanie „kluczy blob”, które zawierają rzeczywisty materiał tajnego klucza, zaszyfrowany, aby Keystore mógł je przechowywać, ale nie używać ani nie ujawniać.

keymasterd to serwer HIDL, który zapewnia dostęp do Keymaster TA. (Ta nazwa nie jest znormalizowana i służy celom koncepcyjnym).

Keymaster TA (zaufana aplikacja) to oprogramowanie działające w bezpiecznym kontekście, najczęściej w TrustZone na ARM SoC, które zapewnia wszystkie bezpieczne operacje magazynu kluczy, ma dostęp do surowego materiału klucza, weryfikuje wszystkie warunki kontroli dostępu do kluczy itp.

LockSettingsService to komponent systemu Android odpowiedzialny za uwierzytelnianie użytkownika, zarówno hasła, jak i odcisku palca. Nie jest częścią magazynu kluczy, ale ma znaczenie, ponieważ wiele kluczowych operacji magazynu kluczy wymaga uwierzytelnienia użytkownika. LockSettingsService współpracuje z Gatekeeper TA i Fingerprint TA w celu uzyskania tokenów uwierzytelniających, które dostarcza do demona magazynu kluczy i które są ostatecznie zużywane przez aplikację Keymaster TA.

Gatekeeper TA (zaufana aplikacja) to kolejny komponent działający w bezpiecznym kontekście, odpowiedzialny za uwierzytelnianie haseł użytkowników i generowanie tokenów uwierzytelniających, które służą do udowodnienia Keymasterowi TA, że uwierzytelnienie zostało wykonane dla konkretnego użytkownika w określonym momencie.

Fingerprint TA (zaufana aplikacja) to kolejny komponent działający w bezpiecznym kontekście, odpowiedzialny za uwierzytelnianie odcisków palców użytkowników i generowanie tokenów uwierzytelniających, które służą do udowodnienia Keymaster TA, że uwierzytelnienie zostało wykonane dla konkretnego użytkownika w określonym momencie.

Architektura

Interfejs API Android Keystore i leżąca u jego podstaw warstwa Keymaster HAL zapewniają podstawowy, ale odpowiedni zestaw prymitywów kryptograficznych umożliwiających implementację protokołów przy użyciu kluczy sprzętowych z kontrolowanym dostępem.

Keymaster HAL to dynamicznie ładowana biblioteka dostarczana przez producenta OEM, używana przez usługę Keystore do świadczenia usług kryptograficznych wspieranych sprzętowo. Aby zapewnić bezpieczeństwo, implementacje HAL nie wykonują żadnych wrażliwych operacji w przestrzeni użytkownika ani nawet w przestrzeni jądra. Wrażliwe operacje są delegowane do bezpiecznego procesora osiąganego przez jakiś interfejs jądra. Wynikowa architektura wygląda następująco:

Dostęp do Keymastera

Rysunek 1. Dostęp do Keymastera

W urządzeniu z Androidem „klient” Keymaster HAL składa się z wielu warstw (np. aplikacji, struktury, demona magazynu kluczy), ale można to zignorować na potrzeby tego dokumentu. Oznacza to, że opisany interfejs Keymaster HAL API jest niskopoziomowy, używany przez wewnętrzne komponenty platformy i nie jest udostępniany twórcom aplikacji. Interfejs API wyższego poziomu jest opisany w witrynie Android Developer .

Celem warstwy Keymaster HAL nie jest implementacja algorytmów wrażliwych na bezpieczeństwo, a jedynie kierowanie i unmarshal żądań do bezpiecznego świata. Format drutu jest zdefiniowany przez implementację.

Kompatybilność z poprzednimi wersjami

Keymaster 1 HAL jest całkowicie niekompatybilny z poprzednio wydanymi HAL, np. Keymaster 0.2 i 0.3. Aby ułatwić interoperacyjność na urządzeniach z systemem Android 5.0 i wcześniejszymi, które zostały uruchomione ze starszymi warstwami Keymaster HAL, Keystore udostępnia adapter, który implementuje warstwę Keymaster 1 HAL z wywołaniami istniejącej biblioteki sprzętowej. Wynik nie może zapewnić pełnego zakresu funkcjonalności Keymaster 1 HAL. W szczególności obsługuje tylko algorytmy RSA i ECDSA, a całe wymuszanie autoryzacji klucza jest wykonywane przez adapter w niezabezpieczonym świecie.

Keymaster 2 jeszcze bardziej uprościł interfejs HAL, usuwając metody get_supported_* i zezwalając metodzie finish() na przyjmowanie danych wejściowych. Zmniejsza to liczbę podróży w obie strony do TEE w przypadkach, gdy dane wejściowe są dostępne od razu, i upraszcza implementację deszyfrowania AEAD.

W systemie Android 8.0 Keymaster 3 przeszedł ze starej struktury HAL w języku C na interfejs C++ HAL wygenerowany na podstawie definicji w nowym języku HIDL (Hardware Interface Definition Language). Implementacja HAL w nowym stylu jest tworzona przez podklasowanie wygenerowanej klasy IKeymasterDevice i implementację czystych metod wirtualnych. W ramach tej zmiany zmieniło się wiele typów argumentów, chociaż typy i metody odpowiadają starym typom i metodom struktury HAL.

Przegląd HIDL-a

Język definicji interfejsu sprzętowego (HIDL) zapewnia niezależny od języka implementacji mechanizm określania interfejsów sprzętowych. Narzędzia HIDL obsługują obecnie generowanie interfejsów C++ i Java. Oczekuje się, że większość implementatorów Trusted Execution Environment (TEE) uzna narzędzia C++ za wygodniejsze, dlatego w tym dokumencie omówiono tylko reprezentację języka C++.

Interfejsy HIDL składają się z zestawu metod wyrażonych jako:

  methodName(INPUT ARGUMENTS) generates (RESULT ARGUMENTS);

Istnieją różne predefiniowane typy, a warstwy HAL mogą definiować nowe typy wyliczeniowe i strukturalne. Aby uzyskać więcej informacji na temat języka HIDL, zobacz sekcję Informacje .

Przykładowa metoda z Keymaster 3 IKeymasterDevice.hal to:

generateKey(vec<KeyParameter> keyParams)
        generates(ErrorCode error, vec<uint8_t> keyBlob,
                  KeyCharacteristics keyCharacteristics);

Jest to odpowiednik następującego z keymaster2 HAL:

keymaster_error_t (*generate_key)(
        const struct keymaster2_device* dev,
        const keymaster_key_param_set_t* params,
        keymaster_key_blob_t* key_blob,
        keymaster_key_characteristics_t* characteristics);

W wersji HIDL argument dev jest usuwany, ponieważ jest niejawny. Argument params nie jest już strukturą zawierającą wskaźnik odwołujący się do tablicy obiektów key_parameter_t , ale vec (wektor) zawierający obiekty KeyParameter . Zwracane wartości są wymienione w klauzuli " generates ", w tym wektor wartości uint8_t dla kluczowego obiektu blob.

Wirtualna metoda C++ generowana przez kompilator HIDL to:

Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
                         generateKey_cb _hidl_cb) override;

Gdzie generateKey_cb jest wskaźnikiem funkcji zdefiniowanym jako:

std::function<void(ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
                   const KeyCharacteristics& keyCharacteristics)>

Oznacza to, że generateKey_cb jest funkcją, która przyjmuje wartości zwracane w klauzuli generowania. Klasa implementacji HAL zastępuje tę metodę generateKey i wywołuje wskaźnik funkcji generateKey_cb w celu zwrócenia wyniku operacji wywołującemu. Zauważ, że wywołanie wskaźnika funkcji jest synchroniczne . Osoba wywołująca wywołuje generateKey , a funkcja generateKey wywołuje dostarczony wskaźnik funkcji, który wykonuje się do końca, zwracając kontrolę do implementacji generateKey , która następnie wraca do osoby wywołującej.

Aby zapoznać się ze szczegółowym przykładem, zobacz domyślną implementację w hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp . Domyślna implementacja zapewnia kompatybilność wsteczną dla urządzeń ze starym stylem keymaster0, keymaster1 lub keymaster2 HALS.

Kontrola dostępu

Najbardziej podstawową zasadą kontroli dostępu do magazynu kluczy jest to, że każda aplikacja ma własną przestrzeń nazw. Ale od każdej reguły jest wyjątek. Magazyn kluczy ma zakodowane na stałe mapy, które umożliwiają niektórym komponentom systemu dostęp do niektórych innych przestrzeni nazw. Jest to bardzo tępy instrument, ponieważ daje jednemu komponentowi pełną kontrolę nad inną przestrzenią nazw. A potem jest kwestia komponentów dostawcy jako klientów Keystore. Obecnie nie mamy możliwości ustanowienia przestrzeni nazw dla komponentów dostawcy, na przykład WPA suplicant.

Aby uwzględnić komponenty dostawców i uogólnić kontrolę dostępu bez zakodowanych na stałe wyjątków, Keystore 2.0 wprowadza domeny i przestrzenie nazw SELinux.

Domeny magazynu kluczy

Dzięki domenom magazynu kluczy możemy oddzielić przestrzenie nazw od identyfikatorów UID. Klienci uzyskujący dostęp do klucza w Magazynie kluczy muszą określić domenę, przestrzeń nazw i alias, do których chcą uzyskać dostęp. Na podstawie tej krotki i tożsamości dzwoniącego możemy określić, do jakiego klucza dzwoniący chce uzyskać dostęp i czy ma odpowiednie uprawnienia.

Wprowadzamy pięć parametrów domeny, które określają, w jaki sposób można uzyskać dostęp do kluczy. Kontrolują semantykę parametru przestrzeni nazw deskryptora klucza i sposób przeprowadzania kontroli dostępu.

  • DOMAIN_APP : Domena aplikacji obejmuje starsze zachowanie. Interfejs SPI Java Keystore domyślnie korzysta z tej domeny. Gdy ta domena jest używana, argument przestrzeni nazw jest ignorowany, a zamiast tego używany jest identyfikator UID wywołującego. Dostęp do tej domeny jest kontrolowany przez etykietę Keystore do klasy keystore_key w polityce SELinux.
  • DOMAIN_SELINUX : Ta domena wskazuje, że przestrzeń nazw ma etykietę w polityce SELinux. Parametr przestrzeni nazw jest wyszukiwany i tłumaczony na kontekst docelowy oraz przeprowadzane jest sprawdzanie uprawnień dla wywołującego kontekstu SELinux dla klasy keystore_key . Gdy uprawnienie zostało ustanowione dla danej operacji, do wyszukiwania klucza używana jest pełna krotka.
  • DOMAIN_GRANT : The grant domain indicates that the namespace parameter is a grant identifier. The alias parameter is ignored. SELinux checks are performed when the grant is created. Further access control only checks if the caller UID matches the grantees UID of the requested grant.
  • DOMAIN_KEY_ID : This domain indicates that the namespace parameter is a unique key id. The key itself may have been created with DOMAIN_APP or DOMAIN_SELINUX . The permission check is performed after the domain and the namespace have been loaded from the key database in the same way as if the blob was loaded by the domain, namespace, and alias tuple. The rationale for the key id domain is continuity. When accessing a key by alias, subsequent calls may operate on different keys, because a new key may have been generated or imported and bound to this alias. The key id, however, never changes. So when using a key by key id after it has been loaded from the Keystore database using the alias once, one can be certain that it is the same key as long as the key id still exists. This functionality is not exposed to app developers. Instead, it is used within the Android Keystore SPI to provide a more consistent experience even when used concurrently in an unsafe way.
  • DOMAIN_BLOB : The blob domain indicates that the caller manages the blob by itself. This is used for clients that need to access the Keystore before the data partition is mounted. The key blob is included in the blob field of the key descriptor.

Using the SELinux domain, we can give vendor components access to very specific Keystore namespaces which can be shared by system components such as the settings dialog.

SELinux policy for keystore_key

Namespace labels are configured using the keystore2_key_context file.
Each line in these files maps a numeric namespace id to an SELinux label. For example,

# wifi_key is a keystore2_key namespace intended to be used by wpa supplicant and
# Settings to share keystore keys.
102            u:object_r:wifi_key:s0

After having set up a new key namespace in this way, we can give access to it by adding an appropriate policy. For example, to allow wpa_supplicant to get and use keys in the new namespace we would add the following line to hal_wifi_supplicant.te :

allow hal_wifi_supplicant wifi_key:keystore2_key { get, use };

After setting up the new namespace, AndroidKeyStore can be used almost as usual. The only difference is that the namespace ID must be specified. For loading and importing keys from and into Keystore, the namespace id is specified using the AndroidKeyStoreLoadStoreParameter . For example,

import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
import java.security.KeyStore;

KeyStore keystore = KeyStore.getInstance("AndroidKeyStore");
keystore.load(new AndroidKeyStoreLoadStoreParameter(102));

To generate a key in a given namespace, the namespace id must be given using KeyGenParameterSpec.Builder#setNamespace():

import android.security.keystore.KeyGenParameterSpec;
KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder();
specBuilder.setNamespace(102);

The following context files may be used to configure Keystore 2.0 SELinux namespaces. Each partition has a different reserved range of 10,000 namespace ids to avoid collisions.

Partition Range Config files
System 0 ... 9,999
/system/etc/selinux/keystore2_key_contexts, /plat_keystore2_key_contexts
Extended System 10,000 ... 19,999
/system_ext/etc/selinux/system_ext_keystore2_key_contexts, /system_ext_keystore2_key_contexts
Product 20,000 ... 29,999
/product/etc/selinux/product_keystore2_key_contexts, /product_keystore2_key_contexts
Vendor 30,000 ... 39,999
/vendor/etc/selinux/vendor_keystore2_key_contexts, /vendor_keystore2_key_contexts

The client requests the key by requesting the SELinux domain and the desired virtual namespace, in this case "wifi_key" , by its numeric id.

Above that, the following namespaces have been defined. If they replace special rules, the following table indicates the UID they used to correspond to.

Namespace ID SEPolicy Label UID Description
0 su_key N/A Super user key. Only used for testing on userdebug and eng builds. Not relevant on user builds.
1 shell_key N/A Namespace available to shell. Mostly used for testing, but can be used on user builds as well from the command line.
100 vold_key N/A Intended for use by vold.
101 odsing_key N/A Used by the on-device signing daemon.
102 wifi_key AID_WIFI(1010) Used by Android's Wifi sybsystem including wpa_supplicant.
120 resume_on_reboot_key AID_SYSTEM(1000) Used by Android's system server to support resume on reboot.

Access Vectors

The SELinux class keystore_key has aged quite a bit and some of the permissions, such as verify or sign have lost their meaning. Here is the new set of permissions, keystore2_key , that Keystore 2.0 will enforce.

Permission Meaning
delete Checked when removing keys from Keystore.
get_info Checked when a key's metadata is requested.
grant The caller needs this permission to create a grant to the key in the target context.
manage_blob The caller may use DOMAIN_BLOB on the given SELinux namespace, thereby managing blobs by itself. This is specifically useful for vold.
rebind This permission controls if an alias may be rebound to a new key. This is required for insertion and implies that the previously bound key will be deleted. It is basically an insert permission, but it captures the semantic of keystore better.
req_forced_op Clients with this permission may create unpruneable operations, and operation creation never fails unless all operation slots are taken by unpruneable operations.
update Required to update the subcomponent of a key.
use Checked when creating a Keymint operation that uses the key material, eg, for signing, en/decryption.
use_dev_id Required when generating device identifying information, such as device id attestation.

Additionally, we split out a set of non key specific keystore permissions in the SELinux security class keystore2 :

Permission Meaning
add_auth Required by authentication provider such as Gatekeeper or BiometricsManager for adding auth tokens.
clear_ns Formerly clear_uid, this permission allows a non owner of a namespace to delete all keys in that namespace.
list Required by the system for enumerating keys by various properties, such as ownership or auth boundedness. This permission is not required by callers enumerating their own namespaces. This is covered by the get_info permission.
lock This permission allows to lock Keystore, that is, evict the master key, such that auth bound keys become unusable and uncreatable.
reset This permission allows to reset Keystore to factory default, deleting all keys that are not vital to the functioning of the Android OS.
unlock This permission is required to attempt to unlock the master key for auth bound keys.