Як зашифрувати байти за допомогою TPM (Trusted Platform Module)


110

Як я можу зашифрувати байти за допомогою модуля TPM машини?

CryptProtectData

Windows надає (відносно) простий API для шифрування крапки за допомогою CryptProtectDataAPI, який ми можемо перетворити на просту у використанні функцію:

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

Деталі ProtectBytesменш важливі, ніж ідея, що ви можете використовувати її досить легко:

  • ось байти, які я хочу зашифрувати секретним ключем, що знаходиться в System
  • поверніть мені зашифровані краплі

Повернута крапка - це незадокументована структура документації, яка містить усе необхідне для дешифрування та повернення вихідних даних (хеш-алгоритм, алгоритм шифрування, сіль, підпис HMAC тощо).

Для повноти, ось приклад реалізації псевдокоду, ProtectBytesякий використовує Crypt APIдля захисту байтів:

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

Як зробити те ж саме з ТПМ?

Вищевказаний код корисний для шифрування даних лише для локальної машини. Дані шифруються за допомогою Systemоблікового запису як генератора ключів ( деталі, хоча вони цікаві, є неважливими ). Кінцевим результатом є те, що я можу зашифрувати дані (наприклад, головний ключ шифрування жорсткого диска), які можна розшифрувати лише на локальній машині.

Тепер саме час зробити цей крок далі. Я хочу зашифрувати деякі дані (наприклад, головний ключ шифрування жорсткого диска), які можна розшифрувати лише локальним TPM. Іншими словами, я хочу замінити середовище довіреного виконання Qualcomm ( TEE ) на блок-схемі нижче для Android на TPM в Windows:

введіть тут опис зображення

Примітка . Я розумію, що TPM не робить підписування даних (або якщо це відбувається, це не гарантує, що підпис одних і тих же даних буде давати один і той же двійковий вихід кожного разу). Ось чому я був би готовий замінити "підписання RSA" на "шифрування 256-бітної крапки з апаратним ключем" .

То де код?

Проблема полягає в тому, що програмування TPM повністю недокументовано на MSDN . Немає API для виконання будь-яких операцій. Натомість вам доведеться знайти копію програмного забезпечення стеку довірених обчислювальних груп (він же TSS) , розібратися, які команди надсилати до TPM, з корисними навантаженнями, в якому порядку і викликати функцію Tbsip_Submit_Command Window для подачі команд безпосередньо:

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 немає API вищого рівня для виконання дій.

Це моральний еквівалент спроби створити текстовий файл, видавши команди SATA вводу / виводу на ваш жорсткий диск .

Чому б не просто використовувати брюки

Trusted Computing Group (TCG) визначила свій власний API: TCB Software Stack (TSS) . Реалізація цього API була створена деякими людьми і називається TrouSerS . Потім хлопець переніс цей проект у Windows .

Проблема з цим кодом полягає в тому, що він не переноситься у світ Windows. Наприклад, ви не можете використовувати його в Delphi, ви не можете використовувати його з C #. Він вимагає:

  • OpenSSL
  • pThread

Я просто хочу, щоб код зашифрував щось за допомогою мого TPM.

Вищезазначене не CryptProtectDataвимагає нічого, крім того, що є у функціональному тілі.

Який еквівалентний код для шифрування даних за допомогою TPM? Як зазначали інші, вам, мабуть, доведеться ознайомитися з трьома посібниками щодо TPM та створити краплі самостійно . Це, ймовірно, передбачає TPM_sealкоманду. Хоча я думаю, що я не хочу скріплювати дані, я думаю, що хочу прив'язати їх:

Прив'язка - шифрує дані за допомогою ключа зв’язування TPM, унікального ключа RSA, що походить із ключа зберігання. Герметизація - шифрує дані аналогічно прив'язці, але додатково визначає стан, у якому TPM повинен бути, щоб дані були розшифровані (незапечатані)

Я намагаюся прочитати три необхідні томи, щоб знайти 20 потрібних рядків коду:

Але я поняття не маю, що я читаю. Якби був якийсь підручник чи приклади, я могла б зняти. Але я повністю загублений.

Тому ми просимо Stackoverflow

Таким же чином я міг надати:

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

може хтось надати відповідний еквівалент:

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

що робить те ж саме, за винятком того, як ключ, заблокований у SystemLSA, заблокований у TPM?

Початок досліджень

Я не знаю точно, що означає прив'язка . Але дивлячись на TPM Main - Частина 3 Команди - Версія 1.2 специфікації, є згадка про прив'язку :

10.3 TPM_UnBind

TPM_UnBind приймає блок даних, який є результатом команди Tspi_Data_Bind, і розшифровує його для експорту до Користувача. Абонент повинен дозволити використання ключа, який розшифрує вхідну крапку. TPM_UnBind працює на основі блоку за блоком і не має поняття про якесь відношення між одним блоком і іншим.

Що толку там немає ні Tspi_Data_Bindкоманди.

Дослідження зусиль

Жахливо, як ніхто ніколи не намагався документувати TPM або його роботу. Так, ніби вони витратили весь свій час, придумуючи цю класну справу, з якою грати, але не хотіли мати справу з болісним кроком зробити її корисною для чогось.

Починаючи з (зараз) безкоштовної книги Практичний посібник з TPM 2.0: Використання модуля довіреної платформи в новій ері безпеки :

Глава 3 - Швидкий посібник з TPM 2.0

TPM має доступ до власного згенерованого приватного ключа, тому він може шифрувати ключі відкритим ключем, а потім зберігати отриману крапку на жорсткому диску. Таким чином, TPM може зберігати практично необмежену кількість ключів, доступних для використання, але не витрачати цінні внутрішні сховища. Клавіші, що зберігаються на жорсткому диску, можна стерти, але їх також можна створити резервну копію, що дизайнерам здавалося прийнятним компромісом.

Як можна зашифрувати ключ відкритим ключем TPM?

Глава 4 - Існуючі програми, що використовують TPM

Програми, які повинні використовувати TPM, але не робити

За останні кілька років кількість веб-додатків зросла. Серед них - веб-резервне копіювання та зберігання даних. Зараз велика кількість компаній пропонує такі послуги, але, наскільки нам відомо, жоден із клієнтів цих послуг не дозволяє користувачеві заблокувати ключ для послуги резервного копіювання до TPM. Якби це було зроблено, було б, звичайно, добре, якби ключ TPM був створений резервним копієм, копіюючи його на декількох машинах. Це представляється можливістю для розробників.

Як розробник блокує ключ до TPM?

Глава 9 - Іерархії

ВИКОРИСТОВУЙТЕ СПРАВИ: ЗБЕРІГАННЯ ПАРОЛЬІВ ВХОДУ

Типовий файл паролів зберігає солоні хеші паролів. Перевірка полягає у засолюванні та хешировании наданого пароля та порівнянні його із збереженим значенням. Оскільки обчислення не включає секрет, він піддається атаці в автономному режимі на файл паролів.

У цьому випадку використання використовується ключ HMAC, згенерований TPM. У файлі паролів зберігається HMAC солоного пароля. Перевірка полягає у засолюванні та HMACing наданого пароля та порівнянні його із збереженим значенням. Оскільки в автономному зловмиснику немає ключа HMAC, зловмисник не може встановити атаку, виконавши обчислення.

Це могло б спрацювати. Якщо TPM має секретний ключ HMAC, і тільки мій TPM знає ключ HMAC, я можу замінити "Знак (він також шифрує TPM його приватним ключем)" на "HMAC". Але потім у наступному рядку він повністю перевертається:

TPM2_Create, вказавши ключ HMAC

Це не секрет TPM, якщо мені потрібно вказати ключ HMAC. Той факт, що ключ HMAC не є секретним, має сенс, коли ви розумієте, що це розділ про криптографічні утиліти, які надає TPM. Замість того, щоб писати SHA2, AES, HMAC або RSA самостійно, ви можете повторно використовувати те, що TPM вже має прокладку.

Розділ 10 - Ключі

У якості пристрою безпеки можливість додатка використовувати ключі, зберігаючи їх у безпеці на апаратному пристрої, є найбільшою силою TPM. TPM може створювати та імпортувати ключі, створені зовні. Він підтримує як асиметричні, так і симетричні ключі.

Відмінно! Як ти це робиш!?

Генератор ключів

Можливо, найбільшою силою TPM є його здатність генерувати криптографічний ключ та захищати свою таємницю в межах апаратного кордону. Генератор ключів заснований на власному генераторі випадкових чисел TPM і не покладається на зовнішні джерела випадковості. Таким чином, це усуває недоліки на основі слабкого програмного забезпечення з недостатнім джерелом ентропії.

Чи має TPM можливість генерувати криптографічні ключі та захищати її секрети в межах апаратного кордону? Так, як?

Глава 12 - Регістри конфігурації платформи

ПЛР для авторизації

ВИКОРИСТОВУЙТЕ СЛУЧАЙ: ЗАПРОШЕННЯ КЛЮЧУ ДЛЯ ДЕРЖАВНОГО ПЛАТФОРМУ

Програми шифрування повного диска набагато безпечніші, якщо TPM захищає ключ шифрування, ніж якщо він зберігається на тому ж диску, захищений лише паролем. По-перше, апаратне забезпечення TPM має захист від удару від удару (див. Розділ 8 для детального опису захисту від нападу словника TPM), що робить грубу силу атаки паролем непрактичною. Ключ, захищений лише програмним забезпеченням, значно вразливіший до слабкого пароля. По-друге, вкрасти програмний ключ, збережений на диску, набагато простіше. Візьміть диск (або резервну копію диска), і ви отримаєте ключ. Коли TPM утримує ключ, повинна бути вкрадена вся платформа, або принаймні диск та материнська плата.

Ущільнення дозволяє захищати ключ не тільки паролем, але й політикою. Типова політика блокує ключ до значень ПЛР (стан програмного забезпечення), поточних під час герметизації. Це передбачає, що стан при першому завантаженні не порушений. Будь-яке попередньо встановлене зловмисне програмне забезпечення, яке присутнє при першому завантаженні, буде вимірюватися в PCR, і таким чином ключ буде запечатаний до стану компрометованого програмного забезпечення. Менш довірливе підприємство може мати стандартне зображення диска та печатку на ПЛР, що представляють це зображення. Ці значення ПЛР будуть перераховані на імовірно більш надійній платформі. Ще більш досконале підприємство буде використовувати TPM2_PolicyAuthorize та надавати декілька квитків, що дозволяють набір надійних значень PCR. Докладний опис авторизації політики та її застосування для вирішення проблеми PCRbrittleness див. У Розділі 14.

Хоча пароль також може захищати ключ, посилення безпеки навіть без пароля ключа TPM. Зловмисник може завантажувати платформу, не надаючи пароль TPMkey, але не зміг увійти в систему без імені користувача та пароля ОС. Безпека ОС захищає дані. Зловмисник може завантажувати альтернативну ОС, скажімо, з живого DVD або USB-накопичувача, а не з жорсткого диска, щоб обійти безпеку входу в ОС. Однак ця інша конфігурація завантаження та програмне забезпечення змінила б значення PCR. Оскільки ці нові ПЛР не відповідатимуть запечатаним значенням, TPM не випустить ключ дешифрування, а жорсткий диск не вдасться розшифрувати.

Відмінно! Це якраз той випадок використання, який я хочу мати. Це також випадок використання, для якого Microsoft використовує TPM. Як це зробити?

Тож я прочитав всю цю книгу, і вона не дала нічого корисного. Що досить вражає, оскільки це 375 сторінок. Вам цікаво, що містила книга - і, озираючись на неї, я поняття не маю.

Тож ми відмовляємося від остаточного керівництва з програмування TPM та звертаємось до певної документації від Microsoft:

З інструментарію крипто-провайдерів платформи Microsoft TPM . У ньому точно зазначено, що я хочу зробити:

Ключ схвалення або ЕК

EK призначений для забезпечення надійного криптографічного ідентифікатора для платформи. Підприємство може підтримувати базу даних ключів схвалення, що належать до TPM всіх ПК на своєму підприємстві, або контролер тканинного центру даних може мати базу даних TPM на всіх лопатках. У Windows ви можете скористатися провайдером NCrypt, описаним у розділі «Платформа Crypto Provider в Windows 8», щоб прочитати публічну частину ЕК.

Десь всередині TPM стоїть приватний ключ RSA. Цей ключ заблокований там - його не побачить зовнішній світ. Я хочу, щоб TPM щось підписав своїм приватним ключем (тобто зашифрував його приватним ключем).

Тому я хочу найпростішої операції, яка може існувати:

введіть тут опис зображення

Зашифруйте щось своїм приватним ключем. Я навіть не (ще) прошу про складніші речі:

  • "герметизація" її на основі стану ПЛР
  • створення ключа і зберігання його в енергонезалежному або енергонезалежній пам'яті
  • створивши симетричний ключ і намагаючись завантажити його в TPM

Я прошу найпростішу операцію, яку може зробити TPM. Чому неможливо отримати будь-яку інформацію про те, як це зробити?

Я можу отримати випадкові дані

Я вважаю, що я піддався гліб, коли я сказав, що підписання RSA - це найголовніше, що може зробити TPM. Саме основне , що довірений платформний модуль може бути запропоновано зробити , це дати мені випадкові байти. Щоб я зрозумів, як це зробити:

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

Фантазія

Я усвідомлюю, що кількість людей, які використовують TPM, дуже низька. Ось чому ніхто в Stackoverflow не має відповіді. Тому я не можу бути занадто жадібним у пошуку рішення моєї загальної проблеми. Але я дійсно хотів би зробити це «запечатати» деякі дані:

введіть тут опис зображення

  • представити TPM деякі дані (наприклад, 32 байти ключового матеріалу)
  • мають TPM шифрувати дані, повертаючи деяку непрозору структуру блобу
  • пізніше попросіть TPM розшифрувати крапку
  • розшифровка буде працювати лише в тому випадку, якщо регістри ПЛР ТПМ такі ж, як і під час шифрування.

Іншими словами:

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

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

Криптографія Next Gen (Cng, aka BCrypt) підтримує TPM

Оригінальний API криптографії в Windows був відомий як Crypto API.

Починаючи з Windows Vista, Crypto API був замінений API Cryptography: Next Generation (внутрішньо відомий як BestCrypt , скорочено BCrypt , не плутати з алгоритмом хешування паролів ).

Windows постачається з двома постачальниками BCrypt :

Постачальник криптовалют платформи не задокументований на MSDN, але він має документацію з веб-сайту Microsoft Research 2012 року:

Набір інструментів крипто-провайдерів платформи TPM

Крипто-провайдер і інструментарій платформи TPM містить зразок коду, утиліти та документацію для використання функцій, пов'язаних з TPM в Windows 8. Описані підсистеми включають платформу крипто-провайдера платформи Crypto-Next-Gen (CNG), підтримувану TPM, та те, як постачальники послуг атестації може використовувати нові функції Windows. Підтримуються як системи на основі TPM1.2, так і TPM2.0.

Створюється враження , що мета Microsoft полягає в поверхню ТРМ функціональність криптографічного з Microsoft Platform Crypto Provider в Cryptography NG API.

Шифрування відкритого ключа за допомогою Microsoft BCrypt

Враховуючи, що:

Шлях уперед може розібратися, як зробити цифрове підписання за допомогою Microsoft Cryptography Next Gen API .

Наступним моїм кроком буде створення коду для шифрування в BCrypt, відкритим ключем RSA, за допомогою стандартного провайдера ( MS_PRIMITIVE_PROVIDER). Наприклад:

  • 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

За допомогою цього коду я можу перейти до використання постачальника TPM ( MS_PLATFORM_CRYPTO_PROVIDER).

22.02.2016: І коли Apple змушена допомагати розшифровувати дані користувачів, з'явився новий інтерес до того, як змусити TPM виконувати найпростіше завдання, для якого він був придуманий - шифрувати щось.

Це приблизно рівноцінно кожному, хто володіє автомобілем, але ніхто не знає, як його запустити. Це може зробити дуже корисні та цікаві речі, якби ми могли лише пройти крок 1 .

Бонусне читання


Для прив'язки (шифрування) немає явної функції, і вона також не потрібна. Ви просто створіть прив'язуючий ключ у TPM і використовуйте його публічну частину для шифрування симетричного ключа шифрування sec із функцією шифрування системи rsa ("RSA / ECB / OAEPWithSHA1AndMGF1Padding") та збережіть її у потрібній структурі ("TcTssConstants.TSS_ENCDATA_BIND"). Для розв’язування (розшифрування) сек ви просто використовуєте функцію відключення TPM і використовуєте sec у будь-якій симетричній функції шифрування, яка вам подобається. У мене є досить стара база коду для цього, яку я робив деякий час тому, можливо, це допомагає: goo.gl/jV1Ouw
evildead

З Вікіпедії, Прив'язка - шифрує дані за допомогою ключа прив’язки TPM, унікального ключа RSA, що походить із ключа зберігання. en.wikipedia.org/wiki/Trusted_Platform_Module Звучить, як ця пара команд (TSpi_Data_Bind / TPM_UnBind) повинна бути достатньою для ваших потреб ...
Alex Mazzariol

1
Я не думаю, що вам доведеться використовувати TPM безпосередньо. Він підтримується за допомогою стандартних API-інтерфейсів CNG / NCryptXXX та "Провайдера криптовалют платформи Microsoft" (для останніх платформ ОС Windows, і якщо апаратне забезпечення в порядку та звичайно включено). Можливо, ви можете подивитися "Набір інструментів крипто-провайдерів платформи TPM тут: research.microsoft.com/en-us/downloads/…, також перевірте це: tiw2013.cse.psu.edu/slides/…
Саймон Мур'є,

CryptProtectData не обов'язково використовує TPM. З іншого боку, якщо ви можете отримати дійсну ручку CNG або CSP для TPM, ви можете використовувати її в крипто-функціях.
Майкл Чурдакіс

1
@ b3nj1 Ні, я не був; ніхто не зміг відповісти на запитання.
Ян Бойд

Відповіді:


7

Буквар

Все, що випливає, - про TPM 1.2. Майте на увазі, що Microsoft потребує TPM 2.0 для всіх майбутніх версій Windows. Покоління 2.0 принципово відрізняється від 1.2

Не існує однолінійного рішення через принципи проектування TPM. Подумайте про ТПМ як про мікроконтролер з обмеженими ресурсами. Основна мета дизайну полягала в тому, щоб бути дешевим і при цьому надійним. Таким чином, TPM був зірваний з усієї логіки, яка не була необхідною для безпечної роботи. Таким чином, TPM працює лише тоді, коли у вас є хоча б якесь більш-менш жирне програмне забезпечення, видаючи безліч команд у правильному порядку. І ці послідовності команд можуть стати дуже складними. Ось чому TCG вказав TSS за допомогою чітко визначеного API. Якщо ви хочете піти Java шляхом, є навіть Java API високого рівня . Мені не відомий подібний проект для C # / .net

Розвиток

У вашому випадку я б запропонував переглянути програмне забезпечення IBM TPM.

В упаковці ви знайдете 3 дуже корисні компоненти:

  • програмний емулятор TPM
  • легкий лімф tpm
  • деякі основні утиліти командного рядка

Вам не обов'язково потрібен програмний емулятор TPM, ви також можете підключитися до HW TPM машини. Однак ви можете перехопити видані команди та переглянути відповіді, дізнавшись, як вони збираються та як вони відповідають специфікації команди.

Високий рівень

Передумови:

  1. TPM активовано
  2. Драйвер TPM завантажений
  3. ви взяли право власності на TPM

Для того, щоб запечатати краплинку, потрібно зробити наступне:

  1. створити ключ
  2. десь зберігати брелок
  3. переконайтеся, що ключ завантажений у TPM
  4. запечатати кашку

Для розблокування вам потрібно:

  1. отримати ключ-блоб
  2. завантажте ключ до TPM
  3. розкрити запечатану краплинку

Ви можете зберігати блокнот у вашій структурі даних, яку використовуєте для зберігання захищених байтів.

Більшість потрібних вам команд TPM є авторизованими. Тому потрібно встановити сеанси авторизації там, де це необхідно. ПІСЛЯ це переважно сеанси OSAP.

Команди TPM

Наразі я не можу запустити версію налагодження, тому не можу надати точну послідовність. Тому вважайте це не упорядкованим списком команд, які вам доведеться використовувати:

  • TPM_OSAP
  • TPM_CreateWrapKey
  • TPM_LoadKey2
  • TPM_Seal

Якщо ви також бажаєте прочитати поточні значення ПЛР:

  • TPM_PCRRead

Майкрософт має свою бібліотеку під керуванням C # .NET для використання TPM . Вони також мають емулятор TPM , до якого керована бібліотека може підключитися як альтернатива налагодження, якщо справжнього TPM немає. Вони також мають інструментарій постачальників платформ TPM , який містить документацію та зразок коду для використання TPM. Тепер, якщо тільки хтось міг зрозуміти, як використовувати TPM для шифрування байтів.
Ян Бойд

Ваші перші два посилання - лише TPM 2.0. Якщо ви хочете скористатися цими, я боюся, що мені нічого не допоможе.
Сколіт

4

Довірені та зашифровані ключі

Довірені та зашифровані ключі - це два нові типи клавіш, додані до існуючої служби зворотного зв’язку ядра. Обидва ці типи є симетричними ключами різної довжини, і в обох випадках усі ключі створюються в ядрі, а простір користувача бачить, зберігає та завантажує лише зашифровані крапки. Довірені ключі вимагають наявності чіпа Trusted Platform Module (TPM) для більшої безпеки, тоді як зашифровані ключі можуть використовуватися в будь-якій системі. Усі краплі на рівні користувача, для зручності відображаються та завантажуються в шістнадцятковий режим, і перевіряються цілісність.

Довірені ключі використовують TPM як для створення, так і для герметизації ключів. Клавіші запечатуються під 2048-бітним ключем RSA в TPM і необов'язково запечатуються до заданих значень PCR (вимірювання цілісності) і не розпечатуються TPM, якщо ПЦР та перевірка цілісності блобу збігаються. Завантажений довірений ключ може бути оновлений новими (майбутніми) значеннями PCR, тому ключі легко мігруються до нових значень ПК, наприклад, коли оновлено ядро ​​та initramfs. Один і той же ключ може мати багато збережених крапок при різних значеннях ПЛР, тому безліч чобіт легко підтримується.

За замовчуванням надійні ключі запечатуються під SRK, який має значення авторизації за замовчуванням (20 нулів). Це може бути встановлено під час takeownership з утилітою штанини в: 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 printповертає шістнадцяткову копію запечатаного ключа, який знаходиться у стандартному форматі TPM_STORED_DATA. Довжина ключів для нових клавіш завжди в байтах. Довірені ключі можуть становити 32 - 128 байт (256 - 1024 біт), верхня межа повинна міститись в межах 2048 бітової SRK (RSA) довжини ключа, з усією необхідною структурою / підкладкою.

Зашифровані ключі не залежать від TPM і швидші, оскільки вони використовують AES для шифрування / дешифрування. Нові ключі створюються з випадкових чисел, що генеруються в ядрі, і шифруються / розшифровуються за допомогою визначеного 'головного' ключа. "Головний" ключ може бути типу довіреного або користувацького ключа. Основним недоліком зашифрованих ключів є те, що якщо вони не вкорінені в надійний ключ, вони є настільки ж безпечними, як і ключ користувача, який їх шифрує. Тому головний користувальницький ключ слід завантажувати якомога безпечнішим способом, бажано на початку завантаження.

Розшифрована частина зашифрованих ключів може містити або простий симетричний ключ, або більш складну структуру. Формат більш складної структури - специфічний для програми, який ідентифікується за допомогою "формату".

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'

Приклади використання надійного та зашифрованого ключа

Створіть і збережіть надійний ключ з назвою "kmk" довжиною 32 байти:

$ 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

Завантажте надійний ключ із збереженої краплі:

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

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

Встановіть надійний ключ під новими значеннями ПК:

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

Початковий споживач надійних ключів - EVM, який під час завантаження потребує якісного симетричного ключа для захисту HMAC метаданих файлів. Використання надійного ключа дає надійні гарантії того, що ключ EVM не був порушений проблемою на рівні користувача та, коли він запечатаний до конкретних значень ПЛР завантаження, захищає від завантажувальних та офлайн-атак. Створіть та збережіть зашифрований ключ "evm" за допомогою вищенаведеного ключа "kmk":

варіант 1: опустити 'формат'

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

варіант 2: явно визначаючи "формат" як "за замовчуванням"

$ 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

Завантажте зашифрований ключ "evm" із збереженого блоку:

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

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

Передбачається інше використання надійних та зашифрованих ключів, наприклад, для шифрування диска та файлів. Зокрема, був визначений новий формат "шифри" для використання зашифрованих ключів для монтажу файлової системи eCryptfs. Більш детальну інформацію про використання можна знайти у файлі "Документація / безпека / ключі-ecryptfs.txt".


Чи маєте ви уявлення, коли додані ці два нові типи ключів? У якій версії я маю на увазі. Наразі я використовую 1.2 (пакети компанії), і цей не підтримує їх. Може, в 1,5+?
Акапулько

1
Яке джерело цієї публікації? Кінець посилається на документDocumentation/security/keys-ecryptfs.tx
goodguys_activate

Це, здається, всі дзвінки до програми командного рядка. Я не бачу коду щодо використання TPM.
Ян Бойд

3

Як я можу зашифрувати байти за допомогою модуля TPM машини?

Залежить від вашого наміру та обставин:

  • Який у вас TPM (1-сімейний або 2-сімейний)?
  • У якому стані знаходиться ТПМ? Це було у власності? Це було передбачено?
  • Яка мова програмування?
  • Ви хочете зашифрувати або підписати? (це невиразно в решті запитання)
  • Наскільки великі дані, які потрібно зашифрувати?
  • Ви хочете використовувати симетричний ключ або асиметричний ключ?
  • Ви хочете використовувати ключ, який вже існує в TPM, або ви хочете, щоб він створив ключ спочатку?
  • Під "шифруванням" ви, мабуть, маєте на увазі "загортати ключ"?
  • Ви хочете заблокувати зашифровані дані до конфігурації системи, щоб їх можна було розшифрувати лише тоді, коли система знову в тій же конфігурації?
  • Ви хочете вимагати авторизації для розшифровки?
  • Можливо, вам взагалі не потрібно шифрувати, а скоріше зберігати дані в TPM?
  • Якщо ви зберігаєте дані всередині TPM, чи потрібно вимагати авторизації або для того, щоб система була в певній конфігурації для пошуку?

Кожен із цих випадків використання (а їх більше) - або їх комбінація - представляє різні шляхи реалізації. Подумайте про TPM як на криптографічних пристроях швейцарської армії: з цим не можна багато чого зробити, але простота використання страждає від цієї універсальності. Питання продовжує підстрибувати між шифруванням, підписанням та блокуванням до конфігурації системи, але основна частина цієї відповіді буде розглядати команду Seal для покриття більшості потреб, описаних у питанні.

Тепер саме час зробити цей крок далі. Я хочу зашифрувати деякі дані (наприклад, головний ключ шифрування жорсткого диска), які можна розшифрувати лише локальним TPM.

Це те, для чого призначена команда Bind (замінена командою Create для TPM 2). Ви завантажуєте ключ, що походить від ключа, пов'язаного з TPM, і шифруєте його (або безпосередньо за допомогою апаратного ключа). Таким чином, дані можна розшифрувати лише з доступом до тієї ж TPM.

Іншими словами, я хочу замінити середовище довіреного виконання Qualcomm (TEE) на блок-схемі нижче для Android на TPM в Windows:

Не впевнений, чи реплікація всього цього процесу є хорошою ідеєю. Для одного немає необхідності використовувати операцію підписання в будь-якому місці процесу. Здавалося б, на час розробки Android 5 API Keystore обмежувався операціями підписання та перевірки. Я найкраще здогадуюсь, що команда шифрування диска зробила все можливе, щоб працювати з тим, що у них було, і розробила алгоритм, за допомогою якого один із проміжних ключів був отриманий під час операції підписання , використовуючи збережений ключ TEE, тим самим прив’язуючи весь процес до апаратних засобів, прив'язаний ключ доступний лише на платформі - оскільки підписання було єдиним способом зробити це на той час. Однак не потрібно обмежувати себе такими способами, якщо матимете доступ до TPM, що дає вам більше можливостей, ніж ви знали, що вам потрібно!

Я розумію, що TPM не робить підписування даних

Це помилково, обидві версії підпису підтримки TPM.

(або якщо це так, це не гарантує, що підписання одних і тих же даних буде давати один і той же двійковий вихід кожного разу)

Це не має сенсу. Підписання ті ж дані з тим же ключем буде виробляти ту ж сигнатуру. Ви можете плутати операцію підписання з операцією котирування, яка буде змішуватися ніколи.

Ось чому я був би готовий замінити "підписання RSA" на "шифрування 256-бітної крапки з апаратним ключем".

Це насправді має бути кращим варіантом, хоча обидва можливі з TPM. Дивись вище.

Проблема полягає в тому, що програмування TPM повністю недокументовано на MSDN. Немає API для виконання будь-яких операцій.

На жаль, документально не багато. API API обмежений парою функцій TBS, які на одному рівні видаляються з драйвера.

Натомість вам доведеться знайти копію програмного забезпечення стеку довірених обчислювальних груп (він же TSS), розібратися, які команди надсилати до TPM, з корисними навантаженнями, в якому порядку і викликати функцію Tbsip_Submit_Command Window для подачі команд безпосередньо:

Насправді, ні, якби у вас був TSS, ви не мали б користуватися Tbsip_submit_Command(). У цьому і полягає вся суть ТСС - деталі низького рівня відлучаються.

У Windows немає API вищого рівня для виконання дій.

Все ще справедливо для TPM 1, але для TPM 2 є TSS.MSR .

Це моральний еквівалент спроби створити текстовий файл, видавши команди SATA I / O на жорсткий диск.

Правильно.

Чому б не просто використовувати брюки ... Проблема з цим кодом полягає в тому, що він не переноситься у світ Windows. Наприклад, ви не можете використовувати його в Delphi, ви не можете використовувати його з C #. Він вимагає: OpenSSL, pThread

Не ясно, що це непереборний виклик. Доступ до TrouSerS через інтероп повинен бути кращим перед перезаписом всього коду структурування даних. Також був doTSSна момент написання питання.

Який еквівалентний код для шифрування даних за допомогою TPM? Ймовірно, це включає команду TPM_seal. Хоча я думаю, що я не хочу скріплювати дані, я думаю, що хочу прив'язати їх:

Питання містить цитату, що описує різницю між двома командами, тому не повинно бути великої плутанини. Герметизація схожа на прив'язку, з додатковим обмеженням, що стан системи повинен бути однаковим, щоб дані, які не повинні бути запечатані.

Таким же чином я міг надати:

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

може хтось надати відповідний еквівалент:

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

що робить те саме, за винятком ключа, заблокованого в System LSA, у блоці TPM?

По-перше, варто зазначити, що є дві основні версії TPM, які абсолютно несумісні між собою. Тому практично жоден код, який ви, можливо, написали для TPM 1, не працюватиме для TPM 2. API TBS є єдиним загальним кодом між цими двома, і, справедливо до Microsoft, це, можливо, було однією з причин того, що цей API ніколи не зростав. Основна частина відповіді представляє код для TPM 1 з двох причин:

  • Питання завантажене конкретними концепціями TPM 1, тому люди, які використовують TPM 1, швидше прибувають сюди, шукаючи їх
  • Існує впровадження Microsoft TSS для TPM 2.

По-друге, давайте зробимо питання більш конкретним. Я переосмислюю це так:

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?

Для цього найкраще підходить команда Seal, оскільки вона виконує ту ж функцію, що і команда Bind, коли для вибору розміру PCR встановлено нуль, але вибір PCR можна легко змінити, щоб включити будь-які PCR, які ви хочете. Це не дивується, чому команда Bind взагалі була включена в специфікацію, і як зазначалося, вона була видалена в специфікації TPM 2, і дві були об'єднані в одну команду Create.

Ось код C # для використання команди TPM 1.2 Seal для шифрування даних лише за допомогою функцій TBS (зверніть увагу: цей код не перевірений і, ймовірно, не працює без налагодження) :

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

}

Аналіз коду:

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

Це деякі з декількох функцій, доступних у Tbs.h, і єдині, які ми тут будемо використовувати. Вони в основному дозволяють відкрити ручку пристрою та спілкуватися з ним, надсилаючи та отримуючи необроблені байти.

    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 - великий ендіанець, Windows - мало ендіака. Таким чином, порядок байтів доведеться змінити для будь-яких даних, які ми надсилаємо. Нам потрібно лише турбуватися про повернення тут 32-бітових та 16-бітових неподписаних входів.

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

Тут ми використовуємо Tbsi_Context_Create (), щоб відкрити ручку для розмови з TPM. TBS_CONTEXT_PARAMSПараметр просто C структура з одним беззнаковим 32-бітових полем INT , який повинен бути встановлений в 1 , щоб поговорити з TPM 1.2 , наприклад, і то , що ми встановлюємо його.

    byte[] cmdBuf = new byte[768];

Це вказано як мінімальний розмір буфера в специфікації клієнта TPM ПК . Тут буде більш ніж достатньо для наших потреб.

Частина 3 TPM 1.2 Spec говорить про таке:

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.

Нам потрібно XOR-зашифрувати цей "секретний" параметр, використовуючи нонсенс, сформований під час сеансу OSAP. Однією з вхідних ручок команди Seal є також ручка OSAP:

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

Тому нам потрібно спочатку встановити цей сеанс OSAP. OSAP описаний у розділі 1 специфікації TPM 1.2 . OSAP, або протокол авторизації для конкретного об'єкта, був винайдений для обробки випадку використання, коли ви хочете використовувати об'єкт TPM, який вимагає авторизації кілька разів, але не бажаєте кожного разу надати авторизацію: замість цього використовується сеанс OSAP, який покладається про поняття "загальної таємниці", що є ВМАКякий змішує дані авторизації об'єкта з безглуздими згенерованими з кожної сторони для запобігання атак відповідей. Тому "спільний секрет" відомий лише двом сторонам у цьому сеансі: стороні, яка ініціювала сеанс (користувач), і стороні, яка прийняла його (TPM); також обидві сторони повинні мати однакові дані авторизації об'єкта, щоб "загальна таємниця" була однаковою; крім того, "загальна таємниця", яка використовується в одному сеансі, буде недійсною в іншому. Ця діаграма з специфікації описує процес:

OSAP

У цьому конкретному випадку ми не будемо використовувати декілька сеансів (насправді цей параметр ігнорується командою Seal!), І ключ, який ми будемо використовувати, не потребує авторизації, але, на жаль, ми все ще зобов'язані специфікації для встановлення OSAP сесія.

    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;

TPM_OSAP командними операндами є:

Операнди TPM_OSAP

Кожна команда TPM 1.2 викладається так:

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

Тег - це двобайтне значення, яке вказує, чи є наступним або вхід, або вихід, і чи є якісь автентичні значення даних за параметрами команди. Для TPM_OSAP тег повинен бути TPM_TAG_RQU_COMMAND (0x00C1) відповідно до специфікації, що означає "команда без авторизації".

Розмір - це чотирибайтове значення, яке визначає розмір команди в байтах, включаючи тег і сам розмір. Ми встановимо це значення пізніше, як тільки обчислимо його.

Код команди - це чотирибайтне значення, яке сервериє як ідентифікатор команди: він повідомляє TPM, як інтерпретувати решту команди. Наш командний код тут TPM_OSAP (0x0000000B).

Наступними двома параметрами є тип сутності та значення сутності. Оскільки ми хочемо використовувати ключ, який вже існує в TPM, ми будемо використовувати сутність типу "SRK" (0x0004), і оскільки ми працюємо за припущенням, що TPM вже належить, можна з впевненістю припустити, що він має SRK завантажується під постійною ручкою 0x40000000 відповідно до специфікації, тому ми будемо використовувати це значення постійної ручки для нашого значення сутності. (SRK розшифровується як "Ключ кореневого зберігання" і є кореневим ключем, з якого походить більшість інших ключів, що належать TPM)

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

Нарешті ми обчислимо розмір команди та встановимо її та відправляємо команду.

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

Дані, які ми маємо отримати від TPM на TPM_OSAP:

Відповідь TPM_OSAP

Отже, ми повертаємось:

  • Ручка авторизації для використання з нашою основною командою (Seal)
  • nonceEven: ніде, згенерований TPM для використання з основною командою
  • nonceEvenOSAP: безвідмовно, що це OSAP, це протилежне значенню, яке ми створили на нашій стороні перед відправленням команди TPM_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);

Потім обчислюємо "загальну таємницю". Відповідно до специфікації, значеннями, що надходять у обчислення, є два знаки OSAP (один, згенерований користувачем, а другий згенерований TPM) та значення авторизації для ключа, який ми хочемо використовувати - SRK. За умовою, значення автентичності SRK є "добре відомим auth": нульовим вихідним 20-байтовим буфером. Технічно можна було б змінити це значення на щось інше, коли взяти право власності на TPM, але це не робиться на практиці, тому ми можемо сміливо вважати, що значення "добре відомого автентичного" є хорошим.

Далі розглянемо, що входить до команди TPM_Seal:

TPM_Seal

Більшість із цих параметрів побудова тривіально, за винятком двох з них: encAuthі pubAuth. Давайте розглянемо їх по черзі.

encAuth"Зашифрована AuthData для запечатаних даних." Наш AuthData - це «добре відомий автор» відтоді, але так, ми все ще повинні його зашифрувати. Оскільки ми використовуємо сеанс OSAP, він шифрується після ADIP або протоколу введення авторизації даних. З специфікації: "ADIP дозволяє створювати нові об'єкти та безпечне вставлення нової сутності AuthData. При передачі нового AuthData використовується шифрування з ключем на основі спільного секрету сеансу OSAP." Додатково: "Для обов'язкового алгоритму шифрування XOR творець створює ключ шифрування, використовуючи хеш SHA-1 спільної таємниці OSAP та без сеансу. Творець XOR шифрує новий AuthData, використовуючи ключ шифрування як одноразову клавіатуру та надсилає ці зашифровані дані разом із запитом на створення до TPM. "

Наступна схема пояснює, як діє ADIP:

АДІП

pubAuthє "Дайджест сеансу авторизації для входів та keyHandle." Частина 1 специфікації, в "Деклараціях параметрів для прикладів OIAP та OSAP", пояснює, як інтерпретувати таблицю параметрів TPM_Seal вище: "У стовпці HMAC # наведено параметри, які використовуються в обчисленні HMAC. Параметри 1S, 2S і т.д. об'єднуються і хешируется на inParamDigest або outParamDigest, неявно називається 1H1 і, можливо, 1H2, якщо є два сеанси авторизації. Для першого сеансу 1H1, 2H1, 3H1 і 4H1 є об'єднаними і HMAC'ed. Для другого сеансу 1H2, 2H2, 3H2, і 4H2 є з'єднаними і HMAC'ed ". Таким чином, нам доведеться хеш простого тексту, його розміру, розміру інформації про ПЛР, encAuthзверху та порядкового порядка TPM_Seal, а потім HMAC, що з двома думками та булевим "продовження сеансу" за допомогою OSAP "

Склавши все це у діаграмі:

обчислення pubAuth

Зверніть увагу, як у цьому коді ми встановлюємо «розмір інформації про ПЛР» до нуля, оскільки ми просто хочемо зашифрувати дані, не замикаючи їх у системному стані. Однак, якщо це потрібно, банально надати структуру "інформація про ПЛР".

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

Нарешті ми конструюємо команду і відправляємо її.

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

Ми використовуємо функцію Tbsip_Context_Close () для закриття нашої ручки зв'язку.

Ми повертаємо відповідь як є тут. В ідеалі ви хочете знову змінити байти і перевірити його, перерахувавши resAuthзначення, щоб запобігти атакам "людина-в-середині".


Що бентежить, що немає команди Tspi_Data_Bind.

Це тому, що Tspi_Data_Bind - це команда TSS, а не команда TPM. Причина тому, що вона не вимагає секретів (використовується лише відкритий ключ), тому це можна зробити без залучення TPM. Однак це спричинило плутанину, і навіть команди, які не потребують ніяких секретів, тепер включені до специфікації TPM 2.

Як можна зашифрувати ключ відкритим ключем TPM?

Залежить від версії TPM. За допомогою команди TPM_CreateWrapKey для TPM 1.2. За допомогою команди TPM2_Create для TPM 2.

Як розробник блокує ключ до TPM?

Або створіть його в TPM, або загорніть його, або використовуйте будь-який інший із доступних методів.

TPM2_Create, вказавши ключ HMAC

Текст у книзі заплутаний. Ви не вказуєте ключ HMAC , ви вказуєте, що потрібно ключ HMAC .

Те, що ключ HMAC не є секретним, має сенс

Ні це не має сенсу. Ключ секретний.

... користуйтеся клавішами, зберігаючи їх у безпеці на апаратному пристрої ... Відмінно! Як ти це робиш!?

Існують команди або створити ключі, або імпортувати їх для обох версій TPM. Для TPM 1 був лише один кореневий ключ - SRK - з якого можна було встановити ієрархію ключів, створивши загорнуті ключі. За допомогою TPM 2 ви можете мати кілька первинних або кореневих ключів.

Чи має TPM можливість генерувати криптографічні ключі та захищати її секрети в межах апаратного кордону? Так, як?

Дивись вище.

Відмінно! Це якраз той випадок використання, який я хочу мати. Це також випадок використання, для якого Microsoft використовує TPM. Як це зробити?

Напевно, залежить від типу накопичувача. Що стосується не-SED-дисків, ключ шифрування диска, ймовірно, обгорнутий ключем TPM. У випадку SED-дисків пароль Admin1 (або такий) запечатується TPM.

Ключ схвалення або ЕК ... Десь усередині TPM знаходиться приватний ключ RSA. Цей ключ заблокований там - його не побачить зовнішній світ. Я хочу, щоб TPM щось підписав своїм приватним ключем (тобто зашифрував його приватним ключем).

EK не є ключем підпису - це ключ шифрування. Однак це не ключ шифрування загального призначення: його можна використовувати лише в певних контекстах .

Але я дуже хотів би зробити це - «запечатати» деякі дані

Дивись вище.


2

Коли це говорить

із зазначенням ключа HMAC

це НЕ означає надати ключ HMAC - це означає "вказати на ключ HMAC, який ви хочете використовувати" .

Як зазначено в книзі, TPM можуть використовувати практично необмежену кількість клавіш HMAC. Ви повинні сказати TPM, який саме використовувати.


Так, можливо, є зразок коду, який показує, як вказати (вказати на) ключ HMAC, який буде використовуватися на C # або іншій мові?
Чад
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.