Desktop version

Home arrow Computer Science arrow A Practical Guide to TPM 2.0

Sample Code

This section shows an example of actual working code for doing decrypt and encrypt sessions. First some notes about this code:

• This code does a write of encrypted data to an NV index (decrypt session attribute set) followed by two reads from the same

NV index: a plain text read (encrypt attribute not set) and a

ciphertext read (encrypt session attribute set). After both reads, the read data is compared to the plain text write data.

Note the reason for doing the plain text read is to verify that the nV index was written with plain text, not encrypted data. if you didn't set the decrypt session attribute, encrypted data would be written to the nV index. But the test would still appear to be working because the encrypted data would be written to the tpM, read back, and decrypted by the calling application, and the test to verify the read and write data would pass. this was actually a mistake that i made on my first pass at writing this code.

to catch this issue, do a plain text read of the nV index and compare this to the unencrypted write data. they should be equal.

• This function tests both CFB and XOR mode encryption. CFB is done on the first pass and XOR on the second pass.

• The code demonstrates some new features of the TSS system API code that couldn't be discussed earlier: [1]

Getting and setting of encrypt and decrypt parameters: These calls enable the caller to get the plain text unencrypted command parameters (Tss2_Sys_GetDecryptParam), encrypt them, and then set the encrypted command parameters in the command byte stream (Tss2_Sys_SetDecryptParam) before sending the command. Likewise, Tss2_Sys_GetEncryptParam and Tss2_Sys_SetEncryptParam enable the caller to properly process response parameters that were encrypted by the TPM.

Asynchronous execution (Tss2_Sys_ExecuteAsync and Tss2_Sys_ExecuteFinish): This mode of execution allows the application to send the command (Tss2_Sys_ExecuteAsync), do some work while waiting for the response, and then get

the response (Tss2_Sys_ExecuteFinish) with a configurable timeout.

Synchronous execution calls (Tss2_Sys_Execute): This function will wait forever for a response, so it assumes that the TPM eventually responds.

Setting command authorizations (Tss2_SetCmdAuths) and getting response authorizations (Tss2_GetRspAuths): These functions are used to set command authorization area parameters and get response area parameters, including nonces, session attributes, passwords (for password sessions), and command and response HMACs. In this example they will be used for nonces, session attributes, and the password. Access to the command and response HMACs isn't needed in this code since the code doesn't use HMACs.

• This code relies heavily on an application-level structure, SESSION, that maintains all session state information including nonces. There are many ways this can be done—this just happens to be the implementation I chose. This structure looks like this:

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;

• The RollNonces function does what it says: it copies nonceNewer to nonceOlder and copies the new nonce to nonceNewer. The nonces must be rolled before each command and after each response, as described in Chapter 13. Here's the complete code for this function:

void RollNonces( SESSION *session, TPM2B_NONCE *newNonce )

{

session->nonceOlder = session->nonceNewer; session->nonceNewer = *newNonce;

}

• The StartAuthSessionWithParams function starts the session, saves its state in a SESSION structure, and adds the SESSION structure to a list of open sessions.

• The EndAuthSession function is used to remove the SESSION structure from the list of open sessions after the session has ended.

• EncryptCommandParam encrypts command parameters, and DecryptResponseParam decrypts response parameters. Both functions examine the authorization structures' TPMA_SESSION bits to determine if the decrypt and/or encrypt bits are set. This chapter doesn't describe the details of these functions, but they perform the encryption and decryption operations as explained in Part 1 of the TPM 2.0 specification.

• For some of the common routines and data structures that aren't described here, please refer to Chapters 7 and 13 as well as the TSS System API specification.

This working code can be downloaded in source form as part of the TSS System API library code and tests. Because the code is a bit long, to help you better understand the flow, notes are interspersed before each major block of functionality. And now for the actual code:

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( " DECRYPT/ENCRYPT SESSION TESTS: " );

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

}

else

{

} // AES encryption/decryption and CFB mode. symmetric.algorithm = TPM_ALG_AES; symmetric.keyBits.aes = 128; symmetric.mode.aes = TPM_ALG_CFB;

// 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 " ); 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 " ); Cleanup();}

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

}

Summary

As you can see, there is a fair amount of work involved in using decrypt and encrypt sessions. Abstracting this work into well-designed functions or even using a higher-level API such as the Feature API helps to reduce this work.

Decrypt and encrypt sessions provide secrecy for sensitive information while in transit to and from the TPM. You've seen what they do, some use cases, and how to program them using the TSS System API, and you've learned about some new functionality in the System API.

The next chapter focuses on TPM context management.

  • [1] For details on these system API calls, review the TSS System Level API and TPM Command Transmission Interface Specification at trustedcomputinggroup.org/developers/ software_stack.
 
< Prev   CONTENTS   Next >

Related topics