Cara mengenkripsi byte menggunakan TPM (Trusted Platform Module)

110

Bagaimana cara mengenkripsi byte menggunakan modul TPM mesin?

CryptProtectData

Windows menyediakan API (relatif) sederhana untuk mengenkripsi blob menggunakan CryptProtectDataAPI, yang dapat kita bungkus dengan fungsi yang mudah digunakan:

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

Detailnya ProtectByteskurang penting daripada gagasan yang bisa Anda gunakan dengan cukup mudah:

  • berikut adalah byte yang saya ingin dienkripsi oleh kunci rahasia yang disimpan di System
  • kembalikan gumpalan terenkripsi itu

Blob yang dikembalikan adalah struktur dokumentasi tidak berdokumen yang berisi semua yang diperlukan untuk mendekripsi dan mengembalikan data asli (algoritme hash, algoritme sandi, garam, tanda tangan HMAC, dll).

Untuk kelengkapannya, berikut ini contoh implementasi pseudocode ProtectBytesyang menggunakan Crypt APIto protect bytes:

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.
}

Bagaimana melakukan hal yang sama dengan TPM?

Kode di atas berguna untuk mengenkripsi data hanya untuk mesin lokal. Data dienkripsi menggunakan Systemakun sebagai penghasil kunci ( detail, meski menarik, tidak penting ). Hasil akhirnya adalah saya dapat mengenkripsi data (misalnya kunci master enkripsi hard drive) yang hanya dapat didekripsi oleh mesin lokal.

Sekarang saatnya untuk mengambil satu langkah lebih jauh. Saya ingin mengenkripsi beberapa data (misalnya kunci master enkripsi hard drive) yang hanya dapat didekripsi oleh TPM lokal. Dengan kata lain, saya ingin mengganti Qualcomm Trusted Execution Environment ( TEE ) dalam diagram blok di bawah ini untuk Android, dengan TPM di Windows:

masukkan deskripsi gambar di sini

Catatan : Saya menyadari bahwa TPM tidak melakukan penandatanganan data (atau jika demikian, tidak menjamin bahwa penandatanganan data yang sama akan memberikan keluaran biner yang sama setiap saat). Itulah mengapa saya bersedia mengganti "penandatanganan RSA" dengan "mengenkripsi blob 256-bit dengan kunci terikat perangkat keras" .

Jadi dimana kodenya?

Masalahnya adalah bahwa pemrograman TPM sama sekali tidak terdokumentasi di MSDN . Tidak ada API yang tersedia untuk melakukan operasi apa pun. Alih-alih Anda harus menemukan sendiri salinan Tumpukan Perangkat Lunak Grup Komputasi Tepercaya (alias TSS) , cari tahu perintah apa yang akan dikirim ke TPM, dengan payload, dalam urutan apa, dan panggil fungsi Tbsip_Submit_Command Window untuk mengirimkan perintah secara langsung:

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
);

Windows tidak memiliki API tingkat yang lebih tinggi untuk melakukan tindakan.

Ini setara moral mencoba membuat file teks dengan mengeluarkan perintah SATA I / O ke hard drive Anda .

Kenapa tidak pakai celana saja

Trusted Computing Group (TCG) menentukan API mereka sendiri: TCB Software Stack (TSS) . Implementasi API ini dibuat oleh beberapa orang, dan disebut TrouSerS . Seorang pria kemudian memindahkan proyek itu ke Windows .

Masalah dengan kode itu adalah bahwa ia tidak portabel ke dunia Windows. Misalnya, Anda tidak dapat menggunakannya dari Delphi, Anda tidak dapat menggunakannya dari C #. Ini membutuhkan:

  • OpenSSL
  • pThread

Saya hanya ingin kode mengenkripsi sesuatu dengan TPM saya.

Hal di atas CryptProtectDatatidak membutuhkan apa pun selain apa yang ada di dalam tubuh fungsi.

Apa kode yang setara untuk mengenkripsi data menggunakan TPM? Seperti yang telah dicatat orang lain, Anda mungkin harus membaca ketiga manual TPM, dan membuat sendiri blob . Ini mungkin melibatkan TPM_sealperintah. Meskipun saya pikir saya tidak ingin menyegel data, saya rasa saya ingin mengikatnya :

Binding - mengenkripsi data menggunakan kunci ikat TPM, kunci RSA unik yang diturunkan dari kunci penyimpanan. Sealing - mengenkripsi data dengan cara yang mirip dengan pengikatan, tetapi juga menentukan keadaan di mana TPM harus berada agar data dapat didekripsi (tidak disegel)

Saya mencoba membaca tiga volume yang diperlukan untuk menemukan 20 baris kode yang saya butuhkan:

Tapi saya tidak tahu apa yang saya baca. Jika ada tutorial atau contoh apa pun, saya mungkin bisa mencobanya. Tapi aku benar-benar tersesat.

Jadi kami meminta Stackoverflow

Dengan cara yang sama saya dapat memberikan:

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

dapatkah seseorang memberikan padanan yang sesuai:

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

yang melakukan hal yang sama, kecuali kunci yang dikunci di SystemLSA, dikunci di TPM?

Mulai Riset

Saya tidak tahu persis apa artinya bind . Tetapi melihat TPM Utama - Perintah Bagian 3 - Spesifikasi Versi 1.2, ada penyebutan bind :

10.3 TPM_UnBind

TPM_UnBind mengambil blob data yang merupakan hasil dari perintah Tspi_Data_Bind dan mendekripsinya untuk diekspor ke Pengguna. Penelepon harus mengizinkan penggunaan kunci yang akan mendekripsi blob yang masuk. TPM_UnBind beroperasi pada basis blok-demi-blok, dan tidak memiliki gagasan tentang hubungan apa pun antara satu blok dan lainnya.

Apa yang membingungkan adalah ada adalah tidak ada Tspi_Data_Bindperintah.

Upaya Penelitian

Sungguh mengerikan bagaimana tidak seorang pun pernah repot-repot mendokumentasikan TPM atau operasinya. Seolah-olah mereka menghabiskan waktu mereka datang dengan ini keren hal untuk bermain dengan, tetapi tidak mau berurusan dengan langkah yang menyakitkan sehingga dapat digunakan untuk sesuatu.

Dimulai dengan buku gratis (sekarang) A Practical Guide to TPM 2.0: Using the Trusted Platform Module in the New Age of Security :

Bab 3 - Tutorial Cepat di TPM 2.0

TPM memiliki akses ke kunci pribadi yang dibuat sendiri, sehingga dapat mengenkripsi kunci dengan kunci publik dan kemudian menyimpan blob yang dihasilkan pada hard disk. Dengan cara ini, TPM dapat menyimpan kunci dalam jumlah yang hampir tidak terbatas yang tersedia untuk digunakan tetapi tidak menyia-nyiakan penyimpanan internal yang berharga. Kunci yang disimpan pada hard disk dapat dihapus, tetapi mereka juga dapat dicadangkan, yang bagi para perancang tampaknya merupakan pertukaran yang dapat diterima.

Bagaimana cara mengenkripsi kunci dengan kunci publik TPM?

Bab 4 - Aplikasi yang Ada yang Menggunakan TPM

Aplikasi Yang Harus Menggunakan TPM tetapi Tidak

Dalam beberapa tahun terakhir, jumlah aplikasi berbasis web mengalami peningkatan. Diantaranya adalah backup dan penyimpanan berbasis web. Sejumlah besar perusahaan sekarang menawarkan layanan seperti itu, tetapi sejauh yang kami ketahui, tidak ada klien untuk layanan ini yang mengizinkan pengguna mengunci kunci layanan cadangan ke TPM. Jika ini dilakukan, alangkah baiknya jika kunci TPM itu sendiri dicadangkan dengan menduplikasinya di beberapa mesin. Ini tampaknya menjadi peluang bagi pengembang.

Bagaimana cara pengembang mengunci kunci TPM?

Bab 9 - Heirarki

GUNAKAN KASUS: MENYIMPAN SANDI LOGIN

File kata sandi biasa menyimpan hash kata sandi yang diasinkan. Verifikasi terdiri dari salting dan hashing kata sandi yang diberikan dan membandingkannya dengan nilai yang disimpan. Karena kalkulasi tidak menyertakan rahasia, itu tunduk pada serangan offline pada file kata sandi.

Kasus penggunaan ini menggunakan kunci HMAC yang dibuat TPM. File kata sandi menyimpan HMAC dari kata sandi asin. Verifikasi terdiri dari salting dan HMACing kata sandi yang disediakan dan membandingkannya dengan nilai yang disimpan. Karena penyerang offline tidak memiliki kunci HMAC, penyerang tidak dapat melakukan serangan dengan melakukan kalkulasi.

Ini bisa berhasil. Jika TPM memiliki kunci HMAC rahasia, dan hanya TPM saya yang mengetahui kunci HMAC tersebut, saya dapat mengganti "Masuk (alias enkripsi TPM dengan kunci pribadinya)" dengan "HMAC". Tapi kemudian di baris berikutnya dia membalikkan dirinya sepenuhnya:

TPM2_Create, dengan menentukan kunci HMAC

Bukan rahasia TPM jika saya harus menentukan kunci HMAC. Fakta bahwa kunci HMAC bukanlah rahasia masuk akal ketika Anda menyadari bahwa ini adalah bab tentang utilitas kriptografi yang disediakan TPM. Daripada Anda harus menulis SHA2, AES, HMAC, atau RSA sendiri, Anda dapat menggunakan kembali apa yang sudah ada di TPM.

Bab 10 - Kunci

Sebagai perangkat keamanan, kemampuan aplikasi untuk menggunakan kunci sekaligus menjaganya tetap aman di perangkat perangkat keras adalah kekuatan terbesar TPM. TPM dapat menghasilkan dan mengimpor kunci yang dibuat secara eksternal. Ini mendukung kunci asimetris dan simetris.

Luar biasa! Bagaimana Anda melakukannya!?

Generator Kunci

Bisa dibilang, kekuatan terbesar TPM adalah kemampuannya untuk menghasilkan kunci kriptografik dan melindungi rahasianya dalam batasan perangkat keras. Generator kunci didasarkan pada generator nomor acak TPM sendiri dan tidak bergantung pada sumber keacakan eksternal. Dengan demikian, ia menghilangkan kelemahan berdasarkan perangkat lunak yang lemah dengan sumber entropi yang tidak mencukupi.

Apakah TPM memiliki kemampuan untuk membuat kunci kriptografi dan melindungi rahasianya dalam batasan perangkat keras? Bagaimana caranya?

Bab 12 - Register Konfigurasi Platform

PCR untuk Otorisasi

GUNAKAN KASUS: SEALING KUNCI ENKRIPSI HARD DISK KE NEGARA PLATFORM

Aplikasi enkripsi disk penuh jauh lebih aman jika TPM melindungi kunci enkripsi daripada jika disimpan di disk yang sama, hanya dilindungi oleh sandi. Pertama, perangkat keras TPM memiliki perlindungan anti-palu (lihat Bab 8 untuk penjelasan rinci tentang perlindungan serangan kamus TPM), membuat serangan brute force pada sandi tidak praktis. Kunci yang hanya dilindungi oleh perangkat lunak jauh lebih rentan terhadap kata sandi yang lemah. Kedua, kunci perangkat lunak yang disimpan di disk jauh lebih mudah untuk dicuri. Ambil disk (atau cadangan dari disk), dan Anda mendapatkan kuncinya. Ketika TPM memegang kunci, seluruh platform, atau setidaknya disk dan motherboard, harus dicuri.

Penyegelan memungkinkan kunci untuk dilindungi tidak hanya dengan kata sandi tetapi juga oleh kebijakan. Kebijakan tipikal mengunci kunci ke nilai PCR (status perangkat lunak) saat ini pada saat penyegelan. Ini mengasumsikan bahwa status saat boot pertama kali tidak terganggu. Malware prainstal apa pun yang ada pada boot pertama akan diukur ke dalam PCR, dan dengan demikian kuncinya akan disegel ke status perangkat lunak yang disusupi. Perusahaan yang kurang percaya mungkin memiliki gambar disk standar dan segel ke PCR yang mewakili gambar itu. Nilai PCR ini akan dihitung sebelumnya pada platform yang mungkin lebih tepercaya. Perusahaan yang lebih canggih akan menggunakan TPM2_PolicyAuthorize, dan menyediakan beberapa tiket yang mengesahkan sekumpulan nilai PCR tepercaya. Lihat Bab 14 untuk penjelasan rinci tentang otorisasi kebijakan dan penerapannya untuk memecahkan masalah kerapuhan PCR.

Meskipun kata sandi juga bisa melindungi kunci, ada keuntungan keamanan bahkan tanpa kata sandi kunci TPM. Penyerang dapat mem-boot platform tanpa memberikan kata sandi TPM, tetapi tidak dapat masuk tanpa nama pengguna dan kata sandi OS. Keamanan OS melindungi data. Penyerang dapat mem-boot OS alternatif, katakanlah dari live DVD atau USB stick daripada dari hard drive, untuk melewati keamanan login OS. Namun, konfigurasi boot dan perangkat lunak yang berbeda ini akan mengubah nilai PCR. Karena PCR baru ini tidak akan cocok dengan nilai yang disegel, TPM tidak akan melepaskan kunci dekripsi, dan hard drive tidak dapat didekripsi.

Luar biasa! Inilah kasus penggunaan yang kebetulan saya inginkan. Ini juga merupakan kasus penggunaan TPM yang digunakan Microsoft. Bagaimana saya melakukannya!?

Jadi saya membaca seluruh buku itu, dan tidak ada yang berguna. Yang cukup mengesankan karena 375 halaman. Anda bertanya-tanya apa isi buku itu - dan melihat ke belakang, saya tidak tahu.

Jadi kami menyerah pada panduan definitif untuk memprogram TPM, dan beralih ke beberapa dokumentasi dari Microsoft:

Dari Toolkit Penyedia Kripto Platform TPM Microsoft . Itu menyebutkan dengan tepat apa yang ingin saya lakukan:

The Endorsement Key atau EK

EK dirancang untuk menyediakan pengenal kriptografi yang andal untuk platform. Perusahaan mungkin memelihara basis data Kunci Pengesahan milik TPM dari semua PC di perusahaan mereka, atau pengontrol kain pusat data mungkin memiliki basis data TPM di semua bilah. Di Windows, Anda dapat menggunakan penyedia NCrypt yang dijelaskan di bagian "Penyedia Platform Crypto di Windows 8" untuk membaca bagian publik dari EK.

Di suatu tempat di dalam TPM terdapat kunci pribadi RSA. Kunci itu terkunci di sana - tidak akan pernah terlihat oleh dunia luar. Saya ingin TPM menandatangani sesuatu dengan kunci privatnya (yaitu mengenkripsinya dengan kunci privat).

Jadi saya ingin operasi paling dasar yang mungkin ada:

masukkan deskripsi gambar di sini

Enkripsi sesuatu dengan kunci pribadi Anda. Saya bahkan belum (belum) meminta hal-hal yang lebih rumit:

  • "menyegel" itu berdasarkan status PCR
  • membuat kunci dan menyimpannya dalam memroy yang mudah menguap atau tidak mudah menguap
  • membuat kunci simetris dan mencoba memuatnya ke TPM

Saya meminta operasi paling dasar yang dapat dilakukan TPM. Mengapa tidak mungkin mendapatkan informasi apa pun tentang cara melakukannya?

Saya bisa mendapatkan data acak

Saya kira saya sedang fasih ketika saya mengatakan penandatanganan RSA adalah hal paling dasar yang dapat dilakukan TPM. Hal paling mendasar yang dapat diminta oleh TPM adalah memberi saya byte acak. Bahwa saya telah menemukan cara melakukannya:

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;
}

Hal Yang Menarik

Saya menyadari volume orang yang menggunakan TPM sangat rendah. Itulah mengapa tidak ada orang di Stackoverflow yang memiliki jawabannya. Jadi saya tidak bisa terlalu serakah dalam mendapatkan solusi untuk masalah umum saya. Namun hal yang sangat ingin saya lakukan adalah "menyegel" beberapa data:

masukkan deskripsi gambar di sini

  • menyajikan TPM beberapa data (misalnya 32 byte materi kunci)
  • meminta TPM mengenkripsi data, mengembalikan beberapa struktur blob yang buram
  • kemudian minta TPM untuk mendekripsi blob tersebut
  • dekripsi hanya akan berfungsi jika register PCR TPM sama seperti pada saat enkripsi.

Dengan kata lain:

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

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

Cryptography Next Gen (Cng, aka BCrypt) mendukung TPM

API Kriptografi asli di Windows dikenal sebagai API Kripto.

Dimulai dengan Windows Vista, Crypto API telah diganti dengan Cryptography API: Next Generation (secara internal dikenal sebagai BestCrypt , disingkat BCrypt , jangan disamakan dengan algoritma hashing kata sandi ).

Windows dikirimkan dengan dua penyedia BCrypt :

The Landasan Crypto penyedia tidak didokumentasikan di MSDN, tetapi memiliki dokumentasi dari situs 2012 Microsoft Research:

Perangkat Penyedia Crypto Platform TPM

Penyedia dan Alat Kripto Platform TPM berisi kode contoh, utilitas, dan dokumentasi untuk menggunakan fungsionalitas terkait TPM di Windows 8. Subsistem yang dijelaskan mencakup penyedia kripto platform Crypto-Next-Gen (CNG) yang didukung TPM, dan bagaimana penyedia layanan pengesahan dapat menggunakan fitur Windows baru. Baik sistem berbasis TPM1.2 dan TPM2.0 didukung.

Tampaknya niat Microsoft adalah untuk menampilkan fungsionalitas crypto TPM dengan Penyedia Crypto Platform Microsoft dari Cryptography NG API.

Enkripsi kunci publik menggunakan Microsoft BCrypt

Mengingat bahwa:

satu cara ke depan adalah mencari cara untuk melakukan penandatanganan digital menggunakan Microsoft Cryptography Next Gen API .

Langkah saya selanjutnya adalah membuat kode untuk melakukan enkripsi di BCrypt, dengan kunci publik RSA, menggunakan penyedia standar ( MS_PRIMITIVE_PROVIDER). Misalnya:

  • 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

Dengan kode yang berfungsi, saya mungkin dapat beralih menggunakan TPM Provider ( MS_PLATFORM_CRYPTO_PROVIDER).

2/22/2016: Dan dengan Apple yang dipaksa untuk membantu mendekripsi data pengguna, muncul minat baru tentang cara membuat TPM melakukan tugas paling sederhana yang diciptakannya - mengenkripsi sesuatu.

Ini kira-kira sama dengan semua orang yang memiliki mobil, tetapi tidak ada yang tahu bagaimana memulainya. Ini dapat melakukan hal-hal yang sangat berguna dan keren, jika saja kita dapat melewati Langkah 1 .

Bonus Membaca

Ian Boyd
sumber
Untuk pengikatan (enkripsi) tidak ada fungsi eksplisit yang tersedia dan itu juga tidak diperlukan. Anda cukup membuat kunci pengikat di TPM dan menggunakan bagian publiknya untuk mengenkripsi sek kunci enkripsi simetris dengan fungsi enkripsi sistem rsa ("RSA / ECB / OAEPWithSHA1AndMGF1Padding") dan menyimpannya dalam struktur yang benar ("TcTssConstants.TSS_ENCDATA_BIND"). Untuk melepaskan (mendekripsi) sek Anda cukup menggunakan fungsi pelepasan TPM dan gunakan sek dalam fungsi enkripsi simetris apa pun yang Anda suka. Saya memiliki basis kode yang agak lama untuk itu saya lakukan beberapa waktu lalu, mungkin membantu: goo.gl/jV1Ouw
evildead
Dari wikipedia, Binding - mengenkripsi data menggunakan kunci ikat TPM, kunci RSA unik yang diturunkan dari kunci penyimpanan. en.wikipedia.org/wiki/Trusted_Platform_Module Kedengarannya pasangan perintah ini (TSpi_Data_Bind / TPM_UnBind) sudah cukup untuk kebutuhan Anda ...
Alex Mazzariol
1
Saya tidak berpikir Anda harus menggunakan TPM secara langsung. Ini didukung melalui API CNG / NCryptXXX standar dan "Penyedia Kripto Platform Microsoft" (untuk platform OS Windows terkini, dan jika perangkat kerasnya baik-baik saja dan tentu saja diaktifkan). Mungkin Anda dapat melihat "TPM Platform Crypto-Provider Toolkit di sini: research.microsoft.com/en-us/downloads/… juga periksa ini: tiw2013.cse.psu.edu/slides/…
Simon Mourier
CryptProtectData tidak selalu menggunakan TPM. Di sisi lain, jika Anda bisa mendapatkan pegangan CNG atau CSP yang valid untuk TPM, Anda dapat menggunakannya dalam fungsi kripto.
Michael Chourdakis
1
@ b3 tidak; tidak ada yang bisa menjawab pertanyaan itu.
Ian Boyd

Jawaban:

7

Primer

Berikut ini adalah tentang TPM 1.2. Ingatlah bahwa Microsoft memerlukan TPM 2.0 untuk semua versi Windows di masa mendatang. Generasi 2.0 secara fundamental berbeda dengan 1.2

Tidak ada solusi satu baris karena prinsip desain TPM. Pikirkan TPM sebagai mikrokontroler dengan sumber daya terbatas. Tujuan desain utamanya adalah menjadi murah, namun tetap aman. Jadi TPM telah merobek semua logika yang tidak diperlukan untuk operasi yang aman. Jadi TPM hanya berfungsi ketika Anda memiliki setidaknya beberapa perangkat lunak yang lebih atau kurang gemuk , mengeluarkan banyak perintah dalam urutan yang benar. Dan urutan perintah tersebut mungkin menjadi sangat kompleks. Itulah mengapa TCG menentukan TSS dengan API yang ditentukan dengan baik. Jika Anda ingin menggunakan Java, bahkan ada API Java tingkat tinggi . Saya tidak mengetahui proyek serupa untuk C # / .net

Pengembangan

Dalam kasus Anda, saya sarankan Anda melihat TPM perangkat lunak IBM.

Dalam paketnya Anda akan menemukan 3 komponen yang sangat berguna:

  • emulator TPM perangkat lunak
  • lib tpm yang ringan
  • beberapa utilitas baris perintah dasar

Anda tidak memerlukan emulator TPM software, Anda juga dapat terhubung ke HW TPM mesin. Namun, Anda dapat mencegat perintah yang dikeluarkan dan melihat tanggapannya, sehingga mempelajari bagaimana mereka dirakit dan bagaimana mereka sesuai dengan spesifikasi perintah.

Level tinggi

Prasyarat:

  1. TPM diaktifkan
  2. Driver TPM dimuat
  3. Anda telah mengambil alih kepemilikan TPM

Untuk menutup blob, Anda perlu melakukan hal berikut:

  1. buat kunci
  2. simpan kunci-blob di suatu tempat
  3. pastikan bahwa kunci dimuat di TPM
  4. tutup gumpalan tersebut

Untuk membuka segel, Anda perlu:

  1. dapatkan kunci-blob
  2. memuat kunci ke TPM
  3. buka segel gumpalan yang tersegel

Anda dapat menyimpan key-blob dalam struktur data yang Anda gunakan untuk menyimpan byte yang dilindungi.

Sebagian besar perintah TPM yang Anda butuhkan adalah perintah resmi. Oleh karena itu, Anda perlu membuat sesi otorisasi jika diperlukan. AFAIR itu kebanyakan sesi OSAP.

Perintah TPM

Saat ini saya tidak dapat menjalankan versi debug, jadi saya tidak dapat memberikan urutan persisnya kepada Anda. Jadi pertimbangkan ini sebagai daftar perintah tidak berurutan yang harus Anda gunakan:

  • TPM_OSAP
  • TPM_CreateWrapKey
  • TPM_LoadKey2
  • TPM_Seal

Jika Anda ingin membaca nilai PCR saat ini juga:

  • TPM_PCRRead
Scolytus
sumber
Microsoft memiliki pustaka yang dikelola C # .NET untuk menggunakan TPM . Mereka juga memiliki emulator TPM , yang dapat dihubungkan dengan library terkelola sebagai alternatif debugging jika TPM sebenarnya tidak ada. Mereka juga memiliki TPM Platform Provider Toolkit , yang berisi dokumentasi dan kode contoh untuk menggunakan TPM. Sekarang seandainya seseorang dapat mengetahui cara menggunakan TPM untuk mengenkripsi byte.
Ian Boyd
Dua tautan pertama Anda hanya TPM 2.0. Jika Anda ingin menggunakan ini, saya khawatir saya tidak membantu.
Scolytus
4

Kunci Terpercaya dan Terenkripsi

Kunci Terpercaya dan Terenkripsi adalah dua jenis kunci baru yang ditambahkan ke layanan key ring kernel yang ada. Kedua jenis baru ini adalah kunci simetris dengan panjang variabel, dan dalam kedua kasus semua kunci dibuat di kernel, dan ruang pengguna hanya melihat, menyimpan, dan memuat gumpalan terenkripsi. Kunci Tepercaya memerlukan ketersediaan chip Trusted Platform Module (TPM) untuk keamanan yang lebih baik, sementara Kunci Terenkripsi dapat digunakan di sistem apa pun. Semua blob tingkat pengguna, ditampilkan dan dimuat dalam hex ascii untuk kenyamanan, dan diverifikasi integritasnya.

Kunci Tepercaya menggunakan TPM untuk menghasilkan dan menyegel kunci. Kunci disegel di bawah kunci RSA 2048 bit di TPM, dan secara opsional disegel ke nilai PCR (pengukuran integritas) yang ditentukan, dan hanya dibuka oleh TPM, jika PCR dan verifikasi integritas blob cocok. Kunci Tepercaya yang dimuat dapat diperbarui dengan nilai PCR baru (di masa mendatang), sehingga kunci mudah dimigrasi ke nilai pcr baru, seperti saat kernel dan initramf diperbarui. Kunci yang sama dapat memiliki banyak blob yang disimpan dengan nilai PCR yang berbeda, sehingga banyak boot dapat dengan mudah didukung.

Secara default, kunci tepercaya disegel di bawah SRK, yang memiliki nilai otorisasi default (20 nol). Ini dapat diatur pada waktu takeownership dengan utilitas celana ini: 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 printmengembalikan salinan hex ascii dari kunci tersegel, yang dalam format TPM_STORED_DATA standar. Panjang kunci untuk kunci baru selalu dalam satuan byte. Kunci Tepercaya dapat berukuran 32 - 128 byte (256 - 1024 bit), batas atas adalah sesuai dengan panjang kunci SRK (RSA) 2048 bit, dengan semua struktur / bantalan yang diperlukan.

Kunci terenkripsi tidak bergantung pada TPM, dan lebih cepat, karena menggunakan AES untuk enkripsi / dekripsi. Kunci baru dibuat dari nomor acak yang dihasilkan kernel, dan dienkripsi / didekripsi menggunakan kunci 'master' yang ditentukan. Kunci 'master' bisa berupa kunci-tepercaya atau jenis kunci-pengguna. Kerugian utama dari kunci terenkripsi adalah jika kunci tersebut tidak di-root pada kunci tepercaya, kunci tersebut hanya seaman kunci pengguna yang mengenkripsinya. Oleh karena itu, kunci pengguna master harus dimuat dengan cara seaman mungkin, sebaiknya di awal boot.

Bagian kunci terenkripsi yang didekripsi dapat berisi kunci simetris sederhana atau struktur yang lebih kompleks. Format struktur yang lebih kompleks adalah khusus aplikasi, yang diidentifikasi dengan 'format'.

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'

Contoh penggunaan kunci tepercaya dan terenkripsi

Buat dan simpan kunci tepercaya bernama "kmk" dengan panjang 32 byte:

$ 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

Muat kunci tepercaya dari blob yang disimpan:

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

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

Ulangi kunci tepercaya di bawah nilai pcr baru:

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

Konsumen awal kunci tepercaya adalah EVM, yang pada saat boot membutuhkan kunci simetris berkualitas tinggi untuk perlindungan HMAC atas metadata file. Penggunaan kunci tepercaya memberikan jaminan kuat bahwa kunci EVM tidak diganggu oleh masalah level pengguna, dan ketika disegel ke nilai PCR booting tertentu, melindungi dari serangan boot dan offline. Buat dan simpan kunci "evm" yang dienkripsi menggunakan kunci tepercaya "kmk" di atas:

opsi 1: menghilangkan 'format'

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

opsi 2: secara eksplisit mendefinisikan 'format' sebagai 'default'

$ 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

Muat kunci terenkripsi "evm" dari blob yang disimpan:

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

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

Penggunaan lain untuk kunci tepercaya dan terenkripsi, seperti untuk disk dan enkripsi file diantisipasi. Secara khusus, format baru 'ecryptfs' telah ditentukan untuk menggunakan kunci terenkripsi untuk memasang sistem file eCryptfs. Detail lebih lanjut tentang penggunaan dapat ditemukan di file 'Documentation / security / keys-ecryptfs.txt'.

chandana
sumber
Apakah Anda tahu kapan di mana dua jenis kunci baru ini ditambahkan? Dalam versi apa yang saya maksud. Saat ini saya menggunakan 1.2 (paket perusahaan) dan yang satu tidak mendukung ini. Mungkin di 1.5+?
Acapulco
1
Dari manakah sumber posting ini? Bagian akhir mengacu pada dokumenDocumentation/security/keys-ecryptfs.tx
goodguys_activate
Ini tampaknya semua panggilan ke program baris perintah. Saya tidak melihat kode tentang cara menggunakan TPM.
Ian Boyd
3

Bagaimana cara mengenkripsi byte menggunakan modul TPM mesin?

Tergantung pada niat dan keadaan Anda:

  • Jenis TPM apa yang Anda miliki (1-keluarga atau 2-keluarga)?
  • Di negara bagian apa TPM berada? Apakah sudah dimiliki? Apakah sudah disediakan?
  • Apa bahasa pemrograman Anda?
  • Apakah Anda ingin mengenkripsi atau menandatangani? (itu tidak jelas dari sisa pertanyaan)
  • Seberapa besar data yang ingin Anda enkripsi?
  • Apakah Anda ingin menggunakan kunci simetris atau kunci asimetris?
  • Apakah Anda ingin menggunakan kunci yang sudah ada di TPM, atau Anda ingin membuatnya membuat kunci terlebih dahulu?
  • Apa yang Anda maksud dengan "mengenkripsi" adalah "membungkus kunci"?
  • Apakah Anda ingin mengunci data yang dienkripsi ke konfigurasi sistem, sehingga hanya dapat didekripsi jika sistem kembali ke konfigurasi yang sama?
  • Apakah Anda ingin memerlukan otorisasi untuk mendekripsi?
  • Mungkin Anda tidak perlu mengenkripsi sama sekali, melainkan menyimpan data di dalam TPM?
  • Jika Anda menyimpan data dalam TPM, apakah Anda ingin memerlukan otorisasi, atau agar sistem berada dalam konfigurasi tertentu, untuk pengambilan?

Masing-masing kasus penggunaan ini (dan masih banyak lagi) - atau kombinasinya - menyajikan jalur implementasi yang berbeda. Pikirkan TPM sebagai pisau Swiss Army untuk perangkat kriptografi: tidak banyak yang tidak dapat Anda lakukan dengannya, tetapi kemudahan penggunaan mempengaruhi keserbagunaan itu. Pertanyaannya terus berpindah antara mengenkripsi, menandatangani, dan mengunci konfigurasi sistem, tetapi bagian utama dari jawaban ini akan mempertimbangkan perintah Seal untuk memenuhi sebagian besar kebutuhan yang dijelaskan dalam pertanyaan.

Sekarang saatnya untuk mengambil satu langkah lebih jauh. Saya ingin mengenkripsi beberapa data (misalnya kunci master enkripsi hard drive) yang hanya dapat didekripsi oleh TPM lokal.

Untuk inilah perintah Bind (digantikan oleh perintah Buat untuk TPM 2). Anda memuat kunci yang berasal dari kunci terikat TPM dan mengenkripsinya (atau langsung dengan kunci terikat perangkat keras). Dengan cara ini data hanya dapat didekripsi dengan akses ke TPM yang sama.

Dengan kata lain, saya ingin mengganti Qualcomm Trusted Execution Environment (TEE) dalam diagram blok di bawah ini untuk Android, dengan TPM di Windows:

Tidak yakin apakah mereplikasi seluruh proses ini adalah ide yang bagus. Pertama, tidak perlu menggunakan operasi penandatanganan di mana pun dalam proses. Tampaknya, pada saat Android 5 sedang dikembangkan, file API Keystore terbatas pada operasi penandatanganan dan verifikasi. Dugaan terbaik saya adalah bahwa tim enkripsi disk melakukan yang terbaik untuk bekerja dengan apa yang mereka miliki dan merancang algoritme di mana salah satu kunci perantara diturunkan dengan operasi penandatanganan , menggunakan kunci TEE yang tersimpan, sehingga mengikat seluruh proses ke perangkat keras- kunci terikat hanya tersedia di platform - karena penandatanganan adalah satu-satunya cara untuk melakukannya pada saat itu. Namun, Anda tidak perlu membatasi diri dengan cara seperti itu jika memiliki akses ke TPM, yang memberi Anda kemampuan lebih dari yang Anda tahu dibutuhkan!

Saya menyadari bahwa TPM tidak melakukan penandatanganan data

Ini salah, kedua versi penandatanganan dukungan TPM.

(atau jika ya, itu tidak menjamin bahwa menandatangani data yang sama akan memberikan keluaran biner yang sama setiap saat)

Ini tidak masuk akal. Penandatanganan data yang sama dengan kunci yang sama akan menghasilkan tanda tangan yang sama. Anda mungkin mengacaukan operasi penandatanganan dengan operasi kutipan, yang akan mencampurkan nonce.

Itulah mengapa saya bersedia mengganti "penandatanganan RSA" dengan "mengenkripsi blob 256-bit dengan kunci terikat perangkat keras".

Ini sebenarnya harus menjadi opsi yang disukai, meskipun keduanya dimungkinkan dengan TPM. Lihat di atas.

Masalahnya adalah bahwa pemrograman TPM sama sekali tidak terdokumentasi di MSDN. Tidak ada API yang tersedia untuk melakukan operasi apa pun.

Sayangnya tidak banyak yang bisa didokumentasikan. Win API terbatas pada beberapa fungsi TBS yang satu levelnya dihapus dari driver.

Alih-alih Anda harus menemukan sendiri salinan Tumpukan Perangkat Lunak Grup Komputasi Tepercaya (alias TSS), cari tahu perintah apa yang akan dikirim ke TPM, dengan muatan, dalam urutan apa, dan panggil fungsi Tbsip_Submit_Command Window untuk mengirimkan perintah secara langsung:

Sebenarnya, tidak, jika Anda memiliki TSS, Anda tidak perlu menggunakannya Tbsip_submit_Command() . Itulah inti dari memiliki TSS - detail tingkat rendah disarikan.

Windows tidak memiliki API tingkat yang lebih tinggi untuk melakukan tindakan.

Masih berlaku untuk TPM 1, tetapi untuk TPM 2 ada TSS.MSR .

Ini setara moral mencoba membuat file teks dengan mengeluarkan perintah SATA I / O ke hard drive Anda.

Benar.

Mengapa tidak menggunakan Celana Panjang ... Masalah dengan kode itu adalah bahwa itu tidak portabel ke dunia Windows. Misalnya, Anda tidak dapat menggunakannya dari Delphi, Anda tidak dapat menggunakannya dari C #. Ini membutuhkan: OpenSSL, pThread

Tidak jelas apakah ini adalah tantangan yang tidak dapat diatasi. Mengakses TrouSerS melalui interop sebaiknya lebih disukai daripada menulis ulang semua kode penataan data. Juga, ada doTSSsaat menulis pertanyaan.

Apa kode yang setara untuk mengenkripsi data menggunakan TPM? Ini mungkin melibatkan perintah TPM_seal. Meskipun saya pikir saya tidak ingin menyegel data, saya rasa saya ingin mengikatnya:

Pertanyaan tersebut berisi kutipan yang menjelaskan perbedaan antara kedua perintah tersebut, jadi seharusnya tidak ada banyak kebingungan. Penyegelan mirip dengan pengikatan, dengan tambahan kendala bahwa status sistem harus sama agar data dapat dibuka.

Dengan cara yang sama saya dapat memberikan:

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

dapatkah seseorang memberikan padanan yang sesuai:

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

yang melakukan hal yang sama, kecuali kunci yang dikunci di Sistem LSA, dikunci di TPM?

Pertama, perlu diperhatikan bahwa ada dua versi utama TPM, yang sama sekali tidak kompatibel satu sama lain. Jadi hampir tidak ada kode yang Anda tulis untuk TPM 1 yang akan berfungsi untuk TPM 2. API TBS adalah satu-satunya kode umum di antara keduanya dan, agar adil bagi Microsoft, ini mungkin salah satu alasan mengapa API tersebut tidak pernah berkembang. Bagian utama dari jawaban akan menampilkan kode untuk TPM 1 karena dua alasan:

  • Pertanyaan tersebut sarat dengan konsep khusus TPM 1, sehingga orang yang menggunakan TPM 1 lebih cenderung ke sini untuk mencari mereka
  • Ada implementasi Microsoft TSS untuk TPM 2.

Kedua, mari kita buat pertanyaannya lebih spesifik. Saya menafsirkannya kembali sebagai berikut:

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?

Perintah Seal paling cocok untuk ini, karena menjalankan fungsi yang sama dengan perintah Bind ketika ukuran pilihan PCR diatur ke nol, tetapi pilihan PCR dapat dengan mudah diubah untuk menyertakan PCR yang Anda inginkan. Itu membuat orang bertanya-tanya mengapa perintah Bind dimasukkan dalam spesifikasi sama sekali, dan seperti yang dicatat itu telah dihapus dalam spesifikasi TPM 2 dan keduanya digabungkan dalam satu perintah Buat.

Berikut adalah kode C # untuk menggunakan perintah TPM 1.2 Seal untuk mengenkripsi data hanya dengan fungsi TBS (catatan: kode ini belum teruji dan tidak mungkin bekerja tanpa debugging) :

[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;

}

Analisis Kode:

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

Ini adalah beberapa dari sedikit fungsi yang tersedia di Tbs.h dan satu-satunya yang akan kami gunakan di sini. Mereka pada dasarnya memungkinkan Anda untuk membuka pegangan ke perangkat dan berkomunikasi dengannya dengan mengirim dan menerima byte mentah.

    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 adalah big endian, Windows adalah little endian. Jadi urutan byte harus dibalik untuk setiap data yang kami kirim. Kita hanya perlu mengkhawatirkan pembalikan unsigned int 32-bit dan 16-bit di sini.

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

Di sini kita menggunakan Tbsi_Context_Create () untuk membuka pegangan untuk berbicara dengan TPM. The TBS_CONTEXT_PARAMSparameter hanya C struct dengan satu unsigned 32-bit int bidang yang harus di set ke 1 untuk berbicara dengan TPM 1.2 misalnya, dan bahwa apa yang kita set ke.

    byte[] cmdBuf = new byte[768];

Ini ditentukan sebagai ukuran buffer minimum dalam Spesifikasi Klien PC TPM . Ini akan lebih dari cukup untuk kebutuhan kita di sini.

TPM 1.2 Spec Part 3 mengatakan hal berikut:

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.

Kita perlu mengenkripsi XOR parameter "rahasia" ini menggunakan nonce yang dihasilkan selama sesi OSAP. Salah satu pegangan masukan perintah Seal juga merupakan pegangan OSAP:

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

Jadi kita perlu membuat sesi OSAP ini terlebih dahulu. OSAP dijelaskan di mana mencampurkan data otorisasi objek dengan nonce yang dihasilkan di setiap sisi untuk mencegah serangan balasan. Oleh karena itu, "rahasia bersama" hanya diketahui oleh dua pihak dalam sesi ini: pihak yang memulai sesi (pengguna) dan pihak yang menerimanya (TPM); juga, kedua belah pihak harus memiliki data otorisasi objek yang sama agar "rahasia bersama" menjadi sama; selain itu, "rahasia bersama" yang digunakan dalam satu sesi akan menjadi tidak valid di sesi lain. Diagram dari spesifikasi ini menjelaskan prosesnya: TPM 1.2 Spesifikasi Bagian 1 . OSAP, atau Object-Specific Authorization Protocol, diciptakan untuk menangani kasus penggunaan di mana Anda ingin menggunakan objek TPM yang memerlukan otorisasi beberapa kali, tetapi tidak ingin memberikan otorisasi setiap kali: sesi OSAP digunakan, yang bergantung pada pada konsep "rahasia bersama", yang merupakan HMAC

OSAP

Kami tidak akan menggunakan beberapa sesi dalam kasus khusus ini (pada kenyataannya, parameter itu diabaikan dengan perintah Seal!) Dan kunci yang akan kami gunakan tidak memerlukan otorisasi, tetapi sayangnya kami masih terikat oleh spesifikasi untuk membuat OSAP sidang.

    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;

Operand perintah TPM_OSAP adalah:

Operand TPM_OSAP

Setiap perintah TPM 1.2 ditata seperti ini:

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

Tag adalah nilai dua byte yang menunjukkan apakah yang berikut adalah masukan atau keluaran, dan apakah ada nilai data autentikasi yang mengikuti parameter perintah. Untuk TPM_OSAP, tagnya harus TPM_TAG_RQU_COMMAND (0x00C1) sesuai spesifikasi, yang berarti "perintah tanpa otorisasi".

Ukuran adalah nilai empat byte yang menentukan ukuran perintah dalam byte, termasuk tag dan ukuran itu sendiri. Kami akan menetapkan nilai ini nanti, setelah kami menghitungnya.

Kode perintah adalah nilai empat byte yang server sebagai ID perintah: ini memberi tahu TPM bagaimana menafsirkan perintah lainnya. Kode perintah kami di sini adalah TPM_OSAP (0x0000000B).

Dua hal berikutnya yang harus ditetapkan adalah tipe entitas dan nilai entitas. Karena kami ingin menggunakan kunci yang sudah ada di TPM, kami akan menggunakan jenis entitas "SRK" (0x0004), dan karena kami bekerja dengan asumsi bahwa TPM telah dimiliki, dapat diasumsikan bahwa ia telah SRK dimuat di bawah pegangan permanen 0x40000000 sesuai spesifikasi, jadi kita akan menggunakan nilai pegangan permanen ini untuk nilai entitas kita. (SRK adalah singkatan dari "Storage Root Key" dan merupakan kunci root dari mana sebagian besar kunci milik TPM lainnya berasal)

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

Akhirnya kami menghitung ukuran perintah dan mengaturnya, dan mengirim perintah.

    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);

Data yang seharusnya kami dapatkan kembali dari TPM di TPM_OSAP adalah:

Tanggapan TPM_OSAP

Jadi kami kembali:

  • Pegangan otorisasi untuk digunakan dengan perintah utama kami (Segel)
  • nonceEven: nonce yang dihasilkan oleh TPM untuk digunakan dengan perintah utama
  • nonceEvenOSAP: OSAP nonce yang merupakan counter-nonce ke nonce yang kita buat di pihak kita sebelum mengirim perintah TPM_OSAP. Kedua nonce ini akan digunakan untuk menghasilkan "rahasia bersama".

Kami mengekstrak nilai-nilai itu dan menyimpannya dalam variabel.

    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);

Kemudian kami menghitung "rahasia bersama". Sesuai spesifikasi, nilai yang masuk ke dalam penghitungan adalah dua nonce OSAP (satu dihasilkan oleh pengguna dan satu dihasilkan oleh TPM) dan nilai otorisasi untuk kunci yang ingin kita gunakan - SRK. Sesuai ketentuan, nilai autentikasi SRK adalah "autentikasi terkenal": buffer 20 byte yang dikosongkan. Secara teknis, seseorang dapat mengubah nilai ini ke sesuatu yang lain saat mengambil kepemilikan TPM, tetapi hal ini tidak dilakukan dalam praktiknya, sehingga kami dapat dengan aman mengasumsikan nilai "autentikasi terkenal" adalah baik.

Selanjutnya mari kita lihat apa yang masuk ke dalam perintah TPM_Seal:

TPM_Seal

Sebagian besar parameter ini mudah dibuat, kecuali dua di antaranya: encAuthdan pubAuth. Mari kita lihat satu per satu.

encAuthadalah "AuthData terenkripsi untuk data tersegel." AuthData kami di sini adalah "auth terkenal" dari sebelumnya, tapi ya kami masih harus mengenkripsinya. Karena kami menggunakan sesi OSAP, sesi itu dienkripsi mengikuti ADIP, atau Protokol Penyisipan Data Otorisasi. Dari spesifikasi: "ADIP memungkinkan pembuatan entitas baru dan penyisipan aman dari entitas baru AuthData. Transmisi AuthData baru menggunakan enkripsi dengan kunci berdasarkan rahasia bersama dari sesi OSAP." Selain itu: "Untuk algoritme enkripsi XOR wajib, pembuat membuat kunci enkripsi menggunakan hash SHA-1 dari rahasia bersama OSAP dan nonce sesi. Pembuat XOR mengenkripsi AuthData baru menggunakan kunci enkripsi sebagai pad satu kali dan mengirimkan data terenkripsi ini bersama dengan permintaan pembuatan ke TPM. "

Diagram berikut menjelaskan bagaimana ADIP beroperasi:

ADIP

pubAuthadalah "Intisari sesi otorisasi untuk input dan keyHandle." Bagian 1 dari spesifikasi, di "Deklarasi Parameter untuk Contoh OIAP dan OSAP" menjelaskan cara menafsirkan tabel parameter TPM_Seal di atas: "Kolom HMAC # merinci parameter yang digunakan dalam penghitungan HMAC. Parameter 1S, 2S, dll. Digabungkan dan di-hash ke inParamDigest atau outParamDigest, secara implisit disebut 1H1 dan mungkin 1H2 jika ada dua sesi otorisasi. Untuk sesi pertama, 1H1, 2H1, 3H1, dan 4H1 digabungkan dan HMAC'ed. Untuk sesi kedua, 1H2, 2H2, 3H2, dan 4H2 digabungkan dan HMAC'ed. " Jadi kita harus encAuthmencirikan teks biasa, ukurannya, ukuran info PCR, dari atas dan ordinal TPM_Seal, dan kemudian HMAC dengan dua nonce dan "lanjutkan sesi" boolean menggunakan OSAP "

Menyatukan semuanya dalam diagram:

komputasi pubAuth

Perhatikan bagaimana kami menyetel "ukuran info PCR" ke nol dalam kode ini, karena kami hanya ingin mengenkripsi data tanpa menguncinya ke status sistem. Namun, memberikan struktur "info PCR" jika perlu adalah hal yang sepele.

    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);

Akhirnya kami membuat perintah dan mengirimkannya.

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

Kami menggunakan fungsi Tbsip_Context_Close () untuk menutup pegangan komunikasi kami.

Kami mengembalikan tanggapan apa adanya di sini. Idealnya Anda ingin membalikkan byte lagi dan memvalidasinya dengan menghitung ulang resAuthnilainya untuk mencegah serangan man-in-the-middle.


Yang membingungkan adalah tidak ada perintah Tspi_Data_Bind.

Ini karena Tspi_Data_Bind adalah perintah TSS, bukan perintah TPM. Alasannya karena tidak memerlukan rahasia (hanya public key yang digunakan) sehingga bisa dilakukan tanpa melibatkan TPM. Hal ini menyebabkan kebingungan, dan bahkan perintah yang tidak memerlukan rahasia sekarang disertakan dalam spesifikasi TPM 2.

Bagaimana cara mengenkripsi kunci dengan kunci publik TPM?

Tergantung pada versi TPM. Dengan perintah TPM_CreateWrapKey untuk TPM 1.2. Dengan perintah TPM2_Create untuk TPM 2.

Bagaimana cara pengembang mengunci kunci TPM?

Buatlah itu di TPM, atau bungkus, atau gunakan metode lain yang tersedia.

TPM2_Create, dengan menentukan kunci HMAC

Teks di buku itu membingungkan. Anda tidak menentukan kunci HMAC , Anda menentukan bahwa Anda menginginkan kunci HMAC .

Fakta bahwa kunci HMAC bukanlah rahasia masuk akal

Tidak, itu tidak masuk akal. Kuncinya adalah rahasia.

... gunakan kunci sambil menyimpannya dengan aman di perangkat keras ... Luar biasa! Bagaimana Anda melakukannya!?

Ada perintah untuk membuat kunci atau mengimpornya untuk kedua versi TPM. Untuk TPM 1 hanya ada satu kunci root - SRK - tempat Anda dapat membuat hierarki kunci dengan membuat kunci yang digabungkan. Dengan TPM 2 Anda dapat memiliki beberapa kunci utama, atau akar,.

Apakah TPM memiliki kemampuan untuk membuat kunci kriptografi dan melindungi rahasianya dalam batasan perangkat keras? Bagaimana caranya?

Lihat di atas.

Luar biasa! Inilah kasus penggunaan yang kebetulan saya inginkan. Ini juga merupakan kasus penggunaan TPM yang digunakan Microsoft. Bagaimana saya melakukannya!?

Mungkin tergantung pada jenis drive. Dalam kasus drive non-SED, kunci enkripsi drive mungkin dibungkus dengan kunci TPM. Dalam kasus drive SED, kata sandi Admin1 (atau semacamnya) disegel dengan TPM.

Kunci Pengesahan atau EK ... Di suatu tempat di dalam TPM terdapat kunci pribadi RSA. Kunci itu terkunci di sana - tidak akan pernah terlihat oleh dunia luar. Saya ingin TPM menandatangani sesuatu dengan kunci pribadinya (yaitu mengenkripsinya dengan kunci pribadinya).

EK bukanlah kunci penandatanganan - ini adalah kunci enkripsi. Namun, ini bukan kunci enkripsi untuk tujuan umum: ini hanya dapat digunakan dalam konteks tertentu .

Tetapi hal yang benar-benar ingin saya lakukan adalah "menyegel" beberapa data

Lihat di atas.

mnistic
sumber
2

Saat tertulis

menentukan kunci HMAC

ini TIDAK berarti memberikan kunci HMAC - ini berarti "menunjuk ke kunci HMAC yang ingin Anda gunakan" .

TPM dapat menggunakan kunci HMAC dalam jumlah yang hampir tidak terbatas, seperti yang ditunjukkan di buku ini. Anda harus memberi tahu TPM mana yang akan digunakan.

DCC
sumber
Jadi, apakah mungkin ada contoh kode yang menunjukkan cara menentukan (menunjuk ke) kunci HMAC untuk digunakan dalam C # atau bahasa lain?
Chad