Podobnie jak większość oprogramowania do szyfrowania dysków i plików, szyfrowanie pamięci w Androidzie tradycyjnie polega na tym, że w pamięci systemowej znajdują się nieprzetworzone klucze szyfrowania, dzięki którym można 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 zaszyfrowane sprzętowo to klucze magazynowe, które są znane w postaci surowej tylko dla dedykowanego sprzętu. Oprogramowanie widzi i działa z tymi kluczami tylko w zaszyfrowanej formie. 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 szyfrowania (lub wbudowany sprzęt do szyfrowania) to sprzęt, który szyfruje/odszyfrowuje dane podczas ich przesyłania do lub z urządzenia magazynującego. Zwykle jest to host UFS lub eMMC. kontroler, który implementuje rozszerzenia kryptograficzne zdefiniowane przez odpowiednie Specyfikacja JEDEC.
Projektowanie
Ta sekcja przedstawia projekt funkcji kluczy w ramach oprogramowania sprzętowego, w tym wymagania dotyczące sprzętu. Ta dyskusja koncentruje się na szyfrowaniu plików (FBE), ale rozwiązanie dotyczy również szyfrowania metadanych.
Jednym ze sposobów uniknięcia potrzeby nieprzetworzonych kluczy szyfrowania w pamięci systemowej jest Przechowuj je wyłącznie w blokach kluczy wbudowanego mechanizmu kryptograficznego. 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 magazynu są zamiast tego tworzone jako klucze opakowane sprzętowo, które mogą być rozpakowane i używane tylko przez dedykowany sprzęt. 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:
Klucz klasy FBE to nieprzetworzony klucz szyfrowania, który Android przekazuje do systemu Linux jądro, aby odblokować określony zestaw zaszyfrowanych katalogów, takich jak szyfrowana danymi uwierzytelniającymi 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:
W porównaniu z poprzednim przykładem hierarchia kluczy została rozszerzona o dodatkowy poziom, a klucz szyfrowania zawartości pliku został przeniesiony. 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 konieczności uzyskiwania przez oprogramowanie dostępu do klucza w postaci surowej. W typowych jądrach Androida interfejs ten odpowiada Operacjablk_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 jądrach wspólnych Androida ten interfejs odpowiada operacjiblk_crypto_ll_ops::derive_sw_secret
, która musi być zaimplementowana przez sterownik pamięci.
Aby wyprowadzić inline_encryption_key
i sw_secret
z surowego klucza magazynu, sprzęt musi używać silnego szyfrowania KDF. Ten KDF
musi przestrzegać sprawdzonych metod kryptograficznych; musi mieć poziom zabezpieczeń
lub co najmniej 256 bitów, czyli tyle, ile wystarczy, aby możliwe było użycie później żadnego algorytmu. Musi także używać tagu
odrębną etykietą, kontekstem i ciągiem 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ż surowy klucz pamięci jest już kluczem losowym o jednolitej rozkładzie.
Technicznie rzecz biorąc, można użyć dowolnego KDF, który spełnia wymagania dotyczące bezpieczeństwa.
Jednak na potrzeby testów należy ponownie zaimplementować ten sam plik KDF w kodzie testowym. Obecnie sprawdzono i wdrożono 1 KDF. Znajdziesz go w kodzie źródłowym vts_kernel_encryption_test
.
Zalecamy, aby sprzęt używał tego KDF, który korzysta z NIST SP 800-108 „KDF w trybie przeciwdziałania” 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 etykiety poszczególnych podkluczy.
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 pierwotny 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 osoba przeprowadzająca atak będzie w stanie wyodrębnić z pamięci systemowej, jest on bezużyteczny nie tylko poza urządzeniem, ale też na urządzeniu po jego restarcie.
Jednocześnie Android nadal musi mieć możliwość przechowywania zaszyfrowanej wersji kluczy na dysku, dzięki czemu można je odblokować. Nieprzetworzone do tego celu. Zaleca się jednak, aby klucze w postaci zwykłych danych nigdy nie były obecne w pamięci systemowej, aby nie można było ich wyodrębnić i użyć poza urządzeniem, nawet jeśli wyodrębnienie nastąpiło w momencie uruchamiania. Z tego powodu koncepcja długoterminowego pakowania danych.
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 magazynu, które są zwracane w pakowanym formacie długoterminowym. Dostęp do tych interfejsów jest możliwy pośrednio przez:
KeyMint i odpowiadają tagowi KeyMint (
TAG_STORAGE_KEY
). Funkcja „generuj” jest używana przezvold
do generowania nowych kluczy pamięci masowej na potrzeby Androida, a funkcja „importuj” jest używana przezvts_kernel_encryption_test
do importowania kluczy testowych. - Interfejs do konwertowania długoterminowego klucza spakowanego w pamięć na klucz spakowany w pamięć krótkotrwałą. Odpowiada to
Metoda
convertStorageKeyToEphemeral
KeyMint. Ta metoda jest używana przezvold
ivts_kernel_encryption_test
do odblokowania pamięci.
Algorytm dodawania kluczy to szczegóły implementacji, ale powinien silny sygnał AEAD, np. AES-256-GCM z losowymi IVW.
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
Implementacja KeyMint na urządzeniu musi zostać zmodyfikowana, aby obsługiwać metodę TAG_STORAGE_KEY
i implementować metodę 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_WRAPPED
w blk_crypto_profile::key_types_supported
,
wykonaj blk_crypto_ll_ops::keyslot_program
i blk_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_KEYS
w keyslot_manager::features
, wykonaj keyslot_mgmt_ll_ops::keyslot_program
i keyslot_mgmt_ll_ops::keyslot_evict
, obsłuż programowanie i 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. Jest to implementowane w 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.TestAesInlineCryptOptimizedHwWrappedKeyPolicy
i DmDefaultKeyTest.TestHwWrappedKey
) nie zostały pominięte z powodu braku obsługi kluczy zaszyfrowanych sprzętowo, ponieważ w takim przypadku wyniki testu są nadal „pozytywny”.
Włączanie klawiszy
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
dofileencryption
. Użyj na przykład elementufileencryption=::inlinecrypt_optimized+wrappedkey_v0
. Więcej informacji znajdziesz w dokumentacji FBE. - Szyfrowanie metadanych: dodaj flagę
wrappedkey_v0
dometadata_encryption
. Na przykład użyj wartościmetadata_encryption=:wrappedkey_v0
Więcej informacji: metadane dokumentacji dotyczącej szyfrowania.