2B or not 2B, that is the question
--Dave Challener,在TCG TSS工作组讨论加密解密会话期间。
第13章简单地介绍了加密和解密会话。你可能还记得,这些都是单个命令相关的修饰符。这一章将会详细介绍这两个会话修饰符:它们用来做什么,它们的实际应用,它们的一些限制,怎样设置它们,以及一些代码示例。
简单来说,加密解密会话保护秘密信息在不安全的信息传输媒介中传送。命令调用者为了保证数据的机密性,可以使用一个他和TPM都知道的加密秘钥来加密密令参数。加密秘钥部分由启动会话的参数决定(稍后介绍更多)。一个解密会话之后会通知TPM第一个参数被加密了。这就意味着TPM受到命令的参数后必须解密它——比如名称,解密会话。对于一个命令响应来说,一个加密会话表明TPM已经加密了第一个命令响应参数,这也是会话称作加密会话的原因。调用者在受到加密的命令响应参数后,他会使用命令响应参数解密秘钥来解密数据。
加密解密会话支持两种类型的对称秘钥模式:XOR和CFB。CFB模式的安全强度较高,但是这需要TPM和命令调用者需要支持哈希算法和加密算法。XOR仅仅要求支持哈希算法,并且适合小型代码工程,但是安全性相对较低。
所以,到底这些加密的模式有什么好处呢?简单快速的回答就是它们有很多用处;参考TPM2.0规范的第3部分中每个包含TPM2B数据作为第一个命令或者命令响应参数的命令。所有这些参数都有可能被加密。
一些小的通用案例如下:
- TPM2_Create:第一个命令参数inSensitive,这是一个包含口令的数据结构(叫做userAuth)。这个参数很可能以加密的方式发送到TPM,这就要求会话被配置为解密会话。
- 写入或者读出TPM NV索引区域的机密数据。假设你想使用TPM NV索引存储区域保存口令和个人信用卡等信息。在发送到TPM或者从TPM接受之前将这些数据加密可以保护它们不被泄露。
- 当我们通过网络与一个远程TPM通信时,使用加密解密会话就显得更加重要。假设你想把秘钥存储到远程服务器,然后在客户端上恢复它们。以明文的方式发送到服务器显然是不安全的。SSL会话可以缓解网络监听的缺点,但是秘钥仍然会以明文存在于客户端和服务器上的多层软件中。加密解密会话可以大大减小攻击面。
机密解密会话对参数的加密解密有一定的限制,同时一个命令对加密解密会话的数量也有限制。
只有命令的第一个参数可以被加密,并且只有命令响应的第一个参数可以被解密。并且在这两中前提下,参数还必须是TPM2B类型的参数。第一个参数不是TPM2B类型的命令不能使用机密会话发送到TPM;同样地,第一个参数不是TPM2B类型的命令响应也不能通过加密会话接受。
你已经在第13章中学过,命令可以有三个会话参数。但是,一个命令最多只能由一个加密会话和一个解密会话。如果一个命令同时允许使用解密和加密会话,可以使用具有两种属性的会话或者两个独立的会话(一个会话分别配置一个属性)。
所以我们到底怎样使能加密解密会话呢?
乍一看,将会话配置成加密以及(或者)解密会话非常简单。对于一个普通的会话来说,所有你需要做的事就是设置命令授权区域中的会话属性:sessionAttributes.decrypt以及sessionAttributes.encrypt。
当然了,事情常常不是这么简单的,这里就更是这样了。对于一个解密会话来说,调用者必须以合适的方式加密第一个参数。同理,对于加密会话来说,命令调用者必须以合适的方式解密从TPM收到的第一个命令响应参数。有两种加密模式可以用于加密解密会话:XOR一个CFB模式。启动会话的时候会设定加密模式。这两种模式都要求明文和密文具有相同的长度,因此数据流的长度不会变化。加密过程会加入会话nonce,这就保证了加密数据块只能使用一次。
对于XOR模式来说,它会使用一个mask(一个一次性的填充)与将要加密或者解密的数据做XOR操作。mask的生成需要一下信息:hashAlg(启动会话时传递的authHash参数),HMAC密钥,字符串“XOR”,nonceNewer,nonceOlder,以及KDFa的输出消息大小。输出的结果与将要被加密或者解密的数据长度相同。一个mask与数据的简单XOR操作就完成了XOR模式的加解密操作。
对于CFB模式来说,KDFa用于生成加密密钥和初始向量(Initialization Vector)。此时KDFa的输入参数包括hashAlg,sessionKey,字符串“CFB”,nonceNewer,nonceOlder,以及对称密钥和IV所需的位数(bits)。输出是bits长度的字符串,其中密钥位于较高的位置,IV则处于低位。IV的大小由加密算法的数据块大小决定。密钥和IV会作为加密算法的输入参数用于加解密操作。
对于XOR和CFB这两种模式来说,nonceNewer和nonceOlder都会用到加密中。对于XOR模式因为nonce会变化,所以命令响应和命令使用不同的mask值。类似的情况是,在CFB模式中,命令和命令响应使用的密钥和IV也不同。对于这两种模式来说,因为每次使用会话nonce都会变,所以加密解密会话被用作是一次性垫子。
你可能会回忆起第13章的内容,会话可以使以下三种类型的一种:HMAC,Policy,或者Trial Policy会话。HMAC和Policy会话可以用作加密解密会话;Trial Policy则不能。
为了简单起见,以下的示例使用一个不用于授权的unbound,unsalted的Policy会话。这个会话仅仅用于加密和解密命令及命令响应参数。示例代码使用一个独立的会话用于授权。这就意味着这个测试代码的加密解密会话不需要计算HMAC或者管理policyDigest。
启动一个会话后,TPM会生成一个会话密钥。为了使用加密和解密会话,调用者需要独立产生这个会话密钥,就像他为了使用HMAC和Policy会话而不得不做的一些额外工作一样。
为了将加密解密会话统一成一个流程,它们涉及的相关步骤如下:
- 使用TPM2_StartAuthSession,并设置对称加密算法相关的参数如下:
- CFB模式:
// AES encryption/decryption and CFB mode. symmetric.algorithm = TPM_ALG_AES; symmetric.keyBits.aes = 128; symmetric.mode.aes = TPM_ALG_CFB;
- XOR模式:
// XOR encryption/decryption. symmetric.algorithm = TPM_ALG_XOR; symmetric.keyBits.exclusiveOr = TPM_ALG_SHA256;
- 生成会话密钥,并保存它。
- 对于第一个参数是TPM2B数据类型的命令,如果你希望加密这个参数,按以下步骤操作:
- 生成HMAC密钥。这个过程会使用会话密钥。
- 对于CFB模式: 1. 生成加密密钥和IV,这会使用会话的哈希算法,HMAC密钥,特殊标志(“CFB”),nonceNewer,nonceOlder,需要加密数据的比特长度。 2. 使用刚刚生成的加密密钥和IV加密第一个参数。
- 对于XOR模式: 1. 生成mask值,需要用到HMAC密钥,会话的哈希算法,nonceNewer,nonceOlder,以及需要加密的字节长度。 2. 使用mask值与明文做XOR操作从而生成密文。
- 设置sessionAttributes.decrypt。
- 如果命令响应的第一个参数也是TPM2B类型的数据,并且你希望TPM加密它,那就设置sessionAttributes.encrypt。
- 向TPM发送命令。
- 从TPM接收命令响应。
- 如果命令响应的第一个参数是TPM2B类型的数据,并且sessionAttributes.encrypt被置位,按以下步骤操作。
- 生成会话用的HMAC密钥。这个密钥的生成会使用会话密钥。
- 对于CFB模式: 1. 生成加密密钥和IV,这会使用会话的哈希算法,HMAC密钥,特殊标志(“CFB”),nonceNewer,nonceOlder,需要解密数据的比特长度。 2. 使用刚刚生成的加密密钥和IV解密第一个参数。
- 对于XOR模式: 1. 生成mask值,需要用到HMAC密钥,会话的哈希算法,nonceNewer,nonceOlder,以及需要解密的字节长度。 2. 使用mask值与密文做XOR操作从机密出第一个命令响应参数。
关于CFB以及XOR加密解密的详细信息,请参考TPM2.0规范第1部分中“Session-based encryption”这一节。
这一小结展示了一个能够实际运行的使用加解密会话的代码示例。以下是关于代码的一些说明:
- 这段代码首先将一个加密的数据写入NV索引中(sessionAttribute.decrypt被设置),然后是针对这个NV索引的两次读操作:一个明文读(sessionAttributes.encrypt没有置位)以及一个密文读(sessionAttributes.encrypt置位)。两次读操作完成以后,读回的数据将会与之前写入的明文数据比较。
注意:增加一次明文读操作是为了验证写操作时实际写到NV区域的数据是明文数据,而不是加密过的数据。如果你在写操作时不设置sessionAttributes.decrypt属性,那NV中的数据就是加密的数据。因此,即使不设置decrypt属性,读操作也会通过,因为读回来的数据经过相同的密钥解密之后仍然与原数据相同。我在第一次写这个测试代码时就犯了这样的错误。
为了避免上述的设置错误,增加一个明文读操作,TPM返回的数据应该直接可以和明文数据对应。
- 这个函数测试了CFB和XOR两种加密模式。第一次时CFB,第二次时COR。
- 这段代码展示了之前没有讨论过的TSS SAPI新特性:
- 设置或者获取加密解密参数:Tss2_Sys_GetDecryptParam命令可以返回未加密的命令参数,用户加密之后可以通过Tss2_Sys_SetDecryptParam将加密后的参数写回到命令数据流中,然后用户就可以发送命令了。类似地,Tss2_Sys_GetEncryptParam和Tss2_Sys_SetEncryptParam用于帮助用户处理TPM返回的加密数据。
- 异步执行(Tss2_Sys_ExecuteAsync和Tss2_Sys_ExecuteFinish):这种执行方式允许用户使用Tss2_Sys_ExecuteAsync命令发送命令之后接着去做别的事情,稍后在合适的时候使用Tss2_Sys_ExecuteFinish命令接收命令响应,并且后者可以设置超时。
- 同步执行(Tss2_Sys_Execute):这个命令会一直等待命令响应,等待期间软件所在线程不能做任何事情,所以它会假设TPM总是会返回数据(做过开发就知道,driver等软件中也都是有超时的,不可能无限等)。
- 设置命令的授权(Tss2_SetCmdAuths)以及获取命令响应授权(Tss2_GetRspAuths):这两个函数分别用于设置命令授权区域的参数和获取命令响应参数,这些参数包括nonces,会话属性,口令(口令会话),以及命令和命令响应HMAC。在这个例子中它们会用于nonces,会话属性,以及口令。因为这段代码并没有使用HMAC,所以这两个命令也就不会用于HMAC了。
- 这段代码重度依赖应用层的数据结构,SESSION,这个数据结构用于维护会话的状态信息,包括nonces。维护会话的状态有很多方式——这仅仅我选择的方式。这个数据结构如下:
typedef struct {
// Inputs to StartAuthSession; these need to be saved
// so that HMACs can be calculated.
TPMI_DH_OBJECT tpmKey;
TPMI_DH_ENTITY bind;
TPM2B_ENCRYPTED_SECRET encryptedSalt;
TPM2B_MAX_BUFFER salt;
TPM_SE sessionType;
TPMT_SYM_DEF symmetric;
TPMI_ALG_HASH authHash;
// Outputs from StartAuthSession; these also need
// to be saved for calculating HMACs and
// other session related functions.
TPMI_SH_AUTH_SESSION sessionHandle;
TPM2B_NONCE nonceTPM;
// Internal state for the session
TPM2B_DIGEST sessionKey;
TPM2B_DIGEST authValueBind; // authValue of bind object
TPM2B_NONCE nonceNewer;
TPM2B_NONCE nonceOlder;
TPM2B_NONCE nonceTpmDecrypt;
TPM2B_NONCE nonceTpmEncrypt;
TPM2B_NAME name; // Name of the object the session handle
// points to. Used for computing HMAC for
// any HMAC sessions present.
//
void *hmacPtr; // Pointer to HMAC field in the marshalled
// data stream for the session.
// This allows the function to calculate
// and fill in the HMAC after marshalling
// of all the inputs is done.
//
// This is only used if the session is an
// HMAC session.
//
UINT8 nvNameChanged;// Used for some special case code
// dealing with the NV written state.
} SESSION;
- RollNonces函数做如下操作:将nonceNewer赋值给nonceCaller,然后将新的nonce赋值给nonceNewer。每次命令发送之前或者接收到命令响应之后,都要执行这样的操作,这一点我们在第13章已经有说明。如下是这个函数的源码:
void RollNonces( SESSION *session, TPM2B_NONCE *newNonce )
{
session->nonceOlder = session->nonceNewer;
session->nonceNewer = *newNonce;
}
- StartAuthSessionWithParams函数用于启动一个会话,然后将会话的状态保存到SESSION数据结构中,然后将这个SESSION数据结构添加到一个已启动会话列表中。
- EndAuthSession函数用于加密命令参数,DecryptResponseParam用于解密命令响应参数。这两个命令都会检查授权数据结构中的TPMA_SESSION来确认decrypt或者encrypt是否置位。这一章并不会详细介绍这个函数,但是它们的操作与TPM2.0规范第1部分中的参数加密解密操作相同。
- 还有一些通用的函数和数据结构没有在这里介绍,请参考第7和第13章以及TSS SAPI规范。
这段代码可以在 TSS SAPI 软件库代码和测试代码中找到(github tpm2-software)。因为代码有点长,为了更好地帮助你理解它的工作流程,在主要功能代码块之前都添加了注释。现在就正式看代码吧:
UINT32 writeDataString = 0xdeadbeef;
void TestEncryptDecryptSession()
{
TSS2_RC rval = TSS2_RC_SUCCESS;
SESSION encryptDecryptSession;
TPMT_SYM_DEF symmetric;
TPM2B_MAX_NV_BUFFER writeData, encryptedWriteData;
TPM2B_MAX_NV_BUFFER encryptedReadData, decryptedReadData,
readData;
size_t decryptParamSize;
uint8_t *decryptParamBuffer;
size_t encryptParamSize;
uint8_t *encryptParamBuffer;
TPM2B_AUTH nvAuth;
TPM2B_DIGEST authPolicy;
TPMA_NV nvAttributes;
int i;
TPMA_SESSION sessionAttributes;
the following lines set up the authorization used for the nV Undefine command.
// Authorization structure for undefine command.
TPMS_AUTH_COMMAND nvUndefineAuth;
// Create and init authorization area for undefine command:
// only 1 authorization area.
TPMS_AUTH_COMMAND *nvUndefineAuthArray[1] = { &nvUndefineAuth };
// Authorization array for command (only has one auth structure).
TSS2_SYS_CMD_AUTHS nvUndefineAuths = { 1, &nvUndefineAuthArray[0] };
printf( "\n\nDECRYPT/ENCRYPT SESSION TESTS:\n" );
Copy the write data array into a TPM2B structure.
writeData.t.size = sizeof( writeDataString );
memcpy( (void *)&writeData.t.buffer, (void *)&writeDataString,
sizeof( writeDataString ) );
Create the nV index.
// Create NV index with empty auth value.
*(UINT32 *)( (void *)&nvAttributes ) = 0;
nvAttributes.TPMA_NV_AUTHREAD = 1;
nvAttributes.TPMA_NV_AUTHWRITE = 1;
nvAttributes.TPMA_NV_PLATFORMCREATE = 1;
// No authorization required.
authPolicy.t.size = 0;
nvAuth.t.size = 0;
rval = DefineNvIndex( TPM_RH_PLATFORM, TPM_RS_PW,
&nvAuth, &authPolicy, TPM20_INDEX_TEST1,
TPM_ALG_SHA1, nvAttributes,
sizeof( writeDataString ) );
//
// 1st pass with CFB mode.
// 2nd pass with XOR mode.
//
for( i = 0; i < 2; i++ )
{
Set up authorization structures for nV read and write commands and responses.
// Authorization structure for NV
// read/write commands.
TPMS_AUTH_COMMAND nvRdWrCmdAuth;
// Authorization structure for
// encrypt/decrypt session.
TPMS_AUTH_COMMAND decryptEncryptSessionCmdAuth;
// Create and init authorization area for
// NV read/write commands:
// 2 authorization areas.
TPMS_AUTH_COMMAND *nvRdWrCmdAuthArray[2] =
{ &nvRdWrCmdAuth, &decryptEncryptSessionCmdAuth };
// Authorization array for commands
// (has two auth structures).
TSS2_SYS_CMD_AUTHS nvRdWrCmdAuths =
{ 2, &nvRdWrCmdAuthArray[0] };
// Authorization structure for NV read/write responses.
TPMS_AUTH_RESPONSE nvRdWrRspAuth;
// Authorization structure for decrypt/encrypt
// session responses.
TPMS_AUTH_RESPONSE decryptEncryptSessionRspAuth;
// Create and init authorization area for NV
// read/write responses: 2 authorization areas.
TPMS_AUTH_RESPONSE *nvRdWrRspAuthArray[2] =
{ &nvRdWrRspAuth, &decryptEncryptSessionRspAuth };
// Authorization array for responses
// (has two auth structures).
TSS2_SYS_RSP_AUTHS nvRdWrRspAuths =
{ 2, &nvRdWrRspAuthArray[0] };
Set the session for CFB or Xor mode encryption/decryption, depending on which pass
through the code is being run. then start the policy session.
// Setup session parameters.
if( i == 0 )
{
// AES encryption/decryption and CFB mode.
symmetric.algorithm = TPM_ALG_AES;
symmetric.keyBits.aes = 128;
symmetric.mode.aes = TPM_ALG_CFB;
}
else
{
// XOR encryption/decryption.
symmetric.algorithm = TPM_ALG_XOR;
symmetric.keyBits.exclusiveOr = TPM_ALG_SHA256;
}
// Start policy session for decrypt/encrypt session.
rval = StartAuthSessionWithParams( &encryptDecryptSession,
TPM_RH_NULL, TPM_RH_NULL, 0, TPM_SE_POLICY,
&symmetric, TPM_ALG_SHA256 );
CheckPassed( rval );
Write the nV index using a password session for authorization and a policy session for
encryption/decryption. First marshal the input parameters (Tss2_Sys_NV_Prepare).
//
// Write TPM index with encrypted parameter used
// as the data to write. Set session for encrypt.
// Use asynchronous APIs to do this.
//
// 1st time: use null buffer, 2nd time use populated one;
// this tests different cases for SetDecryptParam function.
//
// Prepare the input parameters, using unencrypted
// write data. This will be encrypted before the
// command is sent to the TPM.
rval = Tss2_Sys_NV_Write_Prepare( sysContext,
TPM20_INDEX_TEST1, TPM20_INDEX_TEST1,
( i == 0 ? (TPM2B_MAX_NV_BUFFER *)0 : &writeData ),
0 );
CheckPassed( rval );
Set the authorization structures (Tss2_Sys_SetCmdAuths) for the command.
// Set up password authorization session structure.
nvRdWrCmdAuth.sessionHandle = TPM_RS_PW;
nvRdWrCmdAuth.nonce.t.size = 0;
*( (UINT8 *)((void *)&nvRdWrCmdAuth.sessionAttributes ) ) = 0;
nvRdWrCmdAuth.hmac.t.size = nvAuth.t.size;
memcpy( (void *)&nvRdWrCmdAuth.hmac.t.buffer[0],
(void *)&nvAuth.t.buffer[0],
nvRdWrCmdAuth.hmac.t.size );
// Set up encrypt/decrypt session structure.
decryptEncryptSessionCmdAuth.sessionHandle =
encryptDecryptSession.sessionHandle;
decryptEncryptSessionCmdAuth.nonce.t.size = 0;
*( (UINT8 *)((void *)&sessionAttributes ) ) = 0;
decryptEncryptSessionCmdAuth.sessionAttributes =
sessionAttributes;
decryptEncryptSessionCmdAuth.sessionAttributes.continueSession
= 1;
decryptEncryptSessionCmdAuth.sessionAttributes.decrypt = 1;
decryptEncryptSessionCmdAuth.hmac.t.size = 0;
rval = Tss2_Sys_SetCmdAuths( sysContext, &nvRdWrCmdAuths );
CheckPassed( rval );
Get the location and size of the decrypt parameter in the byte stream
(Tss2_Sys_GetDecryptParam), encrypt the write data (EncryptCommandParam), and copy
the encrypted write data into the byte stream (Tss2_Sys_SetDecryptParam).
// Get decrypt parameter.
rval = Tss2_Sys_GetDecryptParam( sysContext,
&decryptParamSize,
(const uint8_t **)&decryptParamBuffer );
CheckPassed( rval );
if( i == 0 )
{
// 1st pass: test case of Prepare inputting a NULL decrypt
// param; decryptParamSize should be 0.
if( decryptParamSize != 0 )
{
printf( "ERROR!! decryptParamSize != 0\n" );
Cleanup();
}
}
// Roll nonces for command.
RollNonces( &encryptDecryptSession,
&decryptEncryptSessionCmdAuth.nonce );
// Encrypt write data.
rval = EncryptCommandParam( &encryptDecryptSession,
(TPM2B_MAX_BUFFER *)&encryptedWriteData,
(TPM2B_MAX_BUFFER *)&writeData, &nvAuth );
CheckPassed( rval );
// Now set decrypt parameter.
rval = Tss2_Sys_SetDecryptParam( sysContext,
(uint8_t )encryptedWriteData.t.size,
(uint8_t *)&encryptedWriteData.t.buffer[0] );
CheckPassed( rval );
Write the nV data (Tss2_Sys_ExecuteAsync and Tss2_Sys_ExecuteFinish). the write uses
asynchronous calls to illustrate this feature of the tSS System api.
// Now write the data to the NV index.
rval = Tss2_Sys_ExecuteAsync( sysContext );
CheckPassed( rval );
rval = Tss2_Sys_ExecuteFinish( sysContext, -1 );
CheckPassed( rval );
Get the response authorizations to set up for the next use of the sessions
(Tss2_Sys_GetRspAuths).
rval = Tss2_Sys_GetRspAuths( sysContext, &nvRdWrRspAuths );
CheckPassed( rval );
// Roll the nonces for response
RollNonces( &encryptDecryptSession,
&nvRdWrRspAuths.rspAuths[1]->nonce );
// Don't need nonces for anything else, so roll
// the nonces for next command.RollNonces( &encryptDecryptSession,
&decryptEncryptSessionCmdAuth.nonce );
read the data back as plain text to be sure the decrypt session worked correctly during the
nV write operation.
// Now read the data without encrypt set.
nvRdWrCmdAuths.cmdAuthsCount = 1;
nvRdWrRspAuths.rspAuthsCount = 1;
rval = Tss2_Sys_NV_Read( sysContext, TPM20_INDEX_TEST1,
TPM20_INDEX_TEST1, &nvRdWrCmdAuths,
sizeof( writeDataString ), 0, &readData,
&nvRdWrRspAuths );
CheckPassed( rval );
nvRdWrCmdAuths.cmdAuthsCount = 2;
nvRdWrRspAuths.rspAuthsCount = 2;
// Roll the nonces for response
RollNonces( &encryptDecryptSession,
&nvRdWrRspAuths.rspAuths[1]->nonce );
// Check that write and read data are equal. This
// verifies that the decrypt session was set up correctly.
// If it wasn't, the data stored in the TPM would still
// be encrypted, and this test would fail.
if( memcmp( (void *)&readData.t.buffer[0],
(void *)&writeData.t.buffer[0], readData.t.size ) )
{
printf( "ERROR!! read data not equal to written data\n" );
Cleanup();
} n
ow read the nV data encrypted using an encrypt session. this time, use a synchronous
call, Tss2_Sys_Execute. the reason is simply to demonstrate another method; you could
use asynchronous calls similar to how the nV write was performed.
//
// Read TPM index with encrypt session; use
// synchronous APIs to do this.
//
rval = Tss2_Sys_NV_Read_Prepare( sysContext, TPM20_INDEX_TEST1,
TPM20_INDEX_TEST1, sizeof( writeDataString ), 0 );
CheckPassed( rval );
// Roll the nonces for next command.
RollNonces( &encryptDecryptSession,
&decryptEncryptSessionCmdAuth.nonce );
decryptEncryptSessionCmdAuth.sessionAttributes.decrypt = 0;
decryptEncryptSessionCmdAuth.sessionAttributes.encrypt = 1;
decryptEncryptSessionCmdAuth.sessionAttributes.continueSession = 1;
rval = Tss2_Sys_SetCmdAuths( sysContext, &nvRdWrCmdAuths );
CheckPassed( rval );
//
// Now Read the data.
//
rval = Tss2_Sys_Execute( sysContext );
CheckPassed( rval );
Use Tss2_Sys_GetEncryptParam and Tss2_Sys_SetEncryptParam combined with
DecryptResponseParam to decrypt the response data.
rval = Tss2_Sys_GetEncryptParam( sysContext, &encryptParamSize,
(const uint8_t **)&encryptParamBuffer );
CheckPassed( rval );
rval = Tss2_Sys_GetRspAuths( sysContext, &nvRdWrRspAuths );
CheckPassed( rval );
// Roll the nonces for response
RollNonces( &encryptDecryptSession,
&nvRdWrRspAuths.rspAuths[1]->nonce );
// Decrypt read data.
encryptedReadData.t.size = encryptParamSize;
memcpy( (void *)&encryptedReadData.t.buffer[0],
(void *)encryptParamBuffer, encryptParamSize );
rval = DecryptResponseParam( &encryptDecryptSession,
(TPM2B_MAX_BUFFER *)&decryptedReadData,
(TPM2B_MAX_BUFFER *)&encryptedReadData, &nvAuth );
CheckPassed( rval );
// Roll the nonces.
RollNonces( &encryptDecryptSession,
&nvRdWrRspAuths.rspAuths[1]->nonce );
rval = Tss2_Sys_SetEncryptParam( sysContext,
(uint8_t)decryptedReadData.t.size,
(uint8_t *)&decryptedReadData.t.buffer[0] );
CheckPassed( rval );
// Get the command results, in this case the read data.
rval = Tss2_Sys_NV_Read_Complete( sysContext, &readData );
CheckPassed( rval );
printf( "Decrypted read data = " );
DEBUG_PRINT_BUFFER( &readData.t.buffer[0], (UINT32 )readData.t.size );
// Check that write and read data are equal.
if( memcmp( (void *)&readData.t.buffer[0],
(void *)&writeData.t.buffer[0], readData.t.size ) )
{
printf( "ERROR!! read data not equal to written data\n" );
Cleanup();
}
rval = Tss2_Sys_FlushContext( sysContext,
encryptDecryptSession.sessionHandle );
CheckPassed( rval );
rval = EndAuthSession( &encryptDecryptSession );
CheckPassed( rval );
}
Delete the nV index.
// Set authorization for NV undefine command.
nvUndefineAuth.sessionHandle = TPM_RS_PW;
nvUndefineAuth.nonce.t.size = 0;
*( (UINT8 *)((void *)&nvUndefineAuth.sessionAttributes ) ) = 0;
nvUndefineAuth.hmac.t.size = 0;
// Undefine NV index.
rval = Tss2_Sys_NV_UndefineSpace( sysContext,
TPM_RH_PLATFORM, TPM20_INDEX_TEST1, &nvUndefineAuths, 0 );
CheckPassed( rval );
}
由上述的示例代码以及前面的介绍中我们可以看到,使用加密解密会话会增加相当大的一部分工作。减少工作量的方式是将相关操作抽象成设计良好的函数,或者使用较高层次的API,例如FAPI。
加密和解密会话可以在向TPM传送或从TPM设备接收隐私信息时提供保密性。这一节你看到它们到底做了什么,看了一些应用案例,以及怎样使用 TSS SAPI来应用它们,同时你也学习了SAPI的一些新功能。
下一章我们将集中介绍TPM上下文管理。