TPM(Trusted Platform Module)を使用してバイトを暗号化する方法


110

マシンのTPMモジュールを使用してバイトを暗号化するにはどうすればよいですか?

CryptProtectData

Windowsは、APIを使用してblobを暗号化する(比較的)単純なAPIを提供しCryptProtectDataます。これを使いやすい関数をラップできます。

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

の詳細は、ProtectBytes非常に簡単に使用できるという考えほど重要ではありません。

  • ここに私が保持している秘密鍵で暗号化したいバイトがあります System
  • 暗号化されたblobを返してください

返されるblobは、ドキュメント化されていないドキュメント構造であり、元のデータ(ハッシュアルゴリズム、暗号化アルゴリズム、salt、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でのみ復号化できる一部のデータ(ハードドライブ暗号化マスターキーなど)を暗号化したい。つまり、以下のAndroidのブロック図にあるQualcomm Trusted Execution Environment(TEE)をWindowsのTPMに置き換えます。

ここに画像の説明を入力してください

:TPMはデータ署名を行わないことを理解しています(または、署名しても、同じデータに署名しても毎回同じバイナリ出力が得られるとは限りません)。これが、「RSA署名」「ハードウェアにバインドされたキーで256ビットのblobを暗号化するに置き換えたいという理由です。

それで、コードはどこにありますか?

問題は、TPMプログラミングがMSDNで完全に文書化されていないことです。操作を実行するために使用できるAPIはありません。代わりに、Trusted Computing Groupのソフトウェアスタック(別名TSS)のコピーを見つけ、TPMに送信するコマンドをペイロードとともに、どのような順序で見つけウィンドウの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 I / Oコマンドを発行してテキストファイルを作成しようとすることと同じです

なぜズボンを使用しないのですか

Trusted Computing Group(TCG)は独自のAPI、TCB Software Stack(TSS)を定義しました。このAPIの実装は一部の人々によって作成され、TrouSerSと呼ばれています。次に、そのプロジェクトをWindowsに移植しました

そのコードの問題は、Windowsの世界に移植できないことです。たとえば、Delphiからは使用できません。C#からは使用できません。以下が必要です。

  • OpenSSL
  • pThread

TPMでコードを暗号化したいだけです。

上記CryptProtectDataは、関数本体にあるもの以外何も必要としません。

TPMを使用してデータを暗号化するための同等のコードは何ですか?他の人が指摘したように、おそらく3つのTPMマニュアルを参照して、自分でblobを構築する必要があります。おそらくTPM_sealコマンドが関係しています。データを封印したくないと思いますが、バインドしたいと思います。

バインド – TPMバインドキーを使用してデータを暗号化します。TPMバインドキーは、ストレージキーから派生した一意のRSAキーです。 封印 –結合と同様の方法でデータを暗号化しますが、さらに、データを復号化(封印解除)するためにTPMが必要な状態を指定します

必要な20行のコードを見つけるために、必要な3つのボリュームを読みます。

しかし、私は何を読んでいるのか分かりません。チュートリアルや例があったら、私が試してみるかもしれません。しかし、私は完全に迷っています。

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メイン-パート3コマンド-仕様バージョン1.2を見ると、bindに関する記述があります。

10.3 TPM_UnBind

TPM_UnBindは、Tspi_Data_Bindコマンドの結果であるデータBLOBを受け取り、それを復号化してユーザーにエクスポートします。呼び出し元は、着信BLOBを復号化するキーの使用を承認する必要があります。TPM_UnBindはブロックごとに動作し、ブロック間の関係についての概念はありません。

混乱しているのTspi_Data_Bindコマンドないことです

研究努力

TPMやその操作を文書化するために誰もこれまで悩まなかったことは恐ろしいことです。まるで、このクールなものを思いつくためにすべての時間を費やしているかのようですが、何かに使用できるようにするための苦痛なステップに対処したくありませんでした。

(現在)無料の本「TPM 2.0の実用ガイド:セキュリティの新時代におけるトラステッドプラットフォームモジュールの使用」から始めます。

第3章-TPM 2.0のクイックチュートリアル

TPMは自己生成された秘密キーにアクセスできるため、公開キーでキーを暗号化し、結果のblobをハードディスクに保存できます。このようにして、TPMは実質的に無制限の数のキーを使用可能に保つことができますが、貴重な内部ストレージを無駄にすることはありません。ハードディスクに保存されたキーは消去できますが、バックアップすることもできます。これは、設計者には許容できるトレードオフのように見えました。

TPMの公開キーを使用してキーを暗号化するにはどうすればよいですか?

第4章-TPMを使用する既存のアプリケーション

TPMを使用する必要があるが使用しないアプリケーション

過去数年間で、Webベースのアプリケーションの数は増加しています。その中には、Webベースのバックアップとストレージがあります。現在、多くの企業がそのようなサービスを提供していますが、私たちが知る限り、これらのサービスのクライアントのいずれも、ユーザーがバックアップサービスのキーをTPMにロックすることを許可していません。これが行われた場合、TPMキー自体が複数のマシンで複製されてバックアップされていれば、確かに良いことです。これは、開発者にとっての機会のようです。

開発者はどのようにしてTPMのキーをロックしますか?

第9章-階層

使用例:ログインパスワードの保存

典型的なパスワードファイルは、パスワードのソルトハッシュを格納します。検証は、提供されたパスワードをソルトおよびハッシュし、それを格納された値と比較することで構成されます。計算には秘密が含まれていないため、パスワードファイルに対するオフライン攻撃の対象になります。

この使用例では、TPMで生成されたHMACキーを使用します。パスワードファイルには、ソルトパスワードのHMACが格納されます。検証は、提供されたパスワードをソルト処理およびHMAC処理し、それを格納された値と比較することで構成されます。オフラインの攻撃者はHMACキーを持っていないため、攻撃者は計算を実行して攻撃を仕掛けることはできません。

これうまくいくかもしれません。TPMに秘密のHMACキーがあり、自分のTPMだけがHMACキーを知っている場合は、「署名(別名TPMはその秘密キーで暗号化)」を「HMAC」に置き換えることができます。しかし、次の行で完全に逆転します。

TMAC2_Create、HMACキーを指定

HMACキーを指定する必要がある場合、それはTPMシークレットではありません。HMACキーが秘密ではないという事実は、これがTPMが提供する暗号化ユーティリティについての章であることを理解した場合に理にかなっています。SHA2、AES、HMAC、またはRSAを自分で記述する必要はなく、TPMがすでに備えているものを再利用できます。

第10章-キー

セキュリティデバイスとして、ハードウェアデバイスでキーを安全に保ちながらキー使用するアプリケーションの機能は、TPMの最大の強みです。TPMは、外部で生成されたキーを生成およびインポートできます。非対称キーと対称キーの両方をサポートしています。

優れた!どうやってやるの!?

キージェネレーター

間違いなく、TPMの最大の強みは、暗号化キーを生成し、ハードウェア境界内でその秘密を保護する能力です。キージェネレーターは、TPM独自の乱数ジェネレーターに基づいており、外部の乱数発生源に依存しません。したがって、エントロピーのソースが不十分な弱いソフトウェアソフトウェアに基づく弱点を排除します。

TPMは、暗号鍵を生成し、ハードウェアの境界内にその秘密を保護する能力を持っていますか?そうですか?

第12章-プラットフォーム構成レジスタ

認可のためのPCR

ユースケース:ハードディスク暗号化キーをプラットフォーム状態にシールする

フルディスク暗号化アプリケーションは、TPMが暗号化キーを保護する場合、同じディスクに保存され、パスワードのみで保護される場合よりもはるかに安全です。第1に、TPMハードウェアにはアンチハンマリング保護機能(TPM辞書攻撃保護の詳細については第8章を参照)があり、パスワードへのブルートフォース攻撃が実用的ではありません。ソフトウェアのみで保護されたキーは、弱いパスワードに対してはるかに脆弱です。第二に、ディスクに保存されているソフトウェアキーは盗むのがはるかに簡単です。ディスク(またはディスクのバックアップ)を取得すると、キーが取得されます。TPMがキーを保持している場合、プラットフォーム全体、または少なくともディスクとマザーボードを盗む必要があります。

シーリングでは、パスワードだけでなくポリシーによってもキーを保護できます。一般的なポリシーでは、封印時にPCR値(ソフトウェアの状態)の現在のキーをロックします。これは、最初の起動時の状態が損なわれていないことを前提としています。最初の起動時に存在するプレインストールされたマルウェアはすべてPCRに測定され、したがって、キーは侵害されたソフトウェア状態に封印されます。信頼性の低い企業には、標準のディスクイメージがあり、そのイメージを表すPCRをシールする場合があります。これらのPCR値は、おそらくより信頼できるプラットフォームで事前計算されます。さらに洗練された企業はTPM2_PolicyAuthorizeを使用し、信頼されたPCR値のセットを承認するいくつかのチケットを提供します。ポリシーの承認とPCRの脆弱性の問題を解決するためのそのアプリケーションの詳細については、第14章を参照してください。

パスワードでキーを保護することもできますが、TPMキーパスワードがなくてもセキュリティは向上します。攻撃者はTPMkeyパスワードを提供せずにプラットフォームを起動できますが、OSユーザー名とパスワードなしではログインできません。OSsecurityはデータを保護します。攻撃者は、OSのログインセキュリティをバイパスするために、ハードドライブからではなく、ライブDVDまたはUSBスティックなどから代替OSを起動する可能性があります。ただし、この異なるブート構成とソフトウェアはPCR値を変更します。これらの新しいPCRは封印された値と一致しないため、TPMは復号化キーを解放せず、ハードドライブを復号化できませんでした。

優れた!これはまさに私が望んでいるユースケースです。これは、MicrosoftがTPMを使用するユースケースでもあります。どうすればいいのですか!?

それで、私はその本全体を読みました、そしてそれは何の役にも立ちませんでした。それは375ページなので、これは非常に印象的です。あなたはその本が何を含んでいたのだろうと思います-そしてそれを振り返って、私にはわかりません。

そのため、TPMのプログラミングに関する決定的なガイドをあきらめ、代わりにMicrosoftのドキュメントを参照します。

マイクロソフトTPMプラットフォーム暗号-プロバイダーツールキット。それは私がしたいことを正確に述べています:

保証キーまたはEK

EKは、プラットフォームに信頼できる暗号識別子を提供するように設計されています。企業は、企業内のすべてのPCのTPMに属する保証キーのデータベースを維持している場合もあれば、データセンターファブリックコントローラーがすべてのブレードのTPMのデータベースを持っている場合もあります。Windowsでは、「Windows 8のプラットフォーム暗号プロバイダー」セクションで説明されているNCryptプロバイダーを使用して、EKのパブリック部分を読み取ることができます。

TPMのどこかにRSA秘密鍵があります。その鍵はそこに閉じ込められています-外の世界からは決して見えません。TPMに秘密キーで何かに署名してほしい(つまり、秘密キーで暗号化する)。

だから私はおそらく存在することができる最も基本的な操作が欲しい:

ここに画像の説明を入力してください

秘密鍵で何かを暗号化します。私は(まだ)より複雑なものを求めているわけではありません。

  • PCRの状態に基づいて「封印」する
  • キーを作成し、それを揮発性または不揮発性メモリに保存する
  • 対称キーを作成して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を使用している人の数が非常に少ないことに気づきました。Stackoverflowの誰も答えを持っていないのはそのためです。だから私は私の共通の問題の解決策を得ることに本当に欲張りすぎることはできません。しかし、私が本当にやりたいことは、いくつかのデータを「シール」することです。

ここに画像の説明を入力してください

  • TPMにいくつかのデータを提示します(例:32バイトの鍵データ)
  • TPMにデータを暗号化させ、不透明なblob構造を返す
  • 後でTPMにblobの復号化を依頼します
  • 復号化は、TPMのPCRレジスタが暗号化中と同じ場合にのみ機能します。

言い換えると:

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

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

暗号化Next Gen(Cng、別名BCrypt)がTPMをサポート

Windowsの元の暗号化APIは、暗号APIとして知られていました。

Windows Vistaでは以降では、暗号化APIはに置き換えられている暗号化API:次世代(内部的として知られているBestCryptのと略記、bcryptのは、と混同しないようにパスワードのハッシュアルゴリズム)。

Windowsには2つのBCrypt プロバイダーが付属しています

プラットフォーム暗号のプロバイダは、MSDNで文書化されていませんが、2012マイクロソフトリサーチサイトからドキュメントを持っています:

TPMプラットフォーム暗号プロバイダーツールキット

TPMプラットフォーム暗号化プロバイダーおよびツールキットには、Windows 8でTPM関連の機能を使用するためのサンプルコード、ユーティリティ、およびドキュメントが含まれています。 Windowsの新機能を使用できます。TPM1.2およびTPM2.0ベースのシステムの両方がサポートされています。

Microsoftの意図は、暗号化NG APIのMicrosoft Platform Crypto ProviderでTPM暗号機能を表面することです。

Microsoft BCryptを使用した公開鍵暗号化

とすれば:

今後の方法は、Microsoft Cryptography Next Gen APIを使用してデジタル署名を行う方法を理解することです。

次のステップは、RSA公開鍵を使用して、BCryptで暗号化を行うためのコードを、標準プロバイダーを使用して作成することです(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)。

2016年2月22日:Appleがユーザーデータの復号化を強制されるようになったことで、TPMが発明した最も単純なタスク、つまり何かを暗号化することをTPMに実行させる方法に新たな関心が集まっています。

これは、車を所有するすべての人とほぼ同じですが、車の始動方法は誰にもわかりません。ステップ1を通過できさえすれば、本当に便利でクールなことができます。

ボーナスリーディング


バインド(暗号化)の場合、明示的な関数はありません。また、それも必要ありません。TPMでバインディングキーを作成し、その公開部分を使用して、システムのrsa暗号化機能( "RSA / ECB / OAEPWithSHA1AndMGF1Padding")で対称暗号化キーsekを暗号化し、正しい構造( "TcTssConstants.TSS_ENCDATA_BIND")に保存します。sekのバインドを解除(復号化)するには、TPMのバインド解除機能を使用し、好きな対称暗号化関数でsekを使用します。私は以前にやったことのあるかなり古いコードベースを持っています、おそらくそれは役に立ちます:goo.gl/jV1Ouw
evildead

ウィキペディアのバインディング– TPMバインドキーを使用してデータを暗号化します。TPMバインドキーは、ストレージキーに由来する一意のRSAキーです。 en.wikipedia.org/wiki/Trusted_Platform_Moduleこのコマンドのペア(TSpi_Data_Bind / TPM_UnBind)のように聞こえれば、ニーズに十分対応できます...
Alex Mazzariol

1
TPMを直接使用する必要はないと思います。これは、標準のCNG / NCryptXXX APIと「Microsoftプラットフォーム暗号プロバイダー」(最近のWindows OSプラットフォームの場合、およびもちろんハードウェアに問題がなく有効になっている場合)を通じてサポートされます。たぶん、「TPM Platform Crypto-Provider Toolkit here:research.microsoft.com/en-us/downloads/…ご覧
Simon

CryptProtectDataは必ずしもTPMを使用するわけではありません。一方、TPMの有効なCNGまたはCSPハンドルを取得できる場合は、それを暗号化関数で使用できます。
Michael Chourdakis

1
@ b3nj1いいえ、私はそうではありませんでした。誰もその質問に答えることができなかった。
イアン・ボイド

回答:


7

プライマー

以降はTPM 1.2についてです。マイクロソフトでは、将来のすべてのWindowsバージョンにTPM 2.0が必要であることを覚えておいてください。2.0世代は1.2と根本的に異なります

TPMの設計原則により、1行のソリューションはありません。TPMは、リソースが限られているマイクロコントローラーと考えてください。その主な設計目標は、安く、しかも安全であることです。そのため、TPMでは、安全な操作に必要のないすべてのロジックが取り除かれました。したがって、TPMは、少なくとも多少の脂肪のあるソフトウェアがあり、多くのコマンドを正しい順序で発行している場合にのみ機能します。そして、それらのコマンドのシーケンスは非常に複雑になる可能性があります。TCGが明確に定義されたAPIでTSSを指定したのはそのためです。Javaを使用したい場合は、高レベルのJava APIもあります。C#/ .netの同様のプロジェクトを知りません

開発

あなたの場合、私はあなたにIBMのソフトウェアTPMを見ることをお勧めします。

パッケージには3つの非常に便利なコンポーネントがあります。

  • ソフトウェアTPMエミュレータ
  • 軽量tpm lib
  • いくつかの基本的なコマンドラインユーティリティ

ソフトウェア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

現在のPCR値も読みたい場合:

  • TPM_PCRRead

Microsoftには、TPMを使用するためのC#.NET管理ライブラリがあります。また、実際のTPMが存在しない場合は、マネージライブラリがデバッグの代替手段として接続できるTPMエミュレーターも備えています。また、TPMを使用するためのドキュメントとサンプルコードを含むTPMプラットフォームプロバイダーツールキットもあります。次に、TPMを使用してバイトを暗号化する方法を誰かが理解できたとしましょう。
Ian Boyd

最初の2つのリンクはTPM 2.0のみです。これらを使用したい場合は、どうしようもありません。
Scolytus、2015年

4

信頼できる暗号化された鍵

信頼できる鍵と暗号化された鍵は、既存のカーネル鍵リングサービスに追加された2つの新しい鍵タイプです。これらの新しいタイプはどちらも可変長の対称キーであり、どちらの場合もすべてのキーはカーネルで作成され、ユーザー空間は暗号化されたblobのみを表示、保存、ロードします。信頼できる鍵には、セキュリティを強化するために信頼できるプラットフォームモジュール(TPM)チップが必要ですが、暗号化された鍵はどのシステムでも使用できます。すべてのユーザーレベルのblobは、便宜上、16進ASCIIで表示およびロードされ、整合性が検証されます。

信頼できるキーは、TPMを使用してキーを生成し、封印します。キーはTPMの2048ビットRSAキーの下で封印され、オプションで指定されたPCR(整合性測定)値に封印され、PCRとblob整合性検証が一致する場合にのみ、TPMによって封印解除されます。ロードされた信頼済みキーは新しい(将来の)PCR値で更新できるため、カーネルやinitramfsが更新されたときなどに、キーは新しいpcr値に簡単に移行できます。同じキーは異なるPCR値の下で多くの保存されたblobを持つことができるので、複数のブートが簡単にサポートされます。

デフォルトでは、信頼できる鍵はSRKの下に封印されています。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標準のTPM_STORED_DATA形式の、封印された鍵のASCII 16進コピーを返します。新しいキーのキーの長さは常にバイト単位です。信頼できるキーは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'

信頼できる暗号化されたキーの使用例

長さが32バイトの "kmk"という名前の信頼できる鍵を作成して保存します。

$ 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

保存された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値に封印されると、ブートおよびオフライン攻撃から保護されます。上記の信頼できるキー「kmk」を使用して、暗号化キー「evm」を作成して保存します。

オプション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

保存された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」ファイルを参照してください。


これら2つの新しいタイプのキーがどこに追加されたのか、何か分かりますか?どちらのバージョンですか。私は現在1.2(会社のパッケージ)を使用していますが、それらはこれらをサポートしていません。たぶん1.5+?
アカプルコ

1
この投稿のソースは何ですか?最後はドキュメントを参照していますDocumentation/security/keys-ecryptfs.tx
goodguys_activate

これらはすべてコマンドラインプログラムの呼び出しのようです。TPMの使用方法に関するコードが表示されません。
Ian Boyd

3

マシンのTPMモジュールを使用してバイトを暗号化するにはどうすればよいですか?

あなたの意図と状況に依存します:

  • どのようなTPMがありますか(1ファミリーまたは2ファミリー)?
  • TPMはどのような状態にありますか?所有されていますか?プロビジョニングされていますか?
  • あなたのプログラミング言語は何ですか?
  • 暗号化または署名しますか?(それは残りの質問から曖昧です)
  • 暗号化するデータの大きさはどれくらいですか?
  • 対称鍵または非対称鍵を使用しますか?
  • TPMに既に存在するキーを使用しますか、それとも最初にキーを作成しますか?
  • 「暗号化」とは、おそらく「鍵をラップする」という意味ですか?
  • 暗号化されたデータをシステム構成にロックして、システムが同じ構成に戻ったときにのみ復号化できるようにしますか?
  • 復号化の承認を要求しますか?
  • おそらく暗号化する必要はなく、TPM内にデータを保存する必要があるでしょうか。
  • TPM内にデータを格納している場合、取得のために認証を要求しますか、それともシステムを特定の構成にしたいですか?

これらの各ユースケース(およびその他のユースケース)、またはそれらの組み合わせは、異なる実装パスを示します。TPMを暗号化デバイスのスイスアーミーナイフと考えてください。それを使用して実行できないことは多くありませんが、その多様性のために使いやすさが損なわれます。質問は暗号化、署名、システム構成へのロックの間を行き来しますが、この回答の主要部分は、質問で説明されているほとんどのニーズをカバーするシールコマンドを検討します。

では、これをさらに一歩進めましょう。ローカルTPMでのみ復号化できる一部のデータ(ハードドライブ暗号化マスターキーなど)を暗号化したい。

これがBindコマンドの目的です(TPM 2のCreateコマンドに置き換えられました)。TPMにバインドされたキーから派生したキーを読み込み、それを使用して(またはハードウェアにバインドされたキーを使用して)暗号化します。このように、データは同じTPMへのアクセスでのみ復号化できます。

つまり、以下のAndroidのブロック図にあるQualcomm Trusted Execution Environment(TEE)をWindowsのTPMに置き換えます。

このプロセス全体を複製することが良いアイデアかどうかはわかりません。1つは、プロセスのどこでも署名操作を使用する必要がないことです。Android 5が開発された当時、Keystore APIは署名と検証の操作に限定されていたようです。私の推測では、ディスク暗号化チームは、自分が持っているものを最大限に活用してアルゴリズムを考案し、中間キーの1つを、保存されたTEEキーを使用して署名操作で導出し、それによってプロセス全体をハードウェアに結び付けたと考えています。バインドされたキーは、プラットフォームでのみ利用可能です。当時は、署名することが唯一の方法だったためです。ただし、TPMにアクセスできる場合は、このような方法で制約する必要はありません。TPMにアクセスすると、必要以上に多くの機能が利用できるようになります。

TPMがデータ署名を行わないことに気付きました

これは誤りです。TPMの両方のバージョンが署名をサポートしています。

(またはそうである場合、同じデータに署名しても毎回同じバイナリ出力が得られるとは限りません)

これは意味がありません。同じキーで同じデータに署名すると、同じ署名生成されます。ナンスが混在する引用操作と署名操作を混同している可能性があります。

これが、「RSA署名」を「ハードウェアにバインドされたキーで256ビットのblobを暗号化する」に置き換えたいという理由です。

これは実際に推奨されるオプションですが、TPMではどちらも可能です。上記を参照。

問題は、TPMプログラミングがMSDNで完全に文書化されていないことです。操作を実行するために使用できるAPIはありません。

残念ながら、文書化することはあまりありません。Win APIは、ドライバーから1レベル削除されたいくつかのTBS関数に制限されています。

代わりに、Trusted Computing Groupのソフトウェアスタック(別名TSS)のコピーを見つけ、TPMに送信するコマンドをペイロードとともに、その順序で把握し、ウィンドウのTbsip_Submit_Command関数を呼び出してコマンドを直接送信する必要があります。

実際には、いいえ。TSSがあれば、を使用する必要はありませんTbsip_submit_Command()。これがTSSの要点です。低レベルの詳細は抽象化されています。

Windowsには、アクションを実行するための上位レベルのAPIがありません。

TPM 1にも当てはまりますが、TPM 2にはTSS.MSRがあります

これは、ハードドライブにSATA I / Oコマンドを発行してテキストファイルを作成しようとすることと同じです。

正しい。

なぜズボンだけを使わないのか...そのコードの問題は、Windowsの世界に移植できないことです。たとえば、Delphiからは使用できません。C#からは使用できません。OpenSSL、pThreadが必要です。

これが乗り越えられない挑戦であることは明らかではありません。相互運用機能を介してTrouSerSにアクセスすることは、すべてのデータ構造化コードを書き換えるよりも望ましいでしょう。また、doTSS質問を書いているときにもありました。

TPMを使用してデータを暗号化するための同等のコードは何ですか?おそらくTPM_sealコマンドが関係しています。データを封印したくないと思いますが、バインドしたいと思います。

質問には2つのコマンドの違いを説明する引用が含まれているため、それほど混乱することはないはずです。シーリングはバインディングと似ていますが、データをアンシールするにはシステム状態が同じでなければならないという制約が追加されています。

同じように私は提供することができました:

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には2つのメジャーバージョンがあり、相互に完全に互換性がないことを指摘する価値があります。したがって、TPM 1用に作成したコードは実質的にTPM 2では機能しません。TBSAPIは2つのコードの間で唯一の共通コードであり、Microsoftに公平を期すために、これがそのAPIが成長しなかった理由の1つであった可能性があります。回答の主要部分には、2つの理由でTPM 1のコードが表示されます。

  • 質問にはTPM 1固有の概念が含まれているため、TPM 1を使用している人々は、それらを検索するためにここに上陸する可能性が高くなります
  • TPM 2用のTSSのMicrosoft実装があります。

第二に、質問をより具体的にしましょう。私はそれを次のように再解釈しています:

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?

PCR選択サイズがゼロに設定されている場合、Bindコマンドと同じ機能を実行するので、Sealコマンドはこれに最適ですが、PCR選択は、必要なPCRを含めるように簡単に変更できます。Bindコマンドが仕様にまったく含まれていた理由が不思議に思われ、指摘されているように、TPM 2仕様では削除され、2つが1つのCreateコマンドに結合されました。

TPM 1.2シールコマンドを使用してTBS関数のみでデータを暗号化するためのC#コードを次に示します(注:このコードはテストされておらず、デバッグなしでは機能しない可能性があります)

[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パラメータは、TPM 1.2インスタンスに話を1に設定する必要があります1、符号なし32ビット整数フィールドを持つだけでCの構造体であり、我々はそれを設定したものという。

    byte[] cmdBuf = new byte[768];

これは、TPM PCクライアント仕様で最小バッファーサイズとして指定されています。ここでのニーズには十分です。

TPM 1.2仕様パート3は次のように述べています。

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.

OSAPセッション中に生成されたナンスを使用して、この「秘密」パラメータをXOR暗号化する必要があります。Sealコマンド入力ハンドルの1つは、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で説明されています。このパート1では、オブジェクトの承認データと、両側で生成されたノンスを混合して、返信攻撃を防止しています。したがって、「共有シークレット」はこのセッションの2つの側だけに知られています。セッションを開始した側(ユーザー)とそれを受け入れた側(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    ....
+---------+------------------+------------------+---------------------------

タグは2バイトの値で、その後に続くものが入力か出力か、およびコマンドパラメータの後に認証データ値があるかどうかを示します。TPM_OSAPの場合、タグは、仕様に従ってTPM_TAG_RQU_COMMAND(0x00C1)である必要があります。これは、「権限のないコマンド」を意味します。

サイズは、コマンドのサイズ(タグとサイズ自体を含む)をバイト単位で指定する4バイトの値です。この値は、計算した後で設定します。

コマンドコードは4バイトの値で、コマンドIDとして機能します。TPMにコマンドの残りの部分を解釈する方法を伝えます。ここでのコマンドコードはTPM_OSAP(0x0000000B)です。

次に設定する2つのことは、エンティティタイプとエンティティ値です。TPMに既に存在するキーを使用するため、エンティティタイプ「SRK」(0x0004)を使用します。また、TPMがすでに所有されているという前提で作業しているので、仕様に従って永続ハンドル0x40000000の下に読み込まれたSRKなので、エンティティー値にはこの永続ハンドル値を使用します。(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_OSAPのTPMから取得する必要があるデータは次のとおりです。

TPM_OSAP応答

だから私たちは戻ってきます:

  • メインコマンド(シール)で使用する承認ハンドル
  • nonceEven:TPMによって生成され、メインコマンドで使用するnonce
  • nonceEvenOSAP:TPM_OSAPコマンドを送信する前にサイドで生成したnonceのカウンターノンスであるOSAP nonce。これら2つのナンスは、「共有秘密」の生成に使用されます。

これらの値を抽出して変数に格納します。

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

次に、「共有秘密」を計算します。仕様に従って、計算に入る値は2つのOSAPナンス(1つはユーザーによって生成され、もう1つはTPMによって生成されます)と、使用するキー(SRK)の認証値です。慣例により、SRK auth値は「よく知られているauth」、つまりゼロ化された20バイトのバッファーです。技術的には、TPMの所有権を取得するときにこの値を別の値に変更できますが、これは実際には行われないため、「既知のauth」値が適切であると想定できます。

次に、TPM_Sealコマンドの内容を見てみましょう。

TPM_Seal

これらのパラメータのほとんどは、そのうちの2を除いて、構築するのは簡単です:encAuthpubAuth。それらを1つずつ見ていきましょう。

encAuth「封印されたデータの暗号化されたAuthData」です。ここでのAuthDataは以前からの「既知の認証」ですが、はい、暗号化する必要があります。OSAPセッションを使用しているため、ADIP、またはAuthorization-Data Insertion Protocolに従って暗号化されます。仕様から:「ADIPでは、新しいエンティティの作成と新しいエンティティのAuthDataの安全な挿入が可能です。新しいAuthDataの送信では、OSAPセッションの共有シークレットに基づくキーによる暗号化が使用されます。」さらに、「必須のXOR暗号化アルゴリズムの場合、作成者はOSAP共有秘密のSHA-1ハッシュとセッションナンスを使用して暗号化キーを作成します。作成者XORは、暗号化キーをワンタイムパッドとして使用して新しいAuthDataを暗号化し、この暗号化されたデータを作成リクエストとともにTPMに送信します。」

次の図は、ADIPの動作を示しています。

ディップ

pubAuth「入力とkeyHandleの認証セッションダイジェスト」です。仕様のパート1の「OIAPおよびOSAPの例のパラメーター宣言」では、上記のTPM_Sealパラメーターテーブルの解釈方法について説明しています。 inParamDigestまたはoutParamDigestにハッシュされ、暗黙的に1H1と呼ばれ、2つの承認セッションがある場合は1H2と呼ばれます。最初のセッションでは、1H1、2H1、3H1、および4H1が連結され、HMACされます。2番目のセッションでは、1H2、2H2、3H2、 4H2は連結され、HMACされます。」したがって、上から平文、そのサイズ、PCR情報サイズ、encAuthTPM_Seal序数、次にHMACを2つのノンスとOSAPを使用した「セッションの継続」ブール値でハッシュする必要があります。

すべてを図にまとめます。

pubAuth計算

このコードでは、「PCR情報サイズ」をゼロに設定していることに注意してください。システム状態にロックせずにデータを暗号化したいだけだからです。ただし、必要に応じて「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 1.2のTPM_CreateWrapKeyコマンドを使用します。TPM 2のTPM2_Createコマンドを使用します。

開発者はどのようにしてTPMのキーをロックしますか?

TPMで作成するか、ラップするか、他の使用可能なメソッドを使用してください。

TMAC2_Create、HMACキーを指定

本のテキストは紛らわしいです。あなたは指定されていないHMACキーを、あなたがしたいことを指定する、HMACキーを

HMACキーが秘密ではないという事実は理にかなっています

いいえ、意味がありません。キーは秘密です。

...ハードウェアデバイスでキーを安全に保ちながら使用する...すばらしい!どうやってやるの!?

TPMの両方のバージョンのキーを作成またはインポートするコマンドがあります。TPM 1の場合、ラップされたキーを作成することでキー階層を確立できるルートキーは1つ(SRK)のみでした。TPM 2では、複数の主キー、つまりルートキーを使用できます。

TPMには、暗号化キーを生成し、ハードウェア境界内でその秘密を保護する機能がありますか?そうですか?

上記を参照。

優れた!これはまさに私が望んでいるユースケースです。これは、MicrosoftがTPMを使用するユースケースでもあります。どうすればいいのですか!?

おそらくドライブのタイプに依存します。SED以外のドライブの場合、ドライブの暗号化キーはTPMキーでラップされている可能性があります。SEDドライブの場合、Admin1パスワード(など)はTPMで封印されます。

保証キーまたはEK ... TPMのどこかにRSA秘密キーがあります。その鍵はそこに閉じ込められています-外の世界からは決して見えません。TPMに秘密キーで何かに署名してほしい(つまり、秘密キーで暗号化する)。

EKは署名鍵ではなく、暗号化鍵です。ただし、これは汎用の暗号化キーではなく、特定のコンテキストでのみ使用できます。

しかし、私が本当にやりたいことは、いくつかのデータを「シール」することです

上記を参照。


2

それが言うとき

HMACキーの指定

それが意味するものではありません提供 HMACキーを-それが意味する「使用したいというHMACキーポイント」

本で指摘されているように、TPMは事実上無制限の数のHMACキーを使用できます。使用するTPMを指定する必要があります。


それでは、C#または別の言語で使用されるHMACキーを指定(ポイント)する方法を示すコードサンプルはあるのでしょうか。
チャド
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.