Wiązanie z wersją

W Keymaster 1 wszystkie klucze kluczmastera były powiązane szyfrowanie z korzenia zaufania lub kluczem Verified Boot. W Keymasterze 2 i 3 wszystkie klucze są również powiązane z systemem operacyjnym i poziomem poprawek obrazu systemu. Dzięki temu atakujący, który odkryje słabość w starszej wersji systemu lub oprogramowania TEE, nie będzie mógł przywrócić urządzenia do wersji podatnej na ataki i używać kluczy utworzonych w nowszej wersji. Ponadto, gdy klucz z danej wersji i poziomu poprawki jest używany na urządzeniu, które zostało zaktualizowane do nowszej wersji lub nowszego poziomu poprawki, klucz jest uaktualniany przed użyciem, a poprzednia wersja klucza staje się nieważna. W ten sposób, gdy urządzenie zostanie zaktualizowane, klucze *przeskakują* wraz z urządzeniem, ale przywrócenie urządzenia do poprzedniej wersji powoduje, że klucze stają się bezużyteczne.

Aby obsługiwać modułową strukturę Treble i odłączyć plik system.img od pliku boot.img, Keymaster 4 zmienił model wiązania wersji klucza, tak aby mieć oddzielne poziomy poprawek dla każdej partycji. Dzięki temu każda partycja może być aktualizowana niezależnie, a jednocześnie nadal zapewniać ochronę przed przywracaniem.

W Androidzie 9 partycje boot, systemvendor mają własny poziom poprawek.

  • Urządzenia z weryfikacją podczas uruchamiania (AVB) w Androidzie mogą umieszczać wszystkie poziomy poprawek oraz wersję systemu w pliku vbmeta, aby bootloader mógł je przekazać Keymasterowi. W przypadku połączonych partycji informacje o wersji partycji znajdują się w połączonym pliku vbmeta. Ogólnie informacje o wersji powinny znajdować się w pliku vbmeta struct, który zawiera dane weryfikacyjne (hash lub hashtree) dla danej partycji.
  • Na urządzeniach bez AVB:
    • Implementacje zweryfikowanego rozruchu muszą przesłać do programu rozruchowego hasz metadanych wersji, aby ten mógł przesłać go do Keymastera.
    • boot.img może nadal przechowywać poziom aktualizacji w nagłówku
    • system.img może nadal przechowywać poziom poprawek i wersję systemu operacyjnego w właściwościach tylko do odczytu.
    • Usługa vendor.img przechowuje poziom poprawki w usługi tylko do odczytu ro.vendor.build.version.security_patch.
    • Bootloader może przekazać kluczowi głównemu hasz wszystkich danych zweryfikowanych przez zweryfikowany rozruch.
  • 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_LEVELOS_VERSION: partycja system. (OS_VERSION zostanie usunięty z nagłówka boot.img.
  • Implementacje Keymaster powinny traktować wszystkie poziomy poprawek niezależnie. Klucze są dostępne, jeśli wszystkie informacje o wersji są zgodne z wartościami powiązanymi z kluczem, a w razie potrzeby IKeymaster::upgradeDevice() przeniesiesz je do wyższego poziomu poprawki.

Zmiany w HAL

Aby obsługiwać wiązanie i weryfikowanie 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 poziomu poprawki odpowiadającej bieżącej wersji systemu operacyjnego lub poziomowi poprawki, jest odrzucana z wartością ErrorCode::KEY_REQUIRES_UPGRADE.

Tag::OS_VERSION to wartość UINT, która reprezentuje główną, podrzędną i podrzędną podrzędną wersję systemu Android w postaci MMmmss, gdzie MM to główna wersja, mm to podrzędna wersja, a ss to podrzędna podrzędna wersja. Na przykład 6.1.2 będzie reprezentowane jako 060102.

Tag::OS_PATCHLEVEL to wartość UINT, która reprezentuje rok i miesiąc ostatniej aktualizacji systemu w formacie RRRRMM, gdzie RRRR to 4-cyfrowy rok, a MM to 2-cyfrowy miesiąc. Na przykład marzec 2016 r. będzie reprezentowany jako 201603.

UpgradeKey

Aby umożliwić uaktualnianie kluczy do nowej wersji systemu operacyjnego i poziomu poprawki systemu, Android 7.1 dodał do interfejsu 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 potrzebne do uaktualnienia klucza. Te informacje obejmują Tag::APPLICATION_ID i Tag::APPLICATION_DATA, które są niezbędne do odszyfrowania klucza blob, jeśli zostały podane podczas generowania.
  • upgradedKeyBlob to parametr wyjściowy służący do zwracania nowego klucza blob.

Jeśli metoda upgradeKey zostanie wywołana z wartością klucza, której nie można przeanalizować lub która jest nieprawidłowa, zwraca wartość ErrorCode::INVALID_KEY_BLOB. Jeśli jest wywoływany z kluczem, którego poziom poprawki jest wyższy niż bieżąca wartość systemowa, zwraca ErrorCode::INVALID_ARGUMENT. Jeśli jest wywoływany z kluczem, którego wersja systemu operacyjnego jest większa niż bieżąca wartość systemu, a ta ostatnia jest różna od 0, zwraca ErrorCode::INVALID_ARGUMENT. Dozwolone są uaktualnienia wersji systemu operacyjnego z niezerowej na zero. W przypadku błędów podczas komunikacji ze środowiskiem bezpiecznym zwraca odpowiednią wartość błędu (na przykład ErrorCode::SECURE_HW_ACCESS_DENIED, ErrorCode::SECURE_HW_BUSY). W przeciwnym razie zwraca ErrorCode::OK i zwraca nowy blok danych klucza w upgradedKeyBlob.

keyBlobToUpgrade pozostaje ważny po upgradeKey wywołaniu i teoretycznie można go ponownie użyć, jeśli urządzenie zostanie przekształcone. W praktyce magazyn kluczy zwykle wywołuje funkcję deleteKey w blobie keyBlobToUpgrade zaraz po wywołaniu funkcji upgradeKey. Jeśli tag keyBlobToUpgrade ma wartość Tag::ROLLBACK_RESISTANT, to tag upgradedKeyBlob powinien też mieć tę samą wartość (i nie powinien być podatny na cofnięcie zmian).

Bezpieczna konfiguracja

Aby zaimplementować powiązanie wersji, kluczowy TA musi mieć możliwość bezpiecznego odbierania bieżącej wersji systemu operacyjnego i poziomu poprawek (informacje o wersji) oraz zapewnienia, aby odbierane informacje były zgodne z informacjami o uruchomionym systemie.

Aby umożliwić bezpieczne dostarczanie informacji o wersji do TA, do nagłówka obrazu rozruchowego dodano pole OS_VERSION. Skrypt kompilacji obrazu rozruchowego automatycznie wypełnia to pole. Producenci OEM i implementatorzy klucza głównego TA muszą współpracować, aby zmodyfikować programy rozruchowe urządzeń w celu wyodrębnienia informacji o wersji z obrazu rozruchowego i przekazania ich do klucza głównego przed uruchomieniem niezabezpieczonego systemu. Dzięki temu atakujący nie mogą zakłócać udostępniania informacji o wersji dostawcy usług.

Musisz też sprawdzić, czy obraz systemu ma te same informacje o wersji co obraz rozruchowy. W tym celu do interfejsu HAL klucza głównego dodano metodę konfiguracji:

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.

Przy pierwszym wywołaniu po uruchomieniu urządzenia funkcja configure powinna zweryfikować, czy informacje o wersji są zgodne z informacjami podanymi przez bootloader. Jeśli informacje o wersji są niezgodne, configure zwraca ErrorCode::INVALID_ARGUMENT, a wszystkie inne metody Keymaster zwracają ErrorCode::KEYMASTER_NOT_CONFIGURED. Jeśli informacje są zgodne, configure zwraca ErrorCode::OK, a inne metody klucza głównego zaczynają działać normalnie.

Kolejne wywołania funkcji configure zwracają tę samą wartość co pierwsze wywołanie i nie zmieniają stanu klucza głównego.

Funkcja configure jest wywoływana przez system, którego zawartość ma być weryfikowana, więc atakujący ma bardzo ograniczone możliwości zaatakowania obrazu systemu i wymuszenia wyświetlenia informacji o wersji, które pasują do obrazu rozruchowego, ale nie są rzeczywistą wersją systemu. Połączenie weryfikacji obrazu rozruchu z weryfikacją zawartości obrazu systemu przez dm-verity oraz fakt, że funkcja configure jest wywoływana bardzo wcześnie podczas rozruchu systemu, powinno utrudnić wykorzystanie tego okna możliwości.