Opisujemy usługę w chmurze, która używa bezpiecznego sprzętu do przechowywania kluczy kryptograficznych w taki sposób, aby dostęp do nich był chroniony przez czynnik wiedza oparty na niskiej entropii (np. kod PIN na ekranie blokady). Bezpieczne urządzenie ma zapobiegać atakom typu „próba błędów”, ponieważ po zbyt wielu nieudanych próbach podania prawidłowego czynnika wiedzy zapisane klucze kryptograficzne są trwale niedostępne.
Autor: Shabsi Walfish
Data wersji: 6 marca 2018 r.
Uwaga: ten dokument jest wciąż w trakcie tworzenia, a szczegóły jego wdrożenia są jeszcze dopracowywane. W miarę stabilizacji systemu i tworzenia kolejnych dokumentów będziemy aktualizować ten poradnik, dodając do niego bardziej szczegółowe informacje (szczególnie w przypadku odpowiednich wersji oprogramowania open source).
Omówienie
Tradycyjnie szyfrowanie (które służy do zapewnienia prywatności danych) wymaga stosowania tajnych informacji o wysokiej entropii z perspektywy atakującego. Wysoka entropia jest wymagana, ponieważ schemat szyfrowania musi być odporny na ataki metodą brutalnej siły, które sprawdzają wszystkie możliwe hasła, dopóki nie zostanie znalezione właściwe. Biorąc pod uwagę obecną dostępność mocy obliczeniowej, rozsądny minimalny poziom entropii dla tajemnic kryptograficznych może wynosić od 70 do 80 bitów. Niestety ludzie mają duże trudności z zapamiętywaniem i odtwarzaniem haseł lub innych tajemnic o tak dużej entropii1, zwłaszcza jeśli są one rzadko używane (częste używanie haseł o wysokiej entropii jest trudne i męczące). Pozostawia to przed nami trudne zadanie: jak możemy chronić dane prywatne za pomocą technologii szyfrowania, jeśli chcemy, aby sekret był „czynnikiem wiedzy”, który użytkownik najprawdopodobniej zapamięta? Z różnych powodów ten problem jest tak trudny do rozwiązania, że usługi przechowywania danych w chmurze zwykle szyfrują dane tylko za pomocą informacji tajnych zarządzanych przez samego dostawcę usługi, zamiast polegać na tym, że użytkownik zapamiętał swoją informację tajną.
Jednym ze sposobów na wypełnienie luki między wymaganiami dotyczącymi tajnych danych kryptograficznych a tajnymi danymi, które można zapamiętać, jest użycie usługi Cloud Key Vault (CKV) do przechowywania „klucza odzyskiwania” o wysokiej entropii, chronionego przez tajne dane, które można zapamiętać, o niskiej entropii. Usługa CKV udostępni klucz odzyskiwania tylko stronie, która udowodni, że zna odpowiednią ludzką tajną frazę. Ataki typu „próba sił” na zapamiętane przez człowieka hasło mogą zostać udaremnione przez usługę CKV, która narzuci bezwzględny limit liczby nieudanych prób udowodnienia znajomości hasła. Klucz odzyskiwania to standardowy symetryczny klucz kryptograficzny, który można stosować w ramach autoryzowanego schematu szyfrowania. Umożliwia on łatwe szyfrowanie dużej ilości danych (np. kopii zapasowej dysku), które można bezpiecznie przechowywać w dowolnym miejscu. Takie zaszyfrowane dane są bezużyteczne dla osób, które nie mają dostępu do klucza odzyskiwania.
Ten dokument zawiera opis naszego podejścia do tworzenia usługi Cloud Key Vault za pomocą zaufanych modułów sprzętowych (THM). Pierwsza implementacja usługi CKV została zaprojektowana w celu ochrony kluczy odzyskiwania za pomocą czynnika wiedzy na ekranie blokady (LSKF) użytkownika, czyli tajnego kodu PIN, hasła lub wzorów przesuwania, które służą do odblokowywania smartfonów. Ludzie mogą z pewnością zapamiętać LSKF. Jednocześnie takie sekrety LSKF mają zazwyczaj wystarczającą ilość entropii, aby stawić opór atakującemu, który ma bardzo ograniczoną liczbę prób, dzięki czemu są one odpowiednie dla usługi CKV.
Pierwsze zastosowanie naszej usługi Cloud Key Vault to umożliwienie szyfrowania po stronie klienta kopii zapasowych Androida. Wcześniej pliki zaszyfrowane lokalnie na urządzeniu z Androidem używały klucza chronionego LSKF użytkownika, ale kopie zapasowe tych plików przechowywane (i zaszyfrowane) w chmurze nie były chronione LSKF. Po raz pierwszy Cloud Key Vault umożliwia ochronę ekranu blokady również w przypadku kopii zapasowych Androida przechowywanych w chmurze. Oznacza to, że serwery Google nie mają możliwości dostępu do treści zaszyfrowanych kopii zapasowych ani ich przywracania. Tylko urządzenie z kluczem LSKF użytkownika może odszyfrować kopie zapasowe.
Podstawowe pojęcia
Początkowo jedyną obsługiwaną platformą klienta dla usługi Cloud Key Vault jest system operacyjny Android 9 Pie. W tym dokumencie, gdy mówimy o kliencie, mamy na myśli urządzenie z systemem operacyjnym Android 9 Pie z usługami Google Play. Nasze rozwiązanie po stronie serwera działa na specjalnie wyznaczonych serwerach Google, na których zainstalowany jest dodatkowy układ Titan2. Zaprojektowany przez Google układ Titan jest komponentem sprzętowym w naszym zaufanym module sprzętowym. Wyposażyliśmy go w specjalny program ładujący i oprogramowanie układowe, które implementują nasze protokoły i mechanizmy egzekwowania zabezpieczeń (jak opisano w tym dokumencie). Aby mieć pewność, że nasz protokół działa na sprzęcie Titan, używamy technik weryfikacji sprzętowej.
Usługa CKV musi być skalowalna, aby obsługiwać ruch z miliardów3 urządzeń z Androidem, bez utraty znacznej ilości danych użytkownika w wyniku awarii sprzętu (np. spalonych układów scalonych) lub przedłużonych przestojów spowodowanych konserwacją centrum danych. Z tego powodu serwery z umieszczonymi na nich układami Titan są zorganizowane w grupy, z których każda składa się z kilku niezależnych THM zawierających po jednej kopii tego samego klucza. Dana kohorta będzie rozproszona po fizycznie oddzielonych centrach danych w różnych strefach konserwacji, aby zapewnić systemowi dostępność i niezawodność. Ze względu na skalowalność klienci będą dzieleni na kilka różnych kohort, abyśmy mogli dostosować pojemność usługi przez dodanie większej liczby serwerów i zwiększenie liczby dostępnych kohort.
Możemy teraz wymienić główne elementy architektury usługi Cloud Key Vault.
Komponenty architektoniczne / Glosariusz
Element wiedzy na ekranie blokady (LSKF): element, który można zapamiętać, np. krótki kod PIN, wzór przesuwania po siatce 3 x 3 lub hasło. Ten klucz jest używany do ochrony możliwości lokalnego odblokowania urządzenia i jest uznawany za główny (czyli „mocny”) czynnik uwierzytelniania dla lokalnej blokady ekranu urządzenia użytkownika.
Klient: urządzenie użytkownika z systemem operacyjnym Android 9 Pie i Usługami Google Play lub równoważnym obsługiwanym oprogramowaniem.
-
Android Framework: używamy tego ogólnego terminu (lub po prostu Framework) w odniesieniu do interfejsów API w wersji Androida 9 Pie lub nowszej. Nie odnosi się on do wcześniejszych wersji.
Usługi Google Play: zbiór usług i aplikacji działających na urządzeniu użytkownika, które umożliwiają współpracę z systemem kont Google i niestandardowymi interfejsami API serwera.
Recovery Agent: aplikacja systemowa działająca w ramach usług Google Play w przestrzeni użytkownika na urządzeniu z Androidem 9 Pie (lub podobnym). Agent ds. odzyskiwania danych jest odpowiedzialny za wykonywanie różnych protokołów po stronie klienta oraz za nawiązywanie interfejsu z systemem operacyjnym Android w celu tworzenia wiadomości protokołów, które obejmują LSKF.
Roszczenie o odzyskanie: gdy użytkownik chce pobrać klucz odzyskiwania, musi utworzyć roszczenie o odzyskanie, które zawiera zaszyfrowaną kopię klucza LSKF, o którym twierdzi, że go zna. Użytkownik zostanie poproszony o wpisanie klucza LSKF starego urządzenia na nowym urządzeniu, na którym próbuje uzyskać dostęp do klucza odzyskiwania starego urządzenia.
Klucz odzyskiwania: klucz kryptograficzny chroniony przez usługę Cloud Key Vault, który służy do szyfrowania (i uwierzytelniania) danych na urządzeniu klienta. Gdy klucz odzyskiwania zostanie umieszczony w Vault (patrz poniżej), lokalną kopię można usunąć, gdy tylko Klient przestanie używać go do szyfrowania danych.
Usługa Cloud Key Vault (CKV): usługa internetowa umożliwiająca urządzeniom klienta przechowywanie kluczy kryptograficznych chronionych za pomocą łatwego do zapamiętania klucza LSKF.
-
Kohorta: zbiór serwerów Vault lub serwerów THM, które mogą służyć jako redundantne kopie zapasowe siebie nawzajem.
Klucz publiczny zbioru: klucz publiczny z pary kluczy wygenerowanej przez konkretny zbiór THM. Odpowiadający mu klucz prywatny jest dostępny tylko w ramach THM-ów, które były w grupie w momencie generowania klucza.
Zaufany moduł sprzętowy (THM): specjalny moduł zabezpieczeń (mikrokontroler) zapewniający minimalne i godne zaufania środowisko komputerowe. Bezpieczny element musi mieć co najmniej możliwość generowania i przechowywania kluczy tajnych oraz utrzymywania stanu nieulotnego (aby można było zapobiegać atakom polegającym na resetowaniu do wcześniejszego stanu).
Vault: konkretny wpis w bazie danych usługi CKV zawierający klucz odzyskiwania chroniony LSKF pojedynczego urządzenia. Użytkownik może mieć wiele schowisk, z których każde odpowiada innemu urządzeniu lub LSKF. Tylko THM na serwerze Vault może przeglądać i wyodrębniać zawartość Vault.
Serwer Vault: maszyna ogólnego przeznaczenia działająca w centrum danych Google, która została specjalnie zmodyfikowana, aby dodać zaufany moduł sprzętowy (Trusted Hardware Module, THM).
Projektowanie protokołu
Protokół CKV składa się z kilku faz:
Inicjowanie
Aby zainicjować system, Google udostępni klucz publiczny „elementu zaufania”, którego Framework użyje do weryfikacji uwierzytelnienia sprzętowego Google. Klucz podpisywania tego głównego zaufanego punktu rozpowszechniania kluczy jest przechowywany w trybie offline i starannie zabezpieczony, tak aby wymagał udziału wielu pracowników do podpisania. Klucz publiczny tego głównego zaufania jest wbudowany w system operacyjny Android i może być zmieniony tylko przez aktualizację systemu.
Google okresowo publikuje też listę publicznych kluczy dla każdej grupy THM oraz zaświadczenie o jej autentyczności. Atestacja na liście używa podpisu, który tworzy łańcuch zaufania do zaufanego głównego certyfikatu. Każda aktualizacja opublikowanej listy zawiera też numer sekwencji, dzięki czemu można zapobiec cofaniu zmian. Usługa odzyskiwania danych pobierze najnowszą opublikowaną listę kluczy publicznych kohorty i przekaże ją do Framework. Następnie weryfikuje poświadczenie i losowo wybiera z listy klucz publiczny grupy, który ma być użyty w fazie tworzenia skarbca.
Tworzenie Vault
Po zakończeniu przez Framework procesu inicjalizacji przez pobranie listy publicznych kluczy kohorty agent odzyskiwania poprosi Framework o utworzenie nowej sekcji Vault. Gdy użytkownik wprowadzi klucz LSKF, Framework wygeneruje nowy klucz odzyskiwania i najpierw zaszyfruje go za pomocą klucza wygenerowanego na podstawie hasha klucza LSKF, a potem za pomocą klucza publicznego kohorty wybranego przez Framework podczas inicjalizacji. Wynikowy zaszyfrowany blok danych to Vault, który jest zwracany przez Framework do agenta ds. odzyskiwania, a następnie przesyłany do usługi CKV Google.
Otwieranie Vault
Gdy Recovery Agent na nowym urządzeniu musi uzyskać dostęp do klucza odzyskiwania przechowywanego w określonym Vault, najpierw wyświetli użytkownikowi prośbę o wpisanie LSKF pierwotnego urządzenia, na którym utworzono Vault. Agent odzyskiwania poprosi następnie Framework o utworzenie roszczenia o odzyskanie za pomocą tego LSKF. Framework wygeneruje nowy klucz roszczeń i zaszyfruje go, a także hasz zaszyfrowanego klucza roszczeń LSKF, tym samym kluczem publicznym grupy, którym zaszyfrowano schownię. Powstały zaszyfrowany blob nosi nazwę roszczenia o odzyskanie. Framework przekazuje je do agenta ds. odzyskiwania, który przedstawia je usłudze CKV.
CKV kieruje roszczenie o przywrócenie (i odpowiadający mu Vault) do serwerów Vault, które należą do odpowiedniej kohorty. Następnie THM na serwerach Vault odszyfrowuje zgłaszoną prośbę o odzyskanie i próbuje wyodrębnić klucz odzyskiwania z oryginalnej skrytki, używając zgłoszonego hasha LSKF (aby wyprowadzić wewnętrzny klucz szyfrowania). Jeśli oryginalny hasz LSKF i podany hasz LSKF są zgodne, THM wyodrębni klucz odzyskiwania z Vault i ponownie zaszyfruje go za pomocą klucza zgłaszającego, który znajdował się w roszczeniu o odzyskiwanie. W przeciwnym razie THM zwiększy licznik nieudanych prób. Gdy licznik nieudanych prób osiągnie limit, THM odmówi przetworzenia kolejnych roszczeń o odzyskanie dotyczących tego Vault.
Jeśli wszystko pójdzie dobrze, ponownie zaszyfrowany klucz odzyskiwania (który jest teraz zaszyfrowany za pomocą klucza zgłaszającego) jest wysyłany z powrotem z serwera sejfu do Framework. Framework używa swojej kopii klucza roszczeń do odszyfrowywania klucza odzyskiwania, a protokół jest już kompletny.
Środki bezpieczeństwa
System Cloud Key Vault ma na celu zapewnienie „głębokich zabezpieczeń” poprzez włączenie zabezpieczeń na wielu poziomach naszego zbioru. Aby pokazać, jak działają te zabezpieczenia, zaczniemy od opisu klienta, a potem przejdziemy do usługi Cloud Key Vault Service.
Zabezpieczenia klienta
W zależności od producenta OEM i urządzenia czynnik wiedzy na ekranie blokady (LSKF) jest zwykle przechowywany i chroniony na urządzeniu za pomocą różnych metod, które różnią się w zależności od producenta OEM. Na przykład urządzenia Google Pixel 2 korzystają z modułu sprzętowego z ochroną przed nieuprawnionym dostępem, który służy do przechowywania LSKF w spoczynku oraz do egzekwowania limitów szybkości opartych na sprzęcie w przypadku weryfikacji LSKF. Nowe interfejsy API Framework, które są wprowadzane, aby umożliwić korzystanie z Cloud Key Vault, zostały zaprojektowane tak, aby w jak największym stopniu zachować dotychczasowe gwarancje bezpieczeństwa, nawet jeśli urządzenie używa takiego modułu sprzętowego do ochrony pamięci LSKF.
W tej sekcji skupimy się na odpowiednich kwestiach bezpieczeństwa i zabezpieczeniach, które wpływają na nową funkcję Cloud Key Vault, zamiast próbować przedstawić pełny obraz wszystkich mechanizmów zabezpieczeń powiązanych z LSKF.
Zabezpieczanie interfejsów API Framework
Nowe interfejsy API Framework dodane w celu obsługi usługi CKV są oznaczone jako @SystemApi i wymagają specjalnych uprawnień, co oznacza, że są dostępne tylko dla zatwierdzonych przez OEM aplikacji systemowych, takich jak usługi Google Play. W ten sposób w dużej mierze eliminuje się bezpośrednią powierzchnię ataku, która może być narażona na ataki ze strony aplikacji instalowanych przez użytkownika na urządzeniu.
Interfejsy API Framework zapewniają też, że sejfy są tworzone tylko dla kluczy publicznych kohorty, które zostały poświadczone przez zaufanego głównego urzędu certyfikacji. Zaufana ścieżka do zaufanego łańcucha zaufania jest wbudowana w ramy przez producenta OEM w momencie ich wysyłki i nie można jej zmienić bez aktualizacji systemu operacyjnego. Dzięki temu masz pewność, że klucz LSKF jest używany tylko do tworzenia sejfów, które będą prawidłowo stosować sprzętowe zabezpieczenia przed atakami typu brute force. Dzięki użyciu THM-ów w usłudze Cloud Key Vault do ochrony przed atakami typu „siłowa próba” w przypadku LSKF możemy uzyskać bezpieczeństwo porównywalne z zabezpieczeniami sprzętowymi na urządzeniu (jak w przypadku urządzeń Google Pixel 2).
Nie zakładamy, że klucz LSKF jest przechowywany gdziekolwiek na urządzeniu poza bezpiecznym sprzętem. Nowy sejf można utworzyć tylko bezpośrednio po odblokowaniu urządzenia. W momencie, gdy użytkownik wprowadzi klucz LSKF, aby odblokować urządzenie, klucz ten jest na krótko udostępniany w ramce w pamięci RAM. W tym momencie nowy interfejs API do tworzenia Vault korzysta z tego klucza. Nie można utworzyć nowego Vault chronionego za pomocą klucza LSKF, gdy urządzenie jest zablokowane, ponieważ klucz LSKF jest niedostępny.
Zabezpieczanie agenta przywracania
Główna ochrona, którą zapewniamy w ramach usługi agenta odzyskiwania, polega na tym, że protokół jest tak zaprojektowany, aby uniemożliwić temu agentowi wyświetlanie klucza LSKF bieżącego urządzenia ani żadnych kluczy odzyskiwania. Tylko Framework powinien widzieć te elementy po stronie klienta, co znacznie utrudnia wykorzystanie potencjalnych błędów lub luk w zabezpieczeniach w usługach odzyskiwania. Usługa Recovery Agent jest używana głównie do zarządzania zdarzeniami cyklu życia i przesyłania danych między usługą w chmurze a Framework. Jedynym wyjątkiem jest sytuacja, gdy przed uruchomieniem protokołu otwierania skarbca użytkownik musi wprowadzić LSKF starego urządzenia. W interfejsie użytkownika, który zbiera LSKF starego urządzenia, jest implementowany agent odzyskiwania4. Jednak implementacja Recovery Agent „zapomina” o LSKF, gdy tylko framework przejmie tworzenie roszczenia o odzyskanie.
Funkcje zabezpieczeń protokołu
Pełna analiza protokołu wykracza poza zakres tego dokumentu, ale chcemy podkreślić kilka wbudowanych w nim zabezpieczeń. W szczególności protokół używa tylko hasha LSKF. Oznacza to, że jeśli klucz LSKF ma wysoką entropię (np.jest to dobre hasło o wysokiej entropii), przechowywanie sejfu jest znacznie lepsze niż przechowywanie hasza hasła. W tym przypadku hasz hasła może zapewnić bezpieczeństwo niezależnie od zabezpieczeń THM. Z tego powodu obsługujemy szyfrowanie mieszane „memory hard” LSKF w ramach protokołu. Ponadto łączymy sejf kryptograficznie z identyfikatorem urządzenia, na którym został utworzony, oraz z noncem, który jest używany jako wyzwanie podczas otwierania sejfu. Dzięki temu możemy mieć pewność, że żądanie odzyskiwania jest aktualne.
Ponieważ klucz odzyskiwania jest generowany na nowo przy każdym utworzeniu sejfu, rotację klucza wdrażamy przez zastąpienie istniejącego wpisu sejfu nowo utworzonym sejfem. Adres dla nieudanego licznika prób używanego przez skarbiec jest wybierany podczas tworzenia skarbca, a framework zapewnia, że adres licznika używany w kolejnych skarbcach nie ulegnie zmianie, chyba że zmieni się LSKF lub pojawi się nowa poświadczona lista kluczy publicznych kohorty. Dzięki temu można rotować klucz odzyskiwania bez naruszania ochrony przed atakami siłowymi w przypadku klucza LSKF.
Bezpieczeństwo serwera w usłudze Cloud Key Vault
Serwer jest implementowany przy użyciu kombinacji oprogramowania działającego na zwykłym sprzęcie serwerowym oraz oprogramowania układowego na sprzęcie specjalistycznym (chip Titan). Opiszemy zabezpieczenia oferowane na poszczególnych poziomach.
Zabezpieczenia sprzętowe
Podstawowe zabezpieczenia zaimplementowane po stronie serwera usługi CKV to zaufane moduły sprzętowe (THM), które są tworzone przy użyciu własnych, niestandardowych układów Titan firmy Google. Urządzenia te działają na oprogramowaniu układowym, które udostępnia niezbędne interfejsy API do implementacji protokołów CKV. W szczególności mogą generować i bezpiecznie udostępniać pary kluczy innym członkom swojej kohorty, tak aby logika oprogramowania układowego chroniła klucz prywatny przed wyciekiem poza układy Titan w kohorcie. Może też wykonać operację otwarcia Vault i utrzymywać ściśle rosnący licznik prób otwarcia Vault (gdzie licznik jest wspierany stanem zapisanym w chipie Titan). Bardziej szczegółowy opis protokołu realizowanego przez oprogramowanie układu scalonego CKV Titan znajdziesz w przyszłej wersji tego dokumentu.
Ponieważ bezpieczeństwo serwera zależy od logiki oprogramowania układów Titan, musimy zadbać o to, aby ta logika nie uległa zmianie w taki sposób, który pozwoliłby układom na ujawnienie tajemnic lub zignorowanie limitów liczników. Aby to osiągnąć, zmieniamy też program ładujący Titana, aby dane przechowywane przez chip (np. klucz prywatny dla kohorty) zostały całkowicie wykasowane przed zastosowaniem aktualizacji. Minusem tej ochrony jest to, że nie możemy łatać błędów w oprogramowaniu bez utraty danych. Aktualizacja oprogramowania jest funkcjonalnie równoważna z zniszczeniem dotychczasowego sprzętu i zastąpieniem go nowymi układami. Jeśli będzie wymagana istotna poprawka oprogramowania układowego, Google będzie musiało utworzyć i opublikować zupełnie nową listę poświadczonych kluczy publicznych kohorty i stopniowo przenieść wszystkich użytkowników na nową listę. Aby zminimalizować to ryzyko, staramy się, aby kod źródłowy oprogramowania układowego był jak najmniejszy, i skrupulatnie go sprawdzamy pod kątem potencjalnych problemów z bezpieczeństwem.
Ochrona przed oprogramowaniem
Oprócz twardych limitów błędów na poziomie schowu danych narzuconych przez THM usługa CKV stosuje również oparte na oprogramowaniu ograniczenie szybkości. Ograniczenie szybkości ma na celu uniemożliwienie przejęcia konta przez przestępcę, który szybko wyczerpałby limit nieudanych prób odzyskania dostępu, skutecznie blokując dostęp prawdziwego użytkownika do kluczy odzyskiwania. Podobnie jak w przypadku opóźnień narzuconych przez urządzenie użytkownika po zbyt dużej liczbie nieudanych prób odblokowania ekranu, usługa CKV będzie narzucać coraz dłuższe opóźnienia po każdej kolejnej nieudanej próbie otwarcia skarbca.
W przypadku usług w chmurze, które przechowują dane użytkowników, stosujemy też standardowe środki bezpieczeństwa, takie jak rygorystyczne kontrole dostępu, monitorowanie i kontrola.
Szczegółowa specyfikacja protokołu
Szczegółowa specyfikacja protokołu jest wciąż opracowywana, a ten dokument zostanie zaktualizowany, aby uwzględnić te szczegóły wraz z opublikowaniem kodu klienta w ramach projektu Android Open Source w tym roku.
Uwagi
- „Towards Reliable Storage of 56-bit Secrets in Human Memory | USENIX” 1 sierpnia 2014 r., https://www.usenix.org/node/184458. ↩
- „Blog Google Cloud Platform: Titan w głębi: bezpieczeństwo w prostej formie” 24 sierpnia 2017 r., https://cloudplatform.googleblog.com/2017/08/Titan-in-depth-security-in-plaintext.html. ↩
- „Google announces over 2 billion monthly active devices on Android ...." (Google ogłasza ponad 2 miliardy aktywnych urządzeń z Androidem miesięcznie), 17 maja 2017 r., https://www.theverge.com/2017/5/17/15654454/android-reaches-2-billion-active-users-monthly. ↩
- Dzięki temu możemy udostępnić elastyczne interfejsy użytkownika do wprowadzania LSKF innego urządzenia. Framework bieżącego urządzenia może nie mieć odpowiedniego interfejsu do wprowadzania LSKF starego urządzenia. ↩