Klucze opakowane sprzętowo

Podobnie jak większość oprogramowania do szyfrowania dysków i plików, szyfrowanie danych na Androidzie tradycyjnie polega na tym, że klucze szyfrowania w postaci binarnej są obecne w pamięci systemowej, aby można było przeprowadzić szyfrowanie. Nawet po przeprowadzeniu szyfrowania za pomocą specjalnego sprzętu, a nie oprogramowania, oprogramowanie nadal musi i zarządzanie nieprzetworzonymi kluczami szyfrowania.

Zwykle nie jest to uważane za problem, ponieważ podczas ataku offline klucze nie są obecne, a to jest główny typ ataku, przed którym ma chronić szyfrowanie pamięci masowej. Chcemy jednak zapewnić lepszą ochronę przed innymi typami ataków, takimi jak ataki z zimnego rozruchu czy ataki online, w których przypadku atakujący może uzyskać dostęp do pamięci systemowej bez całkowitego skompromitowania urządzenia.

Aby rozwiązać ten problem, w Androidzie 11 wprowadzono obsługę kluczy zaszyfrowanych sprzętowo, gdzie dostępna jest obsługa sprzętowa. Klucze opakowane sprzętowo to klucze pamięci znane tylko w postaci nieprzetworzonej dedykowany sprzęt; oprogramowanie wykrywa te klucze i działa z nimi tylko w opakowaniu (zaszyfrowanego). Musi on mieć możliwość generowania i importowania klucze pamięci masowej, opakowanie kluczy pamięci w postaci efemerycznej i długoterminowej, uzyskiwanie bezpośrednio we wbudowanym mechanizmie kryptograficznym. przez zwrócenie osobnego podklucza do oprogramowania.

Uwaga: wbudowany mechanizm kryptograficzny (lub wbudowany) ) odnosi się do sprzętu, który szyfruje/odszyfrowuje dane podczas jest w drodze do/z urządzenia pamięci masowej. Zwykle jest to host UFS lub kontroler eMMC, który implementuje rozszerzenia szyfrowania zdefiniowane w odpowiedniej specyfikacji JEDEC.

Projektowanie

W tej sekcji przedstawiono projekt funkcji kluczy w ramach oprogramowania, w tym wymagania dotyczące sprzętu. Ta dyskusja koncentruje się na szyfrowaniu plików (FBE), ale rozwiązanie dotyczy też szyfrowania metadanych.

Jednym ze sposobów uniknięcia potrzeby przechowywania nieprzetworzonych kluczy szyfrowania w pamięci systemowej jest przechowywanie ich tylko w kluczowych slotach wbudowanego modułu szyfrowania. Jednak natrafią na pewne problemy:

  • Liczba kluczy szyfrowania może przekroczyć liczbę sekcji kluczy.
  • Wbudowane silniki kryptograficzne mogą być używane tylko do szyfrowania/odszyfrowywania pełnych bloków danych na dysku. W przypadku FBE oprogramowanie musi jednak mieć możliwość wykonywania innych operacji kryptograficznych, takich jak szyfrowanie nazw plików i wyodrębnianie identyfikatorów kluczy. Oprogramowanie nadal będzie potrzebować dostępu do nieprzetworzonych kluczy FBE, aby wykonywać inne zadania.

Aby uniknąć tych problemów, klucze pamięci masowej są zamiast tego przekształcane w klucze opakowane sprzętowo, które mogą być tylko wyodrębnione i używane przez dedykowanego sprzętu. Umożliwia to obsługę nieograniczonej liczby kluczy. W hierarchia kluczy zostaje zmodyfikowana i częściowo przeniesiona na ten sprzęt, który pozwala na zwrócenie klucza podrzędnego do oprogramowania w przypadku zadań, które nie mogą używać wbudowany mechanizm kryptograficzny.

Hierarchia kluczy

Klucze można wywnioskować z innych kluczy za pomocą KDF (funkcji derywacji kluczy), np. HKDF, w efekcie hierarchii kluczy.

Poniższy diagram przedstawia typową hierarchię kluczy dla FBE, gdy klucze opakowane sprzętowo nie są używane:

Hierarchia kluczy FBE (standardowa)
Rysunek 1. Hierarchia kluczy FBE (standardowa)

Klucz klasy FBE to surowy klucz szyfrowania, który Android przekazuje jądrowi systemu Linux, aby odblokować określony zestaw zaszyfrowanych katalogów, np. pamięć szyfrowana hasłem dla konkretnego użytkownika Androida. (W jądrze ten klucz jest nazywany kluczem głównym fscrypt). Z tego klucza jądro wyprowadza te klucze podrzędne:

  • Identyfikator klucza. Nie jest ona używana do szyfrowania, lecz stanowi wartość. używany do identyfikowania klucza, z którym dany plik lub katalog i chronione.
  • Klucz szyfrowania zawartości pliku
  • Klucz szyfrowania nazw plików

Poniższy diagram przedstawia hierarchię FBE, gdy: są używane klucze opakowane sprzętowo:

Hierarchia kluczy FBE (z kluczem opakowanym sprzętowo)
Rysunek 2. Hierarchia kluczy FBE (z kluczem opakowanym sprzętowo)
.

W porównaniu z wcześniejszym przypadkiem do klucza dodaliśmy dodatkowy poziom. i przenieśliśmy klucz szyfrowania zawartości plików. Poziom główny wciąż reprezentuje klucz, który Android przekazuje do Linuksa, aby odblokować zestaw zaszyfrowanych katalogów. Teraz jednak ten klucz jest zawijany na bieżąco, Aby mógł być używany, musi zostać przekazany do dedykowanego sprzętu. Wymagania sprzętowe: wdrożyć 2 interfejsy, które przyjmują klucz opakowany na bieżąco:

  • Jeden interfejs do wyprowadzania inline_encryption_key i bezpośredniego programowania w kluczu slotu wbudowanego silnika szyfrowania. Umożliwia to szyfrowanie i odszyfrowywanie treści pliku bez dostępu oprogramowania do klucza w postaci binarnej. W typowych jądrach Androida interfejs ten odpowiada Operacja blk_crypto_ll_ops::keyslot_program, którą musi być zaimplementowane przez sterownik pamięci.
  • Jeden interfejs do wyprowadzenia i zwrotu sw_secret („tajemnicy oprogramowania” – w niektórych miejscach nazywanej też „tajemnicą surową”), która jest kluczem używanym przez Linuksa do wyprowadzenia kluczy podrzędnych do wszystkiego oprócz szyfrowania zawartości pliku. W typowych jądrach Androida interfejs ten odpowiada Operacja blk_crypto_ll_ops::derive_sw_secret, którą musi być zaimplementowane przez sterownik pamięci.

Aby wyprowadzić inline_encryption_keysw_secret z klucza magazynowania w postaci surowych danych, sprzęt musi używać silnego generatora kluczy KDF. Ten algorytm KDF musi być zgodny ze sprawdzonymi metodami kryptografii. Jego moc bezpieczeństwa musi wynosić co najmniej 256 bitów, co wystarcza dla każdego algorytmu używanego później. Musi także używać tagu etykiety, kontekstu i ciągu informacji specyficznych dla aplikacji, gdy wszystkich typów podkluczy, aby zagwarantować, że otrzymane są odizolowane kryptograficznie, czyli wiedza o jednym z nich nie ujawnia inne. Rozciąganie klucza nie jest wymagane, ponieważ nieprzetworzony klucz pamięci masowej jest już jednolicie losowy klucz.

Technicznie rzecz biorąc, można użyć dowolnego KDF, który spełnia wymagania dotyczące bezpieczeństwa. Do celów testowych należy jednak ponownie zaimplementować ten sam KDF w Testuj kod. Obecnie sprawdzono i wdrożono 1 KDF. Znajdziesz go w kodzie źródłowym vts_kernel_encryption_test. Zaleca się, aby sprzęt używał tego kodu KDF, który używa NIST SP 800-108 „KDF w trybie licznika” z AES-256-CMAC jako PRF. Pamiętaj, że aby zapewnić zgodność, wszystkie części algorytmu muszą być identyczne, w tym wybór kontekstów KDF i etykiet dla każdego klucza podrzędnego.

Zawijanie kluczy

Aby osiągnąć cele w zakresie bezpieczeństwa kluczy opakowanych sprzętowo, 2 sposoby dodawania kluczy są zdefiniowane:

  • Efemerydyczny owijacz: sprzęt szyfruje klucz jawny za pomocą klucza, który jest generowany losowo przy każdym uruchomieniu i nie jest bezpośrednio dostępny poza sprzętem.
  • Pakowanie długoterminowe: sprzęt szyfruje klucz wsadowy za pomocą unikalnego, trwałego klucza wbudowanego w sprzęt, który nie jest bezpośrednio dostępny poza sprzętem.

Wszystkie klucze przekazywane do jądra Linuksa w celu odblokowania pamięci są czasowo zapakowane. Dzięki temu, jeśli atakujący uda się wyodrębnić klucz używany z pamięci systemowej, nie będzie on mógł go użyć nie tylko na wyłączonym urządzeniu, ale także po jego ponownym uruchomieniu.

Jednocześnie Android musi mieć możliwość przechowywania zaszyfrowanej wersji kluczy na dysku, aby można było je odblokować. Do tego celu nadają się klucze w postaci tekstu zwykłego. Powinniśmy jednak nigdy nie używać klucze są w ogóle dostępne w pamięci systemowej, dzięki czemu nie można ich wyodrębnić nie powinny być używane poza urządzeniem, nawet jeśli zostały wyodrębnione podczas uruchamiania. Z tego powodu zdefiniowano pojęcie długoterminowego opakowania.

Aby można było zarządzać kluczami opakowanymi na 2 różne sposoby, sprzęt musi zaimplementuj następujące interfejsy:

  • Interfejsy do generowania i importowania kluczy pamięci, zwracanie ich długoterminową formę zawijaną przez długi czas. Dostęp do tych interfejsów jest pośrednio uzyskiwany przez KeyMint i odpowiada tagowi TAG_STORAGE_KEY KeyMint. Przycisk „Wygeneruj” aplikacja vold używa możliwości, aby wygenerować nowe miejsce na dane do użycia w Androidzie, a funkcja importowania umiejętność jest używana przez vts_kernel_encryption_test, aby zaimportować klucze testowe.
  • Interfejs do konwertowania długoterminowego opakowanego klucza pamięci masowej efemerycznie opakowany klucz pamięci masowej. Odpowiada to metodzie convertStorageKeyToEphemeral KeyMint. Ta metoda jest używana przez vold i vts_kernel_encryption_test do odblokowania pamięci.

Algorytm szyfrowania klucza to szczegół implementacji, ale powinien używać silnego algorytmu AEAD, takiego jak AES-256-GCM z losowymi wartościami IV.

Wymagane zmiany w oprogramowaniu

AOSP zawiera już podstawowy framework do obsługi kluczy zaszyfrowanych sprzętowo. Ten obejmuje obsługę komponentów przestrzeni użytkownika, takich jak vold, a także jak ją obsługują w językach blk-crypto, fscrypto oraz dm-default-key.

Wymagane są jednak pewne zmiany związane z wdrożeniem.

Zmiany w KeyMint

Implementację KeyMint urządzenia należy zmodyfikować, aby obsługiwać TAG_STORAGE_KEY i zaimplementuj kod Metoda convertStorageKeyToEphemeral.

W Keymaster zamiast convertStorageKeyToEphemeral użyto exportKey.

Zmiany w jądrze Linuksa

Sterownik jądra Linuksa dla wbudowanego modułu szyfrowania urządzenia musi zostać zmodyfikowany, aby obsługiwał klucze spakowane sprzętowo.

W przypadku jąder android14 i wyższych: ustaw BLK_CRYPTO_KEY_TYPE_HW_WRAPPEDblk_crypto_profile::key_types_supported, wykonaj blk_crypto_ll_ops::keyslot_programblk_crypto_ll_ops::keyslot_evict obsługuj programowanie i usuwanie kluczy w ramach szyfrowania sprzętowego i wdróż blk_crypto_ll_ops::derive_sw_secret.

W przypadku jąder android12 i android13 ustaw BLK_CRYPTO_FEATURE_WRAPPED_KEYS w: blk_keyslot_manager::features, ustaw blk_ksm_ll_ops::keyslot_program i blk_ksm_ll_ops::keyslot_evict obsługi programowania/usuwania opakowanych sprzętowo kluczy, i zaimplementuj blk_ksm_ll_ops::derive_raw_secret.

W przypadku jąder android11 ustaw BLK_CRYPTO_FEATURE_WRAPPED_KEYSkeyslot_manager::features, wykonaj keyslot_mgmt_ll_ops::keyslot_programkeyslot_mgmt_ll_ops::keyslot_evict, obsłuż programowanie/wyrzucanie kluczy w ramach sprzętowych oraz wdrożenie keyslot_mgmt_ll_ops::derive_raw_secret.

Testowanie

Chociaż szyfrowanie za pomocą kluczy sprzętowych jest trudniejsze do przetestowania niż szyfrowanie za pomocą standardowych kluczy, można je przetestować, importując klucz testowy i ponownie implementując wyprowadzenie klucza, które wykonuje sprzęt. Ta funkcja jest wdrożona w sekcji vts_kernel_encryption_test. Aby przeprowadzić ten test: bieg:

atest -v vts_kernel_encryption_test

Przeczytaj dziennik testu i sprawdź, czy przypadki testowe kluczy zaszyfrowanych sprzętowo (na przykład FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicyDmDefaultKeyTest.TestHwWrappedKey) nie zostały pominięte z powodu braku obsługi kluczy zaszyfrowanych sprzętowo, ponieważ w tym przypadku wyniki testu są nadal „pozytywny”.

Włącz klucze

Gdy obsługa kluczy opakowanych sprzętowo na urządzeniu będzie działać prawidłowo, wprowadź te zmiany w pliku fstab na urządzeniu, Android używa go do szyfrowania FBE i metadanych:

  • FBE: dodaj flagę wrappedkey_v0 do fileencryption. Użyj na przykład elementu fileencryption=::inlinecrypt_optimized+wrappedkey_v0. Więcej informacji znajdziesz w dokumentacji FBE.
  • Szyfrowanie metadanych: dodaj flagę wrappedkey_v0 do parametru metadata_encryption. Użyj na przykład elementu metadata_encryption=:wrappedkey_v0. Więcej informacji znajdziesz w dokumentacji dotyczącej szyfrowania metadanych.