Как зашифровать байты с помощью TPM (Trusted Platform Module)

Как я могу зашифровать байты, используя модуль TPM машины?

CryptProtectData

Windows предоставляет (относительно) простой API для шифрования большого двоичного объекта с использованием CryptProtectData API, который мы можем обернуть простой в использовании функцией:

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

Детали ProtectBytes менее важны, чем идея, что вы можете использовать его довольно легко:

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

Возвращенный BLOB- объект представляет собой недокументированную структуру документации, которая содержит все необходимое для расшифровки и возврата исходных данных (алгоритм хеширования, алгоритм шифрования, соль, сигнатура 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.
}

Как сделать то же самое с TPM?

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

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

Примечание. Я понимаю, что доверенный платформенный модуль не выполняет подписывание данных (или, если это так, не гарантирует, что подписывание одних и тех же данных каждый раз будет давать один и тот же двоичный вывод). Вот почему я хотел бы заменить "подпись RSA" на "шифрование 256-битного двоичного объекта с помощью аппаратного связанного ключа".

Так где же код?

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

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 (TSS). Реализация этого API была создана некоторыми людьми и называется TrouSerS. Затем парень перенес этот проект на Windows.

Проблема этого кода в том, что он не переносим в мир Windows. Например, вы не можете использовать его из Delphi, вы не можете использовать его из C#. Это требует:

  • OpenSSL
  • PTHREAD

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

Выше CryptProtectData не требует ничего, кроме того, что находится в теле функции.

Какой эквивалентный код используется для шифрования данных с помощью TPM? Как уже отмечали другие, вам, вероятно, придется обратиться к трем руководствам TPM и создать двоичные объекты самостоятельно. Это, вероятно, включает в себя TPM_seal команда. Хотя я думаю, что не хочу запечатывать данные, я думаю, что хочу связать их:

Связывание - шифрование данных с использованием ключа связывания TPM, уникального ключа RSA, полученного из ключа хранилища. Запечатывание - шифрование данных аналогично связыванию, но дополнительно указывает состояние, в котором TPM должен находиться в состоянии для дешифрования (незапечатывания) данных.

Я пытаюсь прочитать три необходимых тома, чтобы найти нужные мне 20 строк кода:

Но я понятия не имею, что я читаю. Если бы был какой-то учебник или примеры, я мог бы попробовать. Но я полностью потерян.

Поэтому мы просим Stackru

Таким же образом я смог обеспечить:

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 Main - Команды части 3 - Версия спецификации 1.2, есть упоминание о bind:

10.3 TPM_UnBind

TPM_UnBind берет блок данных, являющийся результатом команды Tspi_Data_Bind, и расшифровывает его для экспорта пользователю. Вызывающий должен разрешить использование ключа, который расшифрует входящий BLOB-объект. TPM_UnBind работает на блочной основе и не имеет представления о каких-либо отношениях между одним блоком и другим.

Что сбивает с толку то нет Tspi_Data_Bind команда.

Исследования усилий

Страшно, как никто никогда не документировал TPM или его работу. Как будто они потратили все свое время, придумывая эту классную вещь, чтобы поиграть, но не хотели иметь дело с болезненным шагом сделать его пригодным для чего-то.

Начиная с (теперь) бесплатной книги Практическое руководство по TPM 2.0: Использование модуля Trusted Platform в эпоху новой безопасности:

Глава 3 - Краткое руководство по TPM 2.0

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

Как я могу зашифровать ключ с помощью открытого ключа TPM?

Глава 4 - Существующие приложения, использующие доверенные платформенные модули

Приложения, которые должны использовать TPM, но не

В последние несколько лет количество веб-приложений увеличилось. Среди них - сетевое резервное копирование и хранение. В настоящее время большое количество компаний предлагают такие услуги, но, насколько нам известно, ни один из клиентов этих служб не позволяет пользователю заблокировать ключ для службы резервного копирования в доверенном платформенном модуле. Если бы это было сделано, было бы неплохо создать резервную копию самого ключа TPM, дублируя его на нескольких машинах. Это представляется возможностью для разработчиков.

Как разработчик блокирует ключ к TPM?

Глава 9 - Heirarchies

ПРИМЕР ИСПОЛЬЗОВАНИЯ: ХРАНЕНИЕ ПАРОЛЕЙ ВХОДА

Типичный файл паролей хранит соленые хэши паролей. Проверка состоит из засолки и хэширования предоставленного пароля и сравнения его с сохраненным значением. Поскольку в расчете нет секрета, он подвергается автономной атаке на файл паролей.

В этом случае используется ключ HMAC, созданный TPM. Файл паролей хранит HMAC соленого пароля. Проверка состоит из засолки и HMAC, предоставления предоставленного пароля и сравнения его с сохраненным значением. Поскольку у автономного злоумышленника нет ключа HMAC, злоумышленник не может смонтировать атаку, выполнив вычисление.

Это может сработать. Если у доверенного платформенного модуля есть секретный ключ HMAC, и только мой доверенный платформенный модуль знает ключ HMAC, то я мог бы заменить "Подписать (или шифровать TPM с помощью своего закрытого ключа)" на "HMAC". Но затем в следующей строке он полностью переворачивает себя:

TPM2_Create, указав ключ HMAC

Это не секрет TPM, если мне нужно указать ключ HMAC. Тот факт, что ключ HMAC не является секретным, имеет смысл, когда вы понимаете, что это глава о криптографических утилитах, которые предоставляет TPM. Вместо того, чтобы писать SHA2, AES, HMAC или RSA самостоятельно, вы можете повторно использовать то, что уже есть в TPM.

Глава 10 - Ключи

Как защитное устройство, способность приложения использовать ключи, сохраняя их в безопасности на аппаратном устройстве, является самой сильной стороной TPM. TPM может генерировать и импортировать внешние ключи. Он поддерживает как асимметричные, так и симметричные ключи.

Отлично! Как ты делаешь это!?

Генератор ключей

Возможно, самая сильная сторона TPM - это его способность генерировать криптографический ключ и защищать его секрет в пределах аппаратного обеспечения. Генератор ключей основан на собственном генераторе случайных чисел TPM и не зависит от внешних источников случайности. Таким образом, он устраняет недостатки, основанные на слабом программном обеспечении с недостаточным источником энтропии.

Имеет ли TPM возможность генерировать криптографические ключи и защищать свои секреты в пределах аппаратного обеспечения? Так как?

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

ПЦР для авторизации

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

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

Запечатывание позволяет защитить ключ не только паролем, но и политикой. Типичная политика блокирует ключ к значениям ПЦР (состоянию программного обеспечения), действующим на момент запечатывания. Это предполагает, что состояние при первой загрузке не скомпрометировано. Любое предустановленное вредоносное ПО, присутствующее при первой загрузке, будет измерено в PCR, и, таким образом, ключ будет закрыт до состояния скомпрометированного программного обеспечения. Менее доверчивое предприятие может иметь стандартный образ диска и печать на ПЦР, представляющих этот образ. Эти значения ПЦР будут предварительно рассчитаны на предположительно более надежной платформе. Еще более изощренное предприятие будет использовать TPM2_PolicyAuthorize и предоставит несколько заявок, авторизующих набор доверенных значений PCR. См. Главу 14 для подробного описания политики авторизации и ее применения для решения проблемы хрупкости PCR.

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

Отлично! Это именно тот случай использования, который мне нужен. Это также сценарий использования, для которого Microsoft использует TPM. Как мне это сделать!?

Поэтому я прочитал всю эту книгу, и она не дала ничего полезного. Что весьма впечатляет, потому что это 375 страниц. Вы задаетесь вопросом, что содержала книга - и оглядываясь назад, я понятия не имею.

Поэтому мы отказались от окончательного руководства по программированию TPM и обратимся вместо этого к некоторой документации от Microsoft:

Из набора инструментов для шифрования провайдера платформы Microsoft TPM. Здесь упоминается именно то, что я хочу сделать:

Ключ одобрения или EK

EK разработан для обеспечения надежного криптографического идентификатора для платформы. Предприятие может поддерживать базу данных ключей одобрения, принадлежащих доверенным платформенным модулям всех ПК на своем предприятии, или контроллер фабрики центра обработки данных может иметь базу данных доверенных платформенных модулей во всех блейдах. В Windows вы можете использовать провайдера NCrypt, описанного в разделе "Провайдер криптографии платформы в Windows 8", чтобы прочитать общедоступную часть EK.

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

Итак, я хочу самую базовую операцию, которая может существовать:

Зашифруйте что-нибудь с помощью своего закрытого ключа. Я даже (пока) не прошу более сложные вещи:

  • "запечатывание" его на основе состояния ПЦР
  • создание ключа и сохранение его в энергозависимой или энергонезависимой памяти
  • создать симметричный ключ и попытаться загрузить его в TPM

Я прошу самую основную операцию, которую может выполнить TPM. Почему невозможно получить какую-либо информацию о том, как это сделать?

Я могу получить случайные данные

Я полагаю, что я был беспечен, когда сказал, что подписание RSA - это самое основное, что может сделать TPM. Самое основное, что может попросить 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, очень мало. Вот почему никто на Stackru не имеет ответа. Поэтому я не могу быть слишком жадным в решении моей общей проблемы. Но я бы действительно хотел "запечатать" некоторые данные:

  • представить TPM некоторые данные (например, 32 байта ключевого материала)
  • TPM шифрует данные, возвращая некоторую непрозрачную структуру BLOB-объектов
  • позже попросите TPM расшифровать блоб
  • дешифрование будет работать только в том случае, если регистры PCR TPM такие же, как и во время шифрования.

Другими словами:

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

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

Криптография Next Gen (Cng, aka BCrypt) поддерживает TPM

Оригинальный API шифрования в Windows был известен как API шифрования.

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

Windows поставляется с двумя поставщиками BCrypt:

Поставщик Platform Crypto не задокументирован в MSDN, но имеет документацию с сайта Microsoft Research 2012:

Инструментарий криптографического провайдера платформы TPM

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

Похоже, что намерение Microsoft состоит в том, чтобы раскрыть криптографическую функциональность TPM с помощью провайдера криптографии платформы Microsoft 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 F8 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.

Бонус Чтение

3 ответа

грунтовка

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

Нет единого решения из-за принципов дизайна TPM. Думайте о TPM как о микроконтроллере с ограниченными ресурсами. Основной целью дизайна было стать дешевым, но в то же время безопасным. Таким образом, TPM был лишен всей логики, которая не была необходима для безопасной работы. Таким образом, TPM работает только тогда, когда у вас есть хотя бы какое-то более или менее толстое программное обеспечение, и выдает много команд в правильном порядке. И эти последовательности команд могут быть очень сложными. Вот почему TCG указал TSS с четко определенным API. Если вы хотите пойти по пути Java, есть даже высокоуровневый Java API. Я не знаю о подобном проекте для C# / .net

развитие

В вашем случае я бы посоветовал вам взглянуть на программный TPM IBM.

В пакете вы найдете 3 очень полезных компонента:

  • программный эмулятор TPM
  • облегченная библиотека tpm
  • некоторые базовые утилиты командной строки

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

Высокий уровень

Предпосылки:

  1. TPM активирован
  2. Драйвер TPM загружен
  3. вы вступили во владение TPM

Чтобы запечатать каплю, вам нужно сделать следующее:

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

Для распечатки вам необходимо:

  1. получить ключ-блоб
  2. загрузить ключ в TPM
  3. распечатать запечатанную каплю

Вы можете сохранить ключевой блок в вашей структуре данных, которую вы используете для хранения защищенных байтов.

Большинство команд TPM, которые вам нужны, являются авторизованными. Поэтому вам необходимо установить сеансы авторизации, где это необходимо. AFAIR это в основном сессии OSAP.

Команды TPM

В настоящее время я не могу запустить отладочную версию, поэтому не могу предоставить вам точную последовательность. Поэтому рассмотрим неупорядоченный список команд, которые вам придется использовать:

  • TPM_OSAP
  • TPM_CreateWrapKey
  • TPM_LoadKey2
  • TPM_Seal

Если вы хотите прочитать текущие значения ПЦР, тоже:

  • TPM_PCRRead

Как я могу зашифровать байты с помощью модуля TPM машины?

Зависит от ваших намерений и обстоятельств:

  • Какой у вас TPM (на одну семью или две семьи)?
  • В каком состоянии находится TPM? Это было в собственности? Это было подготовлено?
  • Какой у вас язык программирования?
  • Вы хотите зашифровать или подписать? (это расплывчато от остальной части вопроса)
  • Насколько велики данные, которые вы хотите зашифровать?
  • Вы хотите использовать симметричный ключ или асимметричный ключ?
  • Вы хотите использовать ключ, который уже существует в TPM, или хотите, чтобы он сначала создал ключ?
  • Под "шифрованием" вы, возможно, имеете в виду "обернуть ключ"?
  • Вы хотите привязать зашифрованные данные к конфигурации системы, чтобы их можно было расшифровать только тогда, когда система вернется в ту же конфигурацию?
  • Вы хотите требовать авторизацию для расшифровки?
  • Возможно, вам вообще не нужно шифровать, а лучше хранить данные в TPM?
  • Если вы храните данные в доверенном платформенном модуле, хотите ли вы потребовать авторизацию или чтобы система была в определенной конфигурации для извлечения?

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

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

This is what the Bind command is for (superseded by the Create command for TPM 2). You load a key that derives from a TPM-bound key and encrypt with it (or directlywith a hardware-bound key). This way the data can only be decrypted with access to the same TPM.

In other words, I want to replace the Qualcomm Trusted Execution Environment (TEE) in the block diagram below for Android, with the TPM in Windows:

Not sure if replicating this whole process is a good idea. For one, there is no need to use a signing operation anywhere in the process. It would appear that, at the time when Android 5 was being developed, the Keystore API was limited to signing and verification operations. My best guess is that the disk encryption team did their best to work with what they had and devised an algorithm whereby one of the intermediate keys was derived with a signing operation, using a stored TEE key, thereby tying the whole process to a hardware-bound key only available on the platform -- as signing was the onlyway to do that at the time. However, there is no need to constrain yourself in such ways if have access to a TPM, which gives you more capabilities than you knew you needed!

I realize that the TPM doesn't do data-signing

This is false, both versions of TPM support signing.

(or if it does, it does not guarantee that signing the same data will give the same binary output every time)

This makes no sense. Signing the same data with the same key will produce the same signature. You may be confusing the signing operation with the quoting operation, which will mix in a nonce.

Which is why I'd be willing to replace "RSA signing" with "encrypting a 256-bit blob with a hardware bound key".

This should actually be the preferred option, although both are possible with a TPM. See above.

The problem is that TPM programming is completely undocumented on MSDN. There is no API available to perform any operations.

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

Вместо этого вам нужно найти себе копию программного стека Trusted Computing Group (также известного как TSS), выяснить, какие команды отправлять в TPM с полезными данными, в каком порядке, и вызвать функцию Window Tbsip_Submit_Command для непосредственной отправки команд:

На самом деле, нет, если бы у вас был TSS, вам бы не пришлось использовать Tbsip_submit_Command(). В этом весь смысл TSS - детали низкого уровня абстрагируются.

В Windows нет API более высокого уровня для выполнения действий.

По-прежнему верно для TPM 1, но для TPM 2 есть TSS.MSR.

Это моральный эквивалент попытки создать текстовый файл с помощью команд ввода-вывода SATA на ваш жесткий диск.

Верный.

Почему бы просто не использовать брюки... Проблема с этим кодом в том, что он не переносится в мир 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. TBS API - единственный общий код между ними, и, честно говоря, это могло быть одной из причин, по которой этот 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 PC Client. Для наших нужд здесь будет более чем достаточно.

TPM 1.2 Spec Part 3 говорит следующее:

TPM_Seal requires the encryption of one parameter (“Secret”). For the
sake of uniformitywith 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 описан в спецификации TPM 1.2, часть 1. OSAP или протокол объектно-ориентированной авторизации был изобретен для обработки варианта использования, когда вы хотите использовать объект TPM, который требует авторизации несколько раз, но не хочет предоставлять авторизацию каждый раз: вместо этого используется сеанс OSAP, который полагается о концепции "общего секрета", которым является HMAC который смешивает данные авторизации объекта с одноразовыми номерами, сгенерированными на каждой стороне, чтобы предотвратить ответные атаки. Следовательно, "общий секрет" известен только двум сторонам в этом сеансе: стороне, которая инициировала сеанс (пользователь), и стороне, которая его приняла (TPM); Кроме того, обе стороны должны иметь одинаковые данные авторизации объекта, чтобы "общий секрет" был одинаковым; кроме того, "общий секрет", используемый в одном сеансе, будет недействителен в другом. Эта диаграмма из спецификации описывает процесс:

Мы не будем использовать несколько сеансов в этом конкретном случае (фактически, этот параметр игнорируется командой 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 1.2 представлена ​​следующим образом:

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

The tag is a two-byte value that indicates whether what follows is either input or output, and whether there are any auth data values following command parameters. For TPM_OSAP, the tag must be TPM_TAG_RQU_COMMAND (0x00C1) as per the spec, which means "a command with no authorization".

Size is a four-byte value that specifies the size of the command in bytes, including the tag and size itself. We will set this value later, once we have computed it.

Command code is a four-byte value that servers as a command ID: it tells the TPM how to interpret the rest of the command. Our command code here is 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:

Итак, возвращаемся:

  • Дескриптор авторизации для использования с нашей основной командой (печать)
  • nonceEven: одноразовый номер, сгенерированный TPM для использования с основной командой
  • nonceEvenOSAP: nonce OSAP, который является контр-nonce к nonce, который мы сгенерировали на нашей стороне перед отправкой команды 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 является "хорошо известная авторизация": обнуленный 20-байтовый буфер. Технически, можно было бы изменить это значение на другое, когда вы становитесь владельцем TPM, но на практике это не делается, поэтому мы можем с уверенностью предположить, что значение "well-known auth" хорошее.

Теперь давайте посмотрим, что входит в команду TPM_Seal:

Большинство из этих параметров легко построить, за исключением двух из них: encAuth а также pubAuth. Давайте посмотрим на них по очереди.

encAuthэто "Зашифрованные данные AuthData для запечатанных данных". Наши AuthData здесь - это "хорошо известная аутентификация" из прошлого, но да, нам все еще нужно ее зашифровать. Поскольку мы используем сеанс OSAP, он зашифрован в соответствии с ADIP или протоколом вставки данных авторизации. Из спецификации: "ADIP позволяет создавать новые объекты и безопасную вставку новых объектов AuthData. Передача новых AuthData использует шифрование с ключом, основанным на общем секрете сеанса OSAP". Дополнительно: "Для обязательного алгоритма шифрования XOR создатель создает ключ шифрования, используя хэш SHA-1 общего секрета OSAP и одноразовый номер сеанса. Создатель XOR шифрует новые AuthData, используя ключ шифрования в качестве одноразового блокнота и отправляет эти зашифрованные данные вместе с запросом на создание в TPM."Таким образом, мы должны создать ключ XOR из одноразового номера сеанса и "общего секрета", а затем зашифровать с помощью этого ключа XOR-шифрование нашей "известной аутентификации".

Следующая диаграмма объясняет, как работает ADIP:

pubAuth"Дайджест сеанса авторизации для входов и keyHandle". В части 1 спецификации в разделе "Объявления параметров для примеров OIAP и OSAP" объясняется, как интерпретировать приведенную выше таблицу параметров TPM_Seal: "В столбце HMAC # подробно описаны параметры, используемые в вычислении HMAC. Параметры 1S, 2S и т. Д. Объединены и хешируется в inParamDigest или outParamDigest, неявно вызывается 1H1 и, возможно, 1H2, если есть два сеанса авторизации. Для первого сеанса 1H1, 2H1, 3H1 и 4H1 объединяются и объединяются в HMAC. Для второго сеанса 1H2, 2H2, 3H2, и 4H2 объединены и связаны с HMAC ". Таким образом, нам нужно будет хешировать открытый текст, его размер, размер информации PCR,encAuth сверху и порядковый номер TPM_Seal, а затем HMAC, который с двумя одноразовыми номерами и логическим значением "продолжить сеанс", используя "общий секрет" OSAP в качестве ключа HMAC.

Собираем все вместе на диаграмме:

Обратите внимание, как в этом коде мы устанавливаем "Размер информации PCR" равным нулю, поскольку мы просто хотим зашифровать данные, не привязывая их к состоянию системы. Однако при необходимости предоставить структуру "Информация о ПЦР" - тривиальная задача.

    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.

Ключ подтверждения или EK... Где-то внутри TPM находится закрытый ключ RSA. Этот ключ заперт там - его никогда не увидит внешний мир. Я хочу, чтобы TPM что-то подписывал своим закрытым ключом (то есть зашифровывал это своим закрытым ключом).

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

Но на самом деле я бы хотел "запечатать" некоторые данные.

См. Выше.

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

Доверенные и зашифрованные ключи - это два новых типа ключей, добавленных к существующей службе ключей в ядре. Оба эти новых типа являются симметричными ключами переменной длины, и в обоих случаях все ключи создаются в ядре, а пользовательское пространство видит, хранит и загружает только зашифрованные большие двоичные объекты. Доверенные ключи требуют наличия микросхемы доверенного платформенного модуля (TPM) для большей безопасности, в то время как зашифрованные ключи могут использоваться в любой системе. Все BLOB-объекты уровня пользователя для удобства отображаются и загружаются в шестнадцатеричном формате и проверяются на целостность.

Доверенные ключи используют TPM как для генерации, так и для опечатывания ключей. Ключи запечатываются под 2048-битным ключом RSA в доверенном платформенном модуле и, опционально, запечатываются в соответствии с указанными значениями PCR (измерения целостности) и открываются только доверенным платформенным модулем, если совпадают проверки подлинности PCR и целостности BLOB-объектов. Загруженный доверенный ключ может быть обновлен новыми (будущими) значениями PCR, поэтому ключи легко переносятся на новые значения pcr, например, при обновлении ядра и initramfs. Один и тот же ключ может иметь много сохраненных больших двоичных объектов при разных значениях PCR, поэтому легко поддерживается несколько загрузок.

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

Зашифрованные ключи не зависят от доверенного платформенного модуля и работают быстрее, поскольку они используют 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

Повторно запишите доверенный ключ в новых значениях pcr:

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

Первоначальным потребителем доверенных ключей является EVM, которому во время загрузки требуется высококачественный симметричный ключ для защиты метаданных файла с помощью HMAC. Использование доверенного ключа дает надежные гарантии того, что ключ EVM не был скомпрометирован из-за проблем на уровне пользователя, а при закрытии для определенных значений загрузочной PCR защищает от загрузочных и автономных атак. Создайте и сохраните зашифрованный ключ "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. Более подробную информацию об использовании можно найти в файле "Documentation/security/keys-ecryptfs.txt".

Когда это говорит

указание ключа HMAC

это НЕ означает предоставление ключа HMAC - это означает "указать ключ HMAC, который вы хотите использовать".

TPM могут использовать практически неограниченное количество ключей HMAC, как указано в книге. Вы должны указать TPM, какой из них использовать.

Другие вопросы по тегам