Wiązanie z wersją

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 odczytu ro.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 partycja
    • BOOT_PATCH_LEVEL: boot partycja
    • OS_PATCH_LEVELOS_VERSION:system partycja. (OS_VERSION zostanie usunięty z nagłówka boot.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_VERSIONTag::OS_PATCHLEVEL oraz metody configureupgradeKey. 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ądzenia
  • keyBlobToUpgrade to klucz, który wymaga uaktualnienia
  • upgradeParams to parametry wymagane do uaktualnienia klucza. Obejmują one Tag::APPLICATION_IDTag::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_VERSIONpole. 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_VERSIONTag::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.