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
, system
i vendor
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łówkusystem.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 odczyturo.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
: partycjavendor
BOOT_PATCH_LEVEL
: partycjaboot
OS_PATCH_LEVEL
iOS_VERSION
: partycjasystem
. (OS_VERSION
zostanie usunięty z nagłówkaboot.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_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 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ądzeniakeyBlobToUpgrade
to klucz, który wymaga uaktualnieniaupgradeParams
to parametry potrzebne do uaktualnienia klucza. Te informacje obejmująTag::APPLICATION_ID
iTag::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_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
.
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.