W Keymaster 1 wszystkie klucze Keymaster były kryptograficznie powiązane z głównym źródłem zaufania urządzenia lub kluczem zweryfikowanego rozruchu. W przypadku Keymastera 2 i 3 wszystkie klucze są też powiązane z systemem operacyjnym i poziomem poprawek obrazu systemu. Dzięki temu atakujący, który odkryje słaby punkt w starszej wersji oprogramowania systemowego lub TEE, nie będzie mógł przywrócić urządzenia do podatnej na ataki wersji i użyć kluczy utworzonych w nowszej wersji. Dodatkowo, gdy klucz z daną wersją i poziomem poprawki jest używany na urządzeniu, które zostało uaktualnione do nowszej wersji lub poziomu poprawki, klucz jest uaktualniany przed użyciem, a poprzednia wersja klucza jest unieważniana. W ten sposób podczas uaktualniania urządzenia klucze są „przesuwane” wraz z nim, ale przywrócenie urządzenia do poprzedniej wersji powoduje, że klucze stają się bezużyteczne.
Aby obsługiwać modułową strukturę Treble i zerwać powiązanie system.img z boot.img, Keymaster 4 zmienił model powiązania wersji klucza, tak aby każda partycja miała osobne poziomy poprawek. Dzięki temu każda partycja może być aktualizowana niezależnie, a jednocześnie zapewnia ochronę przed przywracaniem.
Aby wdrożyć to powiązanie wersji, zaufana aplikacja KeyMint musi mieć możliwość bezpiecznego otrzymywania aktualnej wersji systemu operacyjnego i poziomów poprawek oraz zapewnienia, że otrzymywane informacje są zgodne ze wszystkimi informacjami o działającym systemie.
- Urządzenia z weryfikacją podczas uruchamiania w Androidzie (AVB) mogą umieszczać wszystkie poziomy poprawek i wersję systemu w vbmeta, dzięki czemu program rozruchowy może przekazywać je do Keymastera. W przypadku połączonych partycji informacje o wersji partycji znajdują się w połączonym pliku vbmeta. Ogólnie rzecz biorąc, informacje o wersji powinny znajdować się w elemencie
vbmeta struct
, który zawiera dane weryfikacyjne (hash lub drzewo hashów) dla danego podziału. - Na urządzeniach bez AVB:
- Implementacje zweryfikowanego rozruchu muszą przekazywać do programu rozruchowego skrót metadanych wersji, aby program rozruchowy mógł przekazać go do Keymastera.
boot.img
może nadal przechowywać stan aktualizacji w nagłówku.system.img
może nadal przechowywać poziom poprawki i wersję systemu operacyjnego we właściwościach tylko do odczytu.vendor.img
przechowuje poziom poprawki we właściwości tylko do odczyturo.vendor.build.version.security_patch
.- Program rozruchowy może przekazywać do Keymastera skrót wszystkich danych zweryfikowanych przez weryfikację rozruchu.
- W Androidzie 9 użyj tych tagów, aby podać informacje o wersji tych partycji:
VENDOR_PATCH_LEVEL
:vendor
partycjaBOOT_PATCH_LEVEL
:boot
partycjaOS_PATCH_LEVEL
iOS_VERSION
:system
partycja. (OS_VERSION
zostanie usunięty z nagłówkaboot.img
.
-
Implementacje Keymastera powinny traktować wszystkie poziomy poprawek niezależnie od siebie. Klucze są użyteczne, jeśli wszystkie informacje o wersji pasują do wartości powiązanych z kluczem, a w razie potrzeby
IKeymaster::upgradeDevice()
przechodzi na wyższy poziom poprawki.
Zmiany w HAL
Aby obsługiwać wiązanie wersji i atestowanie wersji, w Androidzie 7.1 dodano tagi Tag::OS_VERSION
i Tag::OS_PATCHLEVEL
oraz metody configure
i upgradeKey
. Tagi wersji są automatycznie dodawane przez implementacje Keymaster 2+ do wszystkich nowo wygenerowanych (lub zaktualizowanych) kluczy. Ponadto każda próba użycia klucza, który nie ma wersji systemu operacyjnego lub stanu aktualizacji zgodnego z bieżącą wersją systemu operacyjnego lub stanem aktualizacji, jest odrzucana z kodem ErrorCode::KEY_REQUIRES_UPGRADE
.
Tag::OS_VERSION
to wartość UINT
, która reprezentuje główną, podrzędną i pomniejszą część wersji systemu Android w formacie MMmmss, gdzie MM to wersja główna, mm to wersja podrzędna, a ss to wersja pomniejsza. Na przykład wersja 6.1.2 będzie przedstawiona jako 060102.
Tag::OS_PATCHLEVEL
to wartość UINT
, która reprezentuje rok i miesiąc ostatniej aktualizacji systemu w formacie RRRRMM, gdzie RRRR to rok w formacie czterocyfrowym, a MM to miesiąc w formacie dwucyfrowym. Na przykład marzec 2016 r. będzie przedstawiony jako 201603.
UpgradeKey
Aby umożliwić uaktualnianie kluczy do nowej wersji systemu operacyjnego i poziomu poprawek obrazu systemu, w Androidzie 7.1 dodano do HAL metodę upgradeKey
:
Keymaster 3
upgradeKey(vec keyBlobToUpgrade, vec upgradeParams) generates(ErrorCode error, vec upgradedKeyBlob);
Keymaster 2
keymaster_error_t (*upgrade_key)(const struct keymaster2_device* dev, const keymaster_key_blob_t* key_to_upgrade, const keymaster_key_param_set_t* upgrade_params, keymaster_key_blob_t* upgraded_key);
dev
to struktura urządzeniakeyBlobToUpgrade
to klucz, który wymaga uaktualnieniaupgradeParams
to parametry wymagane do uaktualnienia klucza. Obejmują oneTag::APPLICATION_ID
iTag::APPLICATION_DATA
, które są niezbędne do odszyfrowania obiektu blob klucza, jeśli zostały podane podczas generowania.upgradedKeyBlob
to parametr wyjściowy, który służy do zwracania nowego obiektu klucza.
Jeśli funkcja upgradeKey
zostanie wywołana z blobem klucza, którego nie można przeanalizować lub który jest w inny sposób nieprawidłowy, zwróci wartość ErrorCode::INVALID_KEY_BLOB
. Jeśli wywołasz ją z kluczem, którego poziom aktualizacji jest wyższy niż bieżąca wartość systemowa, zwróci ErrorCode::INVALID_ARGUMENT
. Jeśli funkcja jest wywoływana z kluczem, którego wersja systemu operacyjnego jest nowsza niż bieżąca wartość systemowa, a wartość systemowa jest różna od zera, zwraca wartość ErrorCode::INVALID_ARGUMENT
. Uaktualnienia wersji systemu operacyjnego z wartości innej niż 0 do 0 są dozwolone. W przypadku błędów komunikacji z bezpiecznym środowiskiem zwraca odpowiednią wartość błędu (np. ErrorCode::SECURE_HW_ACCESS_DENIED
, ErrorCode::SECURE_HW_BUSY
). W przeciwnym razie zwraca ErrorCode::OK
i nowy obiekt klucza w upgradedKeyBlob
.
keyBlobToUpgrade
pozostaje ważny po wywołaniu upgradeKey
i teoretycznie można go użyć ponownie, jeśli urządzenie zostanie przywrócone do starszej wersji. W praktyce magazyn kluczy zwykle wywołuje deleteKey
na obiekcie blob keyBlobToUpgrade
krótko po wywołaniu upgradeKey
. Jeśli keyBlobToUpgrade
zawiera tag Tag::ROLLBACK_RESISTANT
, to upgradedKeyBlob
też powinien go zawierać (i powinien być odporny na wycofanie).
Bezpieczna konfiguracja
Aby wdrożyć powiązanie wersji, zaufany moduł Keymaster musi mieć możliwość bezpiecznego otrzymywania bieżącej wersji systemu operacyjnego i poziomu poprawki (informacji o wersji) oraz upewniania się, że otrzymywane informacje są w dużym stopniu zgodne z informacjami o działającym systemie.
Aby zapewnić bezpieczne przekazywanie informacji o wersji do TA, do nagłówka obrazu rozruchowego dodano OS_VERSION
pole. Skrypt kompilacji obrazu rozruchowego automatycznie wypełnia to pole. Producenci OEM i wdrażający TA Keymaster muszą współpracować, aby zmodyfikować programy rozruchowe urządzeń w celu wyodrębnienia informacji o wersji z obrazu rozruchowego i przekazania ich do TA przed uruchomieniem niezabezpieczonego systemu. Dzięki temu atakujący nie mogą ingerować w przekazywanie informacji o wersji do środowiska TA.
Konieczne jest też upewnienie się, że obraz systemu ma te same informacje o wersji co obraz rozruchowy. W tym celu do HAL Keymaster dodano metodę configure:
keymaster_error_t (*configure)(const struct keymaster2_device* dev, const keymaster_key_param_set_t* params);
Argument params
zawiera Tag::OS_VERSION
i Tag::OS_PATCHLEVEL
. Ta metoda jest wywoływana przez klientów keymaster2 po otwarciu HAL, ale przed wywołaniem innych metod. Jeśli przed wywołaniem metody configure zostanie wywołana jakakolwiek inna metoda, TA zwróci wartość ErrorCode::KEYMASTER_NOT_CONFIGURED
.
Gdy funkcja configure
zostanie wywołana po raz pierwszy po uruchomieniu urządzenia, powinna sprawdzić, czy podane informacje o wersji są zgodne z informacjami przekazanymi przez program rozruchowy. Jeśli informacje o wersji nie są zgodne, funkcja
configure
zwraca wartość ErrorCode::INVALID_ARGUMENT
, a wszystkie
inne metody Keymastera nadal zwracają wartość
ErrorCode::KEYMASTER_NOT_CONFIGURED
. Jeśli informacje są zgodne, funkcja
configure
zwraca ErrorCode::OK
, a inne metody Keymastera
zaczynają działać normalnie.
Kolejne wywołania funkcji configure
zwracają tę samą wartość co pierwsze wywołanie i nie zmieniają stanu Keymastera.
Funkcja configure
jest wywoływana przez system, którego zawartość ma weryfikować. Dlatego atakujący ma niewielkie pole do działania, aby naruszyć obraz systemu i zmusić go do podania informacji o wersji, które pasują do obrazu rozruchowego, ale nie są rzeczywistą wersją systemu. Połączenie weryfikacji obrazu rozruchowego, weryfikacji dm-verity zawartości obrazu systemu i faktu, że funkcja configure
jest wywoływana bardzo wcześnie podczas uruchamiania systemu, powinno utrudnić wykorzystanie tej luki.