HMAC and Policy Session Code Example
Listing 13-2 presents a simple example of how to execute HMAC and policy sessions. This function is known to work, and all of its support routines are available in the TSS SAPI test code described in Chapter 7. To keep the code as simple as possible, it uses an unbound and unsalted session. If you'd like to see more complicated examples, all variations of bound/unbound and salted/unsalted are tested in the HmacSessionTest in the TSS SAPI test code.
■ Note
Managing hMaC sessions and calculating hMaC authorizations are complicated tasks. some of the functions called in Listing 13-2 are only explained at a high level. the goal was to demonstrate the high-level flow of an hMaC authorization without overwhelmingyou with low-level details. if you want to dig deeper, the source code for all the subroutines is available at the web site noted for the tss sapi test code in Chapter 7.
To help compare HMAC and policy sessions, this code does HMAC or policy authorizations, depending on the value of the hmacTest input parameter; if conditional statements using the hmacTest parameter are used for all the HMACor policy-specific code. For now, because this section is about HMAC authorizations, ignore all the parts of the code that only pertain to policy sessions (these areas are shaded).
Some notes about this code:
• The code does a write to an NV index followed by a read of the same NV index. Both the read and write are authorized using an HMAC authorization.
• To authorize the read and write operations, this code uses either an HMAC session or a policy session with a TPM2_ PolicyAuthValue command. This provides similar capability
(both sessions use an HMAC for authorization), and thus provides a useful vehicle for comparing HMAC and policy sessions.
• 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. This is described more in the section “Using an HMAC Session to Send Multiple Commands (Rolling Nonces).” Here's the complete code for this function:
void RollNonces( SESSION *session, TPM2B_NONCE *newNonce )
{session->nonceOlder = session->nonceNewer; session->nonceNewer = *newNonce;}
this code example uses a single byte nonceCaller for both the nV write and read commands. this isn't a recommended usage—typically, to maximize replay protection, you would use a nonce that is the size of the session's selected hash algorithm, and you would use different randomly generated nonces for each command being authorized.
• 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;
• StartAuthSessionWithParams starts the session, saves its state in a SESSION structure, and adds the SESSION structure to the application's list of open sessions.
• EndAuthSession is used to remove the SESSION structure from the application's list of open sessions after the session has ended.
Listing 13-2. Simple HMAC and Policy Code Example
1 void SimpleHmacOrPolicyTest( bool hmacTest ) 2 {
3 UINT32 rval, sessionCmdRval;
4 TPM2B_AUTH nvAuth;
5 SESSION nvSession, trialPolicySession;
6 TPMA_NV nvAttributes;
7 TPM2B_DIGEST authPolicy;
8 TPM2B_NAME nvName;
9 TPM2B_MAX_NV_BUFFER nvWriteData, nvReadData;
10 UINT8 dataToWrite[] = { 0x00, 0xff, 0x55, 0xaa };
11 char sharedSecret[] = "shared secret";
12 int i;
13 TPM2B_ENCRYPTED_SECRET encryptedSalt;
14 TPMT_SYM_DEF symmetric;
15 TPMA_SESSION sessionAttributes;
16 TPM_SE tpmSe;
17 char *testString;
set up authorizations for nV index creation and deletion.
18 // Command authorization area: one password session.
19 TPMS_AUTH_COMMAND nvCmdAuth = { TPM_RS_PW, };
20 TPMS_AUTH_COMMAND *nvCmdAuthArray[1] = { &nvCmdAuth };
21 TSS2_SYS_CMD_AUTHS nvCmdAuths = { 1, &nvCmdAuthArray[0] }; 22
23 // Response authorization area.
24 TPMS_AUTH_RESPONSE nvRspAuth;
25 TPMS_AUTH_RESPONSE *nvRspAuthArray[1] = { &nvRspAuth };
26 TSS2_SYS_RSP_AUTHS nvRspAuths = { 1, &nvRspAuthArray[0] }; 27
28 if( hmacTest )
29 testString = "HMAC";
30 else
31 testString = "POLICY"; 32
33 printf( " SIMPLE %s SESSION TEST: ", testString );
34
35 // Create sysContext structure.
36 sysContext = InitSysContext( 1000, resMgrTctiContext, &abiVersion );
37 if( sysContext == 0 )
38 {
39 InitSysContextFailure(); 40 }
Create the nV index, with either an hMaC or a policy authorization required.
41 // Setup the NV index's authorization value.
42 nvAuth.t.size = strlen( sharedSecret );
43 for( i = 0; i < nvAuth.t.size; i++ )
44 nvAuth.t.buffer[i] = sharedSecret[i]; 45
46 //
47 // Create NV index.
48 //
49 if( hmacTest )
50 {
51 // Set NV index's authorization policy
52 // to zero sized policy since we won't be
53 // using policy to authorize. 54
55 authPolicy.t.size = 0; 56 }
57 else
58 {
59
60 // Zero sized encrypted salt, since the session
61 // is unsalted. 62
63 encryptedSalt.t.size = 0; 64
65 // No symmetric algorithm.
66 symmetric.algorithm = TPM_ALG_NULL; 67
68 //
69 // Create the NV index's authorization policy
70 // using a trial policy session.
71 //
72 rval = StartAuthSessionWithParams( &trialPolicySession,
73 TPM_RH_NULL, TPM_RH_NULL, &encryptedSalt,
74 TPM_SE_TRIAL,
75 &symmetric, TPM_ALG_SHA256 );
76 CheckPassed( rval ); 77
78 rval = Tss2_Sys_PolicyAuthValue( sysContext,
79 trialPolicySession.sessionHandle, 0, 0 );
80 CheckPassed( rval ); 81
82 // Get policy digest.
83 rval = Tss2_Sys_PolicyGetDigest( sysContext,
84 trialPolicySession.sessionHandle,
85 0, &authPolicy, 0 );
86 CheckPassed( rval ); 87
88 // End the trial session by flushing it.
89 rval = Tss2_Sys_FlushContext( sysContext,
90 trialPolicySession.sessionHandle );
91 CheckPassed( rval ); 92
93 // And remove the trial policy session from
94 // sessions table.
95 rval = EndAuthSession( &trialPolicySession );
96 CheckPassed( rval ); 97 }
98
99 // Now set the NV index's attributes:
100 // policyRead, authWrite, and platormCreate.
101 *(UINT32 *)( &nvAttributes ) = 0;
102 if( hmacTest )
103 {
104 nvAttributes.TPMA_NV_AUTHREAD = 1;
105 nvAttributes.TPMA_NV_AUTHWRITE = 1;
106 }
107 else
108 {
109 nvAttributes.TPMA_NV_POLICYREAD = 1;
110 nvAttributes.TPMA_NV_POLICYWRITE = 1;
111 }
112 nvAttributes.TPMA_NV_PLATFORMCREATE = 1; 113
114 // Create the NV index.
115 rval = DefineNvIndex( TPM_RH_PLATFORM, TPM_RS_PW,
116 &nvAuth, &authPolicy, TPM20_INDEX_PASSWORD_TEST,
117 TPM_ALG_SHA256, nvAttributes, 32 );
118 CheckPassed( rval ); 119
120 // Add index and associated authorization value to
121 // entity table. This helps when we need
122 // to calculate HMACs.
123 AddEntity( TPM20_INDEX_PASSWORD_TEST, &nvAuth );
124 CheckPassed( rval );
125
126 // Get the name of the NV index.
127 rval = (*HandleToNameFunctionPtr)(
128 TPM20_INDEX_PASSWORD_TEST,
129 &nvName );
130 CheckPassed( rval );
start the hMaC or policy session.
131 //
132 // Start HMAC or real (non-trial) policy authorization session:
133 // it's an unbound and unsalted session, no symmetric
134 // encryption algorithm, and SHA256 is the session's
135 // hash algorithm.
136 //
137
138 // Zero sized encrypted salt, since the session
139 // is unsalted.
140 encryptedSalt.t.size = 0; 141
142 // No symmetric algorithm.
143 symmetric.algorithm = TPM_ALG_NULL; 144
145 // Create the session, hmac or policy depending
146 // on hmacTest.
147 // Session state (session handle, nonces, etc.) gets
148 // saved into nvSession structure for later use.
149 if( hmacTest )
150 tpmSe = TPM_SE_HMAC;
151 else
152 tpmSe = TPM_SE_POLICY; 153
154 rval = StartAuthSessionWithParams( &nvSession, TPM_RH_NULL,
155 TPM_RH_NULL, &encryptedSalt, tpmSe,
156 &symmetric, TPM_ALG_SHA256 );
157 CheckPassed( rval ); 158
159 // Get the name of the session and save it in
160 // the nvSession structure.
161 rval = (*HandleToNameFunctionPtr)( nvSession.sessionHandle,
162 &nvSession.name );
163 CheckPassed( rval );
do an nV write using either an hMaC or a policy authorization.
164 // Initialize NV write data.
165 nvWriteData.t.size = sizeof( dataToWrite );
166 for( i = 0; i < nvWriteData.t.size; i++ )
167 {
168 nvWriteData.t.buffer[i] = dataToWrite[i];
169 }
170
171 //
172 // Now setup for writing the NV index. 173 //
174 if( !hmacTest )
175 {
176 // Send policy command.
177 rval = Tss2_Sys_PolicyAuthValue( sysContext,
178 nvSession.sessionHandle, 0, 0 );
179 CheckPassed( rval );
180 }
181
182 // First call prepare in order to create cpBuffer.
183 rval = Tss2_Sys_NV_Write_Prepare( sysContext,
184 TPM20_INDEX_PASSWORD_TEST,
185 TPM20_INDEX_PASSWORD_TEST, &nvWriteData, 0 );
186 CheckPassed( rval ); 187
188 // Configure command authorization area, except for HMAC.
189 nvCmdAuths.cmdAuths[0]->sessionHandle =
190 nvSession.sessionHandle;
191 nvCmdAuths.cmdAuths[0]->nonce.t.size = 1;
192 nvCmdAuths.cmdAuths[0]->nonce.t.buffer[0] = 0xa5;
193 *( (UINT8 *)(&sessionAttributes ) ) = 0;
194 nvCmdAuths.cmdAuths[0]->sessionAttributes = sessionAttributes;
195 nvCmdAuths.cmdAuths[0]->sessionAttributes.continueSession = 1; 196
197 // Roll nonces for command
198 RollNonces( &nvSession, &nvCmdAuths.cmdAuths[0]->nonce ); 199
200 // Complete command authorization area, by computing
201 // HMAC and setting it in nvCmdAuths.
202 rval = ComputeCommandHmacs( sysContext,
203 TPM20_INDEX_PASSWORD_TEST,
204 TPM20_INDEX_PASSWORD_TEST, &nvCmdAuths,
205 TPM_RC_FAILURE );
206 CheckPassed( rval ); 207
208 // Finally!! Write the data to the NV index.
209 // If the command is successful, the command
210 // HMAC was correct.
211 sessionCmdRval = Tss2_Sys_NV_Write( sysContext,
212 TPM20_INDEX_PASSWORD_TEST,
213 TPM20_INDEX_PASSWORD_TEST,
214 &nvCmdAuths, &nvWriteData, 0, &nvRspAuths );
215 CheckPassed( sessionCmdRval );
Get the response from the nV write. if it's an hMaC session, verify the response hMaC.
216 // Roll nonces for response
217 RollNonces( &nvSession, &nvRspAuths.rspAuths[0]->nonce ); 218
219 if( sessionCmdRval == TPM_RC_SUCCESS )
220 {
221 // If the command was successful, check the
222 // response HMAC to make sure that the
223 // response was received correctly.
224 rval = CheckResponseHMACs( sysContext, sessionCmdRval,
225 &nvCmdAuths, TPM20_INDEX_PASSWORD_TEST,
226 TPM20_INDEX_PASSWORD_TEST, &nvRspAuths );
227 CheckPassed( rval );
228 }
229
230 if( !hmacTest )
231 {
232 // Send policy command.
233 rval = Tss2_Sys_PolicyAuthValue( sysContext,
234 nvSession.sessionHandle, 0, 0 );
235 CheckPassed( rval );
236 }
do an nV read, using an hMaC or a policy session. if it's an hMaC session, verify the response hMaC. Finally, test the read data against the write data to make sure they're equal.
237 // First call prepare in order to create cpBuffer.
238 rval = Tss2_Sys_NV_Read_Prepare( sysContext,
239 TPM20_INDEX_PASSWORD_TEST,
240 TPM20_INDEX_PASSWORD_TEST,
241 sizeof( dataToWrite ), 0 );
242 CheckPassed( rval ); 243
244 // Roll nonces for command
245 RollNonces( &nvSession, &nvCmdAuths.cmdAuths[0]->nonce ); 246
247 // End the session after next command.
248 nvCmdAuths.cmdAuths[0]->sessionAttributes.continueSession = 0; 249
250 // Complete command authorization area, by computing
251 // HMAC and setting it in nvCmdAuths.
252 rval = ComputeCommandHmacs( sysContext,
253 TPM20_INDEX_PASSWORD_TEST,
254 TPM20_INDEX_PASSWORD_TEST, &nvCmdAuths,
255 TPM_RC_FAILURE );
256 CheckPassed( rval ); 257
258 // And now read the data back.
259 // If the command is successful, the command
260 // HMAC was correct.
261 sessionCmdRval = Tss2_Sys_NV_Read( sysContext,
262 TPM20_INDEX_PASSWORD_TEST,
263 TPM20_INDEX_PASSWORD_TEST,
264 &nvCmdAuths, sizeof( dataToWrite ), 0,
265 &nvReadData, &nvRspAuths );
266 CheckPassed( sessionCmdRval ); 267
268 // Roll nonces for response
269 RollNonces( &nvSession, &nvRspAuths.rspAuths[0]->nonce ); 270
271 if( sessionCmdRval == TPM_RC_SUCCESS )
272 {
273 // If the command was successful, check the
274 // response HMAC to make sure that the
275 // response was received correctly.
276 rval = CheckResponseHMACs( sysContext, sessionCmdRval,
277 &nvCmdAuths, TPM20_INDEX_PASSWORD_TEST,
278 TPM20_INDEX_PASSWORD_TEST, &nvRspAuths );
279 CheckPassed( rval );
280 }
281
282 // Check that write and read data are equal.
283 if( memcmp( (void *)&nvReadData.t.buffer[0],
284 (void *)&nvWriteData.t.buffer[0], nvReadData.t.size ) )
285 {
286 printf( "ERROR!! read data not equal to written data " );
287 Cleanup();
288 }
Cleanup: remove the nV index.
|
289 |
||
|
290 |
// |
|
|
291 |
// Now cleanup: undefine the NV index and delete |
|
|
292 |
// the NV index's entity table entry. |
|
|
293 |
// |
|
|
294 |
||
|
295 |
// Setup authorization for undefining the NV index. |
|
|
296 |
nvCmdAuths.cmdAuths[0]->sessionHandle = TPM_RS_PW; |
|
|
297 |
nvCmdAuths.cmdAuths[0]->nonce.t.size = 0; |
|
|
298 |
nvCmdAuths.cmdAuths[0]->hmac.t.size = 0; |
|
|
299 |
||
|
300 |
// Undefine the NV index. |
|
|
301 |
rval = Tss2_Sys_NV_UndefineSpace( sysContext, |
|
|
302 |
TPM_RH_PLATFORM, TPM20_INDEX_PASSWORD_TEST, |
|
|
303 |
&nvCmdAuths, 0 ); |
|
|
304 |
CheckPassed( rval ); |
|
|
305 |
||
|
306 |
// Delete the NV index's entry in the entity table. |
|
|
307 |
rval = DeleteEntity( TPM20_INDEX_PASSWORD_TEST ); |
|
|
308 |
CheckPassed( rval ); |
|
|
309 |
} |
|
I've demonstrated how to send single commands using an HMAC session. Now we need to consider multiple commands and how the nonces work.