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 Keymaster 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 systemu lub oprogramowania TEE, nie będzie mógł przywrócić urządzenia do podatnej na ataki wersji i użyć kluczy utworzonych w nowszej wersji. Ponadto, gdy klucz z daną wersją i poziomem poprawek jest używany na urządzeniu, które zostało uaktualnione do nowszej wersji lub poziomu poprawek, klucz jest uaktualniany przed użyciem, a poprzednia wersja klucza jest unieważniana. W ten sposób, gdy urządzenie jest uaktualniane, klucze są *przesuwane* do przodu wraz z urządzeniem, ale każde 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, w Keymaster 4 zmieniono model powiązania wersji klucza, aby mieć oddzielne poziomy poprawek dla każdej partycji. Dzięki temu każda partycja może być aktualizowana niezależnie, a jednocześnie zapewniona jest ochrona przed przywracaniem.

Aby zaimplementować to powiązanie wersji, zaufana aplikacja KeyMint (TA) musi mieć możliwość bezpiecznego otrzymywania bieżącej wersji systemu operacyjnego i poziomów poprawek oraz upewnienia się, że otrzymywane informacje są zgodne ze wszystkimi informacjami o działającym systemie.

  • Urządzenia z Android Verified Boot (AVB) mogą umieścić wszystkie poziomy poprawek i wersję systemu w vbmeta, dzięki czemu program ładujący może przekazać je do Keymaster. W przypadku partycji połączonych łańcuchowo informacje o wersji partycji znajdują się w połączonym vbmeta. Ogólnie rzecz biorąc, informacje o wersji powinny znajdować się w vbmeta struct która zawiera dane weryfikacyjne (hash lub hashtree) dla danej partycji.
  • Na urządzeniach bez AVB:
    • Implementacje weryfikacji podczas uruchamiania muszą przekazywać do programu rozruchowego hasz metadanych wersji, aby program rozruchowy mógł przekazać hasz do Keymaster.
    • boot.img może nadal przechowywać poziom poprawek w nagłówku.
    • system.img może nadal przechowywać poziom poprawek i wersję systemu operacyjnego we właściwościach tylko do odczytu .
    • vendor.img przechowuje poziom poprawek we właściwości tylko do odczytu ro.vendor.build.version.security_patch.
    • Program rozruchowy może przekazać do Keymaster hasz wszystkich danych zweryfikowanych przez weryfikację podczas uruchamiania.
  • W Androidzie 9 użyj tych tagów, aby podać informacje o wersji dla tych partycji:
    • VENDOR_PATCH_LEVEL: partycja vendor
    • BOOT_PATCH_LEVEL: partycja boot
    • OS_PATCH_LEVEL i OS_VERSION: partycja system. (OS_VERSION jest usuwany z nagłówka boot.img.
  • Implementacje Keymaster powinny traktować wszystkie poziomy poprawek niezależnie. Klucze są użyteczne, jeśli wszystkie informacje o wersji są zgodne z wartościami powiązanymi z kluczem, a w razie potrzeby IKeymaster::upgradeDevice() przechodzi na wyższy poziom poprawek.

Zmiany w HAL

Aby obsługiwać powiązanie wersji i potwierdzenie 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 poziomu poprawek zgodnego z bieżącą wersją systemu operacyjnego lub poziomem poprawek, jest odrzucana z kodem ErrorCode::KEY_REQUIRES_UPGRADE.

Tag::OS_VERSION to wartość UINT, która reprezentuje główne, podrzędne i dodatkowe części wersji systemu Android w formacie MMmmss, gdzie MM to wersja główna, mm to wersja podrzędna, a ss to wersja dodatkowa. Na przykład wersja 6.1.2 byłaby reprezentowana 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 (4 cyfry), a MM to miesiąc (2 cyfry). Na przykład marzec 2016 r. byłby reprezentowany jako 201603.

UpgradeKey

Aby umożliwić uaktualnienie 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 należy uaktualnić.
  • upgradeParams to parametry potrzebne do uaktualnienia klucza. Obejmują one Tag::APPLICATION_ID i Tag::APPLICATION_DATA, które są niezbędne do odszyfrowania obiektu blob klucza, jeśli zostały podane podczas generowania.
  • upgradedKeyBlob to parametr wyjściowy używany do zwracania nowego obiektu blob klucza.

Jeśli metoda upgradeKey zostanie wywołana z obiektem blob klucza, którego nie można przeanalizować lub który jest nieprawidłowy, zwraca ona kod ErrorCode::INVALID_KEY_BLOB. Jeśli zostanie wywołana z kluczem, którego poziom poprawek jest wyższy niż bieżąca wartość systemu, zwraca kod ErrorCode::INVALID_ARGUMENT. Jeśli zostanie wywołana z kluczem, którego wersja systemu operacyjnego jest wyższa niż bieżąca wartość systemu, a wartość systemu jest różna od zera, zwraca kod ErrorCode::INVALID_ARGUMENT. Uaktualnienia wersji systemu operacyjnego z wartości różnej od zera do zera 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 kod ErrorCode::OK i nowy obiekt blob klucza w upgradedKeyBlob.

keyBlobToUpgrade pozostaje ważny po wywołaniu upgradeKey i teoretycznie można go ponownie użyć, jeśli urządzenie zostanie przywrócone do poprzedniej wersji. W praktyce magazyn kluczy zwykle wywołuje metodę deleteKey w przypadku obiektu blob keyBlobToUpgrade krótko po wywołaniu upgradeKey. Jeśli keyBlobToUpgrade miał tag Tag::ROLLBACK_RESISTANT, to upgradedKeyBlob też powinien go mieć (i powinien być odporny na przywracanie).

Bezpieczna konfiguracja

Aby zaimplementować powiązanie wersji, zaufana aplikacja Keymaster (TA) musi mieć możliwość bezpiecznego otrzymywania bieżącej wersji systemu operacyjnego i poziomu poprawek (informacji o wersji) oraz upewnienia się, że otrzymywane informacje są zgodne z informacjami o działającym systemie.

Aby obsługiwać bezpieczne dostarczanie 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 implementatorzy TA Keymaster muszą współpracować, aby zmodyfikować programy ładujące urządzenia 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 udostępnianie informacji o wersji do 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 inna metoda, TA zwraca kod ErrorCode::KEYMASTER_NOT_CONFIGURED.

Przy pierwszym wywołaniu metody configure po uruchomieniu urządzenia należy sprawdzić, czy podane informacje o wersji są zgodne z informacjami podanymi przez program rozruchowy. Jeśli informacje o wersji nie są zgodne, metoda configure zwraca kod ErrorCode::INVALID_ARGUMENT, a wszystkie inne metody Keymaster nadal zwracają kod ErrorCode::KEYMASTER_NOT_CONFIGURED. Jeśli informacje są zgodne, metoda configure zwraca kod ErrorCode::OK, a inne metody Keymaster zaczynają działać normalnie.

Kolejne wywołania metody configure zwracają tę samą wartość co pierwsze wywołanie i nie zmieniają stanu Keymaster.

Ponieważ metoda configure jest wywoływana przez system, którego zawartość ma być zweryfikowana, atakujący ma niewielkie okno możliwości, aby naruszyć obraz systemu i wymusić podanie informacji o wersji zgodnych z obrazem rozruchowym, ale nie z rzeczywistą wersją systemu. Połączenie weryfikacji obrazu rozruchowego, weryfikacji dm-verity zawartości obrazu systemu oraz faktu, że metoda configure jest wywoływana bardzo wcześnie podczas uruchamiania systemu, powinno utrudnić wykorzystanie tego okna możliwości.