如何使用TPM(受信任的平台模块)加密字节


110

如何使用机器的TPM模块加密字节?

加密数据

Windows提供了一个(相对)简单的API来使用该CryptProtectDataAPI 加密Blob ,我们可以包装一个易于使用的函数:

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

的细节ProtectBytes比您可以轻松使用它的想法重要得多:

  • 这是我想通过保存在其中的密钥加密的字节 System
  • 还给我加密的斑点

返回的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解密的数据(例如,硬盘驱动器加密主密钥)。换句话说,我想用Windows中的TPM 替换下面针对Android的框图中的Qualcomm可信执行环境(TEE):

在此处输入图片说明

注意:我意识到TPM不会进行数据签名(或者,如果这样做,则不能保证对相同数据进行签名每次都会给出相同的二进制输出)。这就是为什么我愿意替换“ RSA签名”的原因“加密与硬件绑定钥匙的256位二进制大对象”

那么代码在哪里?

问题在于,TPM编程在MSDN上完全没有记录。没有可用于执行任何操作的API。取而代之的是,您必须自己找到可信计算组的软件堆栈(也称为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 I / O命令来创建文本文件

为什么不只使用裤子

可信计算小组(TCG)确实定义了自己的API:TCB软件堆栈(TSS)。此API的实现是由某些人创建的,称为TrouSerS。然后一个人将该项目移植到Windows

该代码的问题在于它无法移植到Windows世界中。例如,您不能在Delphi中使用它,也不能从C#中使用它。这个需要:

  • 的OpenSSL
  • p线程

我只希望代码使用TPM加密某些内容。

上面的内容CryptProtectData只不过是函数体中的内容。

使用TPM加密数据的等效代码是什么?正如其他人指出的那样,您可能必须查阅三本TPM手册,并自己构建blob。它可能涉及TPM_seal命令。尽管我不想密封数据,但我想绑定数据:

绑定 –使用TPM绑定密钥(存储密钥派生的唯一RSA密钥)加密数据。 密封 –以类似于绑定的方式加密数据,但另外指定了一种状态,在该状态下,TPM必须处于解密状态(未密封)

我尝试阅读三个必需的卷,以找到我需要的20行代码:

但是我知道我在读什么。如果有任何教程或示例,我可能会尝试一下。但是我完全迷路了。

所以我们问Stackoverflow

以同样的方式,我能够提供:

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

有人可以提供相应的等效项:

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

除了将密钥锁定在SystemLSA中之外,是否将相同的东西锁定在TPM中?

研究开始

我不知道绑定到底意味着什么。但是在查看TPM Main-Part 3 Commands-Specification Version 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但不使用TPM的应用程序

在过去的几年中,基于Web的应用程序的数量有所增加。其中包括基于Web的备份和存储。现在有许多公司提供此类服务,但是据我们所知,这些服务的客户端中没有一个允许用户将备份服务的密钥锁定到TPM。如果这样做,那么如果将TPM密钥本身复制到多台计算机上来进行备份,那肯定会很好。对于开发人员来说,这似乎是一个机会。

开发人员如何锁定TPM的密钥?

第9章继承体系

使用案例:存储登录密码

典型的密码文件存储密码的哈希哈希。验证包括对提供的密码进行加盐处理和哈希处理,然后将其与存储的值进行比较。由于计算中不包含机密信息,因此密码文件遭到了脱机攻击。

该用例使用TPM生成的HMAC密钥。密码文件存储加盐密码的HMAC。验证包括对提供的密码进行盐析和HMAC处理并将其与存储的值进行比较。由于脱机攻击者没有HMAC密钥,因此攻击者无法通过执行计算来发起攻击。

可能有效。如果TPM有一个秘密的HMAC密钥,并且只有我的TPM知道HMAC密钥,那么我可以用“ HMAC”代替“ Sign(也称为TPM用其私钥加密)”。但是,在接下来的一行中,他完全扭转了自己:

TPM2_Create,指定一个HMAC密钥

如果必须指定HMAC密钥,这不是TPM秘密。当您意识到这是TPM提供的有关加密实用程序的章节时,HMAC密钥不是秘密这一事实变得很有意义。无需您自己编写SHA2,AES,HMAC或RSA,您可以重用TPM已有的内容。

第10章-键

作为一种安全设备,TPM的最大优势就是能够在硬件设备中保持密钥安全的同时使用应用程序。TPM既可以生成也可以导入外部生成的密钥。它支持非对称和对称密钥。

优秀的!你怎么做呢!?

密钥生成器

可以说,TPM的最大优势在于它能够生成加密密钥并在硬件边界内保护其秘密。密钥生成器基于TPM自己的随机数生成器,并且不依赖于外部随机性源。因此,它消除了基于弱软件而缺乏熵源的弱点。

TPM 是否有能力生成加密密钥并在硬件边界内保护其秘密?是这样吗?

第12章-平台配置寄存器

授权PCR

使用案例:密封硬盘加密密钥以达到平台状态

如果TPM保护加密密钥,则与仅存储在同一磁盘上(仅受密码保护)相比,全磁盘加密应用程序要安全得多。首先,TPM硬件具有防锤击保护(有关TPM词典攻击保护的详细说明,请参见第8章),从而使对密码的暴力攻击变得不切实际。仅受软件保护的密钥更容易受到弱密码的攻击。其次,存储在磁盘上的软件密钥更容易被窃取。拿起磁盘(或磁盘的备份),您会得到密钥。当TPM握住密钥时,整个平台,或者至少磁盘和主板必须被盗。

密封不仅允许密钥受密码保护,还可以受策略保护。典型的策略是在密封时将密钥锁定到当前的PCR值(软件状态)。这假设第一次启动时的状态没有受到影响。首次启动时出现的任何预装恶意软件都将被计入PCR,因此,密钥将被密封为受到威胁的软件状态。信任度较低的企业可能具有标准的磁盘映像,并且可以密封代表该映像的PCR。这些PCR值将在可能更受信任的平台上预先计算。甚至更复杂的企业将使用TPM2_PolicyAuthorize,并提供多个票证以授权一组受信任的PCR值。有关策略授权及其在解决PCRbrittleness问题中的应用的详细说明,请参见第14章。

尽管密码也可以保护密钥,但是即使没有TPM密钥密码也可以获得安全性。攻击者可以在不提供TPMkey密码的情况下引导平台,但在没有操作系统用户名和密码的情况下无法登录。OSsecurity保护数据。攻击者可以从实时DVD或USB闪存盘而不是从硬盘驱动器引导替代操作系统,从而绕过操作系统登录安全性。但是,此不同的引导配置和软件将更改PCR值。因为这些新的PCR将与密封值不匹配,所以TPM将不会释放解密密钥,并且无法对硬盘进行解密。

优秀的!这正是我碰巧想要的用例。这也是Microsoft使用TPM的用例。我该怎么做!?

因此,我阅读了整本书,但没有提供任何帮助。这很令人印象深刻,因为它有375页。您想知道书中包含什么-再回头看,我不知道。

因此,我们放弃了对TPM进行编程的权威指南,转而使用Microsoft的一些文档:

来自Microsoft TPM平台加密提供程序工具包。它确切地提到了我想做什么:

认可密钥或EK

EK旨在为平台提供可靠的密码标识符。企业可能会维护属于其企业中所有PC的TPM的认可密钥的数据库,或者数据中心结构控制器可能在所有刀片中都具有TPM的数据库。在Windows上,您可以使用“ Windows 8中的平台加密提供程序”部分中介绍的NCrypt提供程序来读取EK的公共部分。

TPM内部的某个地方是RSA私钥。那个钥匙被锁在里面-外面的世界永远不会看到。我希望TPM用它的私钥签名(即用它的私钥加密)。

所以我想要可能存在的最基本的操作:

在此处输入图片说明

用您的私钥加密某些内容。我什至还没有要求更复杂的东西:

  • 根据PCR状态对其进行“密封”
  • 创建密钥并将其存储在易失性或非易失性存储器中
  • 创建对称密钥并尝试将其加载到TPM中

我要求TPM可以执行的最基本的操作。为什么不可能获得有关如何做的任何信息?

我可以得到随机数据

当我说RSA签名是TPM可以做的最基本的事情时,我想我是glib。在基础的事情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)
{
   //...
}

下一代密码术(Cng,又名BCrypt)支持TPM

Windows中原始的加密API被称为Crypto API。

从Windows Vista开始,Crypto API已替换为Cryptography API:Next Generation(内部称为BestCrypt,缩写为BCrypt,不要与密码哈希算法混淆)。

Windows附带两个BCrypt 提供程序

平台加密提供商未记录在MSDN,但确实有从2012微软研究院网站文档:

TPM平台加密提供程序工具包

TPM平台加密提供程序和工具包包含用于在Windows 8中使用TPM相关功能的示例代码,实用程序和文档。所描述的子系统包括TPM支持的Crypto-Next-Gen(CNG)平台加密提供程序,以及证明服务提供者的方式。可以使用Windows的新功能。支持基于TPM1.2和基于TPM2.0的系统。

看来,微软的意图是与表面TPM加密功能微软平台加密提供了的密码NG API。

使用Microsoft BCrypt进行公钥加密

鉴于:

一种前进的方法可能是弄清楚如何使用Microsoft Cryptography Next Gen API进行数字签名。

我的下一步将是使用标准提供程序(MS_PRIMITIVE_PROVIDER)提出使用RSA公钥在BCrypt中进行加密的代码。例如:

  • 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 Provider(MS_PLATFORM_CRYPTO_PROVIDER)。

2016年2月22日:随着Apple被迫帮助解密用户数据,人们重新关注如何使TPM执行其发明的最简单任务-加密内容。

这大致相当于每个人都拥有一辆汽车,但没人知道如何启动汽车。只要我们能够通过步骤1,它就可以做真正有用和酷的事情。

奖励阅读


对于绑定(加密),没有显式功能可用,也不需要它。您只需在TPM中创建绑定密钥,并使用其公共部分通过系统rsa加密功能(“ RSA / ECB / OAEPWithSHA1AndMGF1Padding”)对对称加密密钥sek进行加密,然后将其保存在正确的结构中(“ TcTssConstants.TSS_ENCDATA_BIND”)。要解除绑定(解密),则只需使用TPM解除绑定功能,然后在所需的任何对称加密功能中使用sek。我之前有一个相当老的代码库,也许有帮助:goo.gl/jV1Ouw
evildead 2015年

从Wikipedia中,绑定–使用TPM绑定密钥(存储密钥派生的唯一RSA密钥)对数据进行加密。 zh_cn.wikipedia.org/wiki/Trusted_Platform_Module听起来像这对命令(TSpi_Data_Bind / TPM_UnBind)足以满足您的需求……
Alex

1
我认为您不必直接使用TPM。它通过标准的CNG / NCryptXXX API和“ Microsoft平台加密提供程序”(对于最新的Windows OS平台,并且如果硬件正常,并且已启用)进行支持。也许你可以看看在“TPM平台加密提供程序工具包在这里:research.microsoft.com/en-us/downloads/...也检查:tiw2013.cse.psu.edu/slides/...
西蒙Mourier

CryptProtectData不一定使用TPM。另一方面,如果您可以为TPM获得有效的CNG或CSP句柄,则可以在加密函数中使用它。
Michael Chourdakis '16年

1
@ b3nj1不,我不是;没有人能够回答这个问题。
伊恩·博伊德

Answers:


7

底漆

接下来是关于TPM 1.2。请记住,Microsoft要求所有将来的Windows版本都使用TPM 2.0。2.0代与1.2代根本不同

由于TPM设计原则,没有一线解决方案。将TPM视为资源有限的微控制器。它的主要设计目标是价格便宜,同时仍然安全。因此,TPM被剥夺了安全操作不需要的所有逻辑。因此,只有当您至少拥有一些或多或少的软件并以正确的顺序发出许多命令时,TPM才起作用。这些命令序列可能会变得非常复杂。这就是为什么TCG使用定义良好的API指定TSS的原因。如果您想采用Java方式,甚至还有一个高级Java API。我不知道C#/ .net的类似项目

发展历程

对于您的情况,建议您查看IBM的软件TPM。

在软件包中,您将找到3个非常有用的组件:

  • 软件TPM模拟器
  • 轻量级的tpm lib
  • 一些基本的命令行实用程序

您不一定需要软件TPM仿真器,也可以连接到计算机的硬件TPM。但是,您可以拦截发出的命令并查看响应,从而了解它们的组装方式以及它们与命令规范的对应关系。

高水平

先决条件:

  1. TPM已激活
  2. TPM驱动程序已加载
  3. 您已拥有TPM的所有权

为了密封斑点,您需要执行以下操作:

  1. 创建密钥
  2. 将钥匙圈存放在某个地方
  3. 确保密钥已加载到TPM中
  4. 密封斑点

要开封,您需要:

  1. 获取密钥
  2. 将密钥加载到TPM
  3. 解开密封的斑点

您可以将密钥blob存储在用于存储受保护字节的数据结构中。

您需要的大多数TPM命令都是授权命令。因此,您需要在需要的地方建立授权会话。这些活动主要是OSAP会话。

TPM命令

目前,我无法运行调试版本,因此无法为您提供确切的顺序。因此,请考虑以下无序命令列表:

  • TPM_OSAP
  • TPM_CreateWrapKey
  • TPM_LoadKey2
  • TPM_Seal

如果您也想读取当前的PCR值:

  • TPM_PCRRead

微软有他们的C#.NET托管库供TPM使用。它们还具有TPM模拟器,如果不存在实际的TPM,则托管库可以将其连接为调试替代项。他们还具有TPM平台提供程序工具包,其中包含使用TPM的文档和示例代码。现在,只要有人可以弄清楚如何使用TPM加密字节。
伊恩·博伊德

您的前两个链接仅是TPM 2.0。如果您想使用这些,恐怕没有帮助。
Scolytus

4

可信和加密密钥

可信密钥和加密密钥是添加到现有内核密钥环服务中的两种新密钥类型。这两种新类型都是可变长度对称密钥,在两种情况下,所有密钥都是在内核中创建的,并且用户空间只能看到,存储和加载加密的Blob。可信密钥要求使用可信平台模块(TPM)芯片以提高安全性,而加密密钥可在任何系统上使用。为了方便起见,所有用户级别的Blob都以十六进制ascii显示和加载,并且已经过完整性验证。

可信密钥使用TPM来生成和密封密钥。密钥在TPM中的2048位RSA密钥下密封,并且可以选择密封到指定的PCR(完整性测量)值,并且只有在PCR和Blob完整性验证匹配时才由TPM密封。可以使用新的(未来)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)密钥长度,并带有所有必要的结构/填充。

加密密钥不依赖TPM,并且速度更快,因为它们使用AES进行加密/解密。新密钥由内核生成的随机数创建,并使用指定的“主”密钥进行加密/解密。“主”密钥可以是受信任密钥或用户密钥类型。加密密钥的主要缺点是,如果它们不根植于受信任的密钥中,则它们的安全性仅与加密它们的用户密钥相同。因此,应以尽可能安全的方式加载主用户密钥,最好是在启动初期。

加密密钥的解密部分可以包含简单的对称密钥,也可以包含更复杂的结构。较复杂的结构的格式是特定于应用程序的,由“格式”标识。

Usage:
    keyctl add encrypted name "new [format] key-type:master-key-name keylen"
        ring
    keyctl add encrypted name "load hex_blob" ring
    keyctl update keyid "update key-type:master-key-name"

format:= 'default | ecryptfs'
key-type:= 'trusted' | 'user'

可信和加密密钥用法示例

创建并保存一个名为“ kmk”的可信密钥,其长度为32个字节:

$ keyctl add trusted kmk "new 32" @u
440502848

$ keyctl show
Session Keyring
       -3 --alswrv    500   500  keyring: _ses
 97833714 --alswrv    500    -1   \_ keyring: _uid.500
440502848 --alswrv    500   500       \_ trusted: kmk

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

$ keyctl pipe 440502848 > kmk.blob

从保存的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”中找到。


您是否知道何时将这两种新类型的键添加到何处?我指的是哪个版本。我目前正在使用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看作是瑞士军方使用的加密设备:您无法使用它做很多事情,但是通用性使易用性受到损害。该问题在对系统配置的加密,签名和锁定之间不断反弹,但是此答案的主要部分将考虑使用Seal命令来满足问题中描述的大多数需求。

现在是时候进一步采取这一步骤。我想加密一些只能由本地TPM解密的数据(例如,硬盘驱动器加密主密钥)。

这就是Bind命令的用途(已被TPM 2的Create命令取代)。您加载从TPM绑定的密钥派生的密钥并对其进行加密(或直接使用硬件绑定的密钥)进行加密。这样,只能通过访问同一TPM来解密数据。

换句话说,我想用Windows中的TPM替换下面针对Android的框图中的Qualcomm可信执行环境(TEE):

不知道复制整个过程是否是一个好主意。首先,在过程中的任何地方都不需要使用签名操作。似乎在开发Android 5时,Keystore API仅限于签名和验证操作。我最好的猜测是,磁盘加密团队会竭尽所能,并设计了一种算法,其中中间密钥之一是通过使用存储的TEE密钥的签名操作导出的,从而将整个过程与硬件绑定在一起-绑定密钥仅在平台上可用-因为签名是当时唯一的方法。但是,如果可以访问TPM,则无需以这种方式约束自己,这将为您提供比您所需要的更多的功能!

我意识到TPM不会进行数据签名

这是错误的,两个版本的TPM都支持签名。

(或者,如果这样做,则不能保证每次签名相同的数据都将给出相同的二进制输出)

这是没有道理的。使用相同的密钥对相同的数据签名产生相同的签名。您可能会混淆签名操作和引用操作,而引用操作会混入随机数。

这就是为什么我愿意将“ RSA签名”替换为“用硬件绑定密钥加密256位Blob”的原因。

尽管TPM都有可能,但这实际上应该是首选选项。往上看。

问题在于,TPM编程在MSDN上完全没有记录。没有可用于执行任何操作的API。

不幸的是,没有太多要记录的东西。Win API仅限于两个TBS功能,这些功能是从驱动程序中删除的一个级别。

取而代之的是,您必须自己找到可信计算组的软件堆栈(也称为TSS)的副本,找出要以有效负载以什么顺序发送到TPM的命令,然后调用Window的Tbsip_Submit_Command函数直接提交命令:

实际上,不,如果您有TSS,则不必使用Tbsip_submit_Command()。这就是拥有TSS的全部要点-低级细节被抽象掉了。

Windows没有更高级别的API来执行操作。

对于TPM 1仍然适用,但对于TPM 2有TSS.MSR

这在道德上等同于尝试通过向硬盘驱动器发出SATA I / O命令来创建文本文件。

正确。

为什么不只使用Trousers ...该代码的问题是它不能移植到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是两者之间唯一的通用代码,并且对Microsoft来说,这也许就是该API从未增长的原因之一。答案的主要部分将出于两个原因显示TPM 1的代码:

  • 这个问题包含了TPM 1的特定概念,因此使用TPM 1的人们更有可能登陆此处进行搜索
  • 对于TPM 2,有Microsoft的TSS实现。

第二,让问题更具体。我将其重新解释如下:

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命令最适合此操作,因为当PCR选择大小设置为零时,它执行与绑定命令相同的功能,但是可以轻松更改PCR选择以包括您想要的任何PCR。令人奇怪的是,为什么Bind命令完全包含在规范中,并且如前所述,它已在TPM 2规范中删除,而两者又合并在一个Create命令中。

这是使用TPM 1.2 Seal命令仅使用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参数只是一个C结构,带有一个无符号的32位int字段,必须将其设置为1才能与TPM 1.2实例进行通信,而我们要对其进行设置。

    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命令输入句柄之一也是OSAP句柄:

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

因此,我们需要首先建立此OSAP会话。在TPM 1.2规范第1部分中描述了OSAP,。发明了OSAP或特定于对象的授权协议,以处理您要使用需要多次授权但又不想每次都提供授权的TPM对象的用例:而是使用OSAP会话,这依赖于关于“共享秘密”的概念,这是一个HMAC它将对象授权数据与每侧生成的随机数混合在一起,以防止回复攻击。因此,“共享秘密”仅在该会话的双方中是已知的:发起会话的一方(用户)和接受会话的一方(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    ....
+---------+------------------+------------------+---------------------------

标签是一个两字节的值,指示后面的内容是输入还是输出,以及命令参数后是否有任何auth数据值。对于TPM_OSAP,根据规范,标签必须为TPM_TAG_RQU_COMMAND(0x00C1),这意味着“未经授权的命令”。

Size是一个四字节的值,以字节为单位指定命令的大小,包括标记和大小本身。一旦计算出它,我们将在稍后设置它。

命令代码是一个四字节的值,可以作为命令ID进行存储:它告诉TPM如何解释其余命令。我们的命令代码是TPM_OSAP(0x0000000B)。

接下来要设置的两件事是实体类型和实体值。由于我们要使用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生成并与main命令一起使用的nonce
  • nonceEvenOSAP:OSAP随机数,它是在发送TPM_OSAP命令之前在我们这一边生成的随机数的反现值。这两个随机数将用于生成“共享机密”。

我们提取这些值并将其存储在变量中。

    byte[] sharedSecretBuf = new byte[evenOsap.Length + oddOsap.Length];
    Array.Copy (evenOsap, 0, sharedSecretBuf, 0, evenOsap.Length);
    Array.Copy (oddOsap, 0, sharedSecretBuf, evenOsap.Length, oddOsap.Length);
    System.Security.Cryptography.HMACSHA1 sharedSecretHmac = new System.Security.Cryptography.HMACSHA1 (srkAuthdata);
    byte[] sharedSecret = sharedSecretHmac.ComputeHash (sharedSecretBuf);

然后我们计算“共享秘密”。根据规范,计算所用的值是两个OSAP随机数(一个由用户生成,一个由TPM生成)和我们要使用的密钥的授权值-SRK。按照惯例,SRK auth值是“众所周知的auth”:归零的20字节缓冲区。从技术上讲,在拥有TPM所有权时,可以将此值更改为其他值,但是实际上并没有这样做,因此我们可以放心地假设“知名auth”值是好的。

接下来,让我们看一下TPM_Seal命令中包含的内容:

TPM_密封

这些参数中的大多数都很容易构建,除了其中两个:encAuthpubAuth。让我们一一看一下。

encAuth是“用于密封数据的加密AuthData”。这里的AuthData是以前的“知名身份验证”,但是是的,我们仍然必须对其进行加密。因为我们使用的是OSAP会话,所以它按照ADIP或授权数据插入协议进行加密。根据规范:“ ADIP允许创建新实体并安全插入新实体AuthData。新AuthData的传输使用基于OSAP会话共享密钥的密钥进行加密。” 此外:“对于强制性XOR加密算法,创建者使用OSAP共享密钥的SHA-1散列和会话随机数构建加密密钥。创建者XOR使用加密密钥作为一次性密码对新的AuthData进行加密,并且将此加密数据以及创建请求发送到TPM。”

下图说明了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处理。” 因此,我们将必须encAuth从上方对纯文本,其大小,PCR信息大小和TPM_Seal序数进行哈希处理,然后对HMAC 进行哈希处理,以两个随机数和“继续会话”布尔值使用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中创建它,或者包装它,或者使用任何其他可用方法。

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不是签名密钥,而是加密密钥。但是,它不是通用加密密钥:只能在某些情况下使用

但是我真正想做的是“密封”一些数据

往上看。


2

当它说

指定HMAC密钥

这并不意味着提供 HMAC密钥-意味着“指向要使用的HMAC密钥”

正如书中指出的那样,TPM可以使用几乎无限数量的HMAC密钥。您必须告诉TPM使用哪个。


那么,也许有一个代码示例显示了如何指定(指向)要在C#或另一种语言中使用的HMAC密钥?
乍得
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.