Podobnie jak większość oprogramowania do szyfrowania dysków i plików, szyfrowanie pamięci w systemie Android tradycyjnie opiera się na nieprzetworzonych kluczach szyfrowania obecnych w pamięci systemowej, aby można było przeprowadzić szyfrowanie. Nawet jeśli szyfrowanie jest wykonywane przez dedykowany sprzęt, a nie przez oprogramowanie, oprogramowanie generalnie nadal musi zarządzać nieprzetworzonymi 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 pamięci masowej. Istnieje jednak potrzeba zapewnienia zwiększonej ochrony przed innymi typami ataków, takimi jak ataki typu zimny rozruch i ataki online, w przypadku których osoba atakująca może uzyskać wyciek pamięci systemowej bez pełnego narażania urządzenia.
Aby rozwiązać ten problem, w systemie Android 11 wprowadzono obsługę kluczy opakowanych sprzętowo , w których występuje obsługa sprzętowa. Klucze opakowane sprzętowo to klucze do przechowywania, które są znane tylko w postaci surowej dedykowanemu sprzętowi; oprogramowanie widzi i pracuje z tymi kluczami tylko w postaci opakowanej (zaszyfrowanej). Ten sprzęt musi być w stanie generować i importować klucze pamięci masowej, opakowywać klucze pamięci w formy efemeryczne i długoterminowe, wyprowadzać podklucze, bezpośrednio programować jeden podklucz do wbudowanego silnika kryptograficznego i zwracać oddzielny podklucz do oprogramowania.
Uwaga : wbudowany mechanizm kryptograficzny (lub sprzęt do szyfrowania wbudowanego ) 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 wymagane wsparcie sprzętowe. Ta dyskusja koncentruje się na szyfrowaniu opartym na plikach (FBE), ale rozwiązanie dotyczy również szyfrowania metadanych .
Jednym ze sposobów uniknięcia potrzeby surowych kluczy szyfrowania w pamięci systemowej byłoby trzymanie ich tylko w gniazdach klucza wbudowanego silnika kryptograficznego. Takie podejście napotyka jednak na pewne problemy:
- Liczba kluczy szyfrujących może przekraczać liczbę gniazd na klucze.
- Silników kryptograficznych Inline można używać tylko do szyfrowania/odszyfrowywania pełnych bloków danych na dysku. Jednak w przypadku FBE oprogramowanie nadal musi być zdolne do wykonywania innych prac kryptograficznych, takich jak szyfrowanie nazw plików i uzyskiwanie identyfikatorów kluczy. Oprogramowanie nadal wymagałoby dostępu do nieprzetworzonych kluczy FBE w celu wykonania tej innej pracy.
Aby uniknąć tych problemów, klucze magazynu są zamiast tego przekształcane w klucze opakowane sprzętowo , które mogą być rozpakowane i używane tylko przez dedykowany sprzęt. Pozwala to na obsługę nieograniczonej liczby kluczy. Ponadto hierarchia kluczy jest modyfikowana i częściowo przenoszona na ten sprzęt, co umożliwia zwrócenie podklucza do oprogramowania w przypadku zadań, które nie mogą korzystać z wbudowanego silnika kryptograficznego.
Hierarchia kluczy
Klucze można wyprowadzić z innych kluczy za pomocą KDF (funkcji wyprowadzania klucza), takiej jak HKDF , w wyniku czego powstaje hierarchia kluczy .
Poniższy diagram przedstawia typową hierarchię klawiszy dla FBE, gdy nie są używane klucze opakowane sprzętowo:
Klucz klasy FBE to nieprzetworzony klucz szyfrowania, który Android przekazuje do jądra Linuksa w celu odblokowania określonego zestawu zaszyfrowanych katalogów, takich jak zaszyfrowana pamięć masowa dla określonego użytkownika Androida. (W jądrze ten klucz jest nazywany kluczem głównym fscrypt .) Z tego klucza jądro uzyskuje następujące podklucze:
- Identyfikator klucza. Nie jest to używane do szyfrowania, ale raczej jest wartością używaną do identyfikowania klucza, za pomocą którego chroniony jest określony plik lub katalog.
- Klucz szyfrowania zawartości pliku
- Klucz szyfrowania nazw plików
Natomiast poniższy diagram przedstawia hierarchię klawiszy dla FBE, gdy używane są klucze opakowane sprzętowo:
W porównaniu do wcześniejszego przypadku 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 Linuksowi, aby odblokować zestaw zaszyfrowanych katalogów. Jednak teraz ten klucz jest efemerycznie opakowany i aby mógł zostać użyty, musi zostać przekazany do dedykowanego sprzętu. Ten sprzęt musi implementować dwa interfejsy, które pobierają efemerycznie opakowany klucz:
- Jeden interfejs do uzyskiwania
inline_encryption_key
i bezpośredniego programowania go w gnieździe klucza wbudowanego silnika kryptograficznego. Pozwala to na szyfrowanie/odszyfrowywanie zawartości pliku bez oprogramowania mającego dostęp do surowego klucza. We wspólnych jądrach systemu Android ten interfejs odpowiada operacjiblk_crypto_ll_ops::keyslot_program
, która musi być zaimplementowana przez sterownik pamięci masowej. - Jeden interfejs do uzyskiwania i zwracania
sw_secret
(„tajny programowy” — w niektórych miejscach nazywany także „surowym sekretem”), który jest kluczem, którego Linux używa do uzyskiwania podkluczy do wszystkiego innego niż szyfrowanie zawartości plików. We wspólnych jądrach systemu Android ten interfejs odpowiada operacjiblk_crypto_ll_ops::derive_sw_secret
, która musi być zaimplementowana przez sterownik pamięci masowej.
Aby wyprowadzić inline_encryption_key
i sw_secret
z surowego klucza magazynu, sprzęt musi używać silnego kryptograficznie KDF. Ten KDF musi być zgodny z najlepszymi praktykami kryptograficznymi; 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ą kryptograficznie izolowane, tj. znajomość jednego z nich nie ujawnia żadnego innego. Rozciąganie klucza nie jest wymagane, ponieważ surowy klucz magazynu jest już jednakowo losowym kluczem.
Technicznie rzecz biorąc, 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 dokonano przeglądu i wdrożenia jednego KDF; można go znaleźć w kodzie źródłowym dla vts_kernel_encryption_test
. Zaleca się, aby sprzęt korzystał z tego KDF, który wykorzystuje NIST SP 800-108 „KDF w trybie licznika” z AES-256-CMAC jako PRF. Należy zauważyć, że aby zachować kompatybilność, wszystkie części algorytmu muszą być identyczne, w tym wybór kontekstów KDF i etykiet dla każdego podklucza.
Zawijanie kluczy
Aby spełnić cele bezpieczeństwa kluczy opakowanych sprzętowo, zdefiniowano dwa typy opakowywania kluczy:
- Zawijanie efemeryczne : sprzęt szyfruje surowy klucz za pomocą klucza, który jest generowany losowo przy każdym uruchomieniu i nie jest bezpośrednio ujawniany poza sprzętem.
- Zawijanie 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 osoba atakująca będzie w stanie wyodrębnić używany klucz z pamięci systemowej, klucz ten będzie 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 je było odblokować. Surowe klucze będą działać w tym celu. Jednak pożądane jest, aby surowe klucze nigdy nie były obecne 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 uruchamiania. Z tego powodu zdefiniowano pojęcie opakowania długoterminowego.
Aby obsługiwać zarządzanie kluczami opakowanymi na dwa różne sposoby, sprzęt musi implementować następujące interfejsy:
- Interfejsy do generowania i importowania kluczy magazynu, zwracając je w długoterminowej formie opakowanej. Te interfejsy są dostępne pośrednio przez KeyMint i odpowiadają tagowi KeyMint
TAG_STORAGE_KEY
. Zdolność „generowania” jest używana przezvold
do generowania nowych kluczy magazynu do użytku przez system Android, podczas gdy zdolność „importowania” jest używana przezvts_kernel_encryption_test
do importowania kluczy testowych. - Interfejs służący do przekształcania długoterminowego opakowanego klucza magazynu w efemerycznie opakowany klucz magazynu. Odpowiada to metodzie
convertStorageKeyToEphemeral
KeyMint. Ta metoda jest używana zarówno przezvold
, jak ivts_kernel_encryption_test
w celu odblokowania magazynu.
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ę do 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 specyficzne dla implementacji.
Zmiany w KeyMint
Implementacja KeyMint urządzenia musi zostać zmodyfikowana, aby obsługiwała TAG_STORAGE_KEY
i implementowała metodę convertStorageKeyToEphemeral
.
W Keymaster użyto exportKey
zamiast convertStorageKeyToEphemeral
.
Zmiany w jądrze Linuksa
Sterownik jądra systemu Linux dla wbudowanego mechanizmu kryptograficznego urządzenia musi zostać zmodyfikowany, aby obsługiwał klucze opakowane sprzętowo.
W przypadku jąder android14
i nowszych ustaw BLK_CRYPTO_KEY_TYPE_HW_WRAPPED
w blk_crypto_profile::key_types_supported
, spraw, aby blk_crypto_ll_ops::keyslot_program
i blk_crypto_ll_ops::keyslot_evict
obsługiwały programowanie/eksmitowanie kluczy opakowanych sprzętowo i zaimplementuj 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ługują programowanie/eksmitowanie kluczy opakowanych sprzętowo i zaimplementuj blk_ksm_ll_ops::derive_raw_secret
.
W przypadku jąder android11
ustaw BLK_CRYPTO_FEATURE_WRAPPED_KEYS
w keyslot_manager::features
, spraw, aby keyslot_mgmt_ll_ops::keyslot_program
i keyslot_mgmt_ll_ops::keyslot_evict
obsługują programowanie/eksmitowanie kluczy opakowanych sprzętowo i zaimplementuj keyslot_mgmt_ll_ops::derive_raw_secret
.
Testowanie
Chociaż szyfrowanie za pomocą kluczy opakowanych sprzętowo jest trudniejsze do przetestowania niż szyfrowanie za pomocą kluczy standardowych, nadal można je przetestować, importując klucz testowy i ponownie implementując pochodzenie klucza, które wykonuje 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 kluczy opakowanych sprzętowo (np. FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy
i DmDefaultKeyTest.TestHwWrappedKey
) nie zostały pominięte z powodu niewykrycia obsługi kluczy opakowanych sprzętowo, ponieważ wyniki testu nadal będą „przekazywane” w ta walizka.
Włączanie
Gdy obsługa sprzętowego klucza urządzenia działa poprawnie, możesz wprowadzić następujące zmiany w pliku fstab
urządzenia, aby system Android używał go do szyfrowania FBE i metadanych:
- FBE: dodaj flagę
wrappedkey_v0
do parametrufileencryption
. Na przykład użyjfileencryption=::inlinecrypt_optimized+wrappedkey_v0
. Aby uzyskać więcej informacji, zobacz dokumentację FBE . - Szyfrowanie metadanych: dodaj flagę
wrappedkey_v0
do parametrumetadata_encryption
. Na przykład użyjmetadata_encryption=:wrappedkey_v0
. Aby uzyskać więcej informacji, zapoznaj się z dokumentacją dotyczącą szyfrowania metadanych .