Tspi_TPM_PcrExtend()

Tspi_TPM_PcrExtend()について誤解していたので整理しておく。


TPMPCRレジスタに値を書きこむ場合はTPMTPM_Extend命令を使う。PC BIOSは独自のAPIがあり、TPMへの書き込みとログの記録ができる。Linux-IMAはTPMへ直接この命令を発行し、ログも自身で管理する。通常のアプリではTSSの経由になり、その際に使う命令が Tspi_TPM_PcrExtend() になる。
しかしながら、振る舞いがちょっと特殊で、BIOSやIMAのようには使えない。APIは以下のように定義される。


TSS_RESULT Tspi_TPM_PcrExtend(
TSS_HTPM hTPM,
UINT32 ulPcrIndex,
UINT32 ulPcrDataLength,
BYTE* pbPcrData,
TSS_PCR_EVENT* pPcrEvent,
UINT32* pulPcrValueLength,
BYTE** prgbPcrValue);


まず、pPcrEventを指定しない(NULL)場合、pbPcrDataの値が直接 TPM_extend でPCRに記録される。したがってpbPcrDataはSHA1ハッシュ値(20バイト)でなければならない。この場合、ログはTSSのでは保持されないので、ログを使った検証に支障が出る。おそらくアプリレベルでPCRを使用する場合、PCR値とログで検証を行うので、かなり特殊な利用方法と言える。


次に、pPcrEventを指定する場合、pPcrEventを元にログが記録される。この場合、pbPcrDataは何でもよく、pbPcrDataを含む下記のハッシュ値が記録される。


Digest = SHA1(pcrindex || pbPcrData[ulPcrDataLength] || eventtype || eventdata[eventsize]);


ここで問題となるのは、pbPcrData自体はログに記録されないため、ログによる検証ができない。eventdataに同じ値を含めることも可能であるが、冗長だし、ファイルの計測の場合はファイル自体をログに記録する事になりナンセンスである。したがって、eventdataに配置する構造体に必要な情報を含めて、pbPcrDataはNULLで利用するのが妥当である。つまり記録されるハッシュ値は以下のようになる。


Digest = SHA1(pcrindex || eventtype || eventdata[eventsize]);


これであれば、イベントログのみから検証が可能である。(ここで、pcrindex、eventtypeは4バイトのビッグエンディアンである。)


これはとても分かりにくい仕様なので、Next仕様では別の2つのAPIに別けるべきだろう。


補足)TPMのイベント管理で厄介なのはエンディアンの問題で、構造体をBYTE ARRAYで扱う場合、その計算がCPUのエンディアンに依存する。これは注意して実装する意外に対処方法はない。