Klucze owijane sprzętowo

Podobnie jak większość oprogramowania do szyfrowania dysków i plików, szyfrowanie pamięci masowej Androida tradycyjnie opiera się na nieprzetworzonych kluczach szyfrowania obecnych w pamięci systemowej, aby można było wykonać szyfrowanie. Nawet jeśli szyfrowanie odbywa się za pomocą dedykowanego sprzętu, a nie oprogramowania, oprogramowanie zazwyczaj nadal musi zarządzać surowymi kluczami szyfrowania.

Tradycyjnie nie jest to postrzegane jako problem, ponieważ klucze nie będą obecne podczas ataku w trybie offline, który jest głównym rodzajem ataku, przed którym ma chronić szyfrowanie magazynu. Istnieje jednak potrzeba zapewnienia zwiększonej ochrony przed innymi typami ataków, takimi jak ataki zimnego rozruchu i ataki online, w których osoba atakująca może mieć możliwość wycieku pamięci systemowej bez pełnego narażania urządzenia.

Aby rozwiązać ten problem, Android 11 wprowadził obsługę kluczy sprzętowych , w których występuje obsługa sprzętu. Klucze sprzętowe to klucze pamięci, które są znane tylko w postaci surowej dedykowanemu sprzętowi; oprogramowanie widzi i działa tylko z tymi kluczami w opakowanej (zaszyfrowanej) formie. Ten sprzęt musi być w stanie generować i importować klucze pamięci, pakować klucze pamięci w formy efemeryczne i długoterminowe, wyprowadzać podklucze, bezpośrednio programować jeden podklucz w wbudowanym silniku kryptograficznym i zwracać oddzielny podklucz do oprogramowania.

Uwaga : wbudowany mechanizm kryptograficzny (lub wbudowany sprzęt szyfrujący ) odnosi się do sprzętu, który szyfruje/odszyfrowuje dane w drodze do/z urządzenia pamięci masowej. Zwykle jest to kontroler hosta UFS lub eMMC, który implementuje rozszerzenia kryptograficzne zdefiniowane przez odpowiednią specyfikację JEDEC.

Projekt

W tej sekcji przedstawiono projekt funkcji kluczy opakowanych sprzętowo, w tym wymaganą do tego obsługę sprzętu. Ta dyskusja koncentruje się na szyfrowaniu opartym na plikach (FBE), ale rozwiązanie dotyczy również szyfrowania metadanych .

Jednym ze sposobów uniknięcia konieczności używania surowych kluczy szyfrowania w pamięci systemowej byłoby przechowywanie ich tylko w szczelinach kluczy wbudowanego silnika kryptograficznego. Takie podejście napotyka jednak pewne problemy:

  • Liczba kluczy szyfrowania może przekraczać liczbę kluczy.
  • Wbudowane silniki kryptograficzne mogą być używane tylko do szyfrowania/odszyfrowywania pełnych bloków danych na dysku. Jednak w przypadku FBE oprogramowanie nadal musi mieć możliwość wykonywania innych prac kryptograficznych, takich jak szyfrowanie nazw plików i uzyskiwanie identyfikatorów kluczy. Oprogramowanie nadal potrzebowałoby dostępu do surowych kluczy FBE, aby wykonać tę inną pracę.

Aby uniknąć tych problemów, klucze pamięci są zamiast tego przekształcane w klucze sprzętowe , które można rozpakować i używać tylko na dedykowanym sprzęcie. Pozwala to na obsługę nieograniczonej liczby kluczy. Ponadto hierarchia kluczy jest modyfikowana i częściowo przenoszona na ten sprzęt, co pozwala na zwrócenie podklucza do oprogramowania w przypadku zadań, które nie mogą korzystać z wbudowanego silnika kryptograficznego.

Kluczowa hierarchia

Klucze można wyprowadzić z innych kluczy przy użyciu funkcji KDF (funkcja wyprowadzania klucza) , takiej jak HKDF , co skutkuje hierarchią kluczy .

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

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

Klucz klasy FBE to surowy klucz szyfrowania, który system Android przekazuje do jądra systemu Linux w celu odblokowania określonego zestawu zaszyfrowanych katalogów, takich jak pamięć zaszyfrowana poświadczeniami dla konkretnego użytkownika systemu Android. (W jądrze ten klucz jest nazywany kluczem głównym fscrypt .) Z tego klucza jądro wyprowadza następujące podklucze:

  • Identyfikator klucza. Ta wartość nie jest używana do szyfrowania, ale raczej jest wartością używaną do identyfikacji klucza, za pomocą którego chroniony jest określony plik lub katalog.
  • Klucz szyfrowania zawartości pliku
  • Klucz szyfrowania nazw plików

W przeciwieństwie do tego, poniższy diagram przedstawia hierarchię kluczy dla FBE, gdy używane są klucze opakowane sprzętowo:

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

W porównaniu z poprzednim przypadkiem do hierarchii kluczy dodano dodatkowy poziom, a klucz szyfrowania zawartości pliku został przeniesiony. Węzeł główny nadal reprezentuje klucz, który Android przekazuje do Linuksa, aby odblokować zestaw zaszyfrowanych katalogów. Jednak teraz ten klucz jest efemerycznie opakowany i aby mógł być używany, musi zostać przekazany do dedykowanego sprzętu. Ten sprzęt musi implementować dwa interfejsy, które pobierają efemerycznie opakowany klucz:

  • Jeden interfejs do wyprowadzania inline_encryption_key i bezpośredniego programowania go w kluczu wbudowanego silnika kryptograficznego. Pozwala to na szyfrowanie/odszyfrowywanie zawartości pliku bez dostępu oprogramowania do nieprzetworzonego klucza. We wspólnych jądrach systemu Android ten interfejs odpowiada operacji blk_ksm_ll_ops::keyslot_program , która musi zostać zaimplementowana przez sterownik pamięci masowej.
  • Jeden interfejs do wyprowadzania i zwracania sw_secret ("sekret oprogramowania" - w niektórych miejscach nazywany także "surowym sekretem"), który jest kluczem, którego Linux używa do wyprowadzania podkluczy do wszystkiego innego niż szyfrowanie zawartości pliku. We wspólnych jądrach systemu Android ten interfejs odpowiada operacji blk_ksm_ll_ops::derive_raw_secret , która musi zostać zaimplementowana przez sterownik pamięci masowej.

Aby uzyskać inline_encryption_key i sw_secret z nieprzetworzonego klucza pamięci, sprzęt musi używać kryptograficznie silnego KDF. Ten KDF musi przestrzegać najlepszych praktyk kryptograficznych; musi mieć siłę bezpieczeństwa co najmniej 256 bitów, tj. wystarczającą dla dowolnego algorytmu używanego później. Musi również używać odrębnej etykiety, kontekstu i/lub ciągu informacyjnego specyficznego dla aplikacji podczas wyprowadzania każdego typu podklucza, aby zagwarantować, że wynikowe podklucze są izolowane kryptograficznie, tj. wiedza o jednym z nich nie ujawnia żadnego innego. Rozciąganie klucza nie jest wymagane, ponieważ surowy klucz pamięci jest już jednolicie losowym kluczem.

Z technicznego punktu widzenia można użyć dowolnego KDF, który spełnia wymagania bezpieczeństwa. Jednak do celów testowych konieczne jest ponowne zaimplementowanie tego samego KDF w kodzie testowym. Obecnie jeden KDF został sprawdzony i wdrożony; można go znaleźć w kodzie źródłowym dla vts_kernel_encryption_test . Zaleca się, aby sprzęt używał tego KDF, który używa NIST SP 800-108 "KDF w trybie licznika" z AES-256-CMAC jako PRF. Należy zauważyć, że aby zachować zgodność, wszystkie części algorytmu muszą być identyczne, łącznie z wyborem kontekstów KDF i etykiet dla każdego podklucza.

Pakowanie kluczy

Aby spełnić cele bezpieczeństwa związane z kluczami owijanymi sprzętowo, zdefiniowano dwa rodzaje owijania kluczy:

  • Ephemeral wrapping : sprzęt szyfruje surowy klucz za pomocą klucza, który jest generowany losowo przy każdym uruchomieniu i nie jest bezpośrednio eksponowany na zewnątrz sprzętu.
  • Opakowania długoterminowe : sprzęt szyfruje surowy klucz za pomocą unikalnego, trwałego klucza wbudowanego w sprzęt, który nie jest bezpośrednio widoczny na zewnątrz sprzętu.

Wszystkie klucze przekazywane do jądra Linuksa w celu odblokowania pamięci są efemerycznie opakowane. Gwarantuje to, że jeśli atakujący będzie w stanie wydobyć z pamięci systemowej klucz będący w użyciu, będzie on bezużyteczny nie tylko poza urządzeniem, ale także na urządzeniu po ponownym uruchomieniu.

Jednocześnie Android nadal musi mieć możliwość przechowywania zaszyfrowanej wersji kluczy na dysku, aby można było je odblokować w pierwszej kolejności. W tym celu działałyby surowe klucze. Jednak pożądane jest, aby surowe klucze nigdy nie znajdowały się w pamięci systemowej, aby nigdy nie można ich było wyodrębnić w celu użycia poza urządzeniem, nawet jeśli zostaną wyodrębnione podczas rozruchu. Z tego powodu zdefiniowano pojęcie owijania długoterminowego.

Aby obsługiwać zarządzanie kluczami opakowanymi w te dwa różne sposoby, sprzęt musi implementować następujące interfejsy:

  • Interfejsy do generowania i importowania kluczy pamięci, zwracając je w formie długoterminowo opakowanej. Dostęp do tych interfejsów uzyskuje się pośrednio przez KeyMint i odpowiadają one TAG_STORAGE_KEY KeyMint. Zdolność „generuj” jest używana przez vold do generowania nowych kluczy magazynu do użytku przez system Android, podczas gdy zdolność „importowania” jest używana przez vts_kernel_encryption_test do importowania kluczy testowych.
  • Interfejs do konwersji długoterminowego klucza pamięci w opakowaniu efemerycznym. Odpowiada to convertStorageKeyToEphemeral KeyMint. Ta metoda jest używana zarówno przez vold , jak i vts_kernel_encryption_test w celu odblokowania pamięci masowej.

Algorytm zawijania kluczy jest szczegółem implementacji, ale powinien używać silnego AEAD, takiego jak AES-256-GCM z losowymi IV.

Wymagane zmiany oprogramowania

AOSP ma już podstawową strukturę obsługi kluczy opakowanych sprzętowo. Obejmuje to obsługę komponentów przestrzeni użytkownika, takich jak vold , a także obsługę jądra Linuksa w blk-crypto , fscrypt i dm-default-key .

Wymagane są jednak pewne zmiany dotyczące implementacji.

Zmiany KeyMint

Implementacja KeyMint urządzenia musi zostać zmodyfikowana, aby obsługiwała TAG_STORAGE_KEY i implementowała metodę convertStorageKeyToEphemeral .

W Keymaster zamiast exportKey użyto convertStorageKeyToEphemeral .

Zmiany jądra Linuksa

Sterownik jądra systemu Linux dla wbudowanego mechanizmu kryptograficznego urządzenia musi zostać zmodyfikowany, aby ustawić BLK_CRYPTO_FEATURE_WRAPPED_KEYS , aby keyslot_program() i keyslot_evict() obsługiwały programowanie/wyrzucanie kluczy owiniętych sprzętowo, a także implementować derive_raw_secret() .

Testowanie

Chociaż szyfrowanie za pomocą kluczy owiniętych sprzętowo jest trudniejsze do przetestowania niż szyfrowanie za pomocą kluczy standardowych, nadal można je przetestować, importując klucz testowy i ponownie zaimplementując wyprowadzenie klucza, co robi sprzęt. Jest to zaimplementowane w vts_kernel_encryption_test . Aby uruchomić ten test, uruchom:

atest -v vts_kernel_encryption_test

Przeczytaj dziennik testu i sprawdź, czy przypadki testowe z kluczami owiniętymi sprzętowo (np FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy i DmDefaultKeyTest.TestHwWrappedKey ) nie zostały pominięte z powodu niewykrywania kluczy sprzętowych, ponieważ wyniki testu nadal będą „przekazywane” w ta walizka.

Włączanie

Gdy obsługa kluczy opakowanych sprzętowo działa poprawnie, możesz wprowadzić następujące zmiany w pliku fstab urządzenia, aby Android używał go do szyfrowania FBE i metadanych:

  • FBE: dodaj flagę wrappedkey_v0 do parametru fileencryption . Na przykład użyj fileencryption=::inlinecrypt_optimized+wrappedkey_v0 . Więcej szczegółów znajdziesz w dokumentacji FBE .
  • Szyfrowanie metadanych: dodaj flagę wrappedkey_v0 do parametru metadata_encryption . Na przykład użyj metadata_encryption=:wrappedkey_v0 . Więcej informacji znajdziesz w dokumentacji szyfrowania metadanych .