Jak zaszyfrować bajty za pomocą modułu TPM (Trusted Platform Module)

110

Jak mogę zaszyfrować bajty za pomocą modułu TPM komputera?

CryptProtectData

Windows zapewnia (stosunkowo) proste API do szyfrowania obiektu blob za pomocą CryptProtectDataAPI, które możemy opakować w łatwą w użyciu funkcję:

public Byte[] ProtectBytes(Byte[] plaintext)
{
   //...
}

Szczegóły ProtectBytessą mniej ważne niż pomysł, że możesz z niego dość łatwo korzystać:

  • oto bajty, które chcę zaszyfrować tajnym kluczem przechowywanym w System
  • oddaj mi zaszyfrowany obiekt blob

Zwrócony obiekt BLOB to nieudokumentowana struktura dokumentacji , która zawiera wszystko, co jest potrzebne do odszyfrowania i zwrócenia oryginalnych danych (algorytm skrótu, algorytm szyfrujący, sól, podpis HMAC itp.).

Dla kompletności, oto przykładowa implementacja pseudokodu, ProtectBytesktóra używa Crypt APIdo ochrony bajtów:

public Byte[] ProtectBytes(Byte[] plaintext)
{
   //Setup our n-byte plaintext blob
   DATA_BLOB dataIn;
   dataIn.cbData = plaintext.Length;
   dataIn.pbData = Addr(plaintext[0]);

   DATA_BLOB dataOut;

   //dataOut = EncryptedFormOf(dataIn)
   BOOL bRes = CryptProtectData(
         dataIn,
         null,     //data description (optional PWideChar)
         null,     //optional entropy (PDATA_BLOB)
         null,     //reserved
         null,     //prompt struct
         CRYPTPROTECT_UI_FORBIDDEN || CRYPTPROTECT_LOCAL_MACHINE,
         ref dataOut);
   if (!bRes) then
   {
      DWORD le = GetLastError();
      throw new Win32Error(le, "Error calling CryptProtectData");
   }

   //Copy ciphertext from dataOut blob into an actual array
   bytes[] result;
   SetLength(result, dataOut.cbData);
   CopyMemory(dataOut.pbData, Addr(result[0]), dataOut.cbData);

   //When you have finished using the DATA_BLOB structure, free its pbData member by calling the LocalFree function
   LocalFree(HANDLE(dataOut.pbData)); //LocalFree takes a handle, not a pointer. But that's what the SDK says.
}

Jak zrobić to samo z TPM?

Powyższy kod jest przydatny do szyfrowania danych tylko dla lokalnego komputera. Dane są szyfrowane przy użyciu Systemkonta jako generatora kluczy ( szczegóły, choć interesujące, są nieważne ). W rezultacie mogę zaszyfrować dane (np. Klucz główny szyfrowania dysku twardego), które mogą być odszyfrowane tylko przez maszynę lokalną.

Nadszedł czas, aby pójść o krok dalej. Chcę zaszyfrować niektóre dane (np. Klucz główny szyfrowania dysku twardego), które mogą być odszyfrowane tylko przez lokalny moduł TPM. Innymi słowy, chcę zamienić Qualcomm Trusted Execution Environment ( TEE ) na poniższym schemacie blokowym na Androida, na TPM w Windows:

wprowadź opis obrazu tutaj

Uwaga : zdaję sobie sprawę, że moduł TPM nie wykonuje podpisywania danych (a jeśli tak, to nie gwarantuje, że podpisywanie tych samych danych za każdym razem da te same dane binarne). Dlatego byłbym skłonny zastąpić „RSA podpisywanie” z „blob szyfrowania 256-bitowego sprzętowego związany z kluczem” .

Więc gdzie jest kod?

Problem polega na tym, że programowanie TPM jest całkowicie nieudokumentowane w MSDN . Nie ma dostępnego API do wykonywania jakichkolwiek operacji. Zamiast tego musisz znaleźć kopię stosu oprogramowania Trusted Computing Group (aka TSS) , dowiedzieć się, jakie polecenia wysłać do TPM wraz z ładunkami, w jakiej kolejności i wywołać funkcję Tbsip_Submit_Command systemu Windows, aby bezpośrednio przesłać polecenia:

TBS_RESULT Tbsip_Submit_Command(
  _In_     TBS_HCONTEXT hContext,
  _In_     TBS_COMMAND_LOCALITY Locality,
  _In_     TBS_COMMAND_PRIORITY Priority,
  _In_     const PCBYTE *pabCommand,
  _In_     UINT32 cbCommand,
  _Out_    PBYTE *pabResult,
  _Inout_  UINT32 *pcbOutput
);

System Windows nie ma interfejsu API wyższego poziomu do wykonywania działań.

Jest to moralny odpowiednik próby utworzenia pliku tekstowego przez wydanie poleceń SATA I / O na dysk twardy .

Dlaczego nie po prostu użyć spodni

Trusted Computing Group (TCG) zdefiniowała swój własny interfejs API: TCB Software Stack (TSS) . Implementacja tego API została stworzona przez kilka osób i nazywa się TrouSerS . Facet następnie przeportował ten projekt do Windows .

Problem z tym kodem polega na tym, że nie można go przenieść do świata Windows. Na przykład nie możesz go używać z Delphi, nie możesz go używać z C #. To wymaga:

  • OpenSSL
  • p Wątek

Chcę tylko, aby kod zaszyfrował coś za pomocą mojego modułu TPM.

Powyższe CryptProtectDatanie wymaga niczego innego niż to, co znajduje się w treści funkcji.

Jaki jest równoważny kod do szyfrowania danych przy użyciu modułu TPM? Jak zauważyli inni, prawdopodobnie będziesz musiał zapoznać się z trzema podręcznikami TPM i samodzielnie skonstruować obiekty blob . Prawdopodobnie wiąże się to z TPM_sealpoleceniem. Chociaż myślę, że nie chcę zapieczętowywać danych, myślę, że chcę to powiązać :

Wiązanie - szyfruje dane przy użyciu klucza powiązania TPM, unikalnego klucza RSA pochodzącego z klucza magazynu. Pieczętowanie - szyfruje dane w sposób podobny do wiązania, ale dodatkowo określa stan, w jakim musi znajdować się TPM, aby dane mogły zostać odszyfrowane (niezapieczętowane)

Próbuję przeczytać trzy wymagane tomy, aby znaleźć 20 linii kodu, których potrzebuję:

Ale nie mam żadnego pojęcia, co czytam. Gdyby był jakiś samouczek lub przykłady, mógłbym spróbować. Ale jestem kompletnie zagubiony.

Dlatego prosimy o Stackoverflow

W ten sam sposób mogłem zapewnić:

Byte[] ProtectBytes_Crypt(Byte[] plaintext)
{
   //...
   CryptProtectData(...); 
   //...
}

czy ktoś może podać odpowiedni odpowiednik:

Byte[] ProtectBytes_TPM(Byte[] plaintext)
{
   //...
   Tbsip_Submit_Command(...);
   Tbsip_Submit_Command(...);
   Tbsip_Submit_Command(...);
   //...snip...
   Tbsip_Submit_Command(...);
   //...
}

robi to samo, z wyjątkiem tego, że klucz zamknięty w SystemLSA jest zablokowany w TPM?

Początek badań

Nie wiem dokładnie, co oznacza wiązanie . Ale patrząc na TPM Main - Part 3 Commands - Specification Version 1.2, jest wzmianka o bind :

10.3 TPM_UnBind

TPM_UnBind pobiera obiekt BLOB danych będący wynikiem polecenia Tspi_Data_Bind i odszyfrowuje go w celu wyeksportowania do użytkownika. Wzywający musi autoryzować użycie klucza, który odszyfruje przychodzący obiekt BLOB. TPM_UnBind działa na zasadzie blok po bloku i nie ma pojęcia o jakiejkolwiek relacji między jednym blokiem a drugim.

Co jest mylące jest tam jest brak Tspi_Data_Bindpoleceń.

Wysiłek badawczy

Przerażające jest to, że nikt nigdy nie zadał sobie trudu, aby udokumentować TPM lub jego działanie. To tak, jakby spędzali cały swój czas na wymyślaniu tej fajnej rzeczy do zabawy, ale nie chcieli radzić sobie z bolesnym krokiem, aby uczynić ją użyteczną do czegoś.

Począwszy od (teraz) bezpłatnej książki Praktyczny przewodnik po TPM 2.0: Korzystanie z modułu zaufanej platformy w nowej erze bezpieczeństwa :

Rozdział 3 - Krótki samouczek dotyczący TPM 2.0

Moduł TPM ma dostęp do samodzielnie wygenerowanego klucza prywatnego, dzięki czemu może szyfrować klucze za pomocą klucza publicznego, a następnie przechowywać wynikowy obiekt blob na dysku twardym. W ten sposób moduł TPM może przechowywać praktycznie nieograniczoną liczbę kluczy dostępnych do użytku, ale nie marnuje cennej pamięci wewnętrznej. Klucze przechowywane na dysku twardym można wymazać, ale można je również zarchiwizować, co projektantom wydawało się akceptowalnym kompromisem.

Jak zaszyfrować klucz kluczem publicznym modułu TPM?

Rozdział 4 - Istniejące aplikacje korzystające z modułów TPM

Aplikacje, które powinny używać modułu TPM, ale tego nie robią

W ciągu ostatnich kilku lat wzrosła liczba aplikacji internetowych. Wśród nich są internetowe kopie zapasowe i przechowywanie. Obecnie wiele firm oferuje takie usługi, ale o ile nam wiadomo, żaden z klientów tych usług nie pozwala użytkownikowi zablokować klucza usługi kopii zapasowej w module TPM. Gdyby to zostało zrobione, z pewnością byłoby miło, gdyby sam klucz TPM został zarchiwizowany, kopiując go na wielu komputerach. Wydaje się, że jest to szansa dla programistów.

W jaki sposób programista blokuje klucz w module TPM?

Rozdział 9 - Heirarchie

PRZYPADEK: PRZECHOWYWANIE HASŁA DO LOGOWANIA

Typowy plik haseł przechowuje zasolone skróty haseł. Weryfikacja polega na zasoleniu i zahaszowaniu podanego hasła i porównaniu go z zapisaną wartością. Ponieważ obliczenie nie obejmuje hasła, podlega atakowi offline na plik z hasłami.

W tym przypadku użycia jest używany klucz HMAC wygenerowany przez moduł TPM. Plik haseł przechowuje HMAC solonego hasła. Weryfikacja polega na soleniu i HMAC podanego hasła i porównaniu go z przechowywaną wartością. Ponieważ osoba atakująca w trybie offline nie ma klucza HMAC, osoba atakująca nie może zamontować ataku, wykonując obliczenia.

To mogłoby zadziałać. Jeśli moduł TPM ma tajny klucz HMAC i tylko mój moduł TPM zna klucz HMAC, mogę zastąpić „Podpisz (inaczej szyfrowanie TPM kluczem prywatnym)” na „HMAC”. Ale w następnej linii całkowicie się odwraca:

TPM2_Create, określając klucz HMAC

Nie jest tajemnicą TPM, jeśli muszę określić klucz HMAC. Fakt, że klucz HMAC nie jest tajny, ma sens, gdy zdasz sobie sprawę, że jest to rozdział o narzędziach kryptograficznych, które zapewnia moduł TPM. Zamiast samodzielnie pisać SHA2, AES, HMAC lub RSA, możesz ponownie wykorzystać to, co już jest w module TPM.

Rozdział 10 - Klucze

Największą zaletą modułu TPM jest zdolność aplikacji do używania kluczy przy jednoczesnym zachowaniu ich bezpieczeństwa w urządzeniu jako urządzenie zabezpieczające . Moduł TPM może zarówno generować, jak i importować klucze generowane zewnętrznie. Obsługuje klucze asymetryczne i symetryczne.

Doskonały! Jak ty to robisz!?

Generator kluczy

Prawdopodobnie największą siłą TPM jest jego zdolność do generowania klucza kryptograficznego i ochrony jego sekretu w granicach sprzętowych. Generator kluczy jest oparty na własnym generatorze liczb losowych TPM i nie polega na zewnętrznych źródłach losowości. W ten sposób eliminuje słabości oparte na słabym oprogramowaniu z niewystarczającym źródłem entropii.

Czy moduł TPM ma możliwość generowania kluczy kryptograficznych i ochrony swoich danych tajnych w granicach sprzętowych? Czy tak, jak?

Rozdział 12 - Rejestry konfiguracji platformy

PCR do autoryzacji

PRZYKŁAD UŻYCIA: USZCZELNIANIE KLUCZA SZYFROWANIA TWARDEGO DYSKU DO STANU PLATFORMY

Aplikacje szyfrujące na całym dysku są znacznie bezpieczniejsze, jeśli moduł TPM chroni klucz szyfrujący, niż gdyby był przechowywany na tym samym dysku, chronionym tylko hasłem. Po pierwsze, sprzęt TPM ma ochronę przed włamaniem (patrz rozdział 8, aby zapoznać się ze szczegółowym opisem ochrony przed atakiem słownikowym TPM), co sprawia, że ​​atak siłowy na hasło jest niepraktyczny. Klucz chroniony tylko przez oprogramowanie jest znacznie bardziej podatny na słabe hasło. Po drugie, klucz programowy przechowywany na dysku jest znacznie łatwiejszy do kradzieży. Weź dysk (lub kopię zapasową dysku), a otrzymasz klucz. Gdy moduł TPM przechowuje klucz, cała platforma lub przynajmniej dysk i płyta główna muszą zostać skradzione.

Plombowanie umożliwia ochronę klucza nie tylko hasłem, ale także polityką. Typowa polityka blokuje klucz do wartości PCR (stanu oprogramowania) aktualnych w czasie uszczelniania. Zakłada się, że stan przy pierwszym uruchomieniu nie jest zagrożony. Każde preinstalowane złośliwe oprogramowanie obecne przy pierwszym uruchomieniu zostanie zmierzone w PCR, a tym samym klucz zostanie zapieczętowany do stanu zhakowanego oprogramowania. Mniej ufne przedsiębiorstwo może mieć standardowy obraz dysku i pieczęć do PCR reprezentujących ten obraz. Te wartości PCR byłyby wstępnie obliczane na prawdopodobnie bardziej zaufanej platformie. Jeszcze bardziej wyrafinowane przedsiębiorstwo użyłoby TPM2_PolicyAuthorize i dostarczyło kilka biletów autoryzujących zestaw zaufanych wartości PCR. W rozdziale 14 znajduje się szczegółowy opis autoryzacji polityki i jej zastosowania do rozwiązania problemu kruchości PCR.

Chociaż hasło może również chronić klucz, bezpieczeństwo jest większe nawet bez hasła klucza TPM. Osoba atakująca mogła uruchomić platformę bez podania hasła TPMkey, ale nie mogła się zalogować bez nazwy użytkownika i hasła systemu operacyjnego. OSsecurity chroni dane. Osoba atakująca może uruchomić alternatywny system operacyjny, powiedzmy z Live DVD lub pendrive'a zamiast z dysku twardego, aby ominąć zabezpieczenie logowania do systemu operacyjnego. Jednak ta inna konfiguracja rozruchu i oprogramowanie zmieniłyby wartości PCR. Ponieważ te nowe reakcje PCR nie byłyby zgodne z zapieczętowanymi wartościami, moduł TPM nie zwolniłby klucza odszyfrowywania, a dysk twardy nie mógł zostać odszyfrowany.

Doskonały! Tak się składa, że ​​właśnie o to mi chodzi. Jest to również przypadek użycia, do którego Microsoft używa modułu TPM. Jak mam to zrobić!?

Przeczytałem więc całą książkę i nie znalazłem w niej niczego użytecznego. Co robi wrażenie, bo liczy 375 stron. Zastanawiasz się, co zawierała ta książka - i patrząc wstecz, nie mam pojęcia.

Rezygnujemy więc z ostatecznego przewodnika po programowaniu TPM i zamiast tego zwracamy się do dokumentacji firmy Microsoft:

Z zestawu narzędzi Microsoft TPM Platform Crypto-Provider Toolkit . Wskazuje dokładnie, co chcę zrobić:

Klucz poręczenia lub EK

EK ma na celu zapewnienie wiarygodnego identyfikatora kryptograficznego platformy. Przedsiębiorstwo może utrzymywać bazę danych kluczy poparcia należących do modułów TPM wszystkich komputerów w przedsiębiorstwie lub kontroler sieci szkieletowej centrum danych może mieć bazę danych modułów TPM we wszystkich modułach blade. W systemie Windows możesz użyć dostawcy NCrypt opisanego w sekcji „Platform Crypto Provider in Windows 8”, aby odczytać publiczną część EK.

Gdzieś w module TPM znajduje się klucz prywatny RSA. Ten klucz jest tam zamknięty - nigdy go nie widzi świat zewnętrzny. Chcę, aby moduł TPM podpisał coś swoim kluczem prywatnym (tj. Zaszyfrował to swoim kluczem prywatnym).

Chcę więc najbardziej podstawowej operacji, jaka może istnieć:

wprowadź opis obrazu tutaj

Zaszyfruj coś swoim kluczem prywatnym. Nie proszę nawet (jeszcze) o bardziej skomplikowane rzeczy:

  • „uszczelnianie” go na podstawie stanu PCR
  • tworzenie klucza i przechowywanie go w pamięci ulotnej lub nieulotnej
  • tworzenie klucza symetrycznego i próba załadowania go do modułu TPM

Proszę o najbardziej podstawową operację, jaką może wykonać TPM. Dlaczego nie można uzyskać informacji o tym, jak to zrobić?

Mogę uzyskać losowe dane

Wydaje mi się, że byłem bezczelny, kiedy powiedziałem, że podpisywanie RSA jest najbardziej podstawową rzeczą, jaką może zrobić TPM. Najbardziej podstawową rzeczą TPM może zostać poproszony zrobić, to dać mi losowych bajtów. Że nauczyłem się, jak to zrobić:

public Byte[] GetRandomBytesTPM(int desiredBytes)
{
   //The maximum random number size is limited to 4,096 bytes per call
   Byte[] result = new Byte[desiredBytes];

   BCRYPT_ALG_HANDLE hAlgorithm;

   BCryptOpenAlgorithmProvider(
         out hAlgorithm,
         BCRYPT_RNG_ALGORITHM, //AlgorithmID: "RNG"
         MS_PLATFORM_CRYPTO_PROVIDER, //Implementation: "Microsoft Platform Crypto Provider" i.e. the TPM
         0 //Flags
   );
   try
   {                
      BCryptGenRandom(hAlgorithm, @result[0], desiredBytes, 0);
   }
   finally
   {
      BCryptCloseAlgorithmProvider(hAlgorithm);
   }

   return result;
}

Fancy Thing

Zdaję sobie sprawę, że liczba osób korzystających z TPM jest bardzo mała. Dlatego nikt w Stackoverflow nie ma odpowiedzi. Więc nie mogę być zbyt chciwy w znalezieniu rozwiązania mojego powszechnego problemu. Ale to, co naprawdę chciałbym zrobić, to „zapieczętować” niektóre dane:

wprowadź opis obrazu tutaj

  • przedstawić TPM pewne dane (np. 32 bajty materiału klucza)
  • mają moduł TPM zaszyfrować dane, zwracając jakąś nieprzezroczystą strukturę obiektu blob
  • później poproś moduł TPM o odszyfrowanie obiektu BLOB
  • odszyfrowanie zadziała tylko wtedy, gdy rejestry PCR modułu TPM będą takie same, jak podczas szyfrowania.

Innymi słowy:

Byte[] ProtectBytes_TPM(Byte[] plaintext, Boolean sealToPcr)
{
   //...
}

Byte[] UnprotectBytes_TPM(Byte[] protectedBlob)
{
   //...
}

Cryptography Next Gen (Cng, aka BCrypt) obsługuje TPM

Oryginalny Cryptography API w Windows był znany jako Crypto API.

Począwszy od systemu Windows Vista, Crypto API zostało zastąpione przez Cryptography API: Next Generation (wewnętrznie znane jako BestCrypt , w skrócie BCrypt , którego nie należy mylić z algorytmem haszowania haseł ).

Windows jest dostarczany z dwoma dostawcami BCrypt :

Platforma Crypto dostawcy nie są udokumentowane na MSDN, ale ma dokumentację z witryny Microsoft Research 2012:

Platforma TPM Crypto-Provider Toolkit

Dostawca usług kryptograficznych platformy TPM i zestaw narzędzi zawiera przykładowy kod, narzędzia i dokumentację do korzystania z funkcji związanych z modułem TPM w systemie Windows 8. Opisane podsystemy obejmują dostawcę usług kryptograficznych platformy Crypto-Next-Gen (CNG) opartej na technologii TPM oraz dostawców usług poświadczających mogą korzystać z nowych funkcji systemu Windows. Obsługiwane są systemy oparte na module TPM1.2 i TPM2.0.

Wydaje się, że intencją Microsoft jest TPM powierzchnia funkcjonalności krypto z dostawcy Microsoft Platform Crypto z Cryptography NG API.

Szyfrowanie klucza publicznego za pomocą Microsoft BCrypt

Jeśli się uwzględni:

wyjściem może być ustalenie, jak wykonać podpis cyfrowy przy użyciu interfejsu Microsoft Cryptography Next Gen API .

Następnym krokiem będzie wymyślenie kodu do szyfrowania w BCrypt, z publicznym kluczem RSA, przy użyciu standardowego provider ( MS_PRIMITIVE_PROVIDER). Na przykład:

  • modulus: 0xDC 67 FA F4 9E F2 72 1D 45 2C B4 80 79 06 A0 94 27 50 8209 DD 67 CE 57 B8 6C 4A 4F 40 9F D2 D1 69 FB 995D 85 0C 07 A1 F9 47 1B 56 16 6E F6 7F B9 CF 2A 58 36 37 99 29 AA 4F A8 12 E8 4F C7 82 2B 9D 72 2A 9C DE 6F C2 EE 12 6D CF F0 F2 B8 C4 DD 7C 5C 1A C8 17 51 A9 AC DF 08 22 04 9D 2B D7 F9 4B 09 DE 9A EB 5C 51 1A D8 F8 F9 56 9E F8 FB 37 9B 3F D3 74 65 24 0D FF 34 75 57 A4 F5 BF 55
  • publicExponent: 65537

Mając ten kod działający, mogę przełączyć się na używanie dostawcy TPM ( MS_PLATFORM_CRYPTO_PROVIDER).

22.02.2016: A ponieważ Apple jest zmuszony pomóc odszyfrować dane użytkownika, pojawiło się ponowne zainteresowanie tym, jak sprawić, by TPM wykonywał najprostsze zadanie, do którego został wymyślony - zaszyfrowanie czegoś.

To mniej więcej tak, jakby każdy posiadał samochód, ale nikt nie wie, jak go uruchomić. Może robić naprawdę przydatne i fajne rzeczy, gdybyśmy tylko mogli przejść przez krok 1 .

Czytanie bonusowe

Ian Boyd
źródło
Do wiązania (szyfrowania) nie jest dostępna żadna jawna funkcja i nie jest ona również potrzebna. Wystarczy utworzyć klucz wiążący w module TPM i użyć jego części publicznej do zaszyfrowania symetrycznego klucza szyfrowania sek z funkcją szyfrowania systemowego rsa („RSA / ECB / OAEPWithSHA1AndMGF1Padding”) i zapisać go w odpowiedniej strukturze („TcTssConstants.TSS_ENCDATA_BIND”). Aby rozpiąć (odszyfrować) sek, po prostu użyj funkcji unbind modułu TPM i użyj sek w dowolnej funkcji szyfrowania symetrycznego, którą lubisz. Mam dość starą bazę kodu, którą zrobiłem jakiś czas temu, może to pomaga: goo.gl/jV1Ouw
evildead
Z Wikipedii, Binding - szyfruje dane przy użyciu klucza powiązania TPM, unikalnego klucza RSA wywodzącego się z klucza magazynu. en.wikipedia.org/wiki/Trusted_Platform_Module Wygląda na to, że ta para poleceń (TSpi_Data_Bind / TPM_UnBind) powinna wystarczyć na Twoje potrzeby ...
Alex Mazzariol
1
Myślę, że nie musisz bezpośrednio używać TPM. Jest obsługiwany przez standardowe interfejsy API CNG / NCryptXXX i „Microsoft Platform Crypto Provider” (dla najnowszych platform Windows OS i jeśli sprzęt jest w porządku i oczywiście włączony). Może mógłbyś rzucić okiem na „Zestaw narzędzi kryptograficznych platformy TPM dla dostawców kryptowalut tutaj: research.microsoft.com/en-us/downloads/… sprawdź także: tiw2013.cse.psu.edu/slides/ ...
Simon Mourier
CryptProtectData niekoniecznie używa modułu TPM. Z drugiej strony, jeśli możesz uzyskać prawidłowy uchwyt CNG lub CSP dla TPM, możesz go użyć w funkcjach kryptograficznych.
Michael Chourdakis,
1
@ b3nj1 Nie, nie byłem; nikt nie był w stanie odpowiedzieć na pytanie.
Ian Boyd,

Odpowiedzi:

7

Elementarz

Wszystko, co następuje, dotyczy TPM 1.2. Należy pamiętać, że firma Microsoft wymaga modułu TPM 2.0 we wszystkich przyszłych wersjach systemu Windows. Generacja 2.0 różni się zasadniczo od wersji 1.2

Ze względu na zasady projektowania TPM nie ma rozwiązania jednokreskowego. Pomyśl o module TPM jak o mikrokontrolerze z ograniczonymi zasobami. Głównym celem projektowym było bycie tanim, a jednocześnie bezpiecznym. Tak więc moduł TPM został pozbawiony całej logiki, która nie była konieczna do bezpiecznej operacji. Tak więc TPM działa tylko wtedy, gdy masz przynajmniej trochę mniej lub bardziej grubego oprogramowania, wydając wiele poleceń we właściwej kolejności. A te sekwencje poleceń mogą być bardzo złożone. Dlatego TCG określiło TSS z dobrze zdefiniowanym API. Jeśli chcesz iść drogą Java, jest nawet API Java na wysokim poziomie . Nie znam podobnego projektu dla C # / .net

Rozwój

W twoim przypadku proponuję przyjrzeć się oprogramowaniu TPM IBM.

W opakowaniu znajdziesz 3 bardzo przydatne komponenty:

  • programowy emulator TPM
  • lekki tpm lib
  • kilka podstawowych narzędzi wiersza poleceń

Niekoniecznie potrzebujesz programowego emulatora TPM, możesz także połączyć się z HW TPM urządzenia. Możesz jednak przechwycić wydane polecenia i spojrzeć na odpowiedzi, dzięki czemu dowiesz się, jak są one składane i jak odpowiadają specyfikacji polecenia.

Wysoki poziom

Wymagania wstępne:

  1. TPM jest aktywowany
  2. Załadowany jest sterownik TPM
  3. przejąłeś na własność moduł TPM

Aby zapieczętować kroplę, musisz wykonać następujące czynności:

  1. utwórz klucz
  2. przechowywać gdzieś klucz-obiekt blob
  3. upewnij się, że klucz jest załadowany do modułu TPM
  4. zapieczętować kroplę

Aby odpieczętować, musisz:

  1. uzyskać klucz-obiekt blob
  2. załadować klucz do TPM
  3. rozszczelnij zapieczętowaną kroplę

Możesz przechowywać obiekt BLOB klucza w strukturze danych, której używasz do przechowywania chronionych bajtów.

Większość potrzebnych poleceń TPM to polecenia autoryzowane. Dlatego w razie potrzeby musisz ustanowić sesje autoryzacyjne. AFAIR to głównie sesje OSAP.

Polecenia TPM

Obecnie nie mogę uruchomić wersji do debugowania, więc nie mogę podać dokładnej kolejności. Więc potraktuj to jako nieuporządkowaną listę poleceń, których będziesz musiał użyć:

  • TPM_OSAP
  • TPM_CreateWrapKey
  • TPM_LoadKey2
  • TPM_Seal

Jeśli chcesz również odczytać aktualne wartości PCR:

  • TPM_PCRRead
Scolytus
źródło
Firma Microsoft ma swoją bibliotekę zarządzaną C # .NET do korzystania z modułu TPM . Mają również emulator TPM , z którym zarządzana biblioteka może się połączyć jako alternatywa debugowania, jeśli nie ma prawdziwego modułu TPM. Mają również zestaw narzędzi TPM Platform Provider Toolkit , który zawiera dokumentację i przykładowy kod do korzystania z modułu TPM. Gdyby tylko ktoś mógł wymyślić, jak używać modułu TPM do szyfrowania bajtów.
Ian Boyd
Twoje pierwsze dwa łącza to tylko TPM 2.0. Obawiam się, że jeśli chcesz ich użyć, nic mi nie pomoże.
Scolytus
4

Zaufane i zaszyfrowane klucze

Zaufane i zaszyfrowane klucze to dwa nowe typy kluczy dodane do istniejącej usługi pierścienia kluczy jądra. Oba te nowe typy są kluczami symetrycznymi o zmiennej długości iw obu przypadkach wszystkie klucze są tworzone w jądrze, a przestrzeń użytkownika widzi, przechowuje i ładuje tylko zaszyfrowane obiekty blob. Zaufane klucze wymagają dostępności chipa Trusted Platform Module (TPM) w celu zwiększenia bezpieczeństwa, podczas gdy szyfrowane klucze mogą być używane w dowolnym systemie. Wszystkie obiekty blob na poziomie użytkownika są wyświetlane i ładowane w postaci szesnastkowej ascii dla wygody i są weryfikowane pod kątem integralności.

Zaufane klucze używają modułu TPM zarówno do generowania, jak i do pieczętowania kluczy. Klucze są zapieczętowane 2048-bitowym kluczem RSA w module TPM i opcjonalnie zapieczętowane do określonych wartości PCR (pomiar integralności) i otwierane tylko przez TPM, jeśli testy PCR i weryfikacja integralności plamki są zgodne. Załadowany zaufany klucz można aktualizować nowymi (przyszłymi) wartościami PCR, dzięki czemu klucze można łatwo migrować do nowych wartości pcr, na przykład podczas aktualizacji jądra i initramfs. Ten sam klucz może mieć wiele zapisanych obiektów blob pod różnymi wartościami PCR, więc wiele rozruchów jest łatwo obsługiwanych.

Domyślnie zaufane klucze są zapieczętowane pod numerem SRK, który ma domyślną wartość autoryzacji (20 zer). To może być ustawiony w momencie takeownership z programu narzędziowego do spodni za: tpm_takeownership -u -z.

Usage:
    keyctl add trusted name "new keylen [options]" ring
    keyctl add trusted name "load hex_blob [pcrlock=pcrnum]" ring
    keyctl update key "update [options]"
    keyctl print keyid

    options:
    keyhandle= ascii hex value of sealing key default 0x40000000 (SRK)
    keyauth=   ascii hex auth for sealing key default 0x00...i
        (40 ascii zeros)
    blobauth=  ascii hex auth for sealed data default 0x00...
        (40 ascii zeros)
    blobauth=  ascii hex auth for sealed data default 0x00...
        (40 ascii zeros)
    pcrinfo=   ascii hex of PCR_INFO or PCR_INFO_LONG (no default)
    pcrlock=   pcr number to be extended to "lock" blob
    migratable= 0|1 indicating permission to reseal to new PCR values,
                default 1 (resealing allowed)

keyctl printzwraca szesnastkową kopię ascii zapieczętowanego klucza w standardowym formacie TPM_STORED_DATA. Długość klucza dla nowych kluczy jest zawsze w bajtach. Zaufane klucze mogą mieć od 32 do 128 bajtów (256 - 1024 bity), górna granica mieści się w 2048-bitowej długości klucza SRK (RSA), z całą niezbędną strukturą / dopełnieniem.

Zaszyfrowane klucze nie są zależne od modułu TPM i są szybsze, ponieważ używają algorytmu AES do szyfrowania / odszyfrowywania. Nowe klucze są tworzone z losowych liczb generowanych przez jądro i są szyfrowane / odszyfrowywane przy użyciu określonego klucza „głównego”. Klucz „główny” może być kluczem zaufanym lub kluczem użytkownika. Główną wadą zaszyfrowanych kluczy jest to, że jeśli nie są zakorzenione w zaufanym kluczu, są tak bezpieczne, jak szyfrowany klucz użytkownika. Dlatego główny klucz użytkownika powinien być ładowany w możliwie bezpieczny sposób, najlepiej na początku uruchamiania.

Odszyfrowana część zaszyfrowanych kluczy może zawierać prosty klucz symetryczny lub bardziej złożoną strukturę. Format bardziej złożonej struktury zależy od aplikacji, którą określa się za pomocą „formatu”.

Usage:
    keyctl add encrypted name "new [format] key-type:master-key-name keylen"
        ring
    keyctl add encrypted name "load hex_blob" ring
    keyctl update keyid "update key-type:master-key-name"

format:= 'default | ecryptfs'
key-type:= 'trusted' | 'user'

Przykłady użycia zaufanego i zaszyfrowanego klucza

Utwórz i zapisz zaufany klucz o nazwie „kmk” o długości 32 bajtów:

$ keyctl add trusted kmk "new 32" @u
440502848

$ keyctl show
Session Keyring
       -3 --alswrv    500   500  keyring: _ses
 97833714 --alswrv    500    -1   \_ keyring: _uid.500
440502848 --alswrv    500   500       \_ trusted: kmk

$ keyctl print 440502848
0101000000000000000001005d01b7e3f4a6be5709930f3b70a743cbb42e0cc95e18e915
3f60da455bbf1144ad12e4f92b452f966929f6105fd29ca28e4d4d5a031d068478bacb0b
27351119f822911b0a11ba3d3498ba6a32e50dac7f32894dd890eb9ad578e4e292c83722
a52e56a097e6a68b3f56f7a52ece0cdccba1eb62cad7d817f6dc58898b3ac15f36026fec
d568bd4a706cb60bb37be6d8f1240661199d640b66fb0fe3b079f97f450b9ef9c22c6d5d
dd379f0facd1cd020281dfa3c70ba21a3fa6fc2471dc6d13ecf8298b946f65345faa5ef0
f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b
e4a8aea2b607ec96931e6f4d4fe563ba

$ keyctl pipe 440502848 > kmk.blob

Załaduj zaufany klucz z zapisanego obiektu BLOB:

$ keyctl add trusted kmk "load `cat kmk.blob`" @u
268728824

$ keyctl print 268728824
0101000000000000000001005d01b7e3f4a6be5709930f3b70a743cbb42e0cc95e18e915
3f60da455bbf1144ad12e4f92b452f966929f6105fd29ca28e4d4d5a031d068478bacb0b
27351119f822911b0a11ba3d3498ba6a32e50dac7f32894dd890eb9ad578e4e292c83722
a52e56a097e6a68b3f56f7a52ece0cdccba1eb62cad7d817f6dc58898b3ac15f36026fec
d568bd4a706cb60bb37be6d8f1240661199d640b66fb0fe3b079f97f450b9ef9c22c6d5d
dd379f0facd1cd020281dfa3c70ba21a3fa6fc2471dc6d13ecf8298b946f65345faa5ef0
f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b
e4a8aea2b607ec96931e6f4d4fe563ba

Odśwież zaufany klucz pod nowymi wartościami pcr:

$ keyctl update 268728824 "update pcrinfo=`cat pcr.blob`"
$ keyctl print 268728824
010100000000002c0002800093c35a09b70fff26e7a98ae786c641e678ec6ffb6b46d805
77c8a6377aed9d3219c6dfec4b23ffe3000001005d37d472ac8a44023fbb3d18583a4f73
d3a076c0858f6f1dcaa39ea0f119911ff03f5406df4f7f27f41da8d7194f45c9f4e00f2e
df449f266253aa3f52e55c53de147773e00f0f9aca86c64d94c95382265968c354c5eab4
9638c5ae99c89de1e0997242edfb0b501744e11ff9762dfd951cffd93227cc513384e7e6
e782c29435c7ec2edafaa2f4c1fe6e7a781b59549ff5296371b42133777dcc5b8b971610
94bc67ede19e43ddb9dc2baacad374a36feaf0314d700af0a65c164b7082401740e489c9
7ef6a24defe4846104209bf0c3eced7fa1a672ed5b125fc9d8cd88b476a658a4434644ef
df8ae9a178e9f83ba9f08d10fa47e4226b98b0702f06b3b8

Początkowym odbiorcą zaufanych kluczy jest EVM, który w czasie uruchamiania potrzebuje wysokiej jakości klucza symetrycznego do ochrony metadanych plików za pomocą HMAC. Użycie zaufanego klucza zapewnia silne gwarancje, że klucz EVM nie został naruszony przez problem na poziomie użytkownika, a po zapieczętowaniu do określonych wartości rozruchowego PCR chroni przed atakami podczas rozruchu i offline. Utwórz i zapisz zaszyfrowany klucz „evm”, używając powyższego zaufanego klucza „kmk”:

opcja 1: pomijanie „formatowania”

$ keyctl add encrypted evm "new trusted:kmk 32" @u
159771175

opcja 2: jawne zdefiniowanie „formatu” jako „domyślnego”

$ keyctl add encrypted evm "new default trusted:kmk 32" @u
159771175

$ keyctl print 159771175
default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3
82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0
24717c64 5972dcb82ab2dde83376d82b2e3c09ffc

$ keyctl pipe 159771175 > evm.blob

Załaduj zaszyfrowany klucz „evm” z zapisanego obiektu BLOB:

$ keyctl add encrypted evm "load `cat evm.blob`" @u
831684262

$ keyctl print 831684262
default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3
82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0
24717c64 5972dcb82ab2dde83376d82b2e3c09ffc

Przewidywane są inne zastosowania zaufanych i zaszyfrowanych kluczy, na przykład do szyfrowania dysków i plików. W szczególności nowy format „ecryptfs” został zdefiniowany w celu użycia zaszyfrowanych kluczy do zamontowania systemu plików eCryptfs. Więcej szczegółów na temat użycia można znaleźć w pliku „Documentation / security / keys-ecryptfs.txt”.

chandana
źródło
Czy masz pojęcie, kiedy dodano te dwa nowe typy kluczy? W której wersji mam na myśli. Obecnie używam wersji 1.2 (pakiety firmowe), a ta nie obsługuje tych. Może w 1.5+?
Acapulco
1
Jakie jest źródło tego wpisu? Koniec odnosi się do dokumentuDocumentation/security/keys-ecryptfs.tx
goodguys_activate
Wydaje się, że są to wywołania programu wiersza poleceń. Nie widzę kodu, jak korzystać z modułu TPM.
Ian Boyd
3

Jak mogę zaszyfrować bajty za pomocą modułu TPM komputera?

Zależy od Twoich zamiarów i okoliczności:

  • Jaki masz moduł TPM (1-rodzinny lub 2-rodzinny)?
  • W jakim stanie jest moduł TPM? Czy był własnością? Czy zostało to zabezpieczone?
  • Jaki jest Twój język programowania?
  • Chcesz zaszyfrować lub podpisać? (to jest niejasne z reszty pytania)
  • Jak duże są dane, które chcesz zaszyfrować?
  • Chcesz użyć klucza symetrycznego czy asymetrycznego?
  • Czy chcesz użyć klucza, który już istnieje w module TPM, czy chcesz, aby najpierw utworzył klucz?
  • Czy przez „zaszyfruj” masz na myśli „opakuj klucz”?
  • Czy chcesz zablokować zaszyfrowane dane w konfiguracji systemu, aby można je było odszyfrować tylko wtedy, gdy system powróci do tej samej konfiguracji?
  • Czy chcesz wymagać autoryzacji do odszyfrowania?
  • Być może w ogóle nie musisz szyfrować, ale raczej przechowywać dane w module TPM?
  • Jeśli przechowujesz dane w module TPM, czy chcesz wymagać autoryzacji lub aby system znajdował się w określonej konfiguracji w celu pobrania?

Każdy z tych przypadków użycia (a jest ich więcej) - lub ich kombinacja - przedstawia inną ścieżkę implementacji. Pomyśl o TPM jak o szwajcarskim scyzoryku urządzeń kryptograficznych: niewiele można z nim zrobić, ale łatwość użycia cierpi na tę wszechstronność. Pytanie ciągle przeskakuje między szyfrowaniem, podpisywaniem i blokowaniem do konfiguracji systemu, ale główna część tej odpowiedzi zakłada, że ​​polecenie Seal spełnia większość potrzeb opisanych w pytaniu.

Nadszedł czas, aby pójść o krok dalej. Chcę zaszyfrować niektóre dane (np. Klucz główny szyfrowania dysku twardego), które mogą być odszyfrowane tylko przez lokalny moduł TPM.

Do tego służy polecenie Bind (zastąpione przez polecenie Create dla modułu TPM 2). Ładujesz klucz, który pochodzi z klucza związanego z modułem TPM i szyfrujesz za jego pomocą (lub bezpośrednio za pomocą klucza sprzętowego). W ten sposób dane można odszyfrować tylko przy dostępie do tego samego modułu TPM.

Innymi słowy, chcę zamienić Qualcomm Trusted Execution Environment (TEE) na poniższym schemacie blokowym na Androida, na TPM w Windows:

Nie jestem pewien, czy powtórzenie całego procesu jest dobrym pomysłem. Po pierwsze, nie ma potrzeby wykonywania operacji podpisywania w dowolnym miejscu procesu. Wydawałoby się, że w czasie, gdy powstawał Android 5, API Keystore ograniczało się do operacji podpisywania i weryfikacji. Domyślam się, że zespół szyfrujący dysk dołożył wszelkich starań, aby pracować z tym, co miał, i opracował algorytm, w którym jeden z kluczy pośrednich został wyprowadzony podczas operacji podpisywania , przy użyciu przechowywanego klucza TEE, wiążąc w ten sposób cały proces ze sprzętem. związany klucz dostępny tylko na platformie - ponieważ podpisywanie było wówczas jedynym sposobem na to. Nie ma jednak potrzeby ograniczania się w taki sposób, jeśli masz dostęp do modułu TPM, który zapewnia więcej możliwości, niż wiedziałeś, że potrzebujesz!

Zdaję sobie sprawę, że moduł TPM nie podpisuje danych

To nieprawda, obie wersje obsługują podpisywanie TPM.

(a jeśli tak, nie gwarantuje, że podpisanie tych samych danych da za każdym razem to samo wyjście binarne)

To nie ma sensu. Podpisanie tych samych danych tym samym kluczem da taki sam podpis. Możesz pomylić operację podpisywania z operacją cytowania, która będzie mieszała się z wartością jednorazową.

Dlatego chciałbym zamienić „podpisywanie RSA” na „szyfrowanie 256-bitowego obiektu blob za pomocą klucza sprzętowego”.

To faktycznie powinna być preferowana opcja, chociaż obie są możliwe w przypadku modułu TPM. Patrz wyżej.

Problem polega na tym, że programowanie TPM jest całkowicie nieudokumentowane w MSDN. Nie ma dostępnego API do wykonywania jakichkolwiek operacji.

Niestety nie ma wiele do udokumentowania. Win API jest ograniczone do kilku funkcji TBS, które są o jeden poziom usunięte ze sterownika.

Zamiast tego musisz znaleźć kopię stosu oprogramowania Trusted Computing Group (aka TSS), dowiedzieć się, jakie polecenia wysłać do TPM wraz z ładunkami, w jakiej kolejności i wywołać funkcję Tbsip_Submit_Command systemu Windows, aby bezpośrednio przesłać polecenia:

Właściwie nie, gdybyś miał TSS, nie musiałbyś go używać Tbsip_submit_Command(). O to właśnie chodzi w przypadku TSS - szczegóły niskiego poziomu są wyabstrahowane.

System Windows nie ma interfejsu API wyższego poziomu do wykonywania działań.

Nadal prawdziwe dla TPM 1, ale dla TPM 2 istnieje TSS.MSR .

Jest to moralny odpowiednik próby utworzenia pliku tekstowego przez wydanie poleceń SATA I / O na dysk twardy.

Poprawny.

Dlaczego nie po prostu użyć spodni… Problem z tym kodem polega na tym, że nie można go przenieść do świata Windows. Na przykład nie możesz go używać z Delphi, nie możesz go używać z C #. Wymaga: OpenSSL, pThread

Nie jest jasne, czy jest to wyzwanie nie do pokonania. Dostęp do TrouSerS poprzez interop powinien być lepszy niż przepisanie całego kodu strukturyzującego dane. Było też doTSSw momencie pisania pytania.

Jaki jest równoważny kod do szyfrowania danych przy użyciu modułu TPM? Prawdopodobnie wiąże się to z poleceniem TPM_seal. Chociaż myślę, że nie chcę zapieczętowywać danych, myślę, że chcę to powiązać:

Pytanie zawiera cytat opisujący różnicę między tymi dwoma poleceniami, więc nie powinno być dużego zamieszania. Pieczętowanie jest podobne do wiązania, z dodatkowym ograniczeniem, że stan systemu musi być taki sam, aby dane mogły zostać rozpieczętowane.

W ten sam sposób mogłem zapewnić:

Byte[] ProtectBytes_Crypt(Byte[] plaintext)
{
   //...
   CryptProtectData(...); 
   //...
}

czy ktoś może podać odpowiedni odpowiednik:

Byte[] ProtectBytes_TPM(Byte[] plaintext)
{
   //...
   Tbsip_Submit_Command(...);
   Tbsip_Submit_Command(...);
   Tbsip_Submit_Command(...);
   //...snip...
   Tbsip_Submit_Command(...);
   //...
}

robi to samo, z wyjątkiem tego, że klucz zamknięty w systemie LSA jest zablokowany w module TPM?

Po pierwsze, warto zauważyć, że istnieją dwie główne wersje TPM, które są całkowicie niekompatybilne między sobą. Więc praktycznie żaden kod, który napisałeś dla TPM 1 nie będzie działał dla TPM 2. Interfejs API TBS jest jedynym wspólnym kodem między nimi i, aby być uczciwym dla firmy Microsoft, mógł to być jeden z powodów, dla których ten interfejs API nigdy się nie rozwinął. W głównej części odpowiedzi zostanie przedstawiony kod TPM 1 z dwóch powodów:

  • Pytanie jest załadowane konkretnymi koncepcjami TPM 1, więc ludzie używający TPM 1 są bardziej skłonni wylądować tutaj w poszukiwaniu ich
  • Istnieje implementacja TSS firmy Microsoft dla modułu TPM 2.

Po drugie, sprecyzujmy pytanie. Reinterpretuję to następująco:

How do I write code in C#, using only the TBS API, to interface with
an already owned and provisioned TPM to, without user interaction,
encrypt no more than 128 bytes of arbitrary data with an asymmetric
key already resident in the TPM and bound to it, but not protected
with a password, so that in order to decrypt the data the system may
need to be in the same state it was in at encryption time based on an
easily configurable variable?

Do tego celu najlepiej nadaje się polecenie Seal, ponieważ spełnia tę samą funkcję, co polecenie Bind, gdy wielkość selekcji PCR jest ustawiona na zero, ale wybór PCR można łatwo zmienić, aby uwzględnić dowolne reakcje PCR. To sprawia, że ​​można się zastanawiać, dlaczego polecenie Bind zostało w ogóle uwzględnione w specyfikacji i jak zauważono, zostało usunięte ze specyfikacji TPM 2 i oba zostały połączone w jednym poleceniu Create.

Oto kod C # do używania polecenia TPM 1.2 Seal do szyfrowania danych za pomocą tylko funkcji TBS (uwaga: ten kod jest nieprzetestowany i prawdopodobnie nie zadziała bez debugowania) :

[DllImport ("tbs.dll")]
unsafe static extern UInt32 Tbsi_Context_Create (UInt32 * version, IntPtr * hContext);

[DllImport ("tbs.dll")]
unsafe static extern UInt32 Tbsip_Context_Close (IntPtr hContext);

[DllImport ("tbs.dll")]
unsafe static extern UInt32 Tbsip_Submit_Command (
    IntPtr hContext, UInt32 Locality, 
    UInt32 Priority, 
    byte * pCommandBuf, 
    UInt32 CommandBufLen, 
    byte * pResultBuf, 
    UInt32 * pResultBufLen);

byte[] ProtectBytes_TPM (byte[] plaintext) {

    void AddUInt32Reversed (byte[] a, System.UInt32 o, ref int i) {
        byte[] bytes = System.BitConverter.GetBytes (o);
        Array.Reverse (bytes);
        Array.Copy (bytes, 0, a, i, bytes.Length);
        i += bytes.Length;
    }
    void AddUInt16Reversed (byte[] a, System.UInt16 o, ref int i) {
        byte[] bytes = System.BitConverter.GetBytes (o);
        Array.Reverse (bytes);
        Array.Copy (bytes, 0, a, i, bytes.Length);
        i += bytes.Length;
    }
    void AddBool (byte[] a, byte b, ref int i) {
        a[i] = b;
        i += 1;
    }
    void AddBlob (byte[] a, byte[] b, ref int i) {
        Array.Copy (b, 0, a, i, b.Length);
        i += b.Length;
    }
    byte[] Xor (byte[] text, byte[] key) {
        byte[] xor = new byte[text.Length];
        for (int i = 0; i < text.Length; i++) {
            xor[i] = (byte) (text[i] ^ key[i % key.Length]);
        }
        return xor;
    }

    int offset;

    Random rnd = new Random ();

    IntPtr hContext = IntPtr.Zero;
    unsafe {
        UInt32 version = 1;
        IntPtr handle = hContext;
        UInt32 result = Tbsi_Context_Create ( & version, & handle);

        if (result == 0) {
            hContext = handle;
        }
    }

    byte[] cmdBuf = new byte[768];

    //OSAP
    System.UInt32 outSize;

    byte[] oddOsap = new byte[20];
    byte[] evenOsap = new byte[20];
    byte[] nonceEven = new byte[20];
    byte[] nonceOdd = new byte[20];
    System.UInt32 hAuth = 0;

    offset = 0;
    AddUInt16Reversed (cmdBuf, 0x00C1, ref offset);
    offset = 6;
    AddUInt32Reversed (cmdBuf, 0x0000000B, ref offset);

    offset = 2 + 4 + 4; //2 for tag, 4 for size and 4 for command code

    AddUInt16Reversed (cmdBuf, 0x0004, ref offset); //Entity Type SRK = 0x0004
    AddUInt32Reversed (cmdBuf, 0x40000000, ref offset); //Entity Value SRK = 0x40000000
    rnd.NextBytes (oddOsap);
    AddBlob (cmdBuf, oddOsap, ref offset);
    uint cmdSize = (System.UInt32) offset;
    offset = 2;
    AddUInt32Reversed (cmdBuf, cmdSize, ref offset);

    outSize = (System.UInt32) (Marshal.SizeOf (hAuth) + nonceEven.Length + evenOsap.Length);

    byte[] response = new byte[outSize];
    unsafe {
        UInt32 result = 0;

        //uint cmdSize = (uint)offset;
        uint resSize = outSize;
        fixed (byte * pCmd = cmdBuf, pRes = response) {
            result = Tbsip_Submit_Command (hContext, 0, 200, pCmd, cmdSize, pRes, & resSize);
        }
    }

    byte contSession = 0;
    System.UInt32 hKey = 0x40000000; //TPM_KH_SRK;
    System.UInt32 pcrInfoSize = 0;
    byte[] srkAuthdata = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    uint inDataSize = (uint) plaintext.Length;

    offset = 2 + 4 + 4; //2 for tag, 4 for size and 4 for return code
    byte[] hauthbytes = new byte[Marshal.SizeOf (hAuth)];
    Array.Copy (response, offset, hauthbytes, 0, hauthbytes.Length);
    Array.Reverse (hauthbytes);
    hAuth = System.BitConverter.ToUInt32 (hauthbytes, 0);
    offset += Marshal.SizeOf (hAuth);
    Array.Copy (response, offset, nonceEven, 0, nonceEven.Length);
    offset += nonceEven.Length;
    Array.Copy (response, offset, evenOsap, 0, evenOsap.Length);

    //shared-secret = HMAC(srk_auth, even_osap || odd_osap)
    byte[] sharedSecretBuf = new byte[evenOsap.Length + oddOsap.Length];
    Array.Copy (evenOsap, 0, sharedSecretBuf, 0, evenOsap.Length);
    Array.Copy (oddOsap, 0, sharedSecretBuf, evenOsap.Length, oddOsap.Length);
    System.Security.Cryptography.HMACSHA1 sharedSecretHmac = new System.Security.Cryptography.HMACSHA1 (srkAuthdata);
    byte[] sharedSecret = sharedSecretHmac.ComputeHash (sharedSecretBuf);

    byte[] authSha1InBuf = new byte[sharedSecret.Length + nonceEven.Length];
    Array.Copy (sharedSecret, 0, authSha1InBuf, 0, sharedSecret.Length);
    Array.Copy (nonceEven, 0, authSha1InBuf, sharedSecret.Length, nonceEven.Length);
    System.Security.Cryptography.SHA1Managed sha1 = new System.Security.Cryptography.SHA1Managed ();
    byte[] authSha1 = sha1.ComputeHash (authSha1InBuf);
    byte[] encAuth = Xor (srkAuthdata, authSha1);

    //inParamDigest = sha1(1S ~ 6S) 
    int paramInDigestInBufSize =
        sizeof (System.UInt32) + 
        encAuth.Length +
        Marshal.SizeOf (pcrInfoSize) +
        Marshal.SizeOf (inDataSize) +
        (int) inDataSize;
    byte[] paramInDigestInBuf = new byte[paramInDigestInBufSize];
    offset = 0;
    AddUInt32Reversed (paramInDigestInBuf, 0x00000017, ref offset);
    AddBlob (paramInDigestInBuf, encAuth, ref offset);
    AddUInt32Reversed (paramInDigestInBuf, 0x0, ref offset); //PCR info size
    AddUInt32Reversed (paramInDigestInBuf, inDataSize, ref offset);
    AddBlob (paramInDigestInBuf, plaintext, ref offset);

    byte[] paramInDigest = sha1.ComputeHash (paramInDigestInBuf);

    int pubAuthInBufSize = paramInDigest.Length + nonceEven.Length + nonceOdd.Length + Marshal.SizeOf (contSession);
    byte[] pubAuthInBuf = new byte[pubAuthInBufSize];

    offset = 0;
    AddBlob (pubAuthInBuf, paramInDigest, ref offset);
    AddBlob (pubAuthInBuf, nonceEven, ref offset);
    AddBlob (pubAuthInBuf, nonceOdd, ref offset);
    AddBool (pubAuthInBuf, contSession, ref offset);
    System.Security.Cryptography.HMACSHA1 pubAuthHmac = new System.Security.Cryptography.HMACSHA1 (sharedSecret);
    byte[] pubAuth = pubAuthHmac.ComputeHash (pubAuthInBuf);

    //Seal
    offset = 0;
    AddUInt16Reversed (cmdBuf, 0x00C2, ref offset); // TPM_TAG_RQU_AUTH1_COMMAND;
    offset = 6;
    AddUInt32Reversed (cmdBuf, 0x00000017, ref offset); // TPM_ORD_SEAL;
    offset = 2 + 4 + 4; //2 for tag, 4 for size and 4 for command code

    AddUInt32Reversed (cmdBuf, hKey, ref offset);
    AddBlob (cmdBuf, encAuth, ref offset);
    AddUInt32Reversed (cmdBuf, pcrInfoSize, ref offset);
    AddUInt32Reversed (cmdBuf, inDataSize, ref offset);
    AddBlob (cmdBuf, plaintext, ref offset);

    AddUInt32Reversed (cmdBuf, hAuth, ref offset);
    AddBlob (cmdBuf, nonceOdd, ref offset);
    AddBool (cmdBuf, contSession, ref offset);
    AddBlob (cmdBuf, pubAuth, ref offset);
    cmdSize = (System.UInt32) offset;
    offset = 2;
    AddUInt32Reversed (cmdBuf, cmdSize, ref offset);

    outSize = 768;
    uint responseSize = 0;

    response = new byte[outSize];
    unsafe {
        UInt32 result = 0;

        uint resSize = outSize;
        fixed (byte * pCmd = cmdBuf, pRes = response) {
            result = Tbsip_Submit_Command (hContext, 0, 200, pCmd, cmdSize, pRes, & resSize);
        }
        responseSize = resSize;
    }

    byte[] retBuffer = new byte[responseSize - 10];
    Array.Copy (response, 10, retBuffer, 0, retBuffer.Length);
    Tbsip_Context_Close (hContext);
    return retBuffer;

}

Analiza kodu:

[DllImport ("tbs.dll")]
...

To tylko niektóre z nielicznych funkcji dostępnych w Tbs.h i jedyne, których będziemy tutaj używać. Zasadniczo pozwalają one otworzyć uchwyt do urządzenia i komunikować się z nim, wysyłając i odbierając nieprzetworzone bajty.

    void AddUInt32Reversed (byte[] a, System.UInt32 o, ref int i) { ... }
    void AddUInt16Reversed (byte[] a, System.UInt16 o, ref int i) { ... }
    void AddBool (byte[] a, byte b, ref int i) { ... }
    void AddBlob (byte[] a, byte[] b, ref int i) { ... }

TPM to big endian, Windows to little endian. Więc kolejność bajtów będzie musiała zostać odwrócona dla wszystkich przesyłanych danych. Musimy się tylko martwić o odwrócenie 32-bitowych i 16-bitowych intów bez znaku.

    ...
    UInt32 result = Tbsi_Context_Create ( & version, & handle);
    ...

Tutaj używamy Tbsi_Context_Create (), aby otworzyć dojście do komunikacji z TPM. TBS_CONTEXT_PARAMSParametr jest tylko struct C z jednym 32-bit unsigned int pole, które musi być ustawiony na 1, aby rozmawiać z TPM 1.2 instancji, a tym, co możemy ustawić go.

    byte[] cmdBuf = new byte[768];

Jest to określane jako minimalny rozmiar buforu w specyfikacji klienta komputera TPM . Tutaj wystarczy na nasze potrzeby.

TPM 1.2 Spec Part 3 mówi, co następuje:

TPM_Seal requires the encryption of one parameter (“Secret”). For the
sake of uniformity with other commands that require the encryption of
more than one parameter, the string used for XOR encryption is
generated by concatenating a nonce (created during the OSAP session)
with the session shared secret and then hashing the result.

Musimy zaszyfrować XOR ten „tajny” parametr przy użyciu wartości jednorazowej wygenerowanej podczas sesji OSAP. Jeden z uchwytów wejściowych polecenia Seal jest również uchwytem OSAP:

The authorization session handle used for keyHandle authorization.
Must be an OSAP session for this command.

Więc najpierw musimy ustanowić tę sesję OSAP. OSAP jest opisany w specyfikacji TPM 1.2 Część 1 . Protokół OSAP, czyli protokół autoryzacji specyficzny dla obiektu, został wynaleziony do obsługi przypadku użycia, w którym chcesz użyć obiektu TPM, który wymaga wielokrotnej autoryzacji, ale nie chce zapewniać autoryzacji za każdym razem: zamiast tego używana jest sesja OSAP, która polega na na temat „wspólnego sekretu”, którym jest HMACktóry miesza dane autoryzacji obiektu z danymi nonce wygenerowanymi po każdej stronie, aby zapobiec atakom odpowiedzi. Dlatego „wspólne hasło” jest znane tylko dwóm stronom w tej sesji: stronie, która zainicjowała sesję (użytkownik) i stronie, która ją zaakceptowała (TPM); ponadto obie strony muszą mieć te same dane autoryzacji obiektu, aby „wspólne hasło” było takie samo; dodatkowo „wspólne hasło” użyte w jednej sesji będzie nieważne w innej. Ten diagram ze specyfikacji opisuje proces:

OSAP

W tym konkretnym przypadku nie będziemy używać wielu sesji (w rzeczywistości ten parametr jest ignorowany przez polecenie Seal!), A klucz, którego będziemy używać, nie wymaga autoryzacji, ale niestety nadal jesteśmy zobowiązani do ustanowienia OSAP sesja.

    offset = 0;
    AddUInt16Reversed (cmdBuf, 0x00C1, ref offset);
    offset = 6;
    AddUInt32Reversed (cmdBuf, 0x0000000B, ref offset);

    offset = 2 + 4 + 4; //2 for tag, 4 for size and 4 for command code

    AddUInt16Reversed (cmdBuf, 0x0004, ref offset); //Entity Type SRK = 0x0004
    AddUInt32Reversed (cmdBuf, 0x40000000, ref offset); //Entity Value SRK = 0x40000000
    rnd.NextBytes (oddOsap);
    AddBlob (cmdBuf, oddOsap, ref offset);
    uint cmdSize = (System.UInt32) offset;

Argumenty polecenia TPM_OSAP to:

Operandy TPM_OSAP

Każde polecenie TPM 1.2 ma następującą strukturę:

  2 bytes       4 bytes             4 bytes
+---------+------------------+------------------+---------------------------
|   Tag   |       Size       |   Command code   |    Command body    ....
+---------+------------------+------------------+---------------------------

Znacznik jest dwubajtową wartością, która wskazuje, czy to, co następuje, jest wejściem, czy wyjściem i czy istnieją jakieś wartości danych uwierzytelniania po parametrach polecenia. W przypadku TPM_OSAP tag musi mieć wartość TPM_TAG_RQU_COMMAND (0x00C1) zgodnie ze specyfikacją, co oznacza „polecenie bez autoryzacji”.

Rozmiar to czterobajtowa wartość określająca rozmiar polecenia w bajtach, w tym znacznik i sam rozmiar. Wartość tę ustawimy później, po jej obliczeniu.

Kod polecenia to czterobajtowa wartość, która służy jako identyfikator polecenia: informuje moduł TPM, jak zinterpretować pozostałą część polecenia. Nasz kod polecenia to TPM_OSAP (0x0000000B).

Następne dwie rzeczy do ustawienia to typ jednostki i wartość jednostki. Ponieważ chcemy użyć klucza, który już istnieje w module TPM, użyjemy typu jednostki „SRK” (0x0004), a ponieważ pracujemy przy założeniu, że moduł TPM jest już własnością, można bezpiecznie założyć, że ma SRK załadowany pod stałym uchwytem 0x40000000 zgodnie ze specyfikacją, więc użyjemy tej trwałej wartości uchwytu dla naszej wartości encji. (SRK oznacza „Storage Root Key” i jest kluczem głównym, z którego pochodzi większość innych kluczy należących do modułu TPM)

    result = Tbsip_Submit_Command (hContext, 0, 200, pCmd, cmdSize, pRes, & resSize);

Na koniec obliczamy rozmiar polecenia, ustawiamy go i wysyłamy polecenie.

    offset = 2 + 4 + 4; //2 for tag, 4 for size and 4 for return code
    byte[] hauthbytes = new byte[Marshal.SizeOf (hAuth)];
    Array.Copy (response, offset, hauthbytes, 0, hauthbytes.Length);
    Array.Reverse (hauthbytes);
    hAuth = System.BitConverter.ToUInt32 (hauthbytes, 0);
    offset += Marshal.SizeOf (hAuth);
    Array.Copy (response, offset, nonceEven, 0, nonceEven.Length);
    offset += nonceEven.Length;
    Array.Copy (response, offset, evenOsap, 0, evenOsap.Length);

Dane, które powinniśmy odzyskać z TPM na TPM_OSAP to:

Odpowiedź TPM_OSAP

Więc wracamy:

  • Uchwyt autoryzacji do użycia z naszym głównym poleceniem (Pieczęć)
  • nonceEven: wartość jednorazowa wygenerowana przez moduł TPM do użycia z poleceniem głównym
  • nonceEvenOSAP: numer jednorazowy OSAP, który jest przeciwną wartością jednorazową, którą wygenerowaliśmy po naszej stronie przed wysłaniem polecenia TPM_OSAP. Te dwie wartości nonce zostaną użyte do wygenerowania „wspólnego hasła”.

Wyodrębniamy te wartości i przechowujemy je w zmiennych.

    byte[] sharedSecretBuf = new byte[evenOsap.Length + oddOsap.Length];
    Array.Copy (evenOsap, 0, sharedSecretBuf, 0, evenOsap.Length);
    Array.Copy (oddOsap, 0, sharedSecretBuf, evenOsap.Length, oddOsap.Length);
    System.Security.Cryptography.HMACSHA1 sharedSecretHmac = new System.Security.Cryptography.HMACSHA1 (srkAuthdata);
    byte[] sharedSecret = sharedSecretHmac.ComputeHash (sharedSecretBuf);

Następnie obliczamy „wspólny sekret”. Zgodnie ze specyfikacją, wartościami uwzględnianymi w obliczeniach są dwie wartości nonce OSAP (jedna wygenerowana przez użytkownika i jedna wygenerowana przez TPM) oraz wartość autoryzacji dla klucza, którego chcemy użyć - SRK. Zgodnie z konwencją, wartość autoryzacji SRK jest „dobrze znanym auth”: zerowanym 20-bajtowym buforem. Technicznie rzecz biorąc, można zmienić tę wartość na inną, przejmując na własność TPM, ale nie jest to robione w praktyce, więc możemy bezpiecznie założyć, że wartość „dobrze znanego uwierzytelnienia” jest dobra.

Następnie przyjrzyjmy się, co trafia do polecenia TPM_Seal:

TPM_Seal

Większość z tych parametrów jest trywialnych do zbudowania, z wyjątkiem dwóch z nich: encAuthi pubAuth. Spójrzmy na nie jeden po drugim.

encAuthto „Zaszyfrowane dane AuthData dla zapieczętowanych danych”. Nasze AuthData to „dobrze znana autoryzacja” z wcześniej, ale tak, nadal musimy ją zaszyfrować. Ponieważ używamy sesji OSAP, jest ona szyfrowana zgodnie z protokołem ADIP lub Authorization-Data Insertion Protocol. Ze specyfikacji: „ADIP ​​pozwala na tworzenie nowych jednostek i bezpieczne wstawianie nowej jednostki AuthData. Transmisja nowego AuthData wykorzystuje szyfrowanie za pomocą klucza opartego na wspólnym tajnym tajne sesji OSAP”. Dodatkowo: „W przypadku obowiązkowego algorytmu szyfrowania XOR twórca tworzy klucz szyfrowania przy użyciu skrótu SHA-1 wspólnego hasła OSAP i numeru sesji. Twórca XOR szyfruje nowe dane AuthData przy użyciu klucza szyfrowania jako jednorazowej podkładki i wysyła te zaszyfrowane dane wraz z żądaniem utworzenia do modułu TPM ”.

Poniższy diagram wyjaśnia, jak działa ADIP:

ADIP

pubAuthto „Podsumowanie sesji autoryzacji dla danych wejściowych i keyHandle”. Część 1 specyfikacji, w „Deklaracjach parametrów dla przykładów OIAP i OSAP”, wyjaśnia, jak interpretować powyższą tabelę parametrów TPM_Seal: „Kolumna HMAC # zawiera szczegółowe informacje na temat parametrów używanych w obliczeniach HMAC. Parametry 1S, 2S itd. Są łączone i hashowane do inParamDigest lub outParamDigest, niejawnie nazywane 1H1 i prawdopodobnie 1H2, jeśli istnieją dwie sesje autoryzacji. W przypadku pierwszej sesji 1H1, 2H1, 3H1 i 4H1 są łączone i HMAC. W przypadku drugiej sesji 1H2, 2H2, 3H2, i 4H2 są łączone i HMAC ''. Więc będziemy musieli encAuthzaszyfrować tekst jawny, jego rozmiar, rozmiar informacji PCR, od góry i numer porządkowy TPM_Seal, a następnie HMAC, który z dwiema wartościami nonces i wartością logiczną „kontynuuj sesję” przy użyciu OSAP ”

Podsumowując to wszystko na schemacie:

obliczenia pubAuth

Zwróć uwagę, jak ustawiliśmy „rozmiar informacji PCR” na zero w tym kodzie, ponieważ chcemy po prostu zaszyfrować dane bez blokowania ich w stanie systemowym. Jednak w razie potrzeby podanie struktury „informacji o PCR” jest trywialne.

    offset = 0;
    AddUInt16Reversed (cmdBuf, 0x00C2, ref offset); 
    offset = 6;
    AddUInt32Reversed (cmdBuf, 0x00000017, ref offset); // TPM_ORD_SEAL;
    ...
    result = Tbsip_Submit_Command (hContext, 0, 200, pCmd, cmdSize, pRes, & resSize);

Na koniec tworzymy polecenie i wysyłamy.

    byte[] retBuffer = new byte[responseSize - 10];
    Array.Copy (response, 10, retBuffer, 0, retBuffer.Length);
    Tbsip_Context_Close (hContext);
    return retBuffer;

Używamy funkcji Tbsip_Context_Close (), aby zamknąć nasz uchwyt komunikacyjny.

Zwracamy odpowiedź taką, jaka jest tutaj. Idealnie byłoby, gdybyś chciał ponownie odwrócić bajty i sprawdzić poprawność przez ponowne resAuthobliczenie wartości, aby zapobiec atakom typu man-in-the-middle.


Mylące jest to, że nie ma polecenia Tspi_Data_Bind.

Dzieje się tak, ponieważ Tspi_Data_Bind jest poleceniem TSS, a nie poleceniem TPM. Powodem jest to, że nie wymaga żadnych tajemnic (używany jest tylko klucz publiczny), więc można to zrobić bez angażowania modułu TPM. Spowodowało to jednak zamieszanie, a nawet polecenia, które nie wymagają żadnych tajemnic, są teraz zawarte w specyfikacji TPM 2.

Jak zaszyfrować klucz kluczem publicznym modułu TPM?

Zależy od wersji TPM. Za pomocą komendy TPM_CreateWrapKey dla TPM 1.2. Za pomocą polecenia TPM2_Create dla modułu TPM 2.

W jaki sposób programista blokuje klucz w module TPM?

Utwórz go w module TPM, zapakuj go lub użyj dowolnej innej z dostępnych metod.

TPM2_Create, określając klucz HMAC

Tekst w książce jest zagmatwany. Nie określasz klucza HMAC , określasz, że chcesz mieć klucz HMAC .

Fakt, że klucz HMAC nie jest tajny, ma sens

Nie, to nie ma sensu. Klucz jest tajny.

... używaj kluczy, jednocześnie chroniąc je w urządzeniu ... Doskonale! Jak ty to robisz!?

Istnieją polecenia służące do tworzenia kluczy lub importowania ich dla obu wersji TPM. W przypadku modułu TPM 1 był tylko jeden klucz główny - SRK - z którego można było ustalić hierarchię kluczy, tworząc klucze opakowane. Dzięki TPM 2 możesz mieć wiele kluczy podstawowych lub głównych.

Czy moduł TPM ma możliwość generowania kluczy kryptograficznych i ochrony swoich danych tajnych w granicach sprzętowych? Czy tak, jak?

Patrz wyżej.

Doskonały! Tak się składa, że ​​właśnie o to mi chodzi. Jest to również przypadek użycia, do którego Microsoft używa modułu TPM. Jak mam to zrobić!?

Prawdopodobnie zależy to od rodzaju napędu. W przypadku dysków bez SED klucz szyfrowania dysku jest prawdopodobnie owinięty kluczem TPM. W przypadku dysków SED hasło administratora 1 (lub takie) jest zapieczętowane za pomocą modułu TPM.

Klucz poręczenia lub EK ... Gdzieś w module TPM znajduje się klucz prywatny RSA. Ten klucz jest tam zamknięty - nigdy go nie widzi świat zewnętrzny. Chcę, aby moduł TPM podpisał coś swoim kluczem prywatnym (tj. Zaszyfrował to swoim kluczem prywatnym).

EK nie jest kluczem do podpisywania - to klucz szyfrowania. Jednak nie jest to klucz szyfrowania ogólnego przeznaczenia: można go używać tylko w określonych kontekstach .

Ale to, co naprawdę chciałbym zrobić, to „zapieczętować” niektóre dane

Patrz wyżej.

mnistyczny
źródło
2

Kiedy mówi

określenie klucza HMAC

NIE oznacza to podania klucza HMAC - oznacza „wskazanie klucza HMAC, którego chcesz użyć” .

Moduły TPM mogą używać praktycznie nieograniczonej liczby kluczy HMAC, jak wskazano w książce. Musisz powiedzieć modułowi TPM, którego ma użyć.

DCC
źródło
Czy jest więc może przykład kodu pokazujący, jak określić (wskazać) klucz HMAC, który ma być używany w C # lub innym języku?
Czad