Tink używa zestawów kluczy, aby włączyć rotację kluczy. Formalnie zbiór kluczy to niepusta lista kluczy, na której jeden klucz jest oznaczony jako podstawowy (klucz, który jest używany np. do podpisywania i szyfrowania nowych tekstów jawnych). Dodatkowo klucze w zestawie kluczy otrzymują unikalny identyfikator2 i stan klucza, który umożliwia wyłączanie kluczy bez usuwania ich z zestawu.
Zestawy kluczy to główny sposób, w jaki użytkownicy mogą uzyskiwać dostęp do kluczy (za pomocą klasy KeysetHandle). Dzięki temu każdy użytkownik ma kod do obsługi wielu kluczy jednocześnie. Dla większości użytkowników kryptografii obsługa wielu kluczy jest koniecznością: musi być możliwość zmiany kluczy (np. stare klucze mogą wyciec), a prawie nigdy nie ma atomowego „przełączania na następny klucz”, które można zastosować do maszyn, na których działa kod, i wszystkich tekstów zaszyfrowanych na całym świecie i w jednej chwili. Dlatego użytkownik musi napisać kod, który będzie działać, gdy przechodzi z jednego klucza do następnego.
Przykład: AEAD
Rozważ użycie zestawu kluczy AEAD, który zawiera wiele kluczy do prymitywu AEAD. Jak już wspomnieliśmy, każdy klucz jednoznacznie określa 2 funkcje: \(\mathrm{Enc}\) i \(\mathrm{Dec}\). Zestaw kluczy określa teraz też 2 nowe funkcje: \(\mathrm{Enc}\) i \(\mathrm{Dec}\) – \(\mathrm{Enc}\) po prostu odpowiada funkcji \(\mathrm{Enc}\) klucza podstawowego zestawu kluczy, a funkcja \(\mathrm{Dec}\) próbuje odszyfrować dane za pomocą wszystkich kluczy w określonej kolejności (informacje o tym, jak Tink zwiększa wydajność tej funkcji, znajdziesz poniżej).
Warto zauważyć, że zestawy kluczy to pełne klucze: zawierają pełny opis funkcji \(\mathrm{Enc}\) i \(\mathrm{Dec}\) używanych. Oznacza to, że użytkownicy mogą napisać klasę, która przyjmuje jako dane wejściowe KeysetHandle, co oznacza, że klasa potrzebuje pełnego opisu obiektów \(\mathrm{Enc}\) i \(\mathrm{Dec}\) do prawidłowego działania. Umożliwia to użytkownikowi pisanie interfejsów API, które informują, że aby użyć tej klasy, musisz podać opis prymitywu kryptograficznego.
Rotacja kluczy
Załóżmy, że użytkownik Tink pisze program, który najpierw pobiera zestaw kluczy z usługi KMS, a następnie tworzy z niego obiekt AEAD i używa go do szyfrowania i odszyfrowywania tekstu zaszyfrowanego.
Taki użytkownik jest automatycznie przygotowywany do rotacji kluczy i przełączania algorytmów, jeśli wybrany przez niego algorytm nie spełnia już standardów.
Podczas wdrażania takiej rotacji kluczy należy zachować ostrożność: po pierwsze, usługa KMS powinna dodać nowy klucz do zestawu kluczy (ale nie ustawiać go jeszcze jako klucza podstawowego). Następnie nowy zestaw kluczy musi zostać wdrożony we wszystkich plikach binarnych, aby każdy plik binarny korzystający z tego zestawu kluczy miał najnowszy klucz w zestawie. Dopiero wtedy nowy klucz powinien stać się kluczem podstawowym, a wynikowy zestaw kluczy jest ponownie rozpowszechniany we wszystkich plikach binarnych, które go używają.
Kluczowe identyfikatory w szyfrogramach
Rozważmy ponownie przykład zestawu kluczy AEAD. Jeśli zostanie to zrobione w prosty sposób, odszyfrowanie tekstu zaszyfrowanego wymaga od Tink wypróbowania wszystkich kluczy w zestawie kluczy, ponieważ nie ma możliwości ustalenia, którego klucza użyto do zaszyfrowania zestawu kluczy. Może to powodować duże obciążenie wydajności.
Dlatego Tink umożliwia dodawanie do szyfrogramów 5-bajtowego ciągu znaków pochodzącego z identyfikatora. Zgodnie z filozofią „Pełne klucze” opisaną powyżej ten prefiks jest częścią klucza, a wszystkie szyfrogramy kiedykolwiek uzyskane za pomocą tego klucza powinny mieć ten prefiks. Podczas tworzenia kluczy użytkownicy mogą wybrać, czy klucz ma używać takiego prefiksu, czy też ma być używany format tekstu zaszyfrowanego bez niego.
Gdy klucz znajduje się w zestawie kluczy, Tink oblicza ten tag na podstawie identyfikatora klucza w zestawie kluczy. Fakt, że identyfikatory są unikalne2 w ramach zestawu kluczy, oznacza, że tagi są unikalne. Dlatego jeśli używane są tylko klucze z tagami, nie ma utraty wydajności w porównaniu z odszyfrowywaniem za pomocą jednego klucza: podczas odszyfrowywania Tink musi wypróbować tylko jeden z kluczy.
Ponieważ jednak tag jest częścią klucza, oznacza to również, że klucz może znajdować się w zestawie kluczy tylko wtedy, gdy ma określony identyfikator. Ma to pewne konsekwencje przy opisywaniu implementacji kluczowych obiektów w różnych językach.
Klucze z wymaganiem identyfikatora, ale bez prefiksu wyników
Niektóre klucze muszą mieć określony identyfikator, ale nie dodają prefiksu do danych wyjściowych. Na przykład klucze podpisu z wariantem NO_PREFIX_WITH_PREHASH_ID (przechowywane z typem prefiksu wyjściowego WITH_ID_REQUIREMENT) generują podpisy bez prefiksu. Gdy używasz takiego klucza z funkcją pierwotną Prehash, Tink zapisuje identyfikator klucza w wartości prehash, aby zdalny sygnatariusz wiedział, którego klucza użyć do podpisania.
Podobnie jak w przypadku kluczy z prefiksem, taki klucz może znajdować się tylko w zestawie kluczy o tym samym identyfikatorze. Identyfikator klucza w wartości wstępnie zaszyfrowanej to zwykłe metadane, podobnie jak prefiks wyjściowy: podpis nie jest z nim powiązany, a weryfikatorzy nigdy go nie widzą. Informacje o układzie na poziomie bajtów znajdziesz w artykule Format przesyłania danych Tink.
-
Niektóre części Tink nadal traktują zestawy kluczy jako zbiór. Należy jednak to zmienić. Dzieje się tak, ponieważ kolejność jest na ogół ważna: weźmy na przykład typowy cykl życia rotacji kluczy w przypadku AEAD. Najpierw do zestawu kluczy dodawany jest nowy klucz. Ten klucz nie jest jeszcze kluczem podstawowym, ale jest aktywny. Ten nowy zestaw kluczy jest wdrażany we wszystkich plikach binarnych. Gdy wszystkie pliki binarne poznają nowy klucz, staje się on kluczem podstawowym (dopiero wtedy używanie tego klucza jest bezpieczne). W tym drugim kroku rotacja klucza musi znać ostatni dodany klucz. ↩
-
Aby zapewnić zgodność z biblioteką wewnętrzną Google, Tink umożliwia stosowanie zestawów kluczy, w których identyfikatory się powtarzają. Obsługa zostanie wycofana w przyszłości. ↩